feat: atproto OAuth token endpoint, revocation, and resource-server acceptance
Add POST /oauth/token with authorization_code and refresh_token grants. The handler enforces DPoP nonce validation, PKCE and client auth, mints opaque rkat_/rkrt_ tokens stored only as hashes, sets access token expiry to min(900, remaining session lifetime), and uses 14d sessions with guarded single-use refresh rotation.
Add POST /oauth/revoke as an RFC 7009 always-200 endpoint that accepts access or refresh tokens and revokes the owning session plus all issued access tokens without leaking token state.
Add the resource-server branch so XRPC writes and uploadBlob accept AS-issued access tokens through the D1 token-session join with DPoP jkt binding and per-collection scope enforcement, while preserving the existing wm+jwt path.
Add atomic store helpers for consumeOAuthCode, guarded rotateOAuthSessionRefresh, joined getOAuthTokenWithSession, and the Makefile ci target.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat: atproto OAuth authorization-server foundation
- New src/oauth/ module: D1 store (initOAuth + 5 tables, atomic jti-replay, opportunistic cleanup), ES256 DPoP proof validator (validateOauthDpopProof, ecJwkThumbprint, UseDpopNonceError), stateless rotating DPoP nonce (deriveDpopNonce, 300s window, current+previous), client-ID-metadata-document fetcher/validator (fetchClientMetadata, matchRedirectUri, verifyClientAuth for none + private_key_jwt), and the two discovery-metadata builders.
- Two public discovery routes (/.well-known/oauth-authorization-server, /.well-known/oauth-protected-resource) served in all variants; CORS exposes DPoP-Nonce + WWW-Authenticate; new OAUTH_NONCE_SECRET binding.
- Strictly additive — no existing endpoint behavior changes. Exported surface is the contract for the follow-up PAR/authorize/token/revoke lodes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat: atproto OAuth authorization-server foundation
- New src/oauth/ module: D1 store (initOAuth + 5 tables, atomic jti-replay, opportunistic cleanup), ES256 DPoP proof validator (validateOauthDpopProof, ecJwkThumbprint, UseDpopNonceError), stateless rotating DPoP nonce (deriveDpopNonce, 300s window, current+previous), client-ID-metadata-document fetcher/validator (fetchClientMetadata, matchRedirectUri, verifyClientAuth for none + private_key_jwt), and the two discovery-metadata builders.
- Two public discovery routes (/.well-known/oauth-authorization-server, /.well-known/oauth-protected-resource) served in all variants; CORS exposes DPoP-Nonce + WWW-Authenticate; new OAUTH_NONCE_SECRET binding.
- Strictly additive — no existing endpoint behavior changes. Exported surface is the contract for the follow-up PAR/authorize/token/revoke lodes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat: atproto OAuth token endpoint, revocation, and resource-server acceptance
Add POST /oauth/token with authorization_code and refresh_token grants. The handler enforces DPoP nonce validation, PKCE and client auth, mints opaque rkat_/rkrt_ tokens stored only as hashes, sets access token expiry to min(900, remaining session lifetime), and uses 14d sessions with guarded single-use refresh rotation.
Add POST /oauth/revoke as an RFC 7009 always-200 endpoint that accepts access or refresh tokens and revokes the owning session plus all issued access tokens without leaking token state.
Add the resource-server branch so XRPC writes and uploadBlob accept AS-issued access tokens through the D1 token-session join with DPoP jkt binding and per-collection scope enforcement, while preserving the existing wm+jwt path.
Add atomic store helpers for consumeOAuthCode, guarded rotateOAuthSessionRefresh, joined getOAuthTokenWithSession, and the Makefile ci target.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>