Skip to main content

jwt

JSON Web Tokens (JWT) are small, signed messages that carry claims (facts) about a user or a system. They let servers trust information without storing session state.

What is a JWT?

  • A compact string that looks like: xxxxx.yyyyy.zzzzz
  • It has three parts:
    • Header: type and signing algorithm (e.g., HS256, RS256)
    • Payload: claims like sub (user id), exp (expires at), role
    • Signature: proves the token was issued by a trusted party and not changed

Example (conceptual):

Header   { "alg": "RS256", "typ": "JWT" }
Payload { "sub": "123", "name": "Rami", "role": "admin", "exp": 1735756800 }
Signature sign(base64url(header) + "." + base64url(payload), privateKey)

Why use JWTs?

  • Stateless auth: server doesn’t keep per-user session in memory/DB
  • Portable: works across services/APIs and clouds
  • Self-contained: includes the info (claims) APIs need to authorize
  • Standard: widely supported in browsers, mobile, and backends

How they’re used (typical flow)

  1. Issue: An auth server checks credentials, then creates a JWT with claims and an expiry (exp), and signs it.
  2. Send: The token is returned to the client (often in an HTTP-only cookie or response body).
  3. Use: The client sends it on each request, usually in the Authorization header as Bearer <token>.
  4. Verify: The API verifies the signature and expiry. If valid, it trusts the claims and serves the request.

Two practical examples

1) User login to call an API

  • User logs in with email/password.
  • Auth service returns a JWT with: sub=userId, role=user, exp=<short TTL>.
  • Browser stores it in an HTTP-only, Secure cookie (or memory).
  • Subsequent API calls include the token via cookie or Authorization: Bearer ....
  • API verifies signature and exp, reads claims (e.g., role) to allow/deny.

Why it helps: No server-side session store. Multiple API instances can validate the same token if they share the signing public key.

2) Service-to-service (machine) calls

  • A backend job or microservice fetches a JWT from an auth server using its client credentials.
  • It calls another internal API with Authorization: Bearer <token>.
  • The receiving service verifies the token with the issuer’s public key and authorizes by scopes/claims (e.g., scope: payments:write).

Why it helps: Secure, verifiable identity between services without sharing passwords.

Good practices

  • Keep tokens short-lived (exp minutes, not days). Refresh with a separate refresh token if needed.
  • Never put secrets or PII in the payload (it’s only base64url-encoded, not encrypted).
  • Prefer asymmetric signing (RS256/ES256): issuer signs with private key; services verify with public key.
  • Pin audience (aud) and issuer (iss) and check them on every request.
  • Plan for logout/revocation: short TTL, rotate keys, and optionally keep a denylist for critical cases.

Common gotchas

  • “Signed” ≠ encrypted. Anyone who sees the token can read its payload.
  • Clock skew: allow a small leeway when validating nbf/exp.
  • Avoid algorithm confusion: don’t accept alg=none; lock your verifier to expected algorithms.