Advertisement
programming Getting “JWT token invalid or expired” in your Node.js authentication system? Learn why JWT fails, how to refresh tokens safely, and best practices to secure your login system.

JWT Token Invalid or Expired” Errors — A Complete Guide for Node.js Authentication

5 Min Read Verified Content

If you’ve ever built a login system using JSON Web Tokens (JWT), you’ve probably seen errors like:

JsonWebTokenError: invalid signature TokenExpiredError: jwt expired Unauthorized: Invalid Token

Users get logged out randomly.
Protected routes fail.
Mobile clients complain that they “keep getting kicked out.”

And you start to wonder…

“Is JWT even reliable?”

Yes — it is. But most JWT errors come from a few common mistakes.

Let’s fix them — cleanly and properly.


How JWT Authentication Actually Works (Simple Explanation)

Login flow:

  1. User logs in

  2. Server generates access token

  3. Token is returned to client

  4. Client includes token in every request

  5. Server verifies token

  6. Access granted

JWT is stateless — meaning the server doesn’t store sessions.
The token itself contains the data.

Example payload:

{ "id": 12, "email": "user@mail.com", "iat": 1735532111, "exp": 1735535711 }

The expiration time is inside the token.

When expired — the browser cannot refresh it magically.


Most Common JWT Problems (And Real Fixes)

1. Tokens Expire Too Fast

Example:

expiresIn: "5m"

Users will constantly get logged out.

Fix — use refresh tokens.

Access token lifetime:

10–30 minutes

Refresh token lifetime:

7–30 days

Why?

  • Short token = safer if stolen

  • Long refresh = better UX


2. Using the Wrong Secret Key

If your login server uses one key
and verification uses another…

JWT will always be invalid.

Make sure:

JWT_SECRET=same_value_everywhere

And NEVER commit it to GitHub.


3. Clock Skew Issues

If server time is off,
tokens may appear expired.

You’ll see users say:

“I just logged in – why am I expired?”

Fix:
Sync server time (NTP)
or add small tolerance.


4. Token Not Sent in the Right Place

Correct way (HTTP header):

Authorization: Bearer your_token_here

Not query strings
Not body
Not cookies (unless secure HTTP-only)


5. Token Changed by Formatting Bugs

Sometimes the frontend trims or corrupts tokens.

Example mistake:

localStorage.setItem("token", JSON.stringify(token));

This adds quotes — breaking the token.


Implementing JWT Correctly in Node.js (Best Practice Flow)

Login — Generate Access + Refresh Token

import jwt from "jsonwebtoken"; const accessToken = jwt.sign( { id: user.id }, process.env.JWT_SECRET, { expiresIn: "15m" } ); const refreshToken = jwt.sign( { id: user.id }, process.env.JWT_REFRESH_SECRET, { expiresIn: "7d" } );

Store refresh token in database (or Redis).


Middleware — Verify Access Token

function auth(req, res, next) { const header = req.headers.authorization; if (!header) return res.sendStatus(401); const token = header.split(" ")[1]; jwt.verify(token, process.env.JWT_SECRET, (err, user) => { if (err) return res.sendStatus(403); req.user = user; next(); }); }

If failed → return 401 or 403, not 500.


Refresh Endpoint — Issue New Access Token

When access token expires,
the frontend calls /refresh.

const newAccess = jwt.sign( { id: user.id }, process.env.JWT_SECRET, { expiresIn: "15m" } );

This way users stay logged in smoothly.


Security Best Practices (Very Important)

Never:

❌ store JWT in localStorage for banking-level apps
❌ allow refresh tokens without rotation
❌ use same secret for both tokens
❌ set token expiry to “forever”

Better:

✔ HTTP-only cookies when possible
✔ rotate refresh tokens
✔ revoke refresh tokens on logout
✔ use HTTPS always

JWT + HTTPS = safe
JWT + HTTP = disaster waiting


Debug Checklist — If JWT Still Fails

Ask yourself:

✔ Is token expired?
✔ Is signature key identical?
✔ Is Authorization header correct?
✔ Is token modified by frontend?
✔ Is time synced on server?
✔ Are tokens from same environment?

90% of bugs come from one of these.


Final Thoughts

JWT isn’t the problem.
Implementation usually is.

Once you separate:

🔐 short-life access tokens
🔄 long-life refresh tokens

…and validate them correctly,
authentication becomes stable and reliable.

Advertisement
Back to Programming