Skip to Content
Client LayerAuth Flow

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 localStoragenot 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:

  1. Browser redirects to GET /auth/oauth/{provider}/authorize → Rust builds authorization URL and redirects to provider
  2. Provider callback hits GET /auth/oauth/{provider}/callback with code and state
  3. Rust exchanges code for access token, fetches user profile, creates/links account in accounts table
  4. 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