---
# === IDENTITY ===
id: software/patterns/jwt-implementation/2026
canonical_question: "What are the best JWT implementation patterns and pitfalls?"
aliases:
  - "JWT best practices"
  - "JSON Web Token implementation"
  - "JWT security patterns"
  - "how to implement JWT authentication"
  - "JWT signing and verification"
  - "JWT access token refresh token"
  - "JWT algorithm selection RS256 vs HS256"
  - "secure JWT implementation guide"
entity_type: software_reference
domain: software > patterns > jwt_implementation
region: global
jurisdiction: global
temporal_scope: 2020-2026

# === VERIFICATION ===
last_verified: 2026-02-24
confidence: 0.92
version: 1.0
first_published: 2026-02-24

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: stable
  last_breaking_change: "RFC 8725 (Feb 2020) established JWT Best Current Practices; draft-ietf-oauth-rfc8725bis in progress (2025)"
  next_review: 2026-08-23
  change_sensitivity: low

# === CONSTRAINTS ===
constraints:
  - "Always validate the JWT signature before trusting any claims — use verify(), never decode() alone"
  - "Never allow alg:none in production — this disables signature verification entirely (CVE-2015-9235)"
  - "Hard-code the expected algorithm server-side — never trust the alg header from the token (algorithm confusion attack)"
  - "Always validate exp, nbf, iss, and aud claims — missing validation leads to token reuse and privilege escalation"
  - "Use asymmetric algorithms (RS256/ES256) for distributed systems where multiple services verify tokens"
  - "Never store sensitive data (passwords, PII, credit cards) in the JWT payload — it is Base64-encoded, not encrypted"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "Need server-side session management with immediate revocation"
    use_instead: "Server-side sessions with session store (Redis/database) — JWTs cannot be revoked without a denylist"
  - condition: "Need to implement OAuth2 authorization code flow with PKCE"
    use_instead: "software/patterns/oauth2-authorization-code-pkce/2026"
  - condition: "Need encrypted tokens (hiding payload from client)"
    use_instead: "JWE (JSON Web Encryption, RFC 7516) — JWTs only sign, they do not encrypt"

# === AGENT HINTS ===
inputs_needed:
  - key: "use_case"
    question: "What is the primary use case — authentication (access/refresh tokens) or data exchange (signed claims between services)?"
    type: choice
    options: ["authentication", "data_exchange"]
  - key: "signing_algorithm"
    question: "Which signing algorithm do you prefer or require?"
    type: choice
    options: ["HS256", "RS256", "ES256", "EdDSA", "not_sure"]

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/software/patterns/jwt-implementation/2026"
suggested_citation: "Source: knowledgelib.io — AI Knowledge Library (verified 2026-02-24)"

# === RELATED UNITS ===
related_kos:
  related_to:
    - id: "software/patterns/oauth2-authorization-code-pkce/2026"
      label: "OAuth2 Authorization Code with PKCE"
    - id: "software/patterns/session-management/2026"
      label: "Session Management Patterns"
    - id: "software/patterns/sso-saml-oidc/2026"
      label: "SSO with SAML and OpenID Connect"
  solves:
    - id: "software/patterns/rbac-implementation/2026"
      label: "Role-Based Access Control Implementation"
    - id: "software/system-design/auth-system/2026"
      label: "Authentication System Design"
  often_confused_with:
    - id: "software/patterns/opaque-token-auth/2026"
      label: "Opaque Token Authentication — server-side token lookup, not self-contained claims"
    - id: "software/patterns/session-cookies/2026"
      label: "Session Cookies — server-side state, immediate revocation, not stateless tokens"

