Cyber Intelligence

JWT Complete Guide: Structure, How It Works, and Security

JWT (JSON Web Token) is the token format that powers modern API authentication, OAuth 2.0, and enterprise SSO. This guide explains exactly what a JWT contains, how the three-part structure works, its relationship to OAuth 2.0 and OIDC, how to generate tokens securely, and the attacks that break insecure implementations.

I
Microsoft Cloud Solution Architect
JWT JSON Web Token complete guide infographic showing token structure header payload signature OAuth 2.0 OIDC authentication flow security best practices
JWT JSON Web Token complete guide infographic showing token structure header payload signature OAuth 2.0 OIDC authentication flow security best practices
JWTJSON Web TokenOAuth 2.0OIDCAuthenticationAPI SecurityZero TrustIdentity

What Is JWT? The Token Format That Runs Modern Authentication

JSON Web Token (JWT, pronounced "jot") is an open standard defined in [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519) that describes a compact, URL-safe format for transmitting signed (and optionally encrypted) claims between two parties. Those claims are statements about an entity, usually a user, expressed as a JSON object.

The reason JWT became ubiquitous is simple: it is self-contained and stateless. A server that issues a JWT does not need to store anything. The next server that receives it can verify it independently, without a database lookup, without calling back to the issuer, without a session store. The token carries its own proof of authenticity in its signature.

That property powers the entire modern API ecosystem: microservices authenticating to each other, OAuth 2.0 access tokens, OpenID Connect ID tokens, cross-cloud workload identity, and single sign-on across dozens of applications from a single login.

Inside a JWT: Three Base64url-Encoded Segments

A JWT looks like this in transit:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMyIsImVtYWlsIjoiYWxpY2VAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJhZG1pbiJdLCJpc3MiOiJodHRwczovL2F1dGguZXhhbXBsZS5jb20iLCJhdWQiOiJodHRwczovL2FwaS5leGFtcGxlLmNvbSIsImV4cCI6MTc1MDAwMDAwMH0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Three segments, each separated by a dot. Every segment is Base64url-encoded: URL-safe characters, no padding. The three parts are the Header, the Payload, and the Signature.

The Header

The header declares what the token is and how it is signed:

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "key-2026-01"
}

alg specifies the signing algorithm. kid (key ID) is optional but important for key rotation: it tells the validator which public key to use for verification.

AlgorithmTypeKey TypeRecommended Use
HS256Symmetric HMACShared secretInternal services, simple single-server APIs
RS256Asymmetric RSAPublic/private key pairOAuth 2.0, OIDC, enterprise SSO
ES256Asymmetric ECDSAPublic/private key pairHigh-performance or constrained environments
RS512Asymmetric RSAPublic/private key pairHigh-assurance environments requiring stronger hashing
noneNo signatureNoneNever use in production

The Payload

The payload contains the claims: key-value pairs that assert something about the subject. RFC 7519 defines three categories.

Registered claims are standardized fields every JWT library understands:

ClaimFull NameDescription
`iss`IssuerWho issued the token (URL of the auth server)
`sub`SubjectWho the token is about, usually a user ID
`aud`AudienceWho the token is intended for (the API resource)
`exp`Expiration TimeUnix timestamp after which the token is invalid
`iat`Issued AtUnix timestamp when the token was issued
`nbf`Not BeforeUnix timestamp before which the token is not yet valid
`jti`JWT IDUnique identifier for this specific token

Public claims are registered in the IANA JSON Web Token Claims Registry to avoid collisions across different systems: email, name, groups, roles, phone_number.

Private claims are custom claims agreed upon between issuer and consumer, such as tenant_id, subscription_plan, or department.

A decoded payload looks like this:

{
  "iss": "https://auth.example.com",
  "sub": "user_123",
  "aud": "https://api.example.com",
  "exp": 1750000000,
  "iat": 1749996400,
  "jti": "a8f3d1c2-9b4e-4f7a-b5c0-1234567890ab",
  "email": "alice@example.com",
  "roles": ["admin", "billing"],
  "tenant_id": "acme-corp"
}

Critical point: The payload is Base64url-encoded, not encrypted. Anyone who intercepts the token can read every claim. Never store passwords, API keys, credit card numbers, or any sensitive data in a JWT payload.

The Signature

The signature binds the header and payload together and proves they have not been tampered with. For RS256:

Signature = RSA_SHA256_Sign(
  Base64urlEncode(header) + "." + Base64urlEncode(payload),
  private_key
)

