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

Configure Feed

Select the types of activity you want to include in your feed.

add the claude file

+19
+19
.claude/CLAUDE.md
··· 7 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 8 9 9 **Key Design Decisions:** 10 + 10 11 - Handle-only inputs (e.g., `alice.bsky.social`) - no DIDs or URLs accepted 11 12 - Slingshot resolver as default with fallbacks (Bluesky API → direct resolution) 12 13 - Web Crypto API exclusively for cross-platform compatibility ··· 15 16 ## Commands 16 17 17 18 ### Development 19 + 18 20 ```bash 19 21 # Type checking 20 22 deno task check ··· 31 33 ``` 32 34 33 35 ### Testing 36 + 34 37 ```bash 35 38 # Run all tests 36 39 deno task test ··· 43 46 ``` 44 47 45 48 ### Publishing 49 + 46 50 ```bash 47 51 # Publish to JSR (requires proper version in deno.json) 48 52 deno publish ··· 85 89 ### Key Components 86 90 87 91 **`OAuthClient` (src/client.ts)** 92 + 88 93 - Main entry point for OAuth operations 89 94 - Methods: `authorize()`, `callback()`, `store()`, `restore()`, `refresh()`, `signOut()` 90 95 - Manages PKCE flow, token exchange, and session lifecycle 91 96 92 97 **`Session` (src/session.ts)** 98 + 93 99 - Represents authenticated user session 94 100 - Properties: `did`, `handle`, `pdsUrl`, `accessToken`, `refreshToken`, `isExpired` 95 101 - `makeRequest()`: Makes DPoP-authenticated HTTP requests with automatic nonce handling 96 102 - Serializable via `toJSON()` / `fromJSON()` for storage 97 103 98 104 **Storage Implementations (src/storage.ts)** 105 + 99 106 - `MemoryStorage`: In-memory with TTL support (development/testing) 100 107 - `SQLiteStorage`: Example SQLite backend (reference implementation) 101 108 - `LocalStorage`: Browser localStorage wrapper 102 109 - All implement `OAuthStorage` interface: `get()`, `set()`, `delete()` 103 110 104 111 **Error Hierarchy (src/errors.ts)** 112 + 105 113 - Base: `OAuthError` (all OAuth errors inherit from this) 106 114 - Handle errors: `InvalidHandleError`, `HandleResolutionError` 107 115 - Discovery errors: `PDSDiscoveryError`, `AuthServerDiscoveryError` ··· 111 119 ### Critical Implementation Details 112 120 113 121 **Web Crypto API vs Node.js crypto** 122 + 114 123 - MUST use `crypto.subtle.generateKey()` with explicit `namedCurve: "P-256"` 115 124 - MUST use `jsr:@panva/jose` NOT `npm:jose` or Node.js jose packages 116 125 - DPoP key generation MUST set `extractable: true` for JWK export 117 126 - Private key import MUST clean JWK (remove conflicting `key_ops` from exportJWK) 118 127 119 128 **DPoP Nonce Handling** 129 + 120 130 - AT Protocol uses 400 status (not 401) for initial nonce challenges during token exchange 121 131 - Token refresh uses 401 status for nonce challenges 122 132 - Always check `DPoP-Nonce` header and retry with nonce if present 123 133 - Nonce included in JWT payload, not header 124 134 125 135 **Handle Resolution Strategy** 136 + 126 137 - Default: Slingshot with multi-level fallbacks 127 138 - `resolveMiniDoc` returns both DID + PDS in one request (preferred) 128 139 - Standard resolution requires two requests: handle→DID, then DID document→PDS 129 140 - PDS URL extracted from DID document's `AtprotoPersonalDataServer` service 130 141 131 142 **Session Storage Pattern** 143 + 132 144 - PKCE data stored with `pkce:{state}` prefix, 10-minute TTL 133 145 - Sessions stored with `session:{sessionId}` prefix 134 146 - Auto-refresh on restore if token expires within 5 minutes ··· 139 151 Tests use Deno's built-in test framework with the following patterns: 140 152 141 153 **Mock/Fake Pattern** (per user's global CLAUDE.md) 154 + 142 155 - Tests must NOT rely on external services 143 156 - Use injection patterns for all dependencies 144 157 - Mock storage, resolvers, and network calls in tests 145 158 - Test files: `tests/*_test.ts` 146 159 147 160 **Test File Structure** 161 + 148 162 - `errors_test.ts`: Error class behavior and messages 149 163 - `session_test.ts`: Session management, token refresh, serialization 150 164 - `storage_test.ts`: Storage implementations with TTL ··· 153 167 ## Security & OAuth Best Practices 154 168 155 169 **CRITICAL: No OAuth Workarounds** (per user's global CLAUDE.md) 170 + 156 171 - Always follow OAuth 2.0, AT Protocol, and DPoP specs exactly 157 172 - No shortcuts or "good enough" solutions for auth flows 158 173 - Properly validate state parameters (CSRF protection) ··· 160 175 - DPoP proof must include all required claims 161 176 162 177 **Token Management** 178 + 163 179 - Store refresh tokens securely in storage backend 164 180 - Never log tokens or sensitive cryptographic material 165 181 - Clean up PKCE data after use (success or failure) ··· 168 184 ## Common Development Patterns 169 185 170 186 **Adding a new storage backend:** 187 + 171 188 1. Implement `OAuthStorage` interface from `src/types.ts` 172 189 2. Implement `get<T>()`, `set<T>()`, `delete()` with TTL support 173 190 3. Handle TTL expiration in `get()` (return null if expired) 174 191 4. Add tests following pattern in `tests/storage_test.ts` 175 192 176 193 **Adding a new resolver:** 194 + 177 195 1. Implement `HandleResolver` interface from `src/types.ts` 178 196 2. Implement `resolve(handle)` returning `{ did: string; pdsUrl: string }` 179 197 3. Throw `HandleResolutionError` on failure 180 198 4. Consider fallback mechanisms like `SlingshotResolver` 181 199 182 200 **Error handling:** 201 + 183 202 - Catch and re-throw with appropriate error class 184 203 - Preserve error cause chain for debugging 185 204 - All OAuth errors extend `OAuthError` base class