# === SOURCES (8 authoritative sources) ===
sources:
  - id: src1
    title: "RFC 7519: JSON Web Token (JWT)"
    author: IETF
    url: https://datatracker.ietf.org/doc/html/rfc7519
    type: rfc_spec
    published: 2015-05-01
    reliability: authoritative
  - id: src2
    title: "RFC 8725: JSON Web Token Best Current Practices"
    author: IETF
    url: https://datatracker.ietf.org/doc/html/rfc8725
    type: rfc_spec
    published: 2020-02-01
    reliability: authoritative
  - id: src3
    title: "JSON Web Token Introduction"
    author: Auth0
    url: https://jwt.io/introduction
    type: official_docs
    published: 2024-01-15
    reliability: high
  - id: src4
    title: "JSON Web Token for Java Cheat Sheet"
    author: OWASP
    url: https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html
    type: community_resource
    published: 2024-06-10
    reliability: high
  - id: src5
    title: "Testing JSON Web Tokens"
    author: OWASP WSTG
    url: https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/06-Session_Management_Testing/10-Testing_JSON_Web_Tokens
    type: community_resource
    published: 2024-03-20
    reliability: high
  - id: src6
    title: "Algorithm Confusion Attacks on JWT"
    author: PortSwigger Web Security Academy
    url: https://portswigger.net/web-security/jwt/algorithm-confusion
    type: technical_blog
    published: 2024-09-15
    reliability: high
  - id: src7
    title: "The JWT Handbook"
    author: Auth0
    url: https://auth0.com/resources/ebooks/jwt-handbook
    type: technical_blog
    published: 2023-11-01
    reliability: high
  - id: src8
    title: "JWT Vulnerabilities List: 2026 Security Risks & Mitigation Guide"
    author: Red Sentry
    url: https://redsentry.com/resources/blog/jwt-vulnerabilities-list-2026-security-risks-mitigation-guide
    type: technical_blog
    published: 2026-01-15
    reliability: moderate_high
---

# JWT Implementation Patterns: Complete Reference

## TL;DR

- **Bottom line**: Use short-lived, asymmetrically-signed JWTs (RS256/ES256) for access tokens, pair with opaque refresh tokens stored server-side, and always validate signature + all standard claims (exp, nbf, iss, aud) server-side with a hard-coded algorithm.
- **Key tool/command**: `jose` (Node.js), `PyJWT` (Python), `golang-jwt/jwt` (Go), `jjwt` (Java)
- **Watch out for**: Algorithm confusion attacks -- never trust the `alg` header from the token; hard-code the expected algorithm on the server.
- **Works with**: Any language/platform; RFC 7519 is the universal standard. Libraries exist for every major ecosystem.

## Constraints
<!-- Agents: read this section before recommending any action from this unit.
     These are hard constraints that must not be violated. -->

- Always validate the JWT signature using `verify()`, never `decode()` alone -- unsigned tokens grant full access if only decoded [src2]
- Never allow `alg: "none"` in production -- it completely disables signature verification [src4]
- Hard-code the expected algorithm server-side -- never derive it from the token's `alg` header (prevents algorithm confusion attacks) [src6]
- Always validate `exp`, `nbf`, `iss`, and `aud` claims -- missing any one enables token reuse across services or after expiration [src2]
- Use asymmetric keys (RS256/ES256/EdDSA) when multiple services need to verify tokens -- symmetric keys (HS256) require sharing the secret with every verifier [src7]
- Never store sensitive data (passwords, PII, payment info) in the JWT payload -- it is Base64url-encoded, not encrypted [src3]

## Quick Reference

| Algorithm | Type | Key Size | Performance | Use Case | Security Level |
|---|---|---|---|---|---|
| HS256 | Symmetric (HMAC-SHA256) | 256-bit secret | Fastest | Single-service auth, internal APIs | High (if secret is strong) |
| HS384 | Symmetric (HMAC-SHA384) | 384-bit secret | Fast | Higher security symmetric | High |
| HS512 | Symmetric (HMAC-SHA512) | 512-bit secret | Fast | Maximum symmetric security | High |
| RS256 | Asymmetric (RSA-SHA256) | 2048-bit+ RSA | Slower sign, fast verify | Multi-service, public key distribution | High |
| RS384 | Asymmetric (RSA-SHA384) | 2048-bit+ RSA | Slower | Higher security RSA | High |
| RS512 | Asymmetric (RSA-SHA512) | 2048-bit+ RSA | Slowest RSA | Maximum RSA security | High |
| ES256 | Asymmetric (ECDSA P-256) | 256-bit EC | Fast sign and verify | Mobile, IoT, modern APIs | High |
| ES384 | Asymmetric (ECDSA P-384) | 384-bit EC | Fast | Higher security ECDSA | High |
| ES512 | Asymmetric (ECDSA P-521) | 521-bit EC | Moderate | Maximum ECDSA security | High |
| EdDSA | Asymmetric (Ed25519) | 256-bit EdDSA | Fastest asymmetric | Modern systems, highest performance | High |
| PS256 | Asymmetric (RSASSA-PSS) | 2048-bit+ RSA | Similar to RS256 | Compliance-driven, FIPS | High |
| none | None | N/A | N/A | **NEVER use in production** | None |

