A Deno-compatible AT Protocol OAuth client that serves as a drop-in replacement for @atproto/oauth-client-node

remove claude setup files

Changed files
+2 -214
.claude
-213
.claude/CLAUDE.md
··· 1 - # CLAUDE.md 2 - 3 - This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. 4 - 5 - ## Project Overview 6 - 7 - A Deno-compatible AT Protocol OAuth client built specifically for handle-based authentication. This is **NOT a drop-in replacement** for `@atproto/oauth-client-node` - it's an opinionated, handle-focused alternative built on Web Crypto API. 8 - 9 - **Key Design Decisions:** 10 - 11 - - Handle-only inputs (e.g., `alice.bsky.social`) - no DIDs or URLs accepted 12 - - Slingshot resolver as default with fallbacks (Bluesky API → direct resolution) 13 - - Web Crypto API exclusively for cross-platform compatibility 14 - - Built for Deno runtime, not Node.js 15 - 16 - ## Commands 17 - 18 - ### Development 19 - 20 - ```bash 21 - # Type checking 22 - deno task check 23 - 24 - # Format code 25 - deno task fmt # Check formatting 26 - deno task fmt:fix # Auto-fix formatting 27 - 28 - # Linting 29 - deno task lint 30 - 31 - # Run all checks (CI simulation) 32 - deno task ci 33 - ``` 34 - 35 - ### Testing 36 - 37 - ```bash 38 - # Run all tests 39 - deno task test 40 - 41 - # Run specific test file 42 - deno test tests/session_test.ts --allow-net --allow-read 43 - 44 - # Run tests with coverage 45 - deno test --coverage=coverage --allow-net --allow-read 46 - ``` 47 - 48 - ### Publishing 49 - 50 - ```bash 51 - # Publish to JSR (requires proper version in deno.json) 52 - deno publish 53 - ``` 54 - 55 - ## Architecture 56 - 57 - ### Core Flow: OAuth Authorization 58 - 59 - 1. **Handle Resolution** (`src/resolvers.ts`) 60 - - `SlingshotResolver` (default): Uses Slingshot's `resolveMiniDoc` endpoint for fast DID+PDS lookup 61 - - Fallback chain: Slingshot standard → Bluesky API → Direct `.well-known/atproto-did` lookup 62 - - `DirectoryResolver`: Bluesky API only (no Slingshot) 63 - - `CustomResolver`: User-provided resolution logic 64 - 65 - 2. **OAuth Endpoint Discovery** (`src/resolvers.ts`) 66 - - Discover auth server from PDS: `/.well-known/oauth-protected-resource` 67 - - Discover OAuth endpoints from auth server: `/.well-known/oauth-authorization-server` 68 - - Fallback: Try PDS directly if auth server discovery fails 69 - 70 - 3. **Authorization Flow** (`src/client.ts`) 71 - - Generate PKCE parameters (code_verifier, code_challenge) 72 - - Store PKCE data in storage with 10-minute TTL (`pkce:{state}`) 73 - - Push Authorization Request (PAR) to get request_uri 74 - - Return authorization URL for user redirect 75 - 76 - 4. **Token Exchange** (`src/client.ts`) 77 - - Validate state parameter and retrieve PKCE data 78 - - Generate DPoP ES256 key pair (Web Crypto API) 79 - - Exchange authorization code for tokens with DPoP proof 80 - - Handle DPoP nonce challenges (retry with nonce on 400 status) 81 - - Create and return authenticated session 82 - 83 - 5. **DPoP Authentication** (`src/dpop.ts`) 84 - - ES256 (ECDSA P-256) key generation using Web Crypto API 85 - - JWT creation with `jsr:@panva/jose` (NOT npm:jose) 86 - - DPoP proof includes: jti, htm, htu, iat, exp, optional ath (access token hash), optional nonce 87 - - Automatic nonce handling: retry on 401 with `DPoP-Nonce` header 88 - 89 - ### Key Components 90 - 91 - **`OAuthClient` (src/client.ts)** 92 - 93 - - Main entry point for OAuth operations 94 - - Methods: `authorize()`, `callback()`, `store()`, `restore()`, `refresh()`, `signOut()` 95 - - Manages PKCE flow, token exchange, and session lifecycle 96 - 97 - **`Session` (src/session.ts)** 98 - 99 - - Represents authenticated user session 100 - - Properties: `did`, `handle`, `pdsUrl`, `accessToken`, `refreshToken`, `isExpired` 101 - - `makeRequest()`: Makes DPoP-authenticated HTTP requests with automatic nonce handling 102 - - Serializable via `toJSON()` / `fromJSON()` for storage 103 - 104 - **Storage Implementations (src/storage.ts)** 105 - 106 - - `MemoryStorage`: In-memory with TTL support (development/testing) 107 - - `SQLiteStorage`: Example SQLite backend (reference implementation) 108 - - `LocalStorage`: Browser localStorage wrapper 109 - - All implement `OAuthStorage` interface: `get()`, `set()`, `delete()` 110 - 111 - **Error Hierarchy (src/errors.ts)** 112 - 113 - - Base: `OAuthError` (all OAuth errors inherit from this) 114 - - Handle errors: `InvalidHandleError`, `HandleResolutionError` 115 - - Discovery errors: `PDSDiscoveryError`, `AuthServerDiscoveryError` 116 - - Flow errors: `TokenExchangeError`, `AuthorizationError`, `InvalidStateError` 117 - - Auth errors: `DPoPError`, `SessionError` 118 - 119 - ### Critical Implementation Details 120 - 121 - **Web Crypto API vs Node.js crypto** 122 - 123 - - MUST use `crypto.subtle.generateKey()` with explicit `namedCurve: "P-256"` 124 - - MUST use `jsr:@panva/jose` NOT `npm:jose` or Node.js jose packages 125 - - DPoP key generation MUST set `extractable: true` for JWK export 126 - - Private key import MUST clean JWK (remove conflicting `key_ops` from exportJWK) 127 - 128 - **DPoP Nonce Handling** 129 - 130 - - AT Protocol uses 400 status (not 401) for initial nonce challenges during token exchange 131 - - Token refresh uses 401 status for nonce challenges 132 - - Always check `DPoP-Nonce` header and retry with nonce if present 133 - - Nonce included in JWT payload, not header 134 - 135 - **Handle Resolution Strategy** 136 - 137 - - Default: Slingshot with multi-level fallbacks 138 - - `resolveMiniDoc` returns both DID + PDS in one request (preferred) 139 - - Standard resolution requires two requests: handle→DID, then DID document→PDS 140 - - PDS URL extracted from DID document's `AtprotoPersonalDataServer` service 141 - 142 - **Session Storage Pattern** 143 - 144 - - PKCE data stored with `pkce:{state}` prefix, 10-minute TTL 145 - - Sessions stored with `session:{sessionId}` prefix 146 - - Auto-refresh on restore if token expires within 5 minutes 147 - - `isExpired` uses 5-minute buffer to prevent edge cases 148 - 149 - ## Testing Patterns 150 - 151 - Tests use Deno's built-in test framework with the following patterns: 152 - 153 - **Mock/Fake Pattern** (per user's global CLAUDE.md) 154 - 155 - - Tests must NOT rely on external services 156 - - Use injection patterns for all dependencies 157 - - Mock storage, resolvers, and network calls in tests 158 - - Test files: `tests/*_test.ts` 159 - 160 - **Test File Structure** 161 - 162 - - `errors_test.ts`: Error class behavior and messages 163 - - `session_test.ts`: Session management, token refresh, serialization 164 - - `storage_test.ts`: Storage implementations with TTL 165 - - `utils_test.ts`: Utility functions (PKCE, DPoP, etc.) 166 - 167 - ## Security & OAuth Best Practices 168 - 169 - **CRITICAL: No OAuth Workarounds** (per user's global CLAUDE.md) 170 - 171 - - Always follow OAuth 2.0, AT Protocol, and DPoP specs exactly 172 - - No shortcuts or "good enough" solutions for auth flows 173 - - Properly validate state parameters (CSRF protection) 174 - - Use secure PKCE (S256, not plain) 175 - - DPoP proof must include all required claims 176 - 177 - **Token Management** 178 - 179 - - Store refresh tokens securely in storage backend 180 - - Never log tokens or sensitive cryptographic material 181 - - Clean up PKCE data after use (success or failure) 182 - - Revoke tokens on sign out (best effort) 183 - 184 - ## Common Development Patterns 185 - 186 - **Adding a new storage backend:** 187 - 188 - 1. Implement `OAuthStorage` interface from `src/types.ts` 189 - 2. Implement `get<T>()`, `set<T>()`, `delete()` with TTL support 190 - 3. Handle TTL expiration in `get()` (return null if expired) 191 - 4. Add tests following pattern in `tests/storage_test.ts` 192 - 193 - **Adding a new resolver:** 194 - 195 - 1. Implement `HandleResolver` interface from `src/types.ts` 196 - 2. Implement `resolve(handle)` returning `{ did: string; pdsUrl: string }` 197 - 3. Throw `HandleResolutionError` on failure 198 - 4. Consider fallback mechanisms like `SlingshotResolver` 199 - 200 - **Error handling:** 201 - 202 - - Catch and re-throw with appropriate error class 203 - - Preserve error cause chain for debugging 204 - - All OAuth errors extend `OAuthError` base class 205 - - Use specific error types for different failure modes 206 - 207 - ## Important Constraints 208 - 209 - - **Handle-only inputs**: Client only accepts AT Protocol handles, not DIDs or URLs 210 - - **Deno runtime**: Built for Deno, uses Web Standards APIs exclusively 211 - - **No Node.js crypto**: Cannot use Node.js crypto modules (incompatible with Deno) 212 - - **Slingshot dependency**: Default resolver uses third-party Slingshot service (can be configured) 213 - - **ES256 only**: DPoP uses ECDSA P-256 (ES256), not RS256 or other algorithms
+2 -1
.gitignore
··· 36 36 *.swp 37 37 *.swo 38 38 *~ 39 + .claude/ 39 40 40 41 # macOS 41 42 .DS_Store ··· 96 97 test-output/ 97 98 98 99 # Documentation generation 99 - docs/ 100 + docs/