What is a JWT?

A JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. Despite its name, it's not a complex protocol but a simple string structure. A JWT consists of three parts, separated by dots: header.payload.signature. Each part is base64url encoded.

The Header typically contains metadata about the token, such as the type of token (JWT) and the signing algorithm used (e.g., HMAC SHA256 or RSA). The Payload carries the claims, which are statements about an entity (typically, the user) and additional data. Common claims include user ID, roles, expiration time, and issued-at time. The Signature is crucial for security. It's created by taking the encoded header, the encoded payload, a secret (for symmetric algorithms) or a private key (for asymmetric algorithms), and hashing them using the specified algorithm. This signature verifies that the sender of the JWT is who it says it is and that the message hasn't been altered in transit.

Think of a JWT less like a secure vault and more like a stamped and signed letter. The stamp (header) and the content (payload) are visible and encoded, but the signature at the end guarantees that the letter hasn't been opened and rewritten by someone else since it was sealed by the original sender.

Diagram showing the three parts of a JWT: header, payload, and signature

How JWT Authentication Works

The typical authentication flow using JWTs involves several steps:

  1. User Login: The user provides their credentials (e.g., username and password) to the application.
  2. Server Verification: The server verifies these credentials against its user database.
  3. Token Generation: If the credentials are valid, the server generates a JWT. This token contains claims about the user, such as their unique identifier, roles, and an expiration timestamp. The server then signs this token using a secret key known only to the server.
  4. Token Issuance: The server sends the generated JWT back to the client (e.g., a web browser or mobile app).
  5. Client Storage: The client stores the JWT, typically in local storage or a cookie.
  6. Subsequent Requests: For all subsequent requests to protected resources, the client includes the JWT in the request headers, usually in the Authorization header with the scheme Bearer (e.g., Authorization: Bearer ).
  7. Server Validation: Upon receiving a request with a JWT, the server first verifies the token's signature. It does this by using the same secret key (or public key if asymmetric) to re-compute the signature based on the token's header and payload. If the computed signature matches the signature in the token, the server knows the token is authentic and has not been tampered with.
  8. Claim Verification: If the signature is valid, the server then checks the claims within the payload. This includes verifying that the token has not expired (checking the exp claim) and that it meets any other necessary conditions.
  9. Access Grant: If both the signature and claims are valid, the server grants access to the requested resource and processes the request. If any validation fails, the server rejects the request, typically with a 401 Unauthorized or 403 Forbidden status code.

Key JWT Claims

While the payload can contain any JSON data, certain claims are standard and have specific meanings:

  • iss (Issuer): Identifies the principal that issued the JWT.
  • sub (Subject): Identifies the principal that is the subject of the JWT.
  • aud (Audience): Identifies the recipients that the JWT is intended for.
  • exp (Expiration Time): Identifies the expiration time on or after which the JWT MUST NOT be accepted for processing. This is a crucial security claim.
  • iat (Issued At): Identifies the time at which the JWT was issued.
  • nbf (Not Before): Identifies the time before which the JWT MUST NOT be accepted for processing.
  • jti (JWT ID): Provides a unique identifier for the JWT. This can be used to prevent replay attacks.

Security Considerations and Best Practices

While JWTs offer a stateless authentication mechanism that can simplify backend architecture, they come with inherent security risks if not implemented carefully. Developers must be aware of these pitfalls:

  • Never store sensitive data in the payload: The payload is only base64url encoded, not encrypted. Anyone can decode it and read its contents. Treat the payload as public information.
  • Always verify the signature: This is paramount. Without signature verification, an attacker could forge a token or modify its claims (e.g., changing a user ID or role) and the server would blindly trust it.
  • Use strong, secret keys: The security of symmetric JWTs relies entirely on the secrecy of the key. Use long, random, and complex keys, and manage them securely. Avoid hardcoding keys directly into application code. Rotate keys periodically.
  • Set appropriate expiration times: Tokens should have a reasonably short lifespan to limit the window of opportunity for attackers if a token is compromised. Implement refresh tokens for longer-term user sessions.
  • Validate all relevant claims: Beyond expiration, ensure the token is intended for your application (aud claim) and issued by the correct entity (iss claim).
  • Protect against replay attacks: While jti can help, consider other mechanisms if replay is a significant concern.
  • Avoid the `none` algorithm: Some libraries might allow a `none` algorithm, which means no signature is used. This is extremely insecure and should be explicitly disallowed.
  • Use HTTPS: JWTs are transmitted over the network. Always use HTTPS to encrypt the communication channel and prevent man-in-the-middle attacks.

By understanding the structure, the authentication flow, and adhering to these security best practices, developers can leverage JWTs effectively and safely without the common confusion that often surrounds them.