The server signs with its private RSA key. Any validator with the corresponding public key can verify the signature independently, without ever holding the private key. This is what makes JWTs viable in distributed systems: publish your public key at a JWKS endpoint, and every microservice, API gateway, and downstream service can validate tokens without a central session store.

How JWT Authentication Works

sequenceDiagram
    participant User
    participant Client
    participant AuthServer as Auth Server
    participant API as Protected API

    User->>Client: Enter credentials
    Client->>AuthServer: POST /token (credentials or OAuth code)
    AuthServer->>AuthServer: Validate credentials, build claims
    AuthServer->>Client: JWT access token + refresh token
    Client->>Client: Store in HttpOnly cookie
    Client->>API: Request + Authorization: Bearer JWT
    API->>API: Verify signature against JWKS
    API->>API: Check iss, aud, exp, nbf claims
    API->>Client: Protected resource (no DB lookup needed)
    Note over Client,API: Stateless verification at the API layer

The key property highlighted here: the API validates the JWT cryptographically on its own. There is no session store, no database round-trip, and no callback to the auth server per request. Verification is a local cryptographic operation that takes microseconds.

JWT Is Not OAuth 2.0 (But They Work Together)

This is the most common point of confusion in authentication. Here is the precise relationship:

JWT is a token format. It defines how claims are encoded, signed, and transported. It says nothing about how you obtain a token, what authorization flows are involved, or how tokens are revoked.

OAuth 2.0 is an authorization framework (RFC 6749). It defines the flows for delegating access: authorization code, client credentials, device code, and others. The OAuth 2.0 spec says "access token" but intentionally leaves the token format unspecified.

OpenID Connect (OIDC) is an identity layer built on top of OAuth 2.0. OIDC mandates that ID tokens are JWTs because identity assertions need to be cryptographically verifiable. OIDC is what formally connects the two standards.

In practice, most OAuth 2.0 deployments today issue JWT-formatted access tokens because stateless verification scales better than opaque tokens that require token introspection calls. But JWT access tokens are a common implementation choice, not a requirement of the OAuth 2.0 specification itself.

ConceptRoleKey Standard
JWTToken formatRFC 7519
JWSSigned JWTRFC 7515
JWEEncrypted JWTRFC 7516
OAuth 2.0Authorization frameworkRFC 6749
OIDCAuthentication layer on OAuth 2.0OpenID Foundation
RFC 9068JWT profile for OAuth 2.0 access tokensIETF

When you log into a SaaS application with your corporate Google or Microsoft account, you are using OIDC for authentication. The identity provider issues an ID token (always a JWT per OIDC spec) and an access token (often a JWT in practice). The application validates the ID token to confirm who you are, then uses the access token to call APIs on your behalf. All of this runs over OAuth 2.0 authorization flows.

How Organizations Deploy JWT at Scale

API Authentication

Every modern REST API needs to authenticate its callers. JWT is the dominant approach: the client sends Authorization: Bearer <token> with every request, and the API validates the signature and claims without a database call. No session affinity required, infinitely horizontally scalable.

Single Sign-On

Corporate SSO is almost always JWT under the hood. When a user authenticates once with Okta, Microsoft Entra ID, or a similar IdP and then accesses ten internal applications without re-entering credentials, each application receives a JWT from the identity provider. The JWT carries group memberships, roles, and identity claims. Each application validates the token against the IdP's published public key.

Microservices and Service-to-Service Authentication

In a microservices architecture, services need to authenticate both on behalf of users and as themselves. Two patterns dominate: the user's JWT propagates through the call chain (downstream services know who the originating user is), or each service acquires its own machine-level JWT via the client credentials flow. Our guide on [Entra ID workload identity federation](/blog/entra-id-workload-identity-federation-scale) covers the certificate-free approach for Azure workloads, where CI/CD pipelines exchange OIDC tokens for Entra ID access tokens with no stored secret.

Zero Trust and Conditional Access

Zero trust architectures evaluate trust continuously, not just at login. JWTs enable this: claims like amr (authentication method reference), acr (authentication context class reference), and auth_time let resource servers enforce re-authentication requirements, MFA requirements, and device posture checks based on what the token asserts about the authentication event. [Conditional Access policies in Entra ID](/blog/microsoft-entra-id-conditional-access-setup) consume these claims in real time to gate access to sensitive resources.

Generating JWT Securely

Choose the Right Algorithm

For anything beyond an internal prototype, use asymmetric algorithms: RS256 or ES256.

Symmetric HS256 requires every validator to hold the shared secret. A single leaked key compromises the entire system and cannot be rotated without coordinating every validator simultaneously. With RS256, the private key lives in one place (the auth server), and all validators use the public key fetched from the JWKS endpoint.

