···77A 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.
8899**Key Design Decisions:**
1010+1011- Handle-only inputs (e.g., `alice.bsky.social`) - no DIDs or URLs accepted
1112- Slingshot resolver as default with fallbacks (Bluesky API → direct resolution)
1213- Web Crypto API exclusively for cross-platform compatibility
···1516## Commands
16171718### Development
1919+1820```bash
1921# Type checking
2022deno task check
···3133```
32343335### Testing
3636+3437```bash
3538# Run all tests
3639deno task test
···4346```
44474548### Publishing
4949+4650```bash
4751# Publish to JSR (requires proper version in deno.json)
4852deno publish
···8589### Key Components
86908791**`OAuthClient` (src/client.ts)**
9292+8893- Main entry point for OAuth operations
8994- Methods: `authorize()`, `callback()`, `store()`, `restore()`, `refresh()`, `signOut()`
9095- Manages PKCE flow, token exchange, and session lifecycle
91969297**`Session` (src/session.ts)**
9898+9399- Represents authenticated user session
94100- Properties: `did`, `handle`, `pdsUrl`, `accessToken`, `refreshToken`, `isExpired`
95101- `makeRequest()`: Makes DPoP-authenticated HTTP requests with automatic nonce handling
96102- Serializable via `toJSON()` / `fromJSON()` for storage
9710398104**Storage Implementations (src/storage.ts)**
105105+99106- `MemoryStorage`: In-memory with TTL support (development/testing)
100107- `SQLiteStorage`: Example SQLite backend (reference implementation)
101108- `LocalStorage`: Browser localStorage wrapper
102109- All implement `OAuthStorage` interface: `get()`, `set()`, `delete()`
103110104111**Error Hierarchy (src/errors.ts)**
112112+105113- Base: `OAuthError` (all OAuth errors inherit from this)
106114- Handle errors: `InvalidHandleError`, `HandleResolutionError`
107115- Discovery errors: `PDSDiscoveryError`, `AuthServerDiscoveryError`
···111119### Critical Implementation Details
112120113121**Web Crypto API vs Node.js crypto**
122122+114123- MUST use `crypto.subtle.generateKey()` with explicit `namedCurve: "P-256"`
115124- MUST use `jsr:@panva/jose` NOT `npm:jose` or Node.js jose packages
116125- DPoP key generation MUST set `extractable: true` for JWK export
117126- Private key import MUST clean JWK (remove conflicting `key_ops` from exportJWK)
118127119128**DPoP Nonce Handling**
129129+120130- AT Protocol uses 400 status (not 401) for initial nonce challenges during token exchange
121131- Token refresh uses 401 status for nonce challenges
122132- Always check `DPoP-Nonce` header and retry with nonce if present
123133- Nonce included in JWT payload, not header
124134125135**Handle Resolution Strategy**
136136+126137- Default: Slingshot with multi-level fallbacks
127138- `resolveMiniDoc` returns both DID + PDS in one request (preferred)
128139- Standard resolution requires two requests: handle→DID, then DID document→PDS
129140- PDS URL extracted from DID document's `AtprotoPersonalDataServer` service
130141131142**Session Storage Pattern**
143143+132144- PKCE data stored with `pkce:{state}` prefix, 10-minute TTL
133145- Sessions stored with `session:{sessionId}` prefix
134146- Auto-refresh on restore if token expires within 5 minutes
···139151Tests use Deno's built-in test framework with the following patterns:
140152141153**Mock/Fake Pattern** (per user's global CLAUDE.md)
154154+142155- Tests must NOT rely on external services
143156- Use injection patterns for all dependencies
144157- Mock storage, resolvers, and network calls in tests
145158- Test files: `tests/*_test.ts`
146159147160**Test File Structure**
161161+148162- `errors_test.ts`: Error class behavior and messages
149163- `session_test.ts`: Session management, token refresh, serialization
150164- `storage_test.ts`: Storage implementations with TTL
···153167## Security & OAuth Best Practices
154168155169**CRITICAL: No OAuth Workarounds** (per user's global CLAUDE.md)
170170+156171- Always follow OAuth 2.0, AT Protocol, and DPoP specs exactly
157172- No shortcuts or "good enough" solutions for auth flows
158173- Properly validate state parameters (CSRF protection)
···160175- DPoP proof must include all required claims
161176162177**Token Management**
178178+163179- Store refresh tokens securely in storage backend
164180- Never log tokens or sensitive cryptographic material
165181- Clean up PKCE data after use (success or failure)
···168184## Common Development Patterns
169185170186**Adding a new storage backend:**
187187+1711881. Implement `OAuthStorage` interface from `src/types.ts`
1721892. Implement `get<T>()`, `set<T>()`, `delete()` with TTL support
1731903. Handle TTL expiration in `get()` (return null if expired)
1741914. Add tests following pattern in `tests/storage_test.ts`
175192176193**Adding a new resolver:**
194194+1771951. Implement `HandleResolver` interface from `src/types.ts`
1781962. Implement `resolve(handle)` returning `{ did: string; pdsUrl: string }`
1791973. Throw `HandleResolutionError` on failure
1801984. Consider fallback mechanisms like `SlingshotResolver`
181199182200**Error handling:**
201201+183202- Catch and re-throw with appropriate error class
184203- Preserve error cause chain for debugging
185204- All OAuth errors extend `OAuthError` base class