Commit 261081

2026-03-15 06:17:07 Claude (MCP): [mcp] V3 ATProto Auth implementation summary
/dev/null .. Dev/V3_ATProto_Auth_Summary.md
@@ 0,0 1,64 @@
+ ---
+ category: reference
+ tags:
+ - v3
+ - auth
+ - atproto
+ - oauth
+ last_updated: 2026-03-15
+ ---
+
+ # V3: ATProto OAuth Production Auth Service
+
+ **Branch:** `feat/v3-atproto-auth`
+ **Commit:** `4580bbf`
+ **Status:** Implementation complete, 24/24 tests pass (79/79 total)
+
+ ## What Changed
+
+ Replaced the stub auth server (`app/auth_server.py`) with a production ATProto OAuth flow adapted from the VS-1 spike (`spike/atproto-oauth/`).
+
+ ## Deliverables
+
+ | File | Description |
+ |------|-------------|
+ | `app/auth/atproto_oauth.py` | PAR, DPoP, token exchange, refresh, revocation |
+ | `app/auth/atproto_identity.py` | Handle/DID resolution (DNS TXT + HTTP well-known) |
+ | `app/auth/atproto_security.py` | SSRF mitigations, hardened HTTP client |
+ | `app/auth_server.py` | Full Flask app with `create_app()` factory |
+ | `app/auth/templates/` | login.html, signup.html, error.html, base.html (Pico CSS) |
+ | `tests/test_auth_server.py` | 24 tests covering all routes and flows |
+ | `requirements.txt` | Added authlib>=1.3, dnspython>=2.6, requests-hardened>=1.0.0b3 |
+ | `ansible/roles/database/files/schema.sql` | Added `oauth_auth_requests` table, updated `oauth_sessions` |
+ | `ansible/roles/deploy/files/robot-auth.service` | Added EnvironmentFile, CLIENT_JWK_PATH, ROBOT_DB_PATH |
+
+ ## Routes
+
+ | Method | Path | Purpose |
+ |--------|------|---------|
+ | GET | `/auth/client-metadata.json` | ATProto client metadata (client_id URL) |
+ | GET | `/auth/login` | Login page with handle input |
+ | POST | `/auth/login` | Initiate OAuth: resolve identity, PAR, redirect to AS |
+ | GET | `/auth/callback` | Exchange code for tokens, issue platform JWT cookie |
+ | GET | `/auth/signup` | Username form (first-time users) |
+ | POST | `/auth/signup` | Create user record, issue JWT cookie |
+ | GET | `/auth/logout` | Revoke ATProto tokens, clear cookie |
+ | GET | `/.well-known/oauth-authorization-server` | AS metadata stub |
+ | GET | `/.well-known/jwks.json` | RS256 public key |
+
+ ## Key Design Decisions
+
+ - **Platform JWT != ATProto tokens**: OAuth callback issues an RS256 platform JWT cookie; ATProto tokens are stored in `oauth_sessions` for potential future use
+ - **Cookie**: `platform_token` on `.robot.wtf` domain, HttpOnly, Secure, SameSite=Lax, 24h TTL
+ - **Signup flow**: First-time users (no `users` row for their DID) get redirected to `/auth/signup` to choose a username; default derived from handle prefix
+ - **DPoP nonce handling**: Preserved from spike -- automatic retry on `use_dpop_nonce` error
+ - **client_id**: `https://robot.wtf/auth/client-metadata.json` (stable URL)
+ - **Scope**: `atproto` (identity-only, no repo access)
+
+ ## Schema Changes
+
+ Added `oauth_auth_requests` table for in-flight OAuth state (temporary, deleted after callback). Updated `oauth_sessions` to use DID as primary key with ATProto-specific fields (authserver_iss, pds_url, dpop_authserver_nonce, dpop_private_jwk).
+
+ ## Test Coverage
+
+ 24 tests covering: client metadata structure, login page rendering, invalid input handling, OAuth callback (error, missing params, unknown state, returning user, new user), signup flow (no session, form rendering, user creation, invalid/reserved/duplicate username rejection), logout cookie clearing, JWKS endpoint, AS metadata, and default username derivation.
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9