9
votes

When using passport in a node.js app as authentication middleware for Oauth 2.0 flows (such as Facebook, Twitter, etc..) I would like to know what are the common/best practices to store access tokens and refresh tokens in the application. I don't need to store the user account in the application, I just need the access token to call the API.

For example if I want to authenticate the user to an OAuth 2.0 authentication provider to get access token to use for oauth-based API, I can use the following passport strategy:

passport.use(new OAuth2Strategy({
    authorizationURL: 'https://www.example.com/oauth2/authorize',
    tokenURL: 'https://www.example.com/oauth2/token',
    clientID: EXAMPLE_CLIENT_ID,
    clientSecret: EXAMPLE_CLIENT_SECRET,
    callbackURL: "http://localhost:3000/auth/example/callback"
  },
  function(accessToken, refreshToken, profile, cb) {
    // handle user profile and tokens
    // [...]
  }
));

How and where to store the tokens in a secure manner? Would be ok to attach the tokens to the user profile? Like so:

function(accessToken, refreshToken, profile, cb) {
    profile.accessToken = accessToken;
    profile.refreshToken = refreshToken;
    process.nextTick(() => return cb(null, profile))
}
1

1 Answers

3
votes

Probably by know you have figured out the answer but sharing my response because this question has some visits and I just got stuck few hours with the same problem as well.

Before I explain how I solve it please notice I'm using passport-azure-ad (strategy) but I guess it will similar for other strategies.

I noticed some folks are storing tokens in cookies, I'm not a security expert but this is probably not the best idea. I opted to store them on User's session, so they are kept server-side as long as the session is open.

There is one fine detail, in order to store data in the session you need to access the req object within the callback verify function of your strategy, there are 6 different prototypes you can use but if you want to access the req object make sure you enable the option "passReqToCallback: true" and use the following prototype:

function(req, iss, sub, profile, jwtClaims, access_token, refresh_token, params, done)

Check the example below:

passport.use(new OIDCStrategy({
  //... all your strategy options
  passReqToCallback: true,
},
  function (**req**, iss, sub, profile, accessToken, refreshToken, done) {
    // store tokens in session
    req.session.accessToken = accessToken; 
    req.session.refreshToken = refreshToken;
... 

Once you session is updated you can access the value any time you need it on server-side.