Skip to content

feat: add token revocation (RFC 7009) - #51

Merged
germ-mark merged 5 commits into
feat/client-auth-protocolsfrom
feat/token-revocation
Jul 29, 2026
Merged

feat: add token revocation (RFC 7009)#51
germ-mark merged 5 commits into
feat/client-auth-protocolsfrom
feat/token-revocation

Conversation

@ThisIsMissEm

@ThisIsMissEm ThisIsMissEm commented May 29, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add revocationRequest(authServerMetadata:token:) extension methods on ClientAuth.Authenticable for both AccessToken and RefreshToken
  • Add OAuth.RevocableToken enum bundling the token value with its token_type_hint — prevents mismatched type/hint pairs
  • Revocation requests use client authentication only (no DPoP), per RFC 7009 §2
  • Silently no-ops if the server does not advertise a revocation endpoint

Test plan

  • Revoke an access token and verify the server responds 200
  • Revoke a refresh token and verify token_type_hint=refresh_token is sent
  • Verify no DPoP proof is attached to revocation requests
  • Test it works with AT Protocol which does use DPoP binding

@changeset-bot

changeset-bot Bot commented May 29, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: bd6c767

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@germ-network/oauth4swift Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

import GermConvenience
import HTTPTypes

extension HTTPDataResponse {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@germ-mark this should probably be lifted up to GermConvenience, but I didn't want to tie up this work with a change upstream

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we're about to gin up a release for germ-network/GermConvenience#32 so let's put it where it belongs

@ThisIsMissEm

Copy link
Copy Markdown
Contributor Author

Huh, interesting: Bluesky's atproto codebase does use DPoP for token revocation, despite this not explicitly being in either spec. https://github.com/bluesky-social/atproto/blob/f792b919386341d0dc4dcf873506f088af61ae16/packages/oauth/oauth-provider/src/router/create-oauth-middleware.ts#L163

@germ-mark germ-mark left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good, we can move the successOrThrow up to GermConvenience

ThisIsMissEm and others added 5 commits July 29, 2026 01:25
… enum (RFC 7009)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
0.3.0 makes BundledHTTPRequest.request private(set) and body let, so the checks
in its init cannot be undone after construction. The three sites that assigned
header fields in place move to settingHeader(_:for:), which is the narrow API
that replaces them. Behaviour is unchanged - each one only ever added a header.

The floor bump and the migration have to land together: 0.3.0 is what breaks
in-place mutation, so neither builds without the other.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drops the local successOrThrow copy now that it lives upstream, and collapses
the hand-rolled .result/.error switches in the PAR and token-endpoint paths.

Verified against released 0.3.0 with a throwaway harness over PAR, the token
endpoint, discovery and settingHeader: the four unchanged behaviours hold, and
the two that 0.3.0 changes improve - a malformed token response now reports its
own DecodingError instead of Errors.invalidRequest, and a non-OAuth error body
keeps its status and bytes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ests

The silent no-op when the server advertises no revocation endpoint was stated
in the PR description but not in code - both public overloads now document it,
along with revocation never touching local session state and refresh-token
revocation being the way to end a whole grant (RFC 7009 2.1).

RevocableToken is public API and now Sendable; public types get no implicit
synthesis.

First tests for the feature, using GermConvenienceMocks - which exists for
exactly this. Covers the RFC 7009 wire format for both hints, OAuth error
mapping, non-OAuth error bodies preserving status and bytes (the 0.3.0
contract), and the documented no-endpoint no-op.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@germ-mark
germ-mark force-pushed the feat/token-revocation branch from e0375d1 to bd6c767 Compare July 29, 2026 08:25
@germ-mark
germ-mark merged commit a972947 into main Jul 29, 2026
8 checks passed
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.

2 participants