Add SP1 atproto OAuth (login, session, the Agent)
Gate the editor behind atproto OAuth using a browser public client
(@atproto/oauth-client-browser), per Decision 0004 — chosen over the brief's
named @atproto/oauth-client-node because a confidential client needs a
server-side session store, which would reintroduce the database the no-DB/edge
architecture avoids. A public client (PKCE/DPoP, tokens in IndexedDB) needs no
backend; "secrets never in the client" holds trivially (no secret).
Flow: client.init() once on load (restore session or process the redirect
callback) -> signIn(handle) redirects to the user's auth server -> session ->
new Agent(session) for later com.atproto.repo.* calls (SP2).
- Pure config/handle/scope logic in src/lib/auth/config.ts (7 Vitest tests).
- AuthProvider context + useAuth + LoginForm; Studio island gates SkyEditor.
- Dev uses an atproto loopback client; the client_id must be path-less and the
origin must be 127.0.0.1 (not localhost) — oauth.ts builds it explicitly.
- public/client-metadata.json for the hosted (prod) client (origin finalized in
SP7).
Verified end-to-end against a real Bluesky account: redirect to the genuine
bsky.social authorize page, callback exchange, signed-in editor, and session
persistence across reload. SP1 scope is atproto + transition:generic at the
metadata level; granular write scopes land in SP2.