TryMellon
Navigation

JWKS & key rotation

How tryMellon publishes signing keys and how to handle rotation in your validator.

JWKS & key rotation

tryMellon publishes its session-token signing keys at a standards-compliant JWKS endpoint. Your validator caches them, and during rotation the previous and new keys coexist so in-flight tokens stay valid.

Endpoint

GET https://api.trymellonauth.com/.well-known/jwks.json
Cache-Control: public, max-age=3600

Response shape (RFC 7517):

{
  "keys": [
    {
      "kty": "RSA",
      "kid": "k_a4f2…",
      "use": "sig",
      "alg": "RS256",
      "n": "…base64url…",
      "e": "AQAB"
    }
  ]
}

The kid is the RFC 7638 thumbprint of the key — deterministic, so a key has the same kid across deploys.

Token header references the key

Every JWT issued by tryMellon carries a kid in its header that must match an entry in JWKS:

// Header (decoded)
{ "alg": "RS256", "typ": "JWT", "kid": "k_a4f2…" }

Your validator picks the JWK by kid, then verifies with that key’s public components.

Rotation

sequenceDiagram
  participant API as TryMellon
  participant V as Your validator (cached JWKS)
  participant T as Token holder

  Note over API: T+0: rotation initiated
  API->>API: Generate new key (k_b9c3…)
  API->>API: JWKS = [k_a4f2 (previous), k_b9c3 (current)]
  API->>T: New tokens signed with k_b9c3
  V->>API: GET /.well-known/jwks.json (cache miss on k_b9c3)
  API-->>V: Both keys returned
  Note over V: Both old + new tokens verify
  Note over API: T+TTL: previous key removed
  API->>API: JWKS = [k_b9c3]
  T->>V: Old token (k_a4f2) → JWT_KID_MISMATCH

The previous key remains in JWKS for at least the longest valid token TTL (24h by default) so no in-flight token is rejected mid-flight.

Cache strategy

ApproachUse case
jose.createRemoteJWKSet (Node)Default. Refreshes on unknown kid.
jwk.NewCache (Go jwx)Default. TTL refresh + on-demand.
Manual lru_cache(1) (Python)OK for low-throughput. Must invalidate on JWTError with unknown kid.
In-memory cache, no refresh❌ — breaks on rotation.

Rule: if your validator sees a kid not in cache, refresh JWKS once and retry. If still missing, the token is invalid.

Debugging JWT_KID_MISMATCH

# 1. Decode the token header (no verify)
echo "$TOKEN" | cut -d. -f1 | base64 -d | jq .kid

# 2. Compare against current JWKS
curl -s https://api.trymellonauth.com/.well-known/jwks.json | jq '.keys[].kid'

If the token’s kid is missing from JWKS:

  • Token was issued before a rotation completed and is now past its lifetime — re-authenticate.
  • Token was forged or copied from another environment — reject.
  • Your cache is stale — force refresh.