### Token Lifetime Reference

| Token Type | Recommended TTL | Storage | Revocable |
|---|---|---|---|
| Access token | 5-15 minutes | Memory / httpOnly cookie | Not without denylist |
| Refresh token | 7-30 days | httpOnly secure cookie / server DB | Yes (delete from DB) |
| ID token (OIDC) | 5-60 minutes | Memory only | Not needed (one-time use) |
| Service-to-service | 5-60 minutes | Env variable / secrets manager | Rotate keys |

## Decision Tree

> Full script: [decision-tree.txt](scripts/decision-tree.txt) (28 lines)

```
START: What is the token for?
├── Authentication (user login)?
│   ├── Single service (monolith)?
│   │   ├── YES → HS256 with strong secret (>= 256 bits)
│   │   │         Access token: 15min, Refresh: 7 days (opaque, server-stored)
# ... (see full script)
```

## Step-by-Step Guide

### 1. Generate signing keys

Choose your algorithm and generate appropriate keys. [src1]

```bash
# RS256: Generate RSA key pair (2048-bit minimum, 4096 recommended)
openssl genrsa -out private.pem 4096
openssl rsa -in private.pem -pubout -out public.pem

# ES256: Generate ECDSA P-256 key pair
openssl ecparam -name prime256v1 -genkey -noout -out ec-private.pem
openssl ec -in ec-private.pem -pubout -out ec-public.pem

# EdDSA: Generate Ed25519 key pair
openssl genpkey -algorithm Ed25519 -out ed-private.pem
openssl pkey -in ed-private.pem -pubout -out ed-public.pem

# HS256: Generate a cryptographically random secret (>= 256 bits)
openssl rand -base64 32
```

**Verify**: `openssl rsa -in private.pem -check` --> expected: `RSA key ok`

### 2. Create and sign a JWT

Build the token with required claims and sign it. [src3]

```python
# Python (PyJWT >= 2.8.0)
import jwt
import datetime

payload = {
    "sub": "user-123",           # Subject (user ID)
    "iss": "https://api.example.com",  # Issuer
    "aud": "https://app.example.com",  # Audience
    "iat": datetime.datetime.now(datetime.timezone.utc),  # Issued at
    "exp": datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(minutes=15),  # Expires
    "nbf": datetime.datetime.now(datetime.timezone.utc),  # Not before
    "roles": ["user", "admin"],  # Custom claims
    "jti": "unique-token-id-abc" # JWT ID (for revocation tracking)
}

# Sign with RS256
with open("private.pem", "r") as f:
    private_key = f.read()

token = jwt.encode(payload, private_key, algorithm="RS256")
```

**Verify**: Paste the token at https://jwt.io -- decoded payload should match your claims.

### 3. Validate and decode on the server

Always verify signature and validate all standard claims. [src2]

```python
# Python (PyJWT >= 2.8.0) — SECURE validation
import jwt

with open("public.pem", "r") as f:
    public_key = f.read()

try:
    decoded = jwt.decode(
        token,
        public_key,
        algorithms=["RS256"],        # Hard-code allowed algorithms!
        audience="https://app.example.com",  # Validate aud
        issuer="https://api.example.com",    # Validate iss
        options={
            "require": ["exp", "iss", "aud", "sub"],  # Require these claims
            "verify_exp": True,
            "verify_nbf": True,
            "verify_iss": True,
            "verify_aud": True,
        }
    )
    user_id = decoded["sub"]
    roles = decoded.get("roles", [])
except jwt.ExpiredSignatureError:
    # Token has expired — client should use refresh token
    pass
except jwt.InvalidAudienceError:
    # Token was not intended for this service
    pass
except jwt.InvalidIssuerError:
    # Token was not issued by a trusted authority
    pass
except jwt.InvalidTokenError:
    # Catch-all: signature invalid, malformed, etc.
    pass
```

**Verify**: Pass an expired token --> should raise `ExpiredSignatureError`

### 4. Implement refresh token rotation

Use opaque refresh tokens stored server-side for revocability. [src7]

