Auth & Session
This area covers 19 features. Watch the walkthrough, then use the reference below — each feature links to the exact moment it appears (▶).
Who this is for: Project lead / owner. Each feature below lists the role/permission it requires.
Features
Sign-up with username, email, password ▶ 00:06
As a new user, I want to create an account with a username, email, and password, so that I can access the app.
How it works. User can access sign-up form via 'Create an account' link in AccountSwitcher dialog. Form accepts username (3-50 chars), email, and password (≥8 chars). Form displays password requirements: min 8 chars and does not contain email. Password strength indicator shows weak/medium/strong based on length + digits + symbols. Submit button disabled until all fields valid and user online. On success, user signs in and dialog closes.
Key files
src/components/git-import/FrontierSignupForm.tsx:1-180src/lib/frontier/auth.ts:56-75src/hooks/useFrontierSession.ts:32-39
Login with username or email + password ▶ 00:06
As an existing user, I want to sign in with my username or email and password, so that I can access my projects.
How it works. User navigates to /login or clicks 'Add another account' in AccountSwitcher. Login form accepts username/email and password. Show/hide password toggle (eye icon) reveals plaintext. Submit button disabled until both fields filled. On invalid credentials, shows 401 error 'Invalid username or password'. On success, user authenticated and redirected to ?next parameter or /. Offline state detected and submit disabled with message.
Key files
src/pages/Login.tsx:1-137src/components/git-import/FrontierLoginForm.tsx:1-104src/lib/frontier/auth.ts:40-54src/hooks/useFrontierSession.ts:24-30
Show/hide password toggle on login ▶ 00:00
As a user logging in, I want to verify my password by toggling visibility, so that I can confirm I typed it correctly.
How it works. Login form password input type='password' by default. Eye icon button toggles to show password as plaintext (type='text'). Button aria-label changes from 'Show password' to 'Hide password'. Icon switches from Eye to EyeOff. Toggle can be clicked repeatedly.
Key files
src/pages/Login.tsx:89-108src/components/git-import/FrontierLoginForm.tsx:79-96
Forgot password flow ▶ 00:00
As a user who forgot my password, I want to request a reset link, so that I can set a new password.
How it works. From login form, user clicks 'Forgot password?' link. Dialog switches to FrontierForgotPasswordForm with title 'Reset your password'. Form has email input (must match /.+@.+\.+/ pattern). 'Send reset link' button disabled until valid email. On submit, calls POST /api/v2/auth/password-reset/request { email }. Shows success message with email display and 'Back to login' button. Users can click 'Back to login' to return to login form without submitting.
Key files
src/components/git-import/FrontierForgotPasswordForm.tsx:1-73src/lib/frontier/auth.ts:99-115src/components/AccountSwitcher.tsx:72-76
Password reset token verification ▶ 00:00
As a user with a reset link, I want to verify the link is valid before entering a new password, so that I know my reset request was legitimate.
How it works. User navigates to /reset-password?token=<token>&username=<username>. Page calls POST /api/v2/auth/password-reset/verify { token, username }. While verifying, shows 'Verifying link…' loading state. On 401/invalid: shows TokenExpiredView with form to request new link. On 200/valid: shows new password form. Form displays password checklist (8+ chars, not contain email). Submit button disabled until password valid.
Key files
src/pages/ResetPassword.tsx:1-282src/lib/frontier/auth.ts:125-141
Password reset execution ▶ 00:00
As a user resetting my password, I want to set a new password and be automatically signed in, so that I can continue without clicking login again.
How it works. On /reset-password with valid token, user enters new password (≥8 chars). Form validates and shows checklist. On submit, calls POST /api/v2/auth/password-reset/reset { token, username, new_password }. On success, automatically calls login(username, new_password) to sign user in, then redirects to /. On 400/500 error, shows message and keeps user on form.
Key files
src/pages/ResetPassword.tsx:207-223src/lib/frontier/auth.ts:150-170
Password strength indicator ▶ 00:00
As a user creating or resetting a password, I want to see password strength feedback, so that I can create a secure password.
How it works. Signup and reset forms display PasswordChecklist when user types. Shows green Check for 'At least 8 characters' when met. Shows Check for 'Does not contain your email' when met. Displays colored progress bar (red=weak 1/3, yellow=medium 2/3, green=strong full). Text label 'Strength: weak|medium|strong'. Strength calculated as: ≥8 chars (+1), ≥12 chars (+1), digits (+1), symbols (+1). Score ≤1=weak, 2=medium, ≥3=strong.
Key files
src/components/git-import/FrontierSignupForm.tsx:10-77src/pages/ResetPassword.tsx:40-79
Session persistence to IndexedDB ▶ 00:00
As a user, I want my session to persist across page reloads, so that I don't need to log in repeatedly.
How it works. On successful login/register, FrontierSession (jwt, username, createdAt, email) saved to IndexedDB 'frontier' store with key 'envelope'. Session stored in envelope.sessions[username] and envelope.active set to username. On cold boot, loadActiveSession() retrieves session from IndexedDB. If session exists, user is logged in without re-authenticating. Session cleared only on logout.
Key files
src/lib/frontier/session-store.ts:1-156src/lib/frontier/auth.ts:194-209src/hooks/useAccounts.ts:17-22
Auth hint cookie for edge routing ▶ 00:00
As a returning user, I want the Worker/edge to know I'm logged in without waiting for IndexedDB, so that the SPA loads quickly.
How it works. On successful login, IndexedDB write also sets aq_hint=1 cookie (Max-Age=31536000, Path=/, SameSite=Lax, no Domain). On logout, cookie deleted. On app boot, hasAuthHintCookie() checks if aq_hint=1 present. If no hint and not onboarded, RootRedirect sends hard window.location.replace('/homepage'). Hint scoped to host only (dev.aquilla.app vs aquilla.app vs localhost isolated).
Key files
src/lib/frontier/session-store.ts:50-68src/App.tsx:93-106
Account switcher dropdown ▶ 00:00
As a user with multiple accounts, I want to see all my signed-in accounts in one place, so that I can switch between them or manage them.
How it works. AccountSwitcher button shows active user's avatar + username. Click opens dropdown showing 'Signed in' header, active account entry (with checkmark), list of other accounts (clickable to switch), 'Preferences' link, 'Add another account…' button, 'Log out' button. If >1 account, adds 'Sign out of all accounts' (destructive color). Clicking other account calls activateSession(), clears React Query cache and local project data. Clicking remove button (hover icon) on non-active account removes it.
Key files
src/components/AccountSwitcher.tsx:1-343src/hooks/useAccounts.ts:35-45
Single-account logout ▶ 00:00
As a user, I want to sign out, so that my account is no longer active on this device.
How it works. User clicks 'Log out' in AccountSwitcher. If outbox has pending edits, shows warning dialog with edit count and 'Log out anyway' button. On confirm or if no pending edits, clearSession() removes active session from IndexedDB, clears auth hint cookie, clears local project data, and wipes React Query cache. If other accounts exist, next account becomes active. App returns to login state.
Key files
src/components/AccountSwitcher.tsx:122-144, 234-239src/lib/frontier/session-store.ts:145-150src/hooks/useFrontierSession.ts:41-48
Sign out of all accounts ▶ 00:00
As a user with multiple logged-in accounts, I want to sign out globally, so that all my accounts are cleared from this device.
How it works. User clicks 'Sign out of all accounts' (only visible if >1 account). Shows warning about pending edits if any. On confirm, loops through all sessions and calls removeSession() for each, then clearAllLocalData() and query cache clear. All sessions removed from IndexedDB, auth hint cookie deleted. App returns to signed-out state.
Key files
src/components/AccountSwitcher.tsx:241-249, 132-144src/lib/frontier/session-store.ts:115-124
Session expiry banner (401 handling) ▶ 00:00
As a user whose session expired while working, I want a non-intrusive notification, so that I can re-sign-in without losing context.
How it works. When any fetch helper receives 401 (expired session), it calls notifySessionExpired(). SessionExpiredBanner subscribes to signal, shows fixed top banner (amber, role=alert, aria-live=assertive) with message 'Your session expired. Sign in again to continue.' Banner has dismiss button (X icon). Banner clears on navigation or user dismiss. Shows link to /login?next=<current-path> to restore context.
Key files
src/components/SessionExpiredBanner.tsx:1-62src/lib/errors/session-expired-signal.ts:1-31
Dev-only auto-login route (__dev/login) ▶ 00:00
As a developer testing the app, I want a one-click login shortcut, so that I don't manually enter credentials repeatedly.
How it works. Navigate to /__dev/login. If import.meta.env.DEV false, shows error 'dev login is disabled in production builds'. If DEV true, calls devLogin() which hits POST /__dev__/login on auth-worker. Returns mocked session with seeded dev user. On success, redirects to /project/dev-project. On 404 (WRANGLER_LOCAL not set), shows error 'dev login endpoint unavailable'. Cancellation-safe (cleanup on unmount).
Key files
src/components/DevLoginRoute.tsx:1-62src/lib/frontier/auth.ts:83-97
Dev-only logout route (__dev/logout) ▶ 00:00
As a developer, I want a quick way to clear my session and return to onboarding, so that I can test the sign-in flow without manual cleanup.
How it works. Navigate to /__dev/logout. If import.meta.env.DEV true, calls logout() to clear session and redirect to /onboarding. If DEV false (production build), silently redirects to / with session intact. Dev route is no-op in built apps (verified by e2e in preview mode).
Key files
src/components/DevLogoutRoute.tsx:1-35src/hooks/useFrontierSession.ts:41-48
Private browsing mode banner ▶ 00:00
As a user in private browsing, I want to know storage is unavailable, so that I understand why loads are slow.
How it works. PrivateModeBanner detects if OPFS unavailable (navigator.storage.getDirectory() throws in Safari private, Brave Tor, locked profiles). Shows one-time banner per session: 'Private browsing is restricting local storage…' with icon, message, and dismiss button. Banner stored per username+createdAt in sessionStorage. Dismissal persists for duration of session. Accessible state: role=alert not used (informational, not urgent).
Key files
src/components/PrivateModeBanner.tsx:1-83src/hooks/useOpfsAvailability.ts
JWT token persistence and claims extraction ▶ 00:00
As the app, I want to store the JWT and extract user info without validation, so that I know the canonical username and email.
How it works. On login, server returns access_token. Frontend decodes JWT payload (base64url to base64 conversion, JSON.parse) and extracts 'sub' (username) and 'email' claims without signature verification (server verified password). Falls back to login identifier if decode fails. Session stored with username from JWT sub claim, email from claim if present. Invalid/malformed tokens handled gracefully (returns empty {}).
Key files
src/lib/frontier/auth.ts:177-209
PostHog analytics integration with auth ▶ 00:00
As the analytics system, I want to track auth events and attribute them to users, so that we can monitor signup/login patterns.
How it works. On login, computes distinct_id as SHA-256(username), calls posthog.identify(distinctId), and captures 'user logged in' event. On signup, same identify + capture 'user signed up' with email property (only if analytics consent given). On logout, captures 'user logged out' and calls posthog.reset().
Key files
src/hooks/useFrontierSession.ts:24-48
Offline detection on login/signup ▶ 00:00
As a user without internet, I want to see a message explaining why I can't sign in, so that I understand the issue.
How it works. FrontierLoginForm and FrontierSignupForm detect navigator.onLine and window online/offline events. When offline, shows alert message 'You're offline — connect to sign in' and disables submit button. When online again, button re-enables. Message dismisses on successful auth or stays on network error.
Key files
src/components/git-import/FrontierLoginForm.tsx:27-38, 57-59, 99src/components/git-import/FrontierSignupForm.tsx:87-98, 123-127, 105