Refresh token how to handle POST routing

Viewed 570

I am new to JWT and tokens for user verification and login. I used the following extensions for Node JS (NPM)

var jwt = require('jsonwebtoken');
const cookieParser = require('cookie-parser')
require('dotenv').config();
// Express ..

I already have a login that checks in MongoDB (Node JS as server) the user, checks email and password and then sets a cookie with access token and refresh token.

My login code is like

//create the access token with the shorter lifespan
let accessToken = jwt.sign(payload, process.env.ACCESS_TOKEN_SECRET, {
    algorithm: "HS256",
    expiresIn: process.env.ACCESS_TOKEN_LIFE
})

//create the refresh token with the longer lifespan
let refreshToken = jwt.sign(payload, process.env.REFRESH_TOKEN_SECRET, {
    algorithm: "HS256",
    expiresIn: process.env.REFRESH_TOKEN_LIFE
})

//send the access token to the client inside a cookie
res.cookie("_login", accessToken, {secure: true, httpOnly: true})
res.send()

and here is the part for refresh token post

exports.refresh = function (req, res, next){

console.log("Test");

let accessToken = req.cookies._login

if (!accessToken){
    return res.status(403).send()
}

let payload
try{
    payload = jwt.verify(accessToken, process.env.ACCESS_TOKEN_SECRET)
 }
catch(e){
    return res.status(401).send()
}

//retrieve the refresh token from the users array
let refreshToken = payload.email.refreshToken

//verify the refresh token
try{
    jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET)
}
catch(e){
    return res.status(401).send()
}

let newToken = jwt.sign(payload, process.env.ACCESS_TOKEN_SECRET, 
{
    algorithm: "HS256",
    expiresIn: process.env.ACCESS_TOKEN_LIFE
})

res.cookie("_login", newToken, {secure: true, httpOnly: true})
res.send()

My question now is, since I see in so many tutorial and guides that they also work with refresh token via POST, how do I process that with the user?

Would the client send an AJAX or post to middleware for check on access token

-> If Access token is expired

--> code automatically takes refresh token and issues a new access token and gives an OK?

or the client sends to middleware where access token is checked

-> Access token is expired (result to the user)

-> client make now post request to /refresh-token result = new access and refresh token

-> And again post request to original middleware with new post request?

What is the procedure here, I can't find any workaround how this is handled.

1 Answers

Keep in mind that my answer is based on my experience. Feel free for anyone to edit if I happen to make a mistake in my ways.

So, in order to handle refresh token, I use this way:

  • When a user successfully logs in, the JWT (containing user's auth) and the refresh token (containing the user's refresh token) will be placed in the user's cookies (same like you).
  • The user will do his/her things inside your web application, until he/she closes the browser without signing out.
  • Remember that JWT always have expiration date - this expiration date will be kept in mind.
  • In every request, you're going to send that specific JWT (that contains the user's auth) and the refresh token to a middleware where you are going to make a POST request. If the JWT has expired, pick the refresh token and call your /refresh-token-result to get a new token. Else, just don't do anything with the refresh token and proceed with your request.
  • Ensure that your /refresh-token-result accepts a request token. The endpoint will check for its validity and will return a new access token.
  • If the refresh token had expired, log out the user. This is for security reasons and this is important!

Oh, and when a user logs out, ensure that both of your user's token and your user's refresh token is revoked properly, usually by changing the cookie value and the expiresIn attribute. For me, I usually change both cookie values to loggedOut and I'll set the expiresIn to five seconds.

Alternatively, if you're using React (an additional answer), you can do it like this:

  • If a user accesses your website, and the JWT expiration date is close to expiry, you can simply use a useEffect() hook in order to renew your access token.

TL;DR: Your second way is already good.

EDIT: Sample pseudocode to help you. Don't copy paste this right away, it most likely wouldn't work, but it should give you the general idea of how things work.

// middleware.js
const { token, refreshToken } = req.cookies;

// 1. If the token has not expired, call 'next()'
// assume 'isExpired' returns boolean: true or false depending on the state of your token.
if (!token.isExpired()) {
  return next();
}

// 2. If the token has expired AND the refreshToken has not expired, issue a new token, THEN call 'next()'
if (token.isExpired() && !refreshToken.isExpired()) {
  await issueToken();
  return next();
}

// 3. Else, logout the user. I'll keep this one short.
await logoutUser();

res.status(401).json({
  status: 'fail',
  message: 'Your access has expired! Please log in again!',
});

And this is your controller.

// controller.js
const getAllComments = async (req, res, next) => {
  const comments = await Comment.find();
  
  res.status(200).json({
    status: 'success',
    data: comments,
  });
}

And, this is what your route should look like.

// this import might be unresolved - keep in mind!
const middleware = require('./middleware');
const getAllComments = require('./controllers');

router.get('/api/v1/comments/', middleware, 
checkToken, getAllComments); // assume checkToken is your function to check for a token's validity.

Keep in mind I did not include error handling to keep this example short.

Related