```python
import secrets
import hashlib

# Generate opaque refresh token
refresh_token = secrets.token_urlsafe(64)

# Store HASH of refresh token in database (never store plaintext)
refresh_hash = hashlib.sha256(refresh_token.encode()).hexdigest()

# Database row: { user_id, refresh_hash, expires_at, created_at, revoked }
# On refresh: verify hash, issue new access JWT + new refresh token, revoke old one
```

**Verify**: Revoking a refresh token in DB should prevent new access tokens from being issued.

### 5. Set up a JWKS endpoint (asymmetric only)

Publish public keys so verifying services can fetch them dynamically. [src1]

```json
// GET /.well-known/jwks.json
{
  "keys": [
    {
      "kty": "RSA",
      "use": "sig",
      "alg": "RS256",
      "kid": "key-2026-02",
      "n": "<base64url-encoded modulus>",
      "e": "AQAB"
    }
  ]
}
```

**Verify**: `curl https://api.example.com/.well-known/jwks.json | jq '.keys[0].alg'` --> `"RS256"`

## Code Examples

### Python (PyJWT): Full Authentication Middleware

> Full script: [python-pyjwt-full-authentication-middleware.py](scripts/python-pyjwt-full-authentication-middleware.py) (30 lines)

```python
# Input:  HTTP request with Authorization: Bearer <token>
# Output: Authenticated user context or 401 response
# pip install PyJWT>=2.8.0 cryptography>=42.0
import jwt
from functools import wraps
# ... (see full script)
```

### Node.js (jose): Token Creation and Verification

> Full script: [node-js-jose-token-creation-and-verification.js](scripts/node-js-jose-token-creation-and-verification.js) (28 lines)

```javascript
// Input:  User credentials (after authentication)
// Output: Signed JWT access token
// npm install jose@5
import { SignJWT, jwtVerify, importPKCS8, importSPKI } from "jose";
import { readFileSync } from "fs";
# ... (see full script)
```

### Go (golang-jwt/jwt): Middleware Pattern

> Full script: [go-golang-jwt-jwt-middleware-pattern.go](scripts/go-golang-jwt-jwt-middleware-pattern.go) (40 lines)

```go
// Input:  HTTP request with Authorization header
// Output: Validated claims or 401 error
// go get github.com/golang-jwt/jwt/v5
package auth
import (
# ... (see full script)
```

### Java (jjwt): Token Generation and Parsing

> Full script: [java-jjwt-token-generation-and-parsing.java](scripts/java-jjwt-token-generation-and-parsing.java) (30 lines)

```java
// Input:  Authenticated user details
// Output: Signed JWT string
// implementation 'io.jsonwebtoken:jjwt-api:0.12.6'
// runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6'
// runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6'
# ... (see full script)
```

## Anti-Patterns

### Wrong: Using decode() instead of verify()

```python
# BAD -- decode() skips signature verification entirely
payload = jwt.decode(token, options={"verify_signature": False})
user_id = payload["sub"]  # Trusting unverified claims!
```

### Correct: Always use verify() with full validation

```python
# GOOD -- verify() checks signature + all claims
payload = jwt.decode(
    token, public_key,
    algorithms=["RS256"],
    audience="https://app.example.com",
    issuer="https://api.example.com"
)
```

### Wrong: Trusting the alg header from the token

```javascript
// BAD -- attacker can switch alg to "none" or "HS256" with public key as secret
const decoded = jwt.verify(token, key);  // Algorithm derived from token header!
```

### Correct: Hard-code the expected algorithm

```javascript
// GOOD -- algorithm is fixed server-side, ignoring token header
const decoded = jwt.verify(token, publicKey, { algorithms: ["RS256"] });
```

### Wrong: Storing JWTs in localStorage

```javascript
// BAD -- localStorage is accessible to any JS on the page (XSS vulnerable)
localStorage.setItem("token", jwt);
// Any XSS exploit can steal: localStorage.getItem("token")
```

### Correct: Use httpOnly secure cookies

```javascript
// GOOD -- httpOnly cookies are inaccessible to JavaScript
res.cookie("access_token", jwt, {
  httpOnly: true,   // Not accessible via document.cookie
  secure: true,     // HTTPS only
  sameSite: "Strict", // CSRF protection
  maxAge: 900000,   // 15 minutes
  path: "/api",     // Limit scope
});
```

