an atproto pds written in F# (.NET 9) 🦒
pds fsharp giraffe dotnet atproto
5
fork

Configure Feed

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

1# AT Protocol Session & Account Authentication 2 3## Session Authentication (Legacy Bearer JWT) 4 5Based on the [XRPC Spec](https://atproto.com/specs/xrpc#authentication): 6 7### Token Types 8 9| Token | JWT `typ` Header | Lifetime | Purpose | 10| ------------- | ---------------- | --------------------------- | ------------------------------ | 11| Access Token | `at+jwt` | Short (~2min refresh cycle) | Authenticate most API requests | 12| Refresh Token | `refresh+jwt` | Longer (~2 months) | Obtain new access tokens | 13 14### Endpoints 15 16- **`createSession`**: Login with identifier (handle/email) + password → returns `{accessJwt, refreshJwt, handle, did}` 17- **`refreshSession`**: Uses refresh JWT in Bearer header → returns new `{accessJwt, refreshJwt, handle, did}` 18- **`createAccount`**: Register new account → returns session tokens + creates DID 19 20### JWT Claims (Server-Generated) 21 22Servers should implement **domain separation** using the `typ` header field: 23 24- Access: `typ: at+jwt` (per [RFC 9068](https://www.rfc-editor.org/rfc/rfc9068.html)) 25- Refresh: `typ: refresh+jwt` 26 27Standard JWT claims: `sub` (DID), `iat`, `exp`, `jti` (nonce) 28 29### Configuration Required 30 31Yes, JWT signing requires a **secret key** for HMAC-SHA256 (HS256). This should be: 32 33- Loaded from configuration/environment variable (e.g., `PDS_JWT_SECRET`) 34- At least 32 bytes of cryptographically random data 35- Never hardcoded or committed to source control 36 37## Account Storage 38 39### Reference PDS Approach 40 41The Bluesky reference PDS uses: 42 43- **SQLite database per user** (recent architecture) 44- `account.sqlite` contains: handle, email, DID, password hash 45- Accounts indexed by DID (primary) and handle (unique) 46 47## App Passwords 48 49App passwords are a security feature allowing restricted access: 50 51- Format: `xxxx-xxxx-xxxx-xxxx` 52- Created/revoked independently from main password 53- Grants limited permissions (no auth settings changes) 54 55## Inter-Service Auth (Different from Session Auth) 56 57For service-to-service requests, different mechanism: 58 59- Uses **asymmetric signing** (ES256/ES256K) with account's signing key 60- Short-lived tokens (~60sec) 61- Validated against DID document 62 63## Summary: Implementation Decisions 64 65| Aspect | Decision | Rationale | 66| --------------- | -------------------------- | ------------------------------------ | 67| Token signing | HS256 (symmetric) | Simpler, standard for session tokens | 68| Secret storage | Config/env var | Required for security | 69| Account storage | In-memory (initial) | Matches existing patterns | 70| Password hash | SHA-256 + salt | Uses existing Crypto.fs | 71| Token lifetimes | Access: 15min, Refresh: 7d | Conservative defaults | 72 73## References 74 75- [XRPC Authentication Spec](https://atproto.com/specs/xrpc#authentication) 76- [RFC 9068 - JWT Access Tokens](https://www.rfc-editor.org/rfc/rfc9068.html) 77- [Bluesky PDS GitHub](https://github.com/bluesky-social/pds)