JWT token is always invalid when verified

Viewed 999

I am new to node.js, express and JWT. I found similar questions here but they didn't help.

I am able to login and store the token in local storage but when I try set Authorization header for another request with the same token, it fails verification in the server. I checked the token both from the server and client, they are exactly the same but the verification is failing, please help !

Here is the code I am using to verify the token.

exports.verify = function(req, res, next) {
    let accessToken = req.headers.authorization
    if (!accessToken){
        return res.status(403).send()
    }
    
    let payload
    try{
        // Never makes it through this

        payload = jwt.verify(accessToken, process.env.ACCESS_TOKEN_SECRET)
        next()
    }
    catch(e){
        
        return res.status(401).json({success: false, message: "token expired or invalid"})
    }
}

Here in app.js file I use the verify function like this for another route.

const { verify } = require('./controllers/auth')

const userRoutes = require('./routes/userRoutes')

app.use('/user', verify, userRoutes)

Where am I going wrong here ?

EDIT:

I added console.log(e) in the verify function, inside the catch() and got the the below result.

TokenExpiredError: jwt expired at /home/shashank/Documents/sms/server/node_modules/jsonwebtoken/verify.js:152:21 at getSecret (/home/shashank/Documents/sms/server/node_modules/jsonwebtoken/verify.js:90:14) at Object.module.exports [as verify] (/home/shashank/Documents/sms/server/node_modules/jsonwebtoken/verify.js:94:10) at exports.verify (/home/shashank/Documents/sms/server/controllers/auth.js:62:23) at Layer.handle [as handle_request] (/home/shashank/Documents/sms/server/node_modules/express/lib/router/layer.js:95:5) at trim_prefix (/home/shashank/Documents/sms/server/node_modules/express/lib/router/index.js:317:13) at /home/shashank/Documents/sms/server/node_modules/express/lib/router/index.js:284:7 at Function.process_params (/home/shashank/Documents/sms/server/node_modules/express/lib/router/index.js:335:12) at next (/home/shashank/Documents/sms/server/node_modules/express/lib/router/index.js:275:10) at cookieParser (/home/shashank/Documents/sms/server/node_modules/cookie-parser/index.js:57:14) { expiredAt: 2020-10-29T17:30:31.000Z }

Let me show my .env file where I store the secret key info

ACCESS_TOKEN_SECRET=swsh23hjddnns
ACCESS_TOKEN_LIFE=3600
REFRESH_TOKEN_SECRET=dhw782wujnd99ahmmakhanjkajikhiwn2n
REFRESH_TOKEN_LIFE=86400

So, the access token has to last an hour right ?

The token is created like below

const jwt = require('jsonwebtoken')

// Not using a database right now.
let users = {
    email: 'myemail@gmail.com',
    password: 'password'
}

exports.login = function(req, res) {

    let email = req.body.email
    let password = req.body.password
    
    // Simple validation !
    if (!email || !password || users.email !== email || users.password !== password){
        return res.status(401).json({success: false, message: "Incorrect username or password"})
    }    


    //use the payload to store information about the user such as username, user role, etc.
    let payload = {email: email}

    //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
    })

    //store the refresh token in the user array
    users.refreshToken = refreshToken

    //send the access token to the client inside a cookie
    // res.cookie("jwt", accessToken, { httpOnly: true})   //secure: false,   use this along with httpOnly: true in production
    
    // res.setHeader('Authorization', accessToken); 
    res.json({
        accessToken: accessToken,
        success: true, message: "Authentication success"
    });
    res.send()

}
1 Answers

It looks like the problem you are experiencing is because of how you are storing the time out period.

From the Documentation for node-jsonwebtoken

expiresIn: expressed in seconds or a string describing a time span zeit/ms. Eg: 60, "2 days", "10h", "7d". A numeric value is interpreted as a seconds count. If you use a string be sure you provide the time units (days, hours, etc), otherwise milliseconds unit is used by default ("120" is equal to "120ms").

Because you are storing in your process.env, it looks like it is translating it to a string, instead of maintaining the integer value.

Test Code:

const jwt = require('jsonwebtoken')
require('dotenv').config();
let payload = {email: 'email'}
let accessToken = jwt.sign(payload, process.env.ACCESS_TOKEN_SECRET, {
    algorithm: "HS256",
    expiresIn: process.env.ACCESS_TOKEN_LIFE
})
console.log(accessToken);
console.log('waiting 4 seconds');
setTimeout(function() {
    let val = jwt.verify(accessToken, process.env.ACCESS_TOKEN_SECRET);
    console.log(val);
}, 4000);

With the following process.env values, it fails

ACCESS_TOKEN_SECRET=swsh23hjddnns
ACCESS_TOKEN_LIFE=3600
REFRESH_TOKEN_SECRET=dhw782wujnd99ahmmakhanjkajikhiwn2n
REFRESH_TOKEN_LIFE=86400

But if we change the ACCESS_TOKEN_LIFE to

ACCESS_TOKEN_LIFE=3600S

It succeeds

Without the time unit, any request that is delayed by more than 3.6 Seconds will error out.

Related