Skip to content

feat: Implement RFC 7009 token revocation endpoint (#25)#40

Open
alexlovelltroy wants to merge 1 commit into
integration/wormholefrom
25-feature-token-invalidation-endpoint
Open

feat: Implement RFC 7009 token revocation endpoint (#25)#40
alexlovelltroy wants to merge 1 commit into
integration/wormholefrom
25-feature-token-invalidation-endpoint

Conversation

@alexlovelltroy

Copy link
Copy Markdown
Member

Adds token revocation capability per RFC 7009 (OAuth 2.0 Token Revocation) to enable explicit token invalidation before expiry. This is foundational for Issue #37 (parent-child token cascade revocation).

Core Implementation

RevocationStore (pkg/tokenservice/revocation.go):

  • Thread-safe in-memory JTI blocklist with RWMutex protection
  • Auto-pruning of expired entries on IsRevoked() checks
  • Manual Prune() for periodic cleanup
  • Stores revoked JTIs with their original expiry timestamps

HTTP Endpoint (POST /oauth/revoke):

  • RFC 7009 Section 2.2 compliant: returns 200 OK for all requests (valid, invalid, or already-revoked tokens) to prevent token scanning
  • Form-encoded parameter: token (required), token_type_hint (optional)
  • Extracts JTI from parsed token and adds to revocation store

Middleware Integration (pkg/authn/middleware.go):

  • Added RevocationChecker interface for IsRevoked(jti) checks
  • Integrated check after signature verification and claims validation
  • Returns 401 Unauthorized with 'token has been revoked' for revoked JTIs

Test Coverage

RevocationStore Tests:

  • Thread safety with 100 concurrent goroutines (Revoke, IsRevoked, Prune)
  • Auto-pruning on IsRevoked() checks
  • Manual Prune() correctness and memory cleanup
  • Race condition testing (IsRevoked while Pruning)
  • Size tracking and expiry handling

Handler Tests (RFC 7009 compliance):

  • Returns 200 OK for valid tokens (with revocation)
  • Returns 200 OK for invalid/malformed tokens (per RFC 7009 Section 2.2)
  • Returns 200 OK for already-revoked tokens (idempotent)
  • Returns 400 Bad Request for missing token parameter
  • Returns 405 Method Not Allowed for non-POST requests
  • Verifies tokens remain revoked until original expiry

Security Properties

  • Fail-closed: middleware rejects revoked tokens after verification
  • Token scanning prevention: always returns 200 OK (RFC 7009 Section 2.2)
  • Memory bounded: auto-pruning removes expired entries
  • Race-safe: RWMutex protects concurrent access

Future Work

This implementation enables Issue #37 (parent-child tokens with cascade revocation). When a parent token is revoked, the family hierarchy can be traversed to revoke all descendant tokens.

Closes #25

Pull Request Template

Thank you for your contribution! Please ensure the following before submitting:

Checklist

  • My code follows the style guidelines of this project
  • I have added/updated comments where needed
  • I have added tests that prove my fix is effective or my feature works
  • I have run make test (or equivalent) locally and all tests pass
  • DCO Sign-off: All commits are signed off (git commit -s) with my real name and email
  • REUSE Compliance:
    • Each new/modified source file has SPDX copyright and license headers
    • Any non-commentable files include a <filename>.license sidecar
    • All referenced licenses are present in the LICENSES/ directory

Description

Please include a summary of the change and which issue is fixed.
Also include relevant motivation and context.

Fixes #(issue)

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

For more info, see Contributing Guidelines.

Adds token revocation capability per RFC 7009 (OAuth 2.0 Token Revocation)
to enable explicit token invalidation before expiry. This is foundational
for Issue #37 (parent-child token cascade revocation).

## Core Implementation

**RevocationStore** (pkg/tokenservice/revocation.go):
- Thread-safe in-memory JTI blocklist with RWMutex protection
- Auto-pruning of expired entries on IsRevoked() checks
- Manual Prune() for periodic cleanup
- Stores revoked JTIs with their original expiry timestamps

**HTTP Endpoint** (POST /oauth/revoke):
- RFC 7009 Section 2.2 compliant: returns 200 OK for all requests
  (valid, invalid, or already-revoked tokens) to prevent token scanning
- Form-encoded parameter: token (required), token_type_hint (optional)
- Extracts JTI from parsed token and adds to revocation store

**Middleware Integration** (pkg/authn/middleware.go):
- Added RevocationChecker interface for IsRevoked(jti) checks
- Integrated check after signature verification and claims validation
- Returns 401 Unauthorized with 'token has been revoked' for revoked JTIs

## Test Coverage

**RevocationStore Tests**:
- Thread safety with 100 concurrent goroutines (Revoke, IsRevoked, Prune)
- Auto-pruning on IsRevoked() checks
- Manual Prune() correctness and memory cleanup
- Race condition testing (IsRevoked while Pruning)
- Size tracking and expiry handling

**Handler Tests** (RFC 7009 compliance):
- Returns 200 OK for valid tokens (with revocation)
- Returns 200 OK for invalid/malformed tokens (per RFC 7009 Section 2.2)
- Returns 200 OK for already-revoked tokens (idempotent)
- Returns 400 Bad Request for missing token parameter
- Returns 405 Method Not Allowed for non-POST requests
- Verifies tokens remain revoked until original expiry

## Security Properties

- Fail-closed: middleware rejects revoked tokens after verification
- Token scanning prevention: always returns 200 OK (RFC 7009 Section 2.2)
- Memory bounded: auto-pruning removes expired entries
- Race-safe: RWMutex protects concurrent access

## Future Work

This implementation enables Issue #37 (parent-child tokens with cascade
revocation). When a parent token is revoked, the family hierarchy can be
traversed to revoke all descendant tokens.

Closes #25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant