Articles

JWT Authentication: Best Practices and When to Use It

Learn how JSON Web Tokens work, how signed JWTs handle authentication, and best practices for token storage, expiration, refresh rotation, and XSS/CSRF mitigation. Understand when JWTs are the right choice versus server-side sessions.

Written by:
APin

Senior Technology Analyst • Verified Expert

More from this author
JWT Authentication: Best Practices and When to Use It

Learn how JSON Web Tokens work, how signed JWTs handle authentication, and best practices for token storage, expiration, refresh rotation, and XSS/CSRF mitigation. Understand when JWTs are the right choice versus server-side sessions.

What Is JWT and How Are Signed Tokens Built?

JSON Web Token (JWT) is a standardized format for structuring data to be transmitted between two parties, typically a client and a server. A JWT consists of two primary components: a JSON Object Signing and Encryption (JOSE) header and a payload containing claims. Both components are base64url encoded and concatenated with a period (.) separator. The structure is represented as:

(Header).(Payload)

Base64url encoding is required because raw JSON strings contain UTF-8 characters that are not URL-safe, such as “/” or “&”. By utilizing base64url encoding, tokens can be transmitted reliably via HTTP Authorization headers or URI query parameters without encountering character-related corruption. An example of an unsigned token is:

eyJhbGciOiJub25l4oCdfQ.ewogICJpZCI6ICIxMjM0NTY3ODkwIiwKICAibmFtZSI6ICJKb2huIERvZSIsCiAgImFnZSI6IDM2Cn0

While useful for data transport, unsigned tokens are insecure because they lack integrity verification. To address this, developers use signed tokens, known as JSON Web Signatures (JWS). A JWS adds a third component—a cryptographic signature—resulting in a three-part structure:

(Header).(Payload).(Signature)

The signature is generated by applying a cryptographic algorithm, such as HMAC SHA-256, to the base64url-encoded header and payload. The server uses a secret key to create this hash, which is then appended to the token.

Benefits of signed tokens:

  • Integrity Assurance: The signature allows the receiving server to verify that the claims within the payload have not been tampered with in transit.
  • Stateless Authentication: By embedding user claims directly into the token, servers can authenticate requests without performing redundant database lookups.
  • Secure Credential Management: Signed tokens eliminate the need to store sensitive credentials in client-side storage, as the token serves as a cryptographically verifiable proof of identity.

When implementing JWS, the header specifies the algorithm used (e.g., "alg": "HS256"), ensuring the server knows how to validate the integrity of the incoming data.

How JWTs Are Used in Authentication and Where They Fit in OAuth 2.0 and OIDC

In a simplified JWT authentication flow, the user first signs into an authentication server. The server validates the credentials and returns a signed token — a JSON Web Signature (JWS) — containing account claims or a user ID. The client stores the token in localStorage, sessionStorage, or another location the application prefers. For subsequent authenticated requests, the token is retrieved from storage and presented to the API, typically in the Authorization: Bearer header.

The token itself is a JSON Web Token (JWT), a compact, URL-safe string with three dot-separated segments: a JOSE header, a payload of claims, and a signature. The signature allows the receiving party to verify that the claims were not tampered with after issuance.

JWTs are often discussed alongside OAuth 2.0 and OpenID Connect (OIDC), but these are distinct concepts:

  • JWT is the token format.
  • OAuth 2.0 is an authorization framework; it defines how a client obtains limited access to a protected resource on behalf of a resource owner. OAuth 2.0 does not mandate a token format, though JWTs are commonly used for access tokens.
  • OpenID Connect (OIDC) is an identity layer built on OAuth 2.0; it defines how a client authenticates a user and obtains basic profile information.

OIDC flows issue three token types:

  • ID token — always a JWT; contains claims about the authenticated user (e.g., sub, email, name); intended for the client to establish the user's identity.
  • Access token — used to call protected APIs; often a JWT, but not required to be.
  • Refresh token — used to obtain new access tokens; sent only to the authorization server, never to the API.

A common integration error is using the ID token to authorize API calls. Access tokens are the credential APIs should validate; ID tokens are for the client application's local identity context.

Why Use JWTs? Key Benefits

JSON Web Tokens (JWTs) provide a robust mechanism for securing communication between a client and an authentication server by shifting from stateful, server-side session management to stateless, self-contained claims. By utilizing signed tokens, developers can eliminate insecure client-side practices, such as storing raw user credentials in localStorage.

The security of a JWT relies on the integrity of the signature. When an authentication server issues a token, it uses a cryptographic algorithm—such as HMAC SHA-256—to generate a signature based on the header, the payload (claims), and a secret key. This structure ensures that:

  • Authenticity: Because the signature is created with a secret, random key, only the issuing authentication server can produce valid signatures. Any modification to the token's payload by a third party will invalidate the signature.
  • Integrity: The signature provides a cryptographic guarantee that the claims contained within the token have not been tampered with since issuance.
  • Stateless Verification: Backend services can verify the token’s validity locally without querying a database for every request, provided they possess the correct key or public/private key pair to validate the signature.

Implementing JWTs as signed tokens (JSON Web Signatures or JWSs) facilitates a more efficient authorization flow. Instead of performing a database lookup to retrieve user attributes or session status upon every API request, the server can embed necessary user metadata directly into the JWT payload. Because the signature confirms that this data was authorized by the server, the application can trust the claims immediately upon successful signature verification.

For enterprise-grade implementations, security is maintained by treating the signing key as a protected secret. The efficacy of the HMAC SHA-256 or similar signing algorithms depends entirely on the entropy and secrecy of this key. By leveraging these tokens, engineers can reduce database load and improve application performance while ensuring that sensitive credential data never persists within the client’s browser storage.

Token Storage, Expiration, Refresh Tokens, and Attack Surface

JSON Web Tokens (JWTs) are signed tokens whose claims are protected by a cryptographic signature. Access tokens are sent to APIs for authorization and should be short-lived. Access token expiration limits the lifetime of a token, reducing the window in which a stolen token can be replayed. For example, a 15-minute expiration forces an attacker to use a stolen token quickly, after which it becomes invalid. Refresh tokens are long-lived credentials exchanged for new access tokens; they are never sent to APIs, only to the token endpoint that issues new access tokens. Refresh token rotation strengthens this flow by issuing a new refresh token on each refresh and invalidating the previous one, so a stolen refresh token is detected and rejected the first time it is replayed.

Storage choice directly changes the attack surface. Cross-site scripting (XSS) occurs when an attacker injects a malicious script into a trusted page; that script can read any value accessible to JavaScript. If an access token is stored in localStorage or sessionStorage, an XSS payload can exfiltrate it. Cross-site request forgery (CSRF) occurs when a browser automatically attaches cookies to a request, making a forged request appear legitimate. Cookie-based token storage is therefore more exposed to CSRF if the receiving endpoint does not verify request origin.

  • localStorage: Vulnerable to XSS because any script can read it; not vulnerable to CSRF because tokens are not sent automatically. Tokens persist across reloads, increasing exfiltration impact.
  • HttpOnly cookie: Prevents JavaScript access, mitigating XSS token theft; but cookies are sent automatically, so the API must enforce CSRF defenses such as SameSite, CSRF tokens, or custom headers.
  • In-memory storage: Reduces XSS exposure because tokens vanish on page reload; however, the client must use a refresh token to obtain a new access token after reload.

In practice, use short-lived access tokens in memory and store refresh tokens in an HttpOnly, Secure, SameSite cookie. Pair this with refresh token rotation and server-side revocation checks. This limits access-token exposure while keeping the refresh flow resilient to both XSS and CSRF.

Limitations of JWT: What You Should Know Before Using It

JWTs are not encrypted by default. An unsigned JWT is simply a base64url-encoded header and payload separated by a period; when the header contains "alg":"none", the token has no integrity protection, meaning its claims can be modified without detection. Signed JWTs (JWS) add a cryptographic signature over the header and payload, which ensures that the token has not been tampered with. That signature provides integrity and authenticity, not confidentiality. Anyone who possesses a signed token can base64url-decode the payload and read its claims. For example, a token with the payload {"id":"1234567890","name":"John Doe"} exposes those fields in plaintext. If you need to prevent the contents from being read, you must use JWE (JSON Web Encryption) or another encryption mechanism; plain JWT is not sufficient.

JWTs do not require JavaScript. A JWT is a URL-safe string that can be generated, transmitted, and verified in any programming language. JavaScript is commonly involved because browser-based applications often store and attach tokens, but the token format itself is language-agnostic.

JWTs are also subject to size limits. Because JWTs are transmitted in HTTP headers or query parameters on every request, large claims can push the token beyond the maximum header length accepted by web servers, proxies, or browsers. The JWT should be kept small, containing only essential claims.

Based on these constraints, you should not use JWTs in the following scenarios:

  • When you need immediate revocation: A signed JWT remains valid until it expires. If a user logs out or is banned, you cannot invalidate the token immediately unless you maintain a server-side denylist, which undermines the stateless benefit of JWT.
  • When the payload contains sensitive data: Since signed JWTs are not encrypted, any party with access to the token can read its claims. Avoid putting PII, secrets, or other confidential information in a JWT unless it is encrypted.
  • When token size would grow too large: Storing many roles, permissions, or user attributes in claims makes every request larger and may exceed practical header size limits. In such cases, server-side sessions or opaque tokens may be more appropriate.
  • When server-side control is required: If you need to manage sessions centrally, enforce frequent revocation, or track active sessions, server-side session storage gives you more direct control than a self-contained JWT.

JWTs vs. Server-Side Sessions: Which Should You Choose?

Authentication mechanisms differ fundamentally in where state resides. A server-side session stores state on the server and returns the client an opaque session identifier, typically in a cookie or bearer header. A JSON Web Token (JWT) is a self-contained, signed string composed of a JOSE header, a claims payload, and a cryptographic signature, all base64url encoded. The signature—often HMAC SHA-256—assures the claims have not been tampered with, provided the signing key remains secret.

This distinction drives the trade-offs. Server-side sessions support immediate revocation: invalidating a session is a simple deletion from the session store. JWTs are stateless, so a revoked user remains valid until token expiration unless the API consults a denylist, which reintroduces shared server-side state. When immediate revocation matters—admin consoles, employee-facing systems, or high-security deployments—server-side sessions are typically the better choice.

Storage choices compound the security analysis. Storing JWTs in localStorage or sessionStorage exposes them to cross-site scripting (XSS): any injected script can read and exfiltrate the token. Using httpOnly cookies mitigates XSS but introduces cross-site request forgery (CSRF) considerations, because browsers automatically attach credentials to matching requests.

JWTs excel in distributed architectures. Because claims are embedded and locally verifiable, each service can validate a token without a round-trip to a central session store or database. This makes JWTs well suited to microservice topologies and OAuth 2.0/OpenID Connect (OIDC) flows. In OIDC, the ID token is always a JWT for client-side identity, while the access token—often a JWT—is what protected APIs should validate. A frequent mistake is authorizing API calls with the ID token instead of the access token.

Expiration and refresh mechanics differ as well. Short-lived access tokens bound the window of exposure; refresh tokens, which may be opaque or JWTs, obtain new access tokens. Refresh token rotation—issuing a fresh refresh token on each use—limits replay windows.

Practical guidance:

  • Prefer server-side sessions when you need immediate revocation, centralized session termination, or strict audit control over active sessions.
  • Prefer JWTs for stateless APIs, microservice architectures, or OAuth 2.0/OIDC federated login where eliminating store round-trips outweighs revocation latency.

Editorial Policy & Research Methodology

Our findings are based on rigorous internal research, verified industry benchmarks, and direct technical implementation experience from our enterprise client projects. All statistics and technical claims are reviewed by senior engineers before publication to ensure accuracy, transparency, and helpfulness for our readers.

Have an Idea?

Let's Build Something Amazing Together.