Conversation
Sync with upstream
Adds AT Protocol OAuth support so users can sign in with a Bluesky handle or any PDS-hosted identity. Uses @atproto/oauth-client-node with PKCE (public client, minimal "atproto" scope). Core changes: - Migration 034: adds atproto_did column to users table - Store adapters: state and session stores backed by auth_challenges table (no new tables needed) - OAuth client: NodeOAuthClient with cached instances, loopback client ID support for localhost dev (RFC 8252) - Routes: client-metadata.json, login (POST handle), callback (token exchange + user provisioning) - Config: atproto boolean on EmDashConfig, atprotoEnabled in manifest - Middleware: ATProto auth routes and manifest endpoint public, complete-profile page bypasses auth - Route injection: ATProto routes in injectBuiltinAuthRoutes() User provisioning follows the same pattern as GitHub/Google OAuth: DID lookup via oauth_accounts, email auto-linking, allowed_domains gating for new signups. Also registers previously unregistered migration 033 in runner.ts.
- LoginPage: handle input form with "Sign in via the Atmosphere" divider, conditioned on manifest.atprotoEnabled - Router: /complete-profile route (standalone, no Shell wrapper) - AdminManifest: atprotoEnabled field
When the PDS doesn't return an email, the complete-profile page collects one and sends a verification email before creating the account. This prevents users from entering arbitrary emails to gain access via allowed_domains. - complete-profile: validates atproto pending state, sends verification email with token + state params - verify-email: new GET endpoint that verifies token, looks up atproto pending state, provisions user with email_verified=1 - CompleteProfilePage: email form + "check your email" state - Dev mode: full verification URL logged to server console
Covers get/set/del, conflict handling, TTL expiration, and cross-store isolation (state store doesn't see session entries and vice versa).
Adds ATProto login section to the Authentication guide covering setup, email verification flow, access control, and identity storage. Adds atproto config option to the Configuration reference.
🦋 Changeset detectedLatest commit: ae627d0 The changes in this PR will be included in the next version bump. This PR includes changesets to release 10 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
I have read the CLA Document and I hereby sign the CLA You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot. |
Scope checkThis PR changes 1,855 lines across 27 files. Large PRs are harder to review and more likely to be closed without review. If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs. See CONTRIBUTING.md for contribution guidelines. |
Overlapping PRsThis PR modifies files that are also changed by other open PRs:
This may cause merge conflicts or duplicated work. A maintainer will coordinate. |
What does this PR do?
Adds AT Protocol OAuth as a login provider so authors can sign in with their Bluesky handle or any PDS-hosted identity. Uses
@atproto/oauth-client-node with PKCE (public client, minimal atproto scope — no repo access requested).
This is a POC implementation by a Bluesky employee, using the official SDK. One goal is to surface any Cloudflare Workers
incompatibilities for upstream fixes.
Related discussion: #320
How it works
Enable in config:
emdash({
atproto: true,
})
Login flow:
auto-linking)
user provisioned
Key design decisions:
dynamic discovery, handle input)
New routes
/_emdash/api/auth/atproto/client-metadata.json/_emdash/api/auth/atproto/login{ handle }→{ redirectUrl }/_emdash/api/auth/atproto/callback/_emdash/api/auth/atproto/complete-profile/_emdash/api/auth/atproto/verify-emailType of change
Checklist
pnpm typecheckpassespnpm --silent lint:json | jq '.diagnostics | length'returns 0pnpm testpasses (or targeted tests for my change)pnpm formathas been runAI-generated code disclosure