OAuth2: Resource Owner Password Flow using oauth2orize, express 4 and mongoJS

In the last two post I covered the authorization grant flow and the implicit flow. In this one I will cover how to implement the so called Resource Owner Password Flow. With this flow the user is directly sending the username and password to the authorization server and gets an access token with a refresh token back. This flow is less secure than the authorization grant / implicit flow and should only be usable by trusted application. It is, furthermore, a good practice to send a refresh token with the access token to be able to easily retrieve new access tokens when an old one is expired, since then the username/password don’t need to be stored by the application. This flow is especially useful for mobile application, because it does not require the application to redirect to a web page for the authentication of the user.
The implementation of the Resource Owner Password Flow is rather simple; we only need to process one request by the application. The first thing we need to do is authenticate the client. The credentials are usually stored in the authorization header using Basic Auth, but they may also reside in the body. Furthermore, the username and password as well as the grant_type, in this case set to password, must be send within the body.
The following two code snippets show how the client may be authenticated by the authorization server:

passport.use("clientBasic", new BasicStrategy(
    function (clientId, clientSecret, done) {
        db.collection('clients').findOne({clientId: clientId}, function (err, client) {
            if (err) return done(err)
            if (!client) return done(null, false)
            if (!client.trustedClient) return done(null, false)

            if (client.clientSecret == clientSecret) return done(null, client)
            else return done(null, false)
        });
    }
));
passport.use("clientPassword", new ClientPasswordStrategy(
    function (clientId, clientSecret, done) {
        db.collection('clients').findOne({clientId: clientId}, function (err, client) {
            if (err) return done(err)
            if (!client) return done(null, false)
            if (!client.trustedClient) return done(null, false)

            if (client.clientSecret == clientSecret) return done(null, client)
            else return done(null, false)
        });
    }
));

The following one is showing the function which authenticates the user, creates the access and refresh tokens, stores them in the DB and send them back to the client.

server.exchange(oauth2orize.exchange.password(function (client, username, password, scope, done) {
    db.collection('users').findOne({username: username}, function (err, user) {
        if (err) return done(err)
        if (!user) return done(null, false)
        bcrypt.compare(password, user.password, function (err, res) {
            if (!res) return done(null, false)

            var token = utils.uid(256)
            var refreshToken = utils.uid(256)
            var tokenHash = crypto.createHash('sha1').update(token).digest('hex')
            var refreshTokenHash = crypto.createHash('sha1').update(refreshToken).digest('hex')

            var expirationDate = new Date(new Date().getTime() + (3600 * 1000))

            db.collection('accessTokens').save({token: tokenHash, expirationDate: expirationDate, clientId: client.clientId, userId: username, scope: scope}, function (err) {
                if (err) return done(err)
                db.collection('refreshTokens').save({refreshToken: refreshTokenHash, clientId: client.clientId, userId: username}, function (err) {
                    if (err) return done(err)
                    done(null, token, refreshToken, {expires_in: expirationDate})
                })
            })
        })
    })
}))

Now we can already use the given access token for the authentication, however, we also want something to refresh an access token, which is done using the following function:

server.exchange(oauth2orize.exchange.refreshToken(function (client, refreshToken, scope, done) {
    var refreshTokenHash = crypto.createHash('sha1').update(refreshToken).digest('hex')

    db.collection('refreshTokens').findOne({refreshToken: refreshTokenHash}, function (err, token) {
        if (err) return done(err)
        if (!token) return done(null, false)
        if (client.clientId !== token.clientId) return done(null, false)

        var newAccessToken = utils.uid(256)
        var accessTokenHash = crypto.createHash('sha1').update(newAccessToken).digest('hex')

        var expirationDate = new Date(new Date().getTime() + (3600 * 1000))

        db.collection('accessTokens').update({userId: token.userId}, {$set: {token: accessTokenHash, scope: scope, expirationDate: expirationDate}}, function (err) {
            if (err) return done(err)
            done(null, newAccessToken, refreshToken, {expires_in: expirationDate});
        })
    })
}))

Now we can access restricted resources using the access token. The authentication function, using passport, looks as follows:

passport.use("accessToken", new BearerStrategy(
    function (accessToken, done) {
        var accessTokenHash = crypto.createHash('sha1').update(accessToken).digest('hex')
        db.collection('accessTokens').findOne({token: accessTokenHash}, function (err, token) {
            if (err) return done(err)
            if (!token) return done(null, false)
            if (new Date() > token.expirationDate) {
                db.collection('accessTokens').remove({token: accessTokenHash}, function (err) { done(err) })
            } else {
                db.collection('users').findOne({username: token.userId}, function (err, user) {
                    if (err) return done(err)
                    if (!user) return done(null, false)
                    // no use of scopes for no
                    var info = { scope: '*' }
                    done(null, user, info);
                })
            }
        })
    }
))

We just check if the given access token is existing, if it is not expired and if the associated user is existing.
The full example can be found at: https://github.com/reneweb/oauth2orize_resource_owner_password_example

Advertisements

3 thoughts on “OAuth2: Resource Owner Password Flow using oauth2orize, express 4 and mongoJS

  1. Hi,
    Would this “Resource Owner” flow be appropiate for an ajax (can’t use secret) first-party (trusted) app? I read somewhere client authentication could be “dispensed” in those scenarios where the secret couldn’t be kept secret. The implicit flow would be just unacceptable user experience for a first-party app, as is my case. What would be the security tradeoffs?
    Thanks

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s