feat: add confidential OAuth client support for longer-lived sessions (#578)
* feat: add confidential OAuth client support for longer-lived sessions
adds support for ATProto OAuth confidential clients using private_key_jwt
authentication. when OAUTH_JWK is configured, the client authenticates
with a cryptographic key, earning 180-day refresh tokens (vs 2-week for
public clients).
changes:
- add OAUTH_JWK setting to AtprotoSettings for ES256 private key
- update OAuthClient to pass client_secret_key when configured
- add /.well-known/jwks.json endpoint for public key discovery
- update /oauth-client-metadata.json to include confidential client fields
- add scripts/gen_oauth_jwk.py utility to generate keys
- add tests for is_confidential_client() and get_public_jwks()
to enable:
1. run: uv run python scripts/gen_oauth_jwk.py
2. add output to .env as OAUTH_JWK='...'
closes #577
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* address review comments: remove deferred imports, add type annotations
- move imports to top level in main.py (is_confidential_client, get_public_jwks, HTTPException)
- add type annotation to metadata variable: dict[str, Any]
- update return types to dict[str, Any]
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: add OAuth confidential client documentation
explains what confidential clients are, why they matter for plyr.fm,
and how the implementation works. includes sources and key rotation
guidance.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: add token refresh mechanism and migration notes
- explain how token refresh works (trigger, detection, refresh, retry)
- document what gets refreshed (access vs refresh tokens)
- add observability notes (logfire log messages)
- document migration impact for existing sessions
- clarify that existing tokens can't be upgraded (2-week refresh limit)
- note that only 3 internal dev tokens affected in production
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
authored by
zzstoatzz.io
Claude Opus 4.5
and committed by
GitHub
8fc9fecf
7369c1d0