For new systems, ES256 (ECDSA with P-256) deserves consideration: it produces smaller signatures than RS256 and verifies faster, with equivalent security at its key length.

Key Management

Never hardcode signing keys. Never store them in plain environment variables. Store private keys in a dedicated secrets manager: AWS Secrets Manager, HashiCorp Vault, or [Azure Key Vault](/blog/azure-key-vault-best-practices-2026).

For HS256 (when you must use it), generate secrets with a cryptographically secure random number generator at a minimum of 256 bits:

import secrets

# 32 bytes = 256 bits: minimum viable secret for HS256
secret = secrets.token_bytes(32).hex()

For RS256/ES256, generate a key pair and publish the public key at a JWKS endpoint (/.well-known/jwks.json). Support key rotation by including a kid in the JWT header and maintaining multiple active public keys in your JWKS response during the rotation window.

{
  "keys": [
    {
      "kty": "RSA",
      "use": "sig",
      "kid": "key-2026-06",
      "alg": "RS256",
      "n": "...",
      "e": "AQAB"
    },
    {
      "kty": "RSA",
      "use": "sig",
      "kid": "key-2026-01",
      "alg": "RS256",
      "n": "...",
      "e": "AQAB"
    }
  ]
}

Set Realistic Expiry Times

Token TypeRecommended ExpiryNotes
Access token15 minutesShort window limits theft impact
ID token1 hourBrowser session lifecycle
Refresh token24 hours to 30 daysRotate on each use
Machine-to-machine token1 hour or lessMatch the job or pipeline duration

Validate Every Claim on Receipt

Issuing a signed JWT is only half the equation. The receiving service must validate the full set of claims:

def validate_jwt(token: str, expected_audience: str) -> dict:
    header = decode_header(token)

    # Only accept explicitly whitelisted algorithms
    assert header["alg"] in ALLOWED_ALGORITHMS

    claims = verify_signature(token, get_public_key(header["kid"]))

    assert claims["iss"] == AUTH_SERVER_URL        # Known issuer
    assert expected_audience in claims["aud"]       # Correct audience
    assert claims["exp"] > current_unix_time()      # Not expired
    assert claims.get("nbf", 0) <= current_unix_time()  # Active
    assert claims["jti"] not in used_token_store    # Not replayed

    return claims

Common JWT Attacks

The `alg: none` Attack

If a library accepts "alg": "none", an attacker strips the signature entirely, modifies any claim, and re-encodes the token. The library accepts it as valid. Fix: explicitly whitelist accepted algorithms. Reject any token where alg is not on the list.

Algorithm Confusion (RS256 to HS256)

An attacker takes a valid RS256 token, changes the alg header to HS256, re-signs the modified payload using the server's RS256 public key (which is publicly available at the JWKS endpoint), and submits it. A naive library validates it: the HMAC signature checks out because the public key was used as the HS256 secret. Fix: never let the client-controlled alg header dictate how you validate. Pin the algorithm at the validation layer, not the parsing layer.

Weak Secret Brute Force

HS256 tokens signed with short or predictable secrets are crackable offline. An attacker who captures a single valid token can run it through Hashcat with a wordlist and recover the secret in minutes if it is under 128 bits or a common phrase. Fix: use a cryptographically random secret of at least 256 bits. For anything multi-service, switch to RS256.

Token Stored in localStorage

Storing JWTs in localStorage or sessionStorage makes them readable by any JavaScript on the page, including injected scripts from an XSS vulnerability. A single XSS bug becomes a full credential theft. Fix: store tokens in HttpOnly; Secure; SameSite=Strict cookies. The browser attaches them automatically and JavaScript cannot read them.

Missing Signature Verification

Some codebases decode the JWT payload without verifying the signature because verification requires the public key or secret, which is inconvenient in early development. This code sometimes ships to production. Fix: treat any code path that decodes a JWT without verifying the signature as a critical security defect. There is no safe use case for this in production.

Security Best Practices at a Glance

PracticeWhy It Matters
Use RS256 or ES256Private key never leaves the auth server
Store keys in a secrets managerPrevents exposure via config files or env var leaks
Set 15-minute access token expiryLimits the damage window if a token is stolen
Validate `iss`, `aud`, `exp`, `nbf`Prevents cross-tenant reuse and expired token acceptance
Include `jti` and check for replayBlocks replay attacks on high-value operations
Store tokens in HttpOnly cookiesPrevents XSS-based token exfiltration
Whitelist algorithms explicitlyCloses `alg: none` and algorithm confusion attacks
Rotate signing keys on a scheduleLimits blast radius of a key compromise
Publish JWKS with multiple `kid` valuesEnables zero-downtime key rotation
Never put secrets in the payloadPayload is always readable by anyone with the token

