Final implementation of Implementing JSON Web Token (JWT) to secure your app.
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);
});
const accessToken = getAccessTokenFromLocalStorage();
axios
.get('/sensitiveInfo', {
headers: {
authorization: `Bearer ${accessToken}`
}
})
.then(response => {
const sensitiveInfo = response.data.sensitiveInfo;
doStuffWithSensitiveInfo(sensitiveInfo);
})
.catch(error=> {
alertFailed(error);
});
const refreshToken = getRefreshTokenFromLocalStorage()
axios
.post('/refreshToken', {
refreshToken
})
.then(response => {
const newAccessToken = response.data.accessToken;
storeAccessTokenInLocalStorage(newAccessToken);
})
.catch(error=> {
alertFailed(error);
});
/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;
}
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);
}
}
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.
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);
}
});