Token Lifecycle Management for Resource Servers
You know the flow: a client presents a token, your resource server must decide whether to trust it. The problem isn’t the idea — it’s implementing every gate correctly without leaking information or leaving doors open for replay attacks.
This post walks through three layers that together form a complete token lifecycle on the resource-server side: introspection with metadata validation (RFC 7662), revocation workflows (RFC 7009), and endpoint security controls. The code is pure Python standard library so you can follow it without dependencies.
Token introspection and metadata validation
When a resource server receives an access token in the Authorization header, it needs three things: proof the token is active, the permissions attached to it, and identity claims for authorization decisions. RFC 7662 defines how to ask an authorization server — the introspection endpoint — exactly this.
The introspection engine runs seven sequential gates before returning a positive response:
- Existence check — the token must exist in the backing store
- Active flag — not revoked by any party
- Expiry (
exp) — current time must not exceed the expiration timestamp - Not-before (
nbf) — the token isn’t valid until itsnbfclaim passes - Issuer verification — the token’s
issmust match a trusted-issuer list - Audience validation — the token’s
audmust include this resource server - Scope enforcement (optional) — fine-grained permission checks
The key insight: all gates are inclusive failures. If any single gate fails, the response is "active": false with an error code. No partial metadata leaks.
A few observations from the run:
- The active token returns its full claim set (client ID, scope, expiration) so downstream logic can make policy decisions.
- When a scope filter is specified (
"admin"), a token with"read write"is correctly rejected — scope matching is substring-aware, not prefix-based. - An expired token triggers early termination at gate 3; the engine marks it stale in-store to prevent future accidental acceptance on cache hits.
- The
nbfboundary test shows tokens created with a future effective date are rejected even before checking expiry or issuer.
The inactive response always returns {"active": false} regardless of which gate failed. This is deliberate: the spec forbids revealing why a token was rejected — that information helps attackers enumerate valid tokens and target specific gates. The resource server should treat all inactive responses uniformly and let auditing capture the real reason.
Token revocation workflow
Revocation is where the lifecycle closes. A user logs out, a device is lost, or an admin suspends access — somewhere in that chain, someone needs to make this token permanently unusable. RFC 7009 standardizes the endpoint and semantics.
A production revocation implementation needs three properties beyond the basic “set active = false” operation:
- Ownership verification — only the token’s client (or an admin) can revoke it. Any other client must be denied.
- Idempotency — revoking an already-revoked token is not an error; it returns a 200 with
"revoked": true. This matters when clients don’t know the token’s current state. - Audit trail — every revocation event, including idempotent no-ops, gets logged with timestamp, initiator, and status.
The run shows each phase:
- First revocation of
tok-100returns{"revoked": true}and the token is immediately dead (verify_token_active→False). - The second revocation of the same token returns
{"revoked": true, "status": "already_revoked"}— no error to the caller, but a different audit status for observability. - Attempting to revoke another client’s token (
tok-200belongs tomobile-client, called byweb-app) is rejected with{"error": "unauthorized_client"}. - Non-existent tokens get
{"error": "invalid_token"}— the same error as gate 1 of introspection, keeping failure modes uniform across endpoints.
Note: once revoked, a token never un-revokes. This is by design in OAuth 2.0. If you need “temporary suspension,” that’s an application-level concept (deactivate the account, not the token). The token itself is consumed on first revocation.
Endpoint security controls
The introspection and revocation endpoints are high-value attack targets: a successful attack grants access to every resource in your system. Beyond token validation logic, these endpoints need their own security layers.
TLS enforcement. Every production deployment of RFC 7662/7009 MUST require TLS. The introspection endpoint itself doesn’t encrypt the token — it validates one — so the channel must be secure by default. Any non-TLS attempt is rejected at the protocol layer, before any application code executes.
Content-Type validation. Per both RFC 7009 and RFC 7662, these endpoints accept POST requests with Content-Type: application/x-www-form-urlencoded. Accepting other content types opens parsing ambiguities — an attacker could send JSON or plain text that your parser misinterprets. Rejecting non-conforming content types is a defense-in-depth measure.
Host header validation. A request arriving at the wrong host (e.g., due to misconfigured DNS, load balancer, or proxy) should be rejected. If your token endpoint lives at auth.example.com, any request targeting evil.attacker.com — even with valid tokens — is a configuration error that must fail closed.
Rate limiting. Introspection endpoints are query-heavy: every protected resource API call may trigger one introspection request. Without limits, a single compromised client can hammer the authorization server, consuming its database or cache layer. A sliding-window counter per-client is straightforward and effective.
From the rate-limiter run: with 10 requests allowed per 60-second window, requests 1–10 succeed (remaining drops from 9 to 0), and requests 11–12 are denied. The counter prunes entries older than the window on every check, so bursty but sparse traffic isn’t unfairly punished.
The nonce ledger prevents replay: a token’s introspection response contains information that, if intercepted, could be replayed by an attacker within the TTL window. Each client’s nonce is tracked for 5 minutes; replaying the same nonce returns false immediately.
Takeaway
Token lifecycle management at a resource server is not one function — it’s three: validate every gate during introspection and never leak which one failed, revoke idempotently with full audit trail, and protect the endpoints themselves with TLS, header validation, and rate limiting. These three layers together close the loop from token issuance to consumption to destruction.