Security Considerations
Why Token Validation Matters
Section titled “Why Token Validation Matters”A resource server’s primary security responsibility is ensuring that every incoming request carries a valid, non-tampered access token issued by a trusted authorization server. Failures in token validation can lead to:
- Unauthorized access — Attackers accessing protected resources without valid credentials
- Privilege escalation — Tokens with forged scopes granting elevated permissions
- Token replay — Stolen tokens reused from a different context
- Data exfiltration — Compromised tokens used to extract sensitive data
@authrim/server is designed with defense-in-depth principles. Every validation step is implemented to be resistant against known attack vectors.
Built-in Security Features
Section titled “Built-in Security Features”The SDK includes the following security measures by default. These protections are always active and cannot be accidentally disabled.
1. Timing-Safe Comparisons
Section titled “1. Timing-Safe Comparisons”All security-critical string comparisons use constant-time algorithms to prevent timing side-channel attacks:
- Issuer (
iss) validation — Prevents attackers from probing which issuers are trusted - Audience (
aud) validation — Prevents probing of accepted audience values - DPoP thumbprint binding — Prevents inferring expected thumbprint values
Timing-safe comparison ensures that the time taken to compare two strings does not reveal how many characters match, eliminating information leakage.
2. Rejects alg: none
Section titled “2. Rejects alg: none”The SDK rejects any JWT with alg: none in the header. validateToken() reports this as a validation error instead of accepting an unsigned token.
// The SDK automatically rejects this — no configuration needed// Header: { "alg": "none", "typ": "JWT" }// Result: { data: null, error: { code: 'algorithm_mismatch', ... } }This protection cannot be disabled. There is no configuration option to allow unsigned tokens.
3. HTTPS Enforcement
Section titled “3. HTTPS Enforcement”By default, requireHttps: true ensures all external URLs use HTTPS:
- Issuer URL
- JWKS endpoint
- Introspection endpoint
- Revocation endpoint
HTTP URLs are rejected at configuration time, before any network request is made.
4. SSRF Protection (JWKS Redirect Blocking)
Section titled “4. SSRF Protection (JWKS Redirect Blocking)”When fetching JWKS, the SDK blocks cross-origin redirects. This prevents Server-Side Request Forgery (SSRF) attacks where a malicious authorization server redirects the JWKS fetch to an internal network resource.
If the JWKS endpoint returns a redirect to a different origin, the SDK throws an error rather than following the redirect.
5. JWT Size Limit (8 KB)
Section titled “5. JWT Size Limit (8 KB)”The SDK rejects JWTs larger than 8 KB to prevent denial-of-service attacks. Oversized tokens could be used to:
- Exhaust memory during Base64 decoding
- Cause excessive CPU usage during signature verification
- Slow down request processing
Legitimate access tokens are typically well under 2 KB.
6. Single-Flight JWKS Fetch
Section titled “6. Single-Flight JWKS Fetch”When multiple requests arrive simultaneously and trigger a JWKS refresh, the SDK coalesces them into a single network request. This “single-flight” or “thundering herd” prevention:
- Avoids overwhelming the authorization server with parallel JWKS requests
- Ensures consistent key state across concurrent validations
- Reduces latency for all waiting requests
7. Public-Key-Only JWK Import
Section titled “7. Public-Key-Only JWK Import”When processing DPoP proofs, the SDK imports only public key parameters from the JWK in the proof header. Any private key parameters (d, p, q, dp, dq, qi) are rejected.
This prevents:
- Attackers embedding private keys in proofs to trick the server
- Accidental private key leakage through logging or error messages
8. Clock Tolerance for exp / nbf / iat
Section titled “8. Clock Tolerance for exp / nbf / iat”The clockToleranceSeconds setting (default: 60 seconds) provides a buffer for clock skew between the authorization server and your resource server. The SDK applies this tolerance to:
exp(expiration) — Accepts tokens up toclockToleranceSecondspast expirationnbf(not before) — Accepts tokens up toclockToleranceSecondsbefore the not-before timeiat(issued at) — Rejects tokens issued more thanclockToleranceSecondsin the future
9. Request Body Consumption Protection
Section titled “9. Request Body Consumption Protection”Framework middleware adapters handle request body parsing carefully to avoid consuming the body before your application reads it. Token extraction from the Authorization header is header-only and does not interfere with body processing.
10. No Private Key Leakage in DPoP
Section titled “10. No Private Key Leakage in DPoP”The SDK never requires or processes private keys for DPoP validation. Resource servers only need the public key from the DPoP proof header to verify the proof signature and compute the JWK Thumbprint.
Do NOT Checklist
Section titled “Do NOT Checklist”Client Design Guidance
Section titled “Client Design Guidance”When to Create a Confidential Client
Section titled “When to Create a Confidential Client”Some features of @authrim/server require clientCredentials (a client ID and secret). You need a confidential client when:
| Feature | Requires clientCredentials | Why |
|---|---|---|
| Token Validation (JWT) | No | Validated locally using public keys |
| DPoP Validation | No | Validated locally using proof signature |
| Token Introspection | Yes | The authorization server authenticates the caller |
| Token Revocation | Yes | The authorization server authenticates the caller |
Native SSO device_secret introspection/revocation | Yes | Uses the introspection and revocation endpoints with the device_secret hint |
If you only validate JWT access tokens, you do not need client credentials. Register a confidential client only when you need introspection or revocation.
// For JWT validation only — no credentials neededconst authrim = createAuthrimServer({ issuer: 'https://auth.example.com', audience: 'https://api.example.com',});
// For introspection/revocation — credentials requiredconst authrim = createAuthrimServer({ issuer: 'https://auth.example.com', audience: 'https://api.example.com', introspectionEndpoint: 'https://auth.example.com/oauth/introspect', revocationEndpoint: 'https://auth.example.com/oauth/revoke', clientCredentials: { clientId: 'my-resource-server', clientSecret: 'resource-server-secret', },});Audience and Scope Design Patterns
Section titled “Audience and Scope Design Patterns”Design your audience and scope values carefully:
- One audience per API — Use a unique audience URI for each API service (e.g.,
https://api.example.com,https://billing.example.com) - Scope granularity — Use scopes like
read:users,write:orders,admin:settingsrather than broadread/write - Least privilege — Client applications should request only the scopes they need
Token Lifetime Recommendations
Section titled “Token Lifetime Recommendations”Application Responsibilities
Section titled “Application Responsibilities”The SDK handles most security concerns automatically, but some responsibilities remain with your application:
DPoP jti Replay Protection
Section titled “DPoP jti Replay Protection”The SDK validates DPoP proof structure, signature, and binding — but it does not track jti values for replay detection. This is because replay protection requires server-side state (a cache of recently seen jti values), which varies by deployment.
Back-Channel Logout jti Verification
Section titled “Back-Channel Logout jti Verification”Similarly, BackChannelLogoutValidator validates logout token structure and claims but does not track jti values. Your application must:
- Verify the JWT signature (the validator provides the parsed token)
- Check the
jtihas not been seen before - Invalidate the corresponding user session
Error Response Formatting
Section titled “Error Response Formatting”validateToken() returns validation failures as { data: null, error }. Framework middleware adapters format HTTP error responses automatically. If you call validateToken() manually, convert the result into the response shape your API expects:
const result = await authrim.validateToken(accessToken);
if (result.error) { const status = result.error.code === 'insufficient_scope' ? 403 : 401; return res.status(status).json({ error: result.error.code, error_description: result.error.message, });}
return res.json({ sub: result.data.claims.sub });Production Checklist
Section titled “Production Checklist”Before deploying to production, verify the following:
Infrastructure
Section titled “Infrastructure”- HTTPS is enabled — All endpoints (API, issuer, JWKS) use HTTPS.
requireHttpsistrue(default). - Clock synchronization — Your servers use NTP to keep clocks synchronized. Clock drift beyond
clockToleranceSecondscauses false token rejections. - DNS resolution — The issuer hostname resolves correctly and is not vulnerable to DNS spoofing.
Configuration
Section titled “Configuration”- Issuer URL is correct — Matches the
issclaim in tokens exactly (including trailing slash behavior). - Audience value is correct — Matches the
audclaim in tokens. - Clock tolerance is reasonable — Default 60 seconds. Do not exceed 300 seconds.
- JWKS cache TTL is appropriate — Default fallback TTL is 1 hour (
jwksRefreshIntervalMs: 3600000) when the JWKS endpoint does not sendCache-Control: max-age.
Application Code
Section titled “Application Code”- Tokens are never logged — Log
sub,jti,scope, andiatinstead of the raw token string. - Error responses do not leak internal details — Return generic error codes to clients, log details server-side.
- Scopes are enforced per endpoint — Every protected endpoint specifies required scopes.
- DPoP jti replay protection is implemented — If using DPoP, track
jtivalues in a cache (e.g., Redis). - Back-Channel Logout is handled — If using back-channel logout, implement
jtitracking and session invalidation.
Key Rotation
Section titled “Key Rotation”- Key rotation strategy is defined — The authorization server rotates signing keys periodically.
- JWKS cache handles rotation — The SDK auto-retries on
kidnot found, but verify this works in your environment. - Old keys are removed — After a grace period, old keys should be removed from the JWKS to prevent use of compromised keys.
Monitoring
Section titled “Monitoring”- Token validation failures are logged — Monitor for spikes that may indicate attacks.
- JWKS fetch failures are alerted — If the SDK cannot fetch keys, all token validations will fail.
- Clock drift is monitored — Alert if NTP synchronization fails.
Threat Model Summary
Section titled “Threat Model Summary”The following table maps common attacks to the SDK’s built-in defenses:
| Threat | Attack Vector | SDK Defense |
|---|---|---|
| Token forgery | Crafted JWT with alg: none | Rejects alg: none with an algorithm_mismatch validation error |
| Token forgery | JWT signed with wrong key | Signature verification with JWKS kid matching |
| Token replay | Stolen Bearer token | DPoP sender-constraining (application-level jti tracking) |
| Timing attack | Probing issuer/audience values | Timing-safe string comparisons |
| SSRF | Malicious JWKS redirect | Cross-origin redirect blocking |
| DoS | Oversized JWT payload | 8 KB size limit |
| Thundering herd | Concurrent JWKS refetch | Single-flight pattern |
| Key confusion | Private key in DPoP proof | Public-key-only import |
| Expired token | Clock skew exploitation | Configurable clock tolerance with upper bound |
| MitM | Token interception over HTTP | HTTPS enforcement (configurable) |
| Privilege escalation | Missing scope checks | requiredScopes validation on every endpoint |
Understanding this threat model helps you evaluate whether additional application-level security measures are needed for your specific deployment.
Next Steps
Section titled “Next Steps”- Token Validation — Detailed validation API and claims processing
- DPoP Validation — Sender-constrained token verification with replay protection
- JWKS Management — Key caching, rotation, and custom providers
- Introspection & Revocation — Query and revoke tokens at the authorization server