Securing REST APIs: Beyond Standard JWT
CodeCrafts Security
Engineering Team
JSON Web Tokens (JWTs) are a reasonable way to carry identity between a client and an API, but a signed token on its own does not make an API secure. Real protection comes from short token lifetimes, a revocable refresh mechanism, strict validation, careful storage and, above all, authorisation checks on every request. This article explains the common weaknesses of a basic JWT setup and the practical measures that address them.
What JWTs Do Well, and Where They Fall Short
A JWT is a signed set of claims, such as a user ID, roles and an expiry time. Because the API can verify the signature locally, it does not need to look up a session in a database on every request, which simplifies scaling across multiple servers and services.
That same statelessness creates the main drawback: a valid JWT cannot easily be revoked before it expires. If a token is stolen, or a user is deactivated, the token keeps working until its exp time. Other common problems are not flaws in the standard itself but in how it is used:
- Long-lived access tokens, sometimes valid for days or weeks.
- Tokens stored in
localStorage, where any cross-site scripting (XSS) flaw can read them. - Sensitive personal data placed in the payload, which is only Base64url-encoded, not encrypted.
- Weak signing secrets or incomplete validation of the token's header and claims.
Short-Lived Access Tokens With Refresh Token Rotation
The most widely used mitigation is to keep access tokens short-lived, typically somewhere between five and fifteen minutes, and pair them with a refresh token. The refresh token is a long, random value that the server stores (ideally as a hash) and can revoke at any time. When the access token expires, the client calls a dedicated endpoint with the refresh token to obtain a new one.
Refresh tokens should be rotated: every time one is used, it is invalidated and a new one is issued. If an old refresh token is ever presented again, that is a strong sign it was stolen, and the server can revoke the entire token family and force the user to sign in again. Rotation with reuse detection is recommended in current OAuth 2.0 security best-practice guidance.
In browser applications, the refresh token is best kept in a cookie marked HttpOnly and Secure, with an appropriate SameSite setting and scoped to the refresh endpoint. Because cookies are sent automatically, you must also consider cross-site request forgery (CSRF) protection for that endpoint.
When you genuinely need instant revocation
Short expiry limits the damage window but does not close it. For events such as password changes, account suspension or a reported compromise, you can maintain a small denylist of revoked token IDs (the jti claim) in a fast store such as Redis, checked until those tokens would have expired anyway. Another option is a per-user "token version" that is incremented on logout-everywhere events. Both reintroduce a lookup, so use them where the security benefit justifies the cost.
Validate Every Token Strictly
Many JWT vulnerabilities come from libraries or code that accept tokens they should reject. Your API should:
- Accept only an explicit allowlist of signing algorithms, and never
none. - Prevent algorithm confusion, for example a server expecting RS256 being tricked into verifying an HS256 token using the public key as an HMAC secret.
- Check the issuer (
iss), audience (aud), expiry (exp) and not-before (nbf) claims, allowing only a small clock skew. - Use strong keys. Where several services verify tokens they did not issue, asymmetric algorithms such as RS256 or ES256 mean verifiers never hold the signing key.
- Support key rotation, typically by publishing public keys at a JWKS endpoint and selecting them by the
kidheader.
Use a well-maintained library rather than hand-written parsing, and keep it updated.
Binding Tokens to the Client
A standard JWT is a bearer token: whoever holds it can use it. Some articles suggest embedding a browser fingerprint in the token to block reuse from another machine. In practice, browser fingerprints are unreliable, change with updates and settings, and can often be imitated, so they should not be treated as a strong control. At most they are one weak signal for anomaly detection.
The standards-based approach is a sender-constrained token. With DPoP (Demonstrating Proof of Possession, RFC 9449), the client holds a private key and signs a proof for each request, so a stolen token is of little use without that key. For service-to-service traffic, mutual TLS certificate-bound tokens (RFC 8705) achieve a similar result. These add complexity and are best reserved for high-value APIs such as financial or health data.
Authentication Is Not Authorisation
A valid token only tells you who is calling. The top risk in the OWASP API Security Top 10 is broken object level authorisation: an authenticated user changes an ID in a URL, such as /api/patients/1042, and receives someone else's record because the server never checked ownership. No token scheme prevents this. Every endpoint must verify that the caller is allowed to access the specific resource and perform the specific action, ideally through a central, well-tested authorisation layer rather than scattered checks.
Defence in Depth for Production APIs
Token handling is one layer among several. A secure API also needs:
- TLS everywhere, including internal service calls where practical, with HSTS on public endpoints.
- Input validation against a schema, rejecting unexpected fields to prevent mass assignment.
- Rate limiting on login, OTP and refresh endpoints to slow brute-force and credential-stuffing attacks.
- Minimal responses that return only the fields the client needs.
- Logging and monitoring of authentication failures, refresh token reuse and unusual access patterns, without writing tokens or passwords to logs.
- A correct CORS policy, remembering that CORS controls browser behaviour and is not an access control mechanism.
- Secrets management that keeps signing keys out of source code and rotates them on a schedule.
Key Takeaways
- Keep access tokens short-lived and use rotated, revocable refresh tokens.
- Store browser tokens in hardened cookies rather than
localStorage, and protect against CSRF. - Validate algorithms and claims strictly, using a maintained library.
- Use DPoP or mutual TLS when you need tokens bound to a client; do not rely on browser fingerprinting.
- Check authorisation for every object and action, because a valid token is not permission.
How CodeCrafts Secures the APIs We Build
Many of the systems CodeCrafts builds handle sensitive information, from student records to patient data in our hospital management software. We design authentication and authorisation together from the start: short-lived access tokens, rotated refresh tokens, role-based and record-level permission checks, and audit logging of sensitive actions. We review APIs against the OWASP API Security Top 10 and choose stronger measures such as sender-constrained tokens where the risk warrants them. If you would like a second look at the security of an existing API or are planning a new platform, talk to our team.
More from the CodeCrafts blog
- Hospital Management Software for Small Hospitals: A Practical Buying Guide
- Scaling Enterprise Backends in 2025: Why .NET Core Still Reigns Supreme
- React vs. Blazor WebAssembly: Choosing the Right Frontend Paradigm
- Zero-Downtime Deployments on Azure: A Practical Guide
Looking for ready-made software? See our school & college ERP and hospital management system.