aichbauer / express-graphql-boilerplate

Express GraphQL API with JWT Authentication and support for sqlite, mysql, and postgresql
MIT License
248 stars 61 forks source link

Feat: refresh token for jwt #32

Open aichbauer opened 6 years ago

aichbauer commented 6 years ago

Currently the access token is valid as long as the expiration time is set to and than the user has to re-authenticate. This means if the token is valid for a long time, and the token gets compromised by a third party this means that this third party can have access as long as they want to a service, unless we change our secret on for the jwt for our service. There are other ways like: blacklisting compromised tokens, but this is only possible if we can check that it is not the real user, that is using this token to get access to our service. So we need to be sure, which is only possible if a user tells us explicitly that this is not him, or we try to create a pattern a for a user, e.g. storing information on IP, Location, OS, browserinfo, etc. but this would need additional db request every time a user makes a request.

If we used a refresh token, we would send a access token, and a refresh token to the client (maybe as a http only cookie?) and store a relation for the refresh token and a user that uses this refresh token. E.g. in the database or in another mutable store on the server.

The access token expires fast e.g 1 minute, 7 minutes, or longer (depending on the service, security risks, etc). This means there is no additional db call within the time this token is valid, as soon as it expires, we check the refresh token and check if its the same that we stored for our user. if so create a new jwt and refresh token and send it to a user, if not clear the refresh token in the store and the user has to re-authenticate e.g. login with credentials or similar.

something similar like

// this is just pseudo code, e.g. accessToken === valid
const auth = (req, res, next) => {
  const accessToken = req.cookies.accessToken;
  const refreshToken = req.cookies.refreshToken;

  if(accessToken && refreshToken) {
    if(accessToken === valid) {
      return next();
    }
    if(refreshToken === valid) {
      const {
        accessToken,
        refreshToken,
      } = createNewToken(); // clear old refreshToken for user, save new refreshToken

       res.cookie('accessToken', accessToken, cookieOptions);
       res.cookie('refreshToken', refreshToken, cookieOptions);

      return next();
    }
  }

  // possible attack
  return res.status(401).json({ msg: 'Unauthorized' });
};
xfo commented 4 years ago

+1

aichbauer commented 4 years ago

@xfo Would you like to implement that feature? Contribution would be appreciated 👍