Sign in

Using the refresh token

Since this is a SSO system it is important to use the refresh token that is given to not force the user to log in when they don't have to. The refresh token is only part of the Auth Code.

What is the correct way to handle the tokens?

When a user completes the login you will get two tokens, an access token and a refresh token. The access token is used to authenticate towards the different apis and is the one that says that a user has access to the service. The access token contains information about when it will expire and can be used to see when you need to get a new token for the user.

The refresh token can only be used to fetch a new access token and refresh token for a user. The refresh token lasts much longer then the access token and should be used to check if you can request a new access token for the user if the access token is no longer valid.

Important information about the refresh token

The refresh token will only work once. This is important to note, so if you try to do several calls at once and use the refresh token in each of them one or more of your requests might fail.

How to code the refresh handle?

Now that we know what the refresh token does it's time to update our code. This example will use the getting started code as a base.

The idea will be to add middleware to our calls to see if the user is authenticated. If so, we proceed. If not, we will re-authenticate them or log them out if they no longer have access.

What we need to do is to check the incoming request. If the request has a session in passport then we need to check if the user is authenticated. If so, the request can continue. If not, the user should be redirected. The request should also just igno

For this setup we will use some third party npm modules which are the following :)

import request from 'superagent';
import moment from 'moment';
import _ from 'lodash'
const authenticateMiddleware = (req, res, next) => {
  if (!_.isEmpty(req.session.passport)) {
    userIsAuthenticated(req).then(() => {
      next();
    }).catch(() => {
      res.redirect('/');
    });
  } else {
    next();
  }
}

As we can see the code above does this small logic check. What we have abstracted away is the logic for userIsAuthenticated which we will now set up.

For a user to be authenticated the access token should not have expired. This will be the first thing we check. Then if it has expired then we need to check if the user is still logged in to the SSO.

const userIsAuthenticated = req => new Promise((resolve, reject) => {
  const profile = req.session.passport.user;

  if (sessionHasExpired(profile)) {
    validateAndInspectUserToken(profile, resolve, reject, req);
  } else {
    resolve();
  }
});

Let's first check if the session is expired. This will simply be a check to see if the current time is greater then the timestamp in the token

const sessionHasExpired = (profile) => {
  if (profile.tokenExpiration) {
    if (moment(moment.utc()).isBefore(profile.tokenExpiration)) {
      return false;
    }
    return true;
  }
  return true;
};

Now let's validate the user's access token and fetch new access and refresh tokens if required

The way we check this is to ask the provider for information on the user. If the information we get back indicates that the user is logged in we add the information to the profile when the token expires, if not we tryFetchingNewToken. If something fails during the check we log the user out from the session setup.

The CLIENTID, CLIENTSECRET, and IDPAPIRUL is constants that has been declared in the inital setup.

const validateAndInspectUserToken = (profile, resolve, reject, req) => {
  request.post(`${IDPAPIURL}/token/introspect`)
    .type('form')
    .send({
      grant_type: 'implicit',
      token: profile.accessToken,
      client_id: CLIENTID,
      client_secret: CLIENTSECRET,
    }).end((err, res) => {
      if (err || !res.ok) {
        req.logout();
        reject();
      } else if (!res.body.active) {
        tryFetchingNewToken(profile).then(() => {
          resolve();
        }).catch(() => {
          reject();
        });
      } else {
        profile.tokenExpiration = res.body.exp * 1000;
        resolve();
      }
    });
};

The code for fetching the new token is just a small call to the provider which can look like this:

const tryFetchingNewToken = profile => new Promise((resolve, reject) => {
  request.post(`${idpApiUrl}/token`)
    .type('form')
    .send({
      grant_type: 'refresh_token',
      refresh_token: profile.refreshToken,
      client_id: CLIENTID,
      client_secret: CLIENTSECRET,
    }).end((err, res) => {
      if (err || !res.ok) {
        reject();
      } else {
        profile.refreshToken = res.body.refresh_token;
        profile.accessToken = res.body.access_token;
        resolve();
      }
    });
});

All that is missing is to start using the new authentication middleware we just wrote.

app.use(authenticateMiddleware);

Now life should be great :D