Auth Flow
Auth Lifecycle
Token Storage
// lib/auth-utils.ts
const TOKEN_KEY = process.env.NEXT_PUBLIC_AUTH_TOKEN_KEY || 'opentier_auth_token'
getAuthToken() // localStorage.getItem(TOKEN_KEY)
setAuthToken(t) // localStorage.setItem(TOKEN_KEY, t)
removeAuthToken() // localStorage.removeItem(TOKEN_KEY)
getAuthHeaders() // { Authorization: 'Bearer <token>' } | {}Token is a 64-character random alphanumeric string (generated server-side). It is stored in localStorage — not httpOnly cookies. This makes it accessible to JavaScript but vulnerable to XSS attacks (see Security Model).
The environment variable NEXT_PUBLIC_AUTH_TOKEN_KEY allows the key name to be overridden at build time.
AuthContext Interface
interface AuthContextType {
// State
user: UserResponse | null
isLoading: boolean
isAuthenticated: boolean
// Actions
signIn(data: SignInRequest): Promise<void> // → POST /auth/signin
signUp(data: any): Promise<void> // → POST /auth/signup
logout(): Promise<void> // → POST /auth/signout
checkAuth(): Promise<void> // → calls fetchUser()
resendVerification(email: string): Promise<void>
verifyEmail(email: string, otp: string, token?: string): Promise<void>
forgotPassword(email: string): Promise<void>
resetPassword(password: string, token: string): Promise<void>
// Modal state
isModalOpen: boolean
authView: AuthView
authError: string | null
attemptedEmail: string | null
openModal(view?: AuthView): void
closeModal(): void
setAuthView(view: AuthView): void
setAuthError(error: string | null): void
}Sign-In Sequence
SignInResponse Type
interface SignInResponse {
user_id: string
email: string
session_token: string // 64-char random alphanumeric
expires_at: string // ISO 8601, 7-day window
}OAuth Flow (Google / GitHub)
On OAuth login:
- Browser redirects to
GET /auth/oauth/{provider}/authorize→ Rust builds authorization URL and redirects to provider - Provider callback hits
GET /auth/oauth/{provider}/callbackwithcodeandstate - Rust exchanges code for access token, fetches user profile, creates/links account in
accountstable - Session is created; redirect to frontend with session token
The accounts table stores provider, provider_account_id, access_token, refresh_token per user. One user can link multiple providers.
Account Recovery
Soft-deleted users (those with deleted_at IS NOT NULL) can be restored via POST /auth/recover-account. This sets deleted_at = NULL and creates a new session. This pattern avoids permanent data loss for users who delete their account accidentally.
Last updated on