Skip to content

Instantly share code, notes, and snippets.

@PercyPham
Last active January 5, 2022 09:38
Show Gist options
  • Save PercyPham/55ed00f76cacf84d69088589ac2b2fe1 to your computer and use it in GitHub Desktop.
Save PercyPham/55ed00f76cacf84d69088589ac2b2fe1 to your computer and use it in GitHub Desktop.
Implementing Json Web Token (JWT) to secure your app

Final Auth flow implementation with JWT

Final implementation of Implementing JSON Web Token (JWT) to secure your app.

Client-Side

Send login request

axios
  .post('/login', {
    username: 'percy',
    password: 'rocktheworld'
  })
  .then(response => {
    const accessToken = response.data.accessToken;
    const refreshToken = response.data.refreshToken;

    storeAccessTokenInLocalStorage(accessToken);
    storeRefreshTokenInLocalStorage(refreshToken);
  })
  .catch(error=> {
    alertLoginFailed(error);
  });

Send other requests with accessToken

const accessToken = getAccessTokenFromLocalStorage();

axios
  .get('/sensitiveInfo', {
    headers: {
      authorization: `Bearer ${accessToken}`
    }
  })
  .then(response => {
    const sensitiveInfo = response.data.sensitiveInfo;
    doStuffWithSensitiveInfo(sensitiveInfo);
  })
  .catch(error=> {
    alertFailed(error);
  });

Send refreshToken request when accessToken expires

const refreshToken = getRefreshTokenFromLocalStorage()

axios
  .post('/refreshToken', {
    refreshToken
  })
  .then(response => {
    const newAccessToken = response.data.accessToken;
    storeAccessTokenInLocalStorage(newAccessToken);
  })
  .catch(error=> {
    alertFailed(error);
  });

Server-Side

Handle /login route

/login route handler implementation:

app.post('/login', async (request, response) => {
  const username = request.body.username;
  const password = request.body.password;

  const foundUser = await findUserInDbByUsername(username);
  if (!foundUser) {
    response.status(400).send('user not found');
    return;
  }

  const isPasswordCorrect = checkIfPasswordIsCorrect(foundUser, password);
  if (!isPasswordCorrect) {
    response.status(400).send('incorrect password');
    return;
  }

  const accessToken = genAccessToken(foundUser);
  const refreshToken = genRefreshTokenToken(foundUser);
  response.status(200).json({ accessToken, refreshToken });
});

genAccessToken implementation:

const FIFTEEN_MINUTES_IN_SECOND = 15 * 60;

function genAccessToken(user) {
  const userId = user.id;
  const role = user.role;
  const type = 'access';

  const tokenPayload = { type, userId, role };

  const accessToken = jwt.sign(
    tokenPayload,
    JWT_SECRET_KEY,
    { expiresIn: FIFTEEN_MINUTES_IN_SECOND }
  );
  return accessToken;
}

genRefreshToken implementation:

function genRefreshToken(user) {
  const userId = user.id;
  const role = user.role;
  const type = 'refresh';

  const password = user.password;
  const key = genKey(userId, password);

  const tokenPayload = { type, userId, role, key };

  const refreshToken = jwt.sign(tokenPayload, JWT_SECRET_KEY);
  return refreshToken;
}

Handle authentication in middleware

function authenticationMiddleware(request, response, nextHandler) {
  const accessToken = getAccessTokenFromHeader(request);

  try {
    const tokenPayload = jwt.verify(accessToken, JWT_SECRET_KEY);
    if (tokenPayload.type !== 'access') throw new Error('wrong token type);

    response.locals.user = tokenPayload;
    nextHandler();
  } catch (error) {
    response.status(401).send(error.message);
  }
}

Handle request required authentication

app.get('/sensitiveInfo', authenticationMiddleware, async (request, response) => {
  const userInfo = response.locals.user;

  if (userInfo.role === 'admin') {
    const sensitiveInfo = await getSensitiveInfoFromDb(userInfo.userId);
    response.status(200).json(sensitiveInfo);
    return;
  }

  response.status(403).send('forbidden request');
});

Note: authenticationMiddleware is before request handler.

Handle /refreshToken route

app.post('/refreshToken', async (request, response) => {
  const refreshToken = request.body.refreshToken;

  try {
    const tokenPayload = jwt.verify(refreshToken, JWT_SECRET_KEY);
    if (tokenPayload.type !== 'refresh') throw new Error('wrong token type');

    const userId = tokenPayload.userId;
    const userInDb = await findUserById(userId);
    const password = userInDb.password;

    const keyToCompare = genKey(userId, password);
    if (keyToCompare !== tokenPayload.key) {
      throw new Error('password changed');
    }

    const newAccessToken = genAccessToken(userInDb);
    response.status(200).json({ accessToken });
  } catch (error) {
    response.status(401).send(error.message);
  }
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment