Skip to content

oauth2: implement RFC 7009 token revocation endpoint POST /v4/oauth2/{domain_id}/revoke #1258

Description

@gtema

Roadmap step 4 (M1). Audit findings F-A1 (P0), F-A7.

Current state

There is no way for a relying party, CLI, or user to end an OP session. The only paths that revoke anything are refresh-reuse detection and emergency signing-key rotation's --revoke-jti (crates/core/src/oauth2_key/). revoked_jtis and its check in crates/core/src/oauth2_client/verify.rs already exist but can only be populated during an emergency rotation.

What must be done

  • New handler crates/keystone/src/api/v4/oauth2/revoke.rs, route POST /{domain_id}/revoke, form body token (required), token_type_hint (refresh_token | access_token, optional), client credentials via Basic or body as in /token.
  • Authenticate the client with the existing authenticate_client helper (extract it from token.rs into a shared module); apply the pre-hash oauth2_token_rate_limiter and check_ip before any lookup.
  • Refresh token: hash the bearer, load the record, revoke the family (step 2 tombstone, reason rp_revoke) only if record.client_id == client_id && record.domain_id == domain_id. Per RFC 7009 §2.2 always answer 200 with an empty body, including for unknown tokens and for tokens owned by another client, so the endpoint is not an oracle; audit the mismatch case.
  • Access token: parse the JWT, verify signature against the domain's active keys, require client_id/aud to match the authenticated client, then add its jti with its exp to the JTI revocation list via a new Oauth2KeyApi::revoke_jti(state, domain_id, jti, expires_at) and backend method (crates/core/src/oauth2_key/{provider_api,backend}.rs, raft driver add_revoked_jtis_impl at crates/oauth2-key-driver-raft/src/lib.rs:716). Tokens without jti (IdTokenClaims) are ignored with 200.
  • Also revoke the refresh family that minted the access token if it can be located (store family_id on OidcAccessTokenClaims/OpenStackAccessTokenClaims as a private claim sid, or skip and document).
  • Emit a CADF revoke event via emit_oauth2_session_event with the real initiator.
  • Add revocation_endpoint to well_known.rs and revocation_endpoint_auth_methods_supported.
  • Unit tests: rate limit before lookup; unknown token → 200; foreign client's refresh token → 200 and family untouched; own refresh token → family tombstoned; own access token → jti appears in /jwks/revocation.
  • test_api live test: full code flow, revoke refresh token, subsequent refresh is invalid_grant.
  • Document in doc/src/user/features/oauth2.md and the admin guide.

Activity

  1. added theissue type on Sep 18, 2026
  2. added 3 commits that reference this issue on Oct 3, 2026
    55363e5
    c0f93ea
    6ae0420
  3. added a commit that references this issue on Oct 6, 2026
    0262362
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    • Status
      Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions