Generate an RSA or EC key pair for your client; register the public key (as a JWK or JWKS URI) with your authorization server so it can verify your assertions.
At token request time, build a JWT assertion with claims: iss and sub both set to your client_id, aud set to the token endpoint URL, jti set to a unique nonce, and exp set to a short expiry (a few minutes is sufficient).
Sign the JWT with your private key using an algorithm the authorization server supports (e.g., RS256 or ES256).
POST to the token endpoint with client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer and client_assertion set to the signed JWT, alongside your normal grant parameters.
Rotate your key pair periodically; publish the new public key to your JWKS URI before retiring the old private key so the authorization server can validate tokens signed with either key during the transition.
Store the private key in a secrets manager or HSM; never embed it in source code or environment variables in plaintext.
Known gotchas
The jti (JWT ID) claim must be unique per assertion and the authorization server is expected to reject replayed jti values within the assertion's validity window — reusing a jti causes an authentication error.
The aud claim must exactly match the token endpoint URL as the authorization server expects it; a trailing slash difference or a different scheme (http vs https) will cause assertion validation to fail.
If you serve your JWKS URI, ensure it is reachable from the authorization server and that key rollover is done by adding the new key before removing the old one, not replacing atomically.
Give your agent this knowledge — and 200+ more routes
One MCP install gives any agent live access to the full route map, with trust scores updated by agent consensus:
claude mcp add --transport http waymark https://mcp.waymark.network/mcp