highLogins and sessions

JWT Without Expiration

JWT tokens are created without an expiration time.

Why it matters

A JWT without expiration is valid forever. If an attacker obtains the token (via XSS, network sniffing, or a leaked log), they have permanent access to the user's account. There is no way to invalidate the token without changing the signing secret (which invalidates ALL tokens).

What it looks like

This is the shape of code that triggers the rule. AI tools produce it because it works, and nothing tells them it is unsafe.

jwt.sign without options
const token = jwt.sign(payload, secret);

The smallest fix

minimal patch
// Add expiresIn to jwt.sign:
const token = jwt.sign(payload, secret, { expiresIn: '1h' });

The better fix

If you have a few more minutes, this is the approach that holds up as the app grows.

safe alternative
// Use short-lived access tokens with refresh tokens:
const accessToken = jwt.sign(payload, secret, { expiresIn: '15m' });
const refreshToken = jwt.sign({ userId }, refreshSecret, { expiresIn: '7d' });

Let your AI tool fix it

When the scanner finds this in your project, it fills in the file and line for you. This is the prompt it gives you to paste into Claude Code, Cursor or whatever you use.

The file [the file] at line [the line number] creates a JWT without expiration: [the flagged code]. Add an expiresIn option to limit token lifetime.

How to check the fix worked

1. Create a token and verify it has an exp claim
2. Create a token with short expiry and verify it expires
3. Verify expired tokens are rejected

Further reading