Home / Blogs / Node.js

The JWT checks worth testing on an API

Every request proves who it is. The network it came from is not the proof.

Token auth fails in a short list of ways. Test those ways directly. A green suite that never sends an expired token has not tested auth.

What the token may claim

An access token can carry a subject, a role, and an expiry. It should not carry the id of the record being viewed, and it should not carry a permission that was true at login and might be false now. Role decides which routes exist. The route still loads the row and checks that this subject may see it.

A record can be reassigned while a tab stays open. The token is still valid. The row may no longer belong to that subject. A claim baked in at login would keep showing the file. The database check does not.

What verification rejects

const claims = jwt.verify(token, key, {
  algorithms: ["RS256"],
  audience: "api",
  issuer: "auth",
});

Pin the algorithm list. A token that names a method this API does not use is not verified under some friendlier method. Reject a missing audience. Reject an expired token. Do not add a grace window that accepts a dead token because a clock might be skewed. If clocks are wrong, fix the hosts.

Refresh is a different call, with a different secret. The refresh token is the one you can revoke in a table. Do not put it where a third-party script can read it, and do not write either token into a log line.

Two tests cover most of the bugs

One test calls a real route with an expired token and expects 401. One calls it with a valid token for a user who does not own the row and expects 403. Most auth bugs are variations of those two.

A socket handshake can use the same verification, and then it should stop. A live event is not a side door around the row check. That split is what the event is allowed to contain.

Topics Node.jsSecurity