Frequently Asked Questions

A traditional session cookie stores only a random session ID. The server looks up that ID in a session store on every request to find the user data. A JWT embeds the user data directly in the token, signed so it cannot be tampered with. The server validates the signature and reads the claims with no storage lookup. Sessions require centralized state; JWTs are stateless. The trade-off is revocation: revoking a session is immediate (delete the record), revoking a JWT requires waiting for expiry or maintaining a revocation list with a per-request check.

Can JWT be encrypted, and when should I use encryption?

Yes. A JSON Web Encryption (JWE) token encrypts the payload, making it unreadable without the decryption key. The signed-but-not-encrypted format (JWS) is far more common because integrity without confidentiality is sufficient for most authentication scenarios. Use JWE when the payload contains data that must not be visible to intermediaries, such as in federated healthcare or financial services contexts where tokens pass through third-party systems.

How does JWT revocation work if tokens are stateless?

Short expiry times are the primary mechanism: a 15-minute access token becomes invalid quickly without any server action. For immediate revocation (compromised account, forced logout), add a server-side component: a revocation list keyed on the jti claim, or a per-user issued_before timestamp stored in a fast cache. Any incoming token with iat before that timestamp is rejected. This reintroduces minimal state but only for the revocation check, not for normal request processing.

Is JWT part of OAuth 2.0?

OAuth 2.0 (RFC 6749) does not specify a token format, so JWT is not part of the core OAuth spec. However, RFC 9068 defines a standardized JWT profile for OAuth 2.0 access tokens, and OIDC (built on OAuth 2.0) mandates JWT for ID tokens. In modern deployments, the two are practically inseparable: virtually every OAuth 2.0 authorization server issues JWT-formatted access tokens because stateless validation at the resource server scales better than opaque token introspection.

What JWT claims does Microsoft Entra ID include in its access tokens?

Entra ID access tokens include standard claims (iss, sub, aud, exp, iat) plus Microsoft-specific claims: tid (tenant ID), oid (object ID in Entra), roles (app roles assigned to the principal), scp (OAuth scopes granted), amr (authentication methods used, for Conditional Access evaluation), and idtyp (identity type, distinguishing users from service principals and managed identities). The exact claim set depends on the token version (v1.0 or v2.0) and optional claims configured in the app registration manifest.

How is a JWT different from an API key?

An API key is an opaque string that the server looks up in a database on every request to determine who is calling and what permissions they have. It carries no information itself. A JWT is self-describing: it contains identity, permissions, expiry, and issuer information, and its authenticity is verified cryptographically without a database call. API keys are simpler to implement but require centralized validation; JWTs are more complex but enable stateless, distributed verification. For machine-to-machine authentication in modern distributed systems, JWTs issued through the OAuth 2.0 client credentials flow are generally preferred.

Conclusion

JWT is the most widely deployed token format in modern authentication because it solves a concrete architectural problem: how to verify identity in a distributed system without shared session state. Its three-part structure, standardized claim set, and cryptographic signature make it self-contained, independently verifiable, and horizontally scalable.

Understanding the boundaries of what JWT is and what it is not prevents the most common implementation mistakes. JWT is a format, not a framework. OAuth 2.0 is the framework. OIDC is the authentication layer. JWT is what the tokens look like. The security properties come entirely from correct implementation: algorithm selection, key management, claim validation on every request, and safe token storage.

For teams building on Azure, the identity stack extends further: [workload identity federation](/blog/entra-id-workload-identity-federation-scale) eliminates stored credentials by replacing them with OIDC-based JWT exchange for CI/CD pipelines, while [Conditional Access](/blog/microsoft-entra-id-conditional-access-setup) consumes JWT claims in real time to enforce step-up authentication and device posture requirements. JWT is not just an authentication token: it is the connective tissue of modern zero trust identity architecture.

Get weekly security insights

Cloud security, zero trust, and identity guides โ€” straight to your inbox.

I

Microsoft Cloud Solution Architect

Cloud Solution Architect with deep expertise in Microsoft Azure and a strong background in systems and IT infrastructure. Passionate about cloud technologies, security best practices, and helping organizations modernize their infrastructure.

Share this article

Questions & Answers

Related Articles

Need Help with Your Security?

Our team of security experts can help you implement the strategies discussed in this article.

Contact Us