key-cape/docs/online-token-status.md
tegwick 08c083bae9
Some checks failed
Authentication acceptance / acceptance (push) Failing after 59s
Authentication acceptance / provider-contract (push) Successful in 13s
Build and Publish Container Image / build-and-push (push) Successful in 31s
Share current-token verification and scoped cleanup through one producer client
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a14bfc-9643-7872-aa12-98326dcf25e7
2026-10-18 11:36:26 +02:00

7.3 KiB

Optional online access-token status

Source proposal under KEY-WP-0042, driven by CUST-WP-0080 and existing NK-WP-0048-T34/T42/T43 and RAPPS-WP-0016-T08/T09. Independent issuer/consumer contract review and native admission remain required. Existing deployments are unchanged. This extends the bounded issuer; it does not introduce a broker, approval store, PDP, or a second application-local token registry.

The legacy profile authenticates self-contained JWTs until expiry. That does not establish that an operation's initiating token is still unrevoked. The optional profile adds issuer-owned current access-token status using RFC 7662 introspection and RFC 7009 revocation. It supports access tokens only, without refresh tokens or token exchange.

Enablement and lifecycle

Owner-admitted configuration opts in explicitly:

tokenStatus:
  enabled: true
  maxEntries: 10000

The issuer must use HTTPS at its existing trusted ingress. The server's internal HTTP listener and reverse-proxy topology are unchanged; direct HTTP exposure is not admitted by an HTTPS issuer string. Omitted configuration keeps the existing expiry-only token/UserInfo contract and advertises no new endpoints. Enabling status registers /introspect and /revoke, advertises their implemented client authentication methods, and connects the same store to issuance and UserInfo.

Human PKCE and service grants add a cryptographically random jti to each access token. ID-token claims and existing assurance/tenant provenance remain unchanged. Distinct grants in the same second have distinct bearer tokens: a new grant can never remint a revoked token. A successful response is emitted only after the token hash, expiry and current registration commitment have been recorded. No bearer, client secret or identity/group snapshot is retained in the status store.

The store is bounded to the configured count (default 10000, maximum 1000000). Live records and revocation tombstones are never evicted to admit another grant; capacity exhaustion refuses issuance with a fixed 503. Expired entries are collected when admitting a new token. Capacity and per-client ingress rate limits need workload-representative admission: this is not a throughput/availability claim or protection against an authorized client filling the bound.

This profile uses KeyCape's existing single-instance topology. Status is process-local. Restart, deployment, rollback or store loss makes every earlier token inactive at online endpoints, even if its JWT signature/expiry still validates offline. Consumers must reauthenticate; there is no recovery replay or token-state import that can resurrect revoked authority. Multiple replicas are unsupported. Native admission must include this interruption and the measured reauthentication/recovery procedure. A persisted/shared store is not introduced here.

Introspection and revocation

Both endpoints accept POST form bodies only, bound to 64 KiB; token values are bound to 32 KiB. Query parameters, duplicate form or authentication fields, unsupported content types and malformed authentication refuse without echoing input. Responses use Cache-Control: no-store and Pragma: no-cache.

Introspection requires the existing registered confidential client's client_secret_basic authentication. Only that client's own issued tokens can be inspected. A separate resource audience is returned as aud, not confused with the issuing client_id. Cross-client resource-server delegation needs a separately reviewed authorization contract and is not implicitly granted by a matching audience string. Public clients cannot enumerate token status.

An active response means the exact access token was issued in this process, has not expired or been revoked, still matches its current complete registration, and passes current issuer/signature/purpose validation. For humans, directory lookup must succeed and the current account must be enabled and not suspended. Status is checked again after directory lookup. Service subjects remain bound to their current registration. Active output contains only standard access-token metadata; inactive output contains only active: false. Unknown tokens, ID tokens, altered tokens, another client's tokens and expired/revoked tokens all return that same inactive response to an authenticated introspector.

Revocation authenticates confidential clients using the same method. A registered public client may revoke its own token using client_id plus possession of that bearer; public-client introspection remains unavailable. Revocation only affects the exact token issued to that client. Unknown, other-client, expired and already revoked tokens all produce the same empty 200. No token existence is disclosed. Token-type hints do not bypass the actual access-token lookup. Removal or changes to a registration invalidate online status; the commitment explicitly includes the secret digest and internal token lifetime omitted from public Client JSON.

When enabled, actual UserInfo consults the same store before lookup and again after it. Revocation therefore affects that existing endpoint immediately on its next check. Ordinary login/logout and upstream Authelia sessions are distinct: neither browser logout nor deleting a local cookie is represented as global JWT revocation.

Consumer contract and remaining acceptance

Online status establishes current access-token status, not permission for an application resource or privileged effect. Consumers still independently validate the actual signed token, admitted issuer/key/audience/type, subject, tenant, principal type, assurance and freshness. The authenticated introspection response must refer to that exact token/client. Obtain a new current response before each protected operation; unavailable, malformed, inactive or ambiguous responses refuse. A cached positive is not a current-session check.

Access Engine retains resource/action decisions, Approval Engine exact one-shot approval, Warden admission and owner adapters execution. Bind their existing descriptor, case, transaction and exact effect before dispatch. A separate new operation/inverse token must be acquired and revoked on every terminal path; renewed authentication cannot revive a consumed approval or replay a mutation. This issuer capability alone implements none of those consumer bindings.

The producer-owned current-token client supplies the shared Go/CLI transport and cryptographic/status join. Its private observations retain this distinction: an inactive-for-client result is not an original ownership or execution receipt. Native client/profile adoption remains open.

Native completion still requires independent issuer/profile review, exact image/config/trust admission, bounded retained-token custody, real human/service issuance, private transport, current-token/revocation integration in each owner, all exit-path cleanup, restart/expiry/unavailable/refusal checks, ongoing HTTP/API/Git/SSH authorization and audit evidence. Offline consumers which never consult the endpoint retain their existing JWT validity; no estate-wide immediate revocation claim follows from source implementation. Disabling the feature is not an acceptable fallback for a consumer admitted under the online profile.