# Verifying JWTs Correctly — Authentication & Authorization

Source: https://www.skillbyai.com/en/authentication/j-verify

> Algorithm allow-lists, claims and key rotation with JWKS.

## Every check, every time

Verification is where most JWT bugs live. Use a maintained library and: **pin an allow-list of algorithms** (never trust the token's `alg`; this blocks `alg: none` and the RS256-to-HS256 confusion attack where the public key is misused as an HMAC secret); verify the **signature** with the right key for the `kid`; check **`exp`** and **`nbf`** with small clock skew; check **`iss`** equals your issuer and **`aud`** includes your API. Fetch issuer public keys from its **JWKS** endpoint (a JSON Web Key Set), cache them and refetch on an unknown `kid`, which makes **key rotation** smooth: the issuer publishes the new key, starts signing with it, and retires the old one after tokens expire. Never use a "decode" function where you meant "verify".

## Verifying with jose and a remote JWKS

The jose library for Node.js; issuer and audience values are examples.

```typescript
import { createRemoteJWKSet, jwtVerify } from 'jose';

const JWKS = createRemoteJWKSet(
  new URL('https://auth.example.com/.well-known/jwks.json'), // cached, refetched on unknown kid
);

export async function authenticate(req: Request, res: Response, next: NextFunction) {
  const header = req.get('authorization') ?? '';
  const token = header.startsWith('Bearer ') ? header.slice(7) : null;
  if (!token) return res.status(401).end();
  try {
    const { payload } = await jwtVerify(token, JWKS, {
      algorithms: ['ES256'],                 // allow-list
      issuer: 'https://auth.example.com',
      audience: 'https://api.example.com',
      clockTolerance: 30,                    // seconds of skew
    });
    req.user = { id: payload.sub!, scope: String(payload.scope ?? '') };
    next();
  } catch {
    res.status(401).end();
  }
}
```

## Validate tokens meant for you

An ID token from your login provider, or an access token for a different API from the same issuer, can carry a valid signature. Checking aud stops a token minted for one audience being replayed against another.

**Quiz:** What does pinning an algorithm allow-list during verification prevent?

- [ ] Key rotation through JWKS
- [ ] Clock skew between servers
- [ ] Tokens being read by the client
- [x] Attacks that switch alg, such as alg none or RS256-to-HS256 confusion

*Answer:* Attacks that switch alg, such as alg none or RS256-to-HS256 confusion. The verifier, not the token, decides which algorithms are acceptable.
