Skip to content

Instantly share code, notes, and snippets.

@dyarfi
Forked from PercyPham/finalImplementations.md
Created June 11, 2020 07:25
Show Gist options
  • Save dyarfi/9be330613735e2c5b83e5be4d3d47922 to your computer and use it in GitHub Desktop.
Save dyarfi/9be330613735e2c5b83e5be4d3d47922 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 authorization in middleware

function authorizationMiddleware(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 authorization

app.get('/sensitiveInfo', authorizationMiddleware, 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: authorizationMiddleware 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