2026-09-29 · 8 min read
- oauth
- jwt
- security
OAuth private-key JWT client authentication and the JWT bearer grant both present a signed JWT to the token endpoint. The assertion identifies the client and is checked for issuer, subject, audience, lifetime, and signature before the authorization server issues a token.
An assertion is a credential. Keep its lifetime short, bind it to the token endpoint that should accept it, and store enough replay state to reject a captured JWT that an attacker resubmits.
Bind the assertion to one token endpoint
Read the authorization server metadata and the client's registered authentication method. Set the audience to the exact value that server requires, commonly the token endpoint URL or the issuer identifier, and reject configuration that copies an API audience into the assertion.
- Match iss and sub to the registered client identifier.
- Pin aud to the documented token-endpoint value.
- Use a dedicated signing key for client authentication.
- Keep authorization-server environments on separate keys.
Keep lifetimes and replay windows small
Set iat to the time the assertion is minted and exp only far enough ahead to absorb normal clock skew and a single token request. A long-lived assertion is a bearer secret that can be replayed until it expires.
Include a unique jti when the server supports replay detection, and do not reuse an assertion across processes or retries unless the profile you implement explicitly allows that reuse.
Rotate signing keys without an outage
Publish the new public key, wait until the authorization server can select it by kid, then switch the signer. During overlap, sign with one current key and keep the previous key only for verification if you also validate assertions locally.
Reject unexpected algorithms, including none, and prefer the algorithm registered for that client. A kid that points at the wrong key family should fail rather than fall through to a shared verification set.
Fail closed at the token endpoint
The token endpoint should validate the signature, issuer binding, audience, time claims, and client registration before it looks at requested scopes or grants. Error responses can name the class of failure without echoing the assertion or the private material that signed it.
Distinguish client authentication from the JWT bearer grant in logs and metrics. They can share signing code and still require different audience and subject rules.
Test claims, keys, and clocks
Use Flashman's JWT decoder for synthetic assertions, the timestamp converter for iat, nbf, and exp, the JSON formatter for token errors, the URL parser to compare audiences with the token endpoint, and the hash tool when you fingerprint a public test key.
Cover wrong audiences, expired and early assertions, reused identifiers, algorithm confusion, key rotation, issuer and subject swaps, clock skew, and both client authentication and the grant if the deployment uses both.