Developer & API

JWT

Also known as: JSON Web Token, Bearer token

4 min read·Updated 2026-05-06

Quick definition

JWT (JSON Web Token) is a compact, URL-safe token format for transmitting authenticated identity claims between parties — typically signed with HMAC or RSA so the receiver can verify the token wasn't tampered with. JWTs are the dominant authentication primitive for REST APIs, including Supabase, Auth0, Clerk, and most modern social platform APIs.

Contents
  1. 1. What is a JWT?
  2. 2. How JWTs work in practice
  3. 3. JWT security pitfalls
  4. API example
  5. Common pitfalls
  6. Tips
  7. FAQ

What is a JWT?

JWT (pronounced 'jot') stands for JSON Web Token — an open standard (RFC 7519) for representing identity claims as a compact, URL-safe string that can be passed around HTTP headers, query params, and cookies. The structure is three Base64URL-encoded parts separated by dots: header.payload.signature. The header declares the signing algorithm (e.g., HS256, RS256). The payload contains claims — user ID, expiration timestamp, scopes, custom fields. The signature is a cryptographic hash of header + payload signed with a secret key (HMAC) or private key (RSA/ECDSA), so the receiver can verify the token wasn't modified after signing.

JWTs solve a specific problem: stateless authentication. Traditional session-based auth requires the server to look up a session ID in a database on every request. JWT lets the server verify identity by validating the signature locally — no database lookup needed. The trade-off: JWTs can't be revoked easily because they're self-contained. The fix is short expiration times (typically 15 minutes for access tokens) paired with longer-lived refresh tokens that can be revoked server-side.

How JWTs work in practice

Standard flow for an API call. (1) User logs in with username + password. The auth server validates credentials, generates a JWT containing the user's ID + expiration + scopes, and signs it with a secret key. The JWT is returned to the client. (2) The client stores the JWT (in localStorage, cookie, or memory) and includes it in every subsequent API request as 'Authorization: Bearer eyJhbGc...' header. (3) The API server receives the request, extracts the JWT, verifies the signature with the same secret key (or paired public key for RSA), checks expiration, and extracts the user ID + scopes from the payload. If the signature is valid and not expired, the request is authenticated. (4) When the JWT expires (typically 15-60 minutes), the client uses a refresh token to get a fresh JWT.

CodivUpload's API supports JWT authentication for dashboard users (via Supabase auth) and API key authentication for programmatic access. The dual-auth pattern is common — JWT for human-facing flows, API keys for server-to-server.

JWT security pitfalls

Three traps that have caused real-world breaches. (1) Storing JWTs in localStorage — vulnerable to XSS attacks. Any malicious script on the page can steal the token. Use httpOnly cookies instead, which JavaScript cannot read. (2) Using the 'none' algorithm — older JWT libraries had a vulnerability where setting algorithm to 'none' bypassed signature verification entirely. Always whitelist accepted algorithms server-side. (3) Long-lived JWTs without refresh — issuing a JWT with a 30-day expiration means a stolen token is valid for 30 days. Use short-lived access tokens (15 min) paired with refresh tokens.

JWT payloads are signed but not encrypted by default — anyone who reads the JWT can decode the payload. Don't put secrets, passwords, or sensitive PII in JWT claims. For sensitive data, use JWE (JSON Web Encryption) or keep sensitive data server-side and reference by ID.

Decode a JWT (debugging only)

typescript

import jwt from "jsonwebtoken";

// Verify and decode (production)
const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";
try {
  const decoded = jwt.verify(token, process.env.JWT_SECRET!, {
    algorithms: ["HS256"], // whitelist algorithms
  });
  console.log(decoded); // { sub: "user_123", exp: 1735689600, ... }
} catch (err) {
  console.error("Invalid or expired token");
}

// Decode without verification (DEBUG ONLY — never trust unverified payloads)
const unsafePayload = jwt.decode(token);
console.log(unsafePayload);

Common pitfalls

  • ×Storing JWTs in localStorage where XSS can steal them — use httpOnly cookies
  • ×Trusting JWT payload without verifying signature — decode != verify
  • ×Issuing long-lived JWTs (days/weeks) without refresh tokens — stolen tokens valid too long
  • ×Putting sensitive data in JWT payload — payload is signed but not encrypted
  • ×Allowing the 'none' algorithm in JWT verification — historical vulnerability vector

Tips

  • Use 15-minute access tokens with 7-day refresh tokens as the default pattern
  • Whitelist accepted algorithms ['HS256'] or ['RS256'] in your verify() call
  • Rotate signing keys periodically — JWT libraries support multiple active keys for rotation windows
  • Use httpOnly + Secure + SameSite=Strict cookies to prevent XSS + CSRF for browser-based JWTs
  • Add a 'jti' (JWT ID) claim if you need server-side revocation lists

Frequently asked questions

What's the difference between JWT and an API key?+

JWTs are time-bounded and self-contained (carry user identity + scopes); API keys are static identifiers looked up in a database. JWTs are typically used for human-user sessions; API keys for programmatic server-to-server access. CodivUpload supports both.

Are JWTs encrypted?+

No, the standard JWT (JWS — JSON Web Signature) is signed but not encrypted. Payload is Base64URL encoded, which anyone can decode and read. For encrypted tokens, use JWE (JSON Web Encryption) — less common in practice.

How do I revoke a JWT?+

You can't revoke a JWT directly — it's self-contained. The standard pattern: issue short-lived JWTs (15 min) and require refresh-token exchange. Revoke the refresh token to lock the user out within 15 minutes.

What's the maximum JWT size?+

No hard limit, but practical limit is around 4-8KB because JWTs travel in HTTP headers and most servers cap header size at 8KB. Keep payload claims minimal.

Should I roll my own JWT library?+

No. Use vetted libraries (jsonwebtoken in Node, jose in modern JS, PyJWT in Python). Rolling your own is a known way to introduce signature-bypass vulnerabilities. JWT correctness is harder than it looks.

Authenticate via JWT or API key — your choice

CodivUpload's REST API supports both JWT (for dashboard users) and API key authentication (for programmatic access). Same endpoints, dual-auth.

See API documentation

Related glossary terms

Back to all 209 glossary terms