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

124 lines
7.3 KiB
Markdown

# 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](https://www.rfc-editor.org/rfc/rfc7662.html) introspection and
[RFC 7009](https://www.rfc-editor.org/rfc/rfc7009.html) revocation. It supports
access tokens only, without refresh tokens or token exchange.
## Enablement and lifecycle
Owner-admitted configuration opts in explicitly:
```yaml
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](current-token-client.md) 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.