feat(auth): M3 auth flow -- AT Protocol OAuth (#26)
* feat(auth): add auth API types, client functions, and MSW handlers
Add AuthSession/AuthUser types with displayName and avatarUrl fields.
Add initiateLogin, handleCallback, refreshSession, logout, and
getCurrentUser API client functions. Add corresponding MSW mock
handlers for all auth endpoints.
* feat(auth): add auth-aware fetch wrapper with 401 retry
Creates createAuthFetch factory that intercepts 401 responses,
attempts silent token refresh, and retries the request once.
Deduplicates concurrent refresh requests.
* feat(auth): add AuthProvider context and useAuth hook
AuthProvider stores access token in useRef (memory only, never
localStorage). Attempts silent refresh on mount via HTTP-only cookie.
Exposes login, logout, setSessionFromCallback, getAccessToken, and
authFetch. useAuth hook provides runtime guard for provider boundary.
Wraps children in root layout.
* feat(auth): ✨ add login, callback, and protected route pages
Login page accepts AT Protocol handle and redirects to PDS OAuth.
Callback page processes code/state params and sets session in context.
Both wrapped in Suspense for useSearchParams. ProtectedRoute redirects
unauthenticated users to /login with returnTo param.
* feat(auth): ✨ add UserMenu dropdown and integrate in forum layout
UserMenu shows avatar dropdown for authenticated users with profile,
settings, and logout links. Shows login button for guests. Uses Radix
DropdownMenu via shadcn/ui. Replaces notification bell area in header.
* refactor(auth): replace MOCK_TOKEN and localStorage with useAuth
Migrate all pages from hardcoded MOCK_TOKEN and localStorage-based
token retrieval to getAccessToken() from the useAuth hook. Removes
all direct token storage access in favor of memory-only tokens.
* test(auth): add auth flow tests and update existing test mocks
Add tests for AuthProvider, auth-fetch wrapper, login page, callback
page, protected route, and user menu. Add useAuth mock to all existing
tests that render components using the auth context. Make getAccessToken
mock configurable for tests checking unauthenticated behavior.
* fix(auth): type mock getAccessToken to accept null return value
authored by