### Wrong: Using symmetric keys in distributed systems

```javascript
// BAD -- sharing HMAC secret with every microservice
// If ANY service is compromised, attacker can forge tokens for ALL services
const token = jwt.sign(payload, "shared-secret-across-10-services", { algorithm: "HS256" });
```

### Correct: Use asymmetric keys with JWKS

```javascript
// GOOD -- only the auth service has the private key
// All other services verify with the public key (cannot forge)
const token = new SignJWT(payload)
  .setProtectedHeader({ alg: "RS256", kid: "key-2026-02" })
  .sign(privateKey);
// Verifiers fetch public key from /.well-known/jwks.json
```

### Wrong: JWTs as long-lived sessions without revocation

```python
# BAD -- 30-day JWT with no revocation mechanism
payload = {"sub": "user-123", "exp": now + timedelta(days=30)}
token = jwt.encode(payload, key, algorithm="RS256")
# User changes password, gets banned, or account is compromised
# --> token is still valid for up to 30 days!
```

### Correct: Short-lived access + revocable refresh

```python
# GOOD -- 15-min access token + server-side refresh token
access_payload = {"sub": "user-123", "exp": now + timedelta(minutes=15)}
access_token = jwt.encode(access_payload, private_key, algorithm="RS256")

# Refresh token is opaque, stored in DB, revocable instantly
refresh_token = secrets.token_urlsafe(64)
db.store_refresh_token(user_id="user-123", token_hash=sha256(refresh_token))
```

## Common Pitfalls

- **Clock skew between servers**: Tokens rejected as expired on servers with slightly different clocks. Fix: Add 30-60 second `leeway` parameter to verification: `jwt.decode(token, key, leeway=30)`. [src2]
- **Missing `kid` (Key ID) in header**: When rotating keys, verifiers cannot determine which key to use. Fix: Always include `kid` in the JWT header and match it against your JWKS endpoint. [src1]
- **Oversized JWT payloads**: Adding too many claims bloats the token beyond cookie size limits (4KB) or URL limits. Fix: Keep JWTs lean -- store user details in a database, reference by `sub`. Token should be < 1KB. [src3]
- **Not rotating signing keys**: Using the same key indefinitely. If compromised, all tokens ever issued are forgeable. Fix: Rotate keys every 90 days, publish previous key in JWKS with a grace period matching max token TTL. [src7]
- **Confusing encoding with encryption**: Base64url encoding is NOT encryption -- anyone can decode the payload. Fix: If confidentiality is needed, use JWE (RFC 7516) on top of JWS, or keep payloads minimal. [src4]
- **Accepting tokens from any issuer**: Not validating `iss` claim allows tokens from unrelated systems to be accepted. Fix: Always validate `iss` against a whitelist of trusted issuers. [src2]
- **Race condition on refresh token rotation**: Two concurrent requests with the same refresh token can both succeed, creating token families. Fix: Implement refresh token family tracking -- if a previously-used refresh token is reused, revoke the entire family. [src7]
- **Using JWT for CSRF tokens**: JWTs are self-contained tokens, not CSRF tokens. Fix: Use a separate, random CSRF token with the Synchronizer Token Pattern or SameSite cookies. [src5]

## Diagnostic Commands

```bash
# Decode a JWT without verification (inspect claims)
echo "eyJhbGciOiJSUz..." | cut -d. -f2 | base64 -d 2>/dev/null | python3 -m json.tool

# Check RSA key pair match (modulus should be identical)
openssl rsa -in private.pem -modulus -noout | openssl md5
openssl rsa -pubin -in public.pem -modulus -noout | openssl md5

# Verify a JWT signature with openssl (RS256)
# Split token into header.payload and signature
TOKEN="eyJhbGci..."
echo -n "${TOKEN%.*}" | openssl dgst -sha256 -verify public.pem -signature <(echo -n "${TOKEN##*.}" | base64 -d)

# Generate a strong HS256 secret (256 bits)
openssl rand -base64 32

# Check token expiration (Unix timestamp)
echo "eyJhbGciOiJSUz..." | cut -d. -f2 | base64 -d 2>/dev/null | python3 -c "import sys,json,datetime; d=json.load(sys.stdin); print('Expires:', datetime.datetime.fromtimestamp(d['exp']))"

# Test JWKS endpoint
curl -s https://api.example.com/.well-known/jwks.json | python3 -m json.tool

# Verify ES256 key curve is P-256
openssl ec -in ec-private.pem -text -noout 2>&1 | grep "ASN1 OID: prime256v1"
```

