API authentication: OAuth2, JWT, HMAC
TL; DR
OAuth2/OIDC + JWT is a standard for client applications and server integrations with complex authorization (scopes/roles/tenants), SSO and short TTL tokens.
HMAC signatures are the best choice for webhooks and simple partner calls "server→server" with deterministic verification and strong protection against replay.
Strengthen security: mTLS or DPoP (sender-constrained tokens), short TTL (5-15 min), key rotation (JWKS), refresh tokens with rotation/anti-reuse, strict validation'aud/iss/exp/nbf/kid'and policy-as-code on the gateway.
1) Solution map: what to apply where
2) OAuth2/OIDC: flows and clients
2. 1 Flows
Authorization Code + PKCE (Web/Mobile) - Protects the authorization code from interception.
Client Credentials: server↔server without user; scopes - minimum required.
Device Code: for devices without a browser.
Refresh Token: for trusted clients only; rotate and enable reuse detection.
2. 2 Client types
Confidential (servers, BFF): keep secrets; use mTLS.
Public (SPA/mobile): the secret cannot be stored → PKCE, DPoP, short TTL and limited scopes.
3) JWT: structure, timing, verification
3. 1 Fields (claims)
Mandatory: 'iss', 'sub', 'aud', 'exp', 'iat', 'nbf', 'jti', 'scope '/' permissions', 'tenant' (if multi-lease), 'kid'.
3. 2 Lifetimes
Access token: 5-15 minutes.
Refresh token: days/weeks, rotate with each exchange; when re-submitting the old one, we block the session.
Clock skew: tolerance ≤ 60 s
3. 3 JWKS and keys
Storing keys in KMS/Vault, 'kid' is required.
Two active keys (rolling), reissued once every 60-90 days or in an incident.
JWKS cache on gateway ≤ 5 minutes, with auto-disability.
3. 4 Verification on gateway/services
Check: signature, 'aud' (approved services), 'iss', 'exp/nbf', list of locks (revocation).
Do not trust fields from the body without verifying the signature; ignore'alg = none '.
Authorization: Bearer <JWT>
X-Trace-Id: <uuid>
4) Binding token to client: mTLS, DPoP
mTLS (TLS client certificates): the token is issued and validated only if there is a client certificate → the token is useless outside the key + certificate combination.
DPoP (Demonstration of Proof-of-Possession): the client signs each request with a one-time key → protection against replay and token theft in public clients.
For critical routes (payment mutations) - require one of the mechanisms.
5) Authorization: scopes, roles, ABAC
Scopes - minimum actions ('payments: write', 'payments: status: read').
Roles - units for admins; do not use them directly without scopes.
ABAC - attributes in the token ('tenant', 'country', 'kyc _ level', 'risk _ flags') → policies on gateway/OPA.
Policy at the route/field level (GraphQL) and at the domain operation level (REST/gRPC).
6) HMAC signatures: webhooks and partners
6. 1 Concept
Each integration has its own secret.
Caption above canonical line: 'timestamp + "\n "+ method + "\n" + path + "\n "+ sha256 (body)'
Titles:
X-Signature: v1=base64(hmac_sha256(secret, canonical_string))
X-Timestamp: 2025-11-03T12:34:56Z
X-Event-Id: 01HF...
Time window: ≤ 300 seconds; Reject requests with expired/future'X-Timestamp '.
Idempotence: Store 'X-Event-Id' on TTL (24h) - discard duplicates.
6. 2 Best practices
Secrets in KMS/Vault, rotating every 90 days.
For public clients, HMAC is not suitable (the secret leaks); use OAuth2/DPoP.
7) Protection against replay, brute force and leaks
Nonce/' jti 'for sensitive operations; blacklist of used identifiers.
Rate/quotas: by key/client/tenant/route; "expensive" operations are separate quotas.
IP/ASN/Geo allow-lists for partners and webhooks.
Content-type allow ('application/json'), body size limit.
PII masking in logs; prohibition to log tokens/secrets.
8) Errors and answers (single format)
Error structure:json
{
"error": "invalid_token",
"error_description": "expired",
"trace_id": "4e3f-..."
}
Statuses:
- '401 '- no/invalid token (WWW-Authenticate).
- '403 '- insufficient rights (scope/ABAC).
- '429 '- limits/quotas.
- gRPC: `UNAUTHENTICATED`/`PERMISSION_DENIED`/`RESOURCE_EXHAUSTED`.
9) Term and session policies
Access ≤ 15 min; Refresh - rotation + reuse detection (submitted old - session recall).
Webhooks HMAC: TTL signatures ≤ 5 min; repeated delivery with exponential backoff.
Session/key revocation → immediate entry into the revocation list (cache on the gateway ≤ 1 min).
10) Observability and audit
Correlation by 'trace _ id '/' span _ id'.
Metrics: auth success rate, '401/403/429', OIDC/JWKS latency, rotation frequency, reuse refresh, share of DPoP/mTLS traffic.
Audit log: "who, when, with which 'sub/tenant/scope' caused what," key/secret changes, failed HMAC signatures.
11) Embedding in the Gateway API
JWT validation (JWKS cache) and OPA/ABAC on the gateway.
mTLS profiles for trusted clients/partners.
HMAC verification of webhooks on edge (before internal services).
Rate/Quota policies, circuit-breaker on OIDC provider (cache JWK).
Feature-flags: quick disconnection of the client/key, change of the signature algorithm.
12) Mini snippets
Pseudo: JWT verification
pseudo jwks = cache. getOrFetch(iss + "/.well-known/jwks. json")
key = jwks[kid]
assert verify_signature(jwt, key)
assert aud in ALLOWED_AUDIENCES and iss in TRUSTED_ISSUERS assert now in [nbf-60s, exp+60s]
Pseudo: checking HMAC webhook
pseudo canonical = timestamp + "\n" + method + "\n" + path + "\n" + sha256(body)
sig = base64(hmac_sha256(secret, canonical))
assert abs(now - timestamp) <= 300 assert not seen(event_id)
assert timingSafeEqual(sig, header["X-Signature"].split("v1=")[1])
markSeen(event_id, ttl=86400)
Example scope-policy (OPA/Rego idea)
rego allow {
input. jwt. scope[_] == "payments:write"
input. jwt. tenant == input. route. tenant
}
13) Incident playbooks
Private key leak/JWT-signer: key reissue, JWKS update, immediate disabling of the old ('kid' → deny), disability refresh, forced logout.
Substitution of webhooks: rotation of secrets, IP allow-list, amplification of the 'X-Timestamp' window, repeated delivery of missed events.
Replay/brute-force: enable DPoP/mTLS on critical routes, narrow quotas, temporary blocks by IP/ASN, enable 'jti '-block list.
Outage OIDC: degradation of cached tokens (grace), circuit-breaker provider, customer notification.
14) Implementation checklists
Authentication (minimum):- OAuth2: Code+PKCE (Web/Mobile), Client Credentials (server-to-server)
- TTL: Access ≤ 15 min, Refresh with rotation and reuse detection
- JWKS: two active keys, 'kid', cache ≤ 5 min
- Webhooks: HMAC v1, 'X-Timestamp', 'X-Event-Id', window ≤ 300 sec, idempotency
- Sender-constrained: mTLS/DPoP on critical routes
- ABAC/OPA: scopes + tenant/risk in gateway policies
- Rate/Quota и 429; IP/ASN allow-lists for partners
- Audit and alerts (401/403/429, reuse refresh, HMAC signatures)
- Do not log tokens/secrets/full card bodies
- PII masking; DSAR support; the shelf life of logs is limited
15) Anti-patterns
'alg = none'or trust a token without signature verification/JWKS.
Long-lived access tokens (hours/day).
One common HMAC secret for all partners.
Webhooks without timestamp/idempotency.
Refresh tokens without rotation and without reuse detection.
Lack of 'aud '/' iss' validation and 'kid' rotation.
Storing secrets in environment variables without KMS/Vault.
16) NFT/SLO (landmarks)
OIDC/JWKS availability ≥ 99. 95% (edge cache reduces dependency).
JWT validation additive on gateway ≤ 2-5 ms p95.
Authentication errors ('401') ≤ 0. 5% of total traffic (excluding bots).
Signed webhooks: share of successfully verified ≥ 99. 9%, average delivery delay ≤ 3 s.
Summary
Combine mechanisms: OAuth2/OIDC + JWT for users and rich server scripts, HMAC for webhooks/simple partners, and for critical operations - mTLS/DPoP. Keep short TTLs, key rotations (JWKS), strict ABAC/OPA policies, protect loops from replay and leaks, and automate everything at the API Gateway level. So authentication will become predictable, scalable and secure - without compromises for UX and monetization.