## Version History & Compatibility

| Standard / Library | Version | Status | Key Changes |
|---|---|---|---|
| RFC 7519 (JWT) | 1.0 | Current (since 2015) | Original JWT specification |
| RFC 7515 (JWS) | 1.0 | Current (since 2015) | JSON Web Signature |
| RFC 7516 (JWE) | 1.0 | Current (since 2015) | JSON Web Encryption |
| RFC 7517 (JWK) | 1.0 | Current (since 2015) | JSON Web Key (JWKS format) |
| RFC 7518 (JWA) | 1.0 | Current (since 2015) | JSON Web Algorithms |
| RFC 8725 (BCP) | 1.0 | Current (since 2020) | JWT Best Current Practices -- security hardening guidance |
| PyJWT (Python) | 2.8+ | Current | `algorithms` param required since 2.x (breaking from 1.x) |
| jsonwebtoken (Node) | 9.x | Current | `algorithms` option enforced by default since 9.x |
| jose (Node) | 5.x | Current | Modern, standards-compliant; recommended over jsonwebtoken for new projects |
| golang-jwt/jwt (Go) | v5.x | Current | Replaced `dgrijalva/jwt-go`; `WithValidMethods()` enforced |
| jjwt (Java) | 0.12.x | Current | Fluent builder API; explicit algorithm selection required |

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Stateless authentication across microservices | Need immediate token revocation (ban user, password change) | Server-side sessions with Redis/DB store |
| Short-lived access tokens (5-15 min TTL) | Storing sensitive user data in the token | Opaque tokens with server-side data store |
| Single Sign-On (SSO) via OpenID Connect | Token needs to exceed 4KB (cookie/header limits) | Reference tokens (opaque ID pointing to server-side data) |
| Service-to-service authentication (M2M) | Client cannot securely store tokens (public terminal) | OAuth2 authorization code flow with backend-for-frontend |
| API authentication where latency matters (no DB lookup) | Need to track active sessions (admin dashboard, concurrent login limit) | Server-side session store with session list |
| Mobile app authentication with offline verification | Regulatory requirement for server-side audit of every active session | Database-backed session tokens |
| Signed data exchange between trusted parties | Token contents must be hidden from the client | JWE (encrypted JWT) or opaque tokens |

## Important Caveats

- JWTs are NOT encrypted by default -- the payload is Base64url-encoded and readable by anyone. Use JWE (RFC 7516) if payload confidentiality is required. [src3]
- The `jsonwebtoken` npm package (v8.x and earlier) was vulnerable to algorithm confusion if `algorithms` was not specified explicitly. Always pin to v9+ and specify `algorithms`. [src6]
- PyJWT 1.x allowed `alg: "none"` by default; PyJWT 2.x requires explicit `algorithms` parameter. Ensure you are on PyJWT >= 2.0 and always pass `algorithms=["RS256"]`. [src4]
- JWTs cannot be truly revoked -- once issued, they are valid until expiration. Build a token denylist (Redis set of `jti` values) if you need emergency revocation. [src2]
- ECDSA signatures (ES256) are non-deterministic -- the same payload signed twice produces different signatures. This is expected behavior, not a bug. [src1]
- EdDSA (Ed25519) support varies by library -- verify your JWT library supports it before choosing this algorithm. `jose` (Node.js) and `PyJWT` with `cryptography` backend support it; older libraries may not. [src8]
- Refresh token rotation MUST be implemented with family tracking -- if a refresh token is used twice, assume compromise and revoke all tokens in the family. [src7]

## Related Units

- [OAuth2 Authorization Code with PKCE](/software/patterns/oauth2-authorization-code-pkce/2026)
- [Session Management Patterns](/software/patterns/session-management/2026)
- [SSO with SAML and OpenID Connect](/software/patterns/sso-saml-oidc/2026)
- [Role-Based Access Control Implementation](/software/patterns/rbac-implementation/2026)
- [Authentication System Design](/software/system-design/auth-system/2026)
