Authentication & Session Flow
Relevant source files
The following files were used as context for generating this wiki page:
Introduction
The Authentication and Session Flow in the Product Compare project is built around a GraphQL-first architecture that leverages Phoenix as the cookie-backed session authority. The system is currently migrating from legacy REST endpoints to a unified GraphQL contract served at /api/graphql. This flow handles user identification, account creation, and session persistence through secure, hashed tokens and browser cookies.
The frontend, built with Bun and React Router, interacts with the backend via Relay-compliant GraphQL mutations. While the backend handles the persistence of session state in PostgreSQL and temporary runtime state in supervised Elixir processes, the frontend manages user state by responding to the presence or absence of a viewer object returned by the GraphQL API.
Sources: AGENTS.md:16-24, ARCHITECTURE.md:12-15, backend_starting_guide.md:7-14
System Architecture & Constraints
The authentication system adheres to several non-negotiable conventions to ensure security and maintainability.
- Session Authority: Phoenix remains the sole authority for cookie-backed sessions. GraphQL mutations for login and registration set or clear these cookies directly.
- Token Security: Token secrets are never stored in raw form. They are persisted as SHA3 hashes (
:sha3_256). - Transport Format: Authentication tokens follow a specific serialization:
"<uuid>.<base64url(raw_secret_without_padding)>". - Database Defaults: Relation tables, including those for users, use
bigintprimary keys anduuidv7entropy IDs.
Sources: backend_starting_guide.md:21-30, backend_starting_guide.md:125-135, AGENTS.md:20-22
Authentication Data Model
The following table describes the core fields required for token handling and user identification.
| Field | Type | Description |
|---|---|---|
id | uuidv7 | DB-generated unique identifier. |
secret_hash | binary | SHA3-256 hash of the token secret. |
context | enum | Purpose of the token (e.g., session, reset). |
sent_to | string | Target identifier (optional). |
authenticated_at | datetime | Timestamp of successful validation. |
Sources: backend_starting_guide.md:127-133, backend_starting_guide.md:104-108
GraphQL Authentication Contract
The frontend auth contract is defined by the viewer query and specific mutations. Browser-facing auth must occur over /api/graphql rather than REST.
Mutation Flow
When a user attempts to sign in or register, the frontend executes a GraphQL mutation. The backend validates credentials (using argon2_elixir) and returns an AuthSessionPayload.
Core Auth Mutations
The project implements several key mutations to manage the authentication lifecycle:
login: Authenticates a user and establishes a session.register: Creates a new user account and logs them in.logout: Clears the session cookie.forgotPassword: Initiates a recovery flow by sending a reset link.resetPassword: Consumes a recovery token to update user credentials.verifyEmail: Validates a user's email address via a token.
Sources: assets/schema.graphql:13-20, assets/src/routes/auth/actions.ts:40-108
Frontend Implementation
The frontend uses a set of shared primitives and action helpers to interact with the GraphQL auth service.
Session Management Helpers
The actions.ts file provides wrapper functions that handle the execution of GraphQL mutations and the normalization of response payloads.
// assets/src/routes/auth/actions.ts:110-113
export async function loginWithPassword(email: string, password: string) {
const response = await fetchGraphQL(LOGIN_MUTATION, { email, password });
return parseSessionPayload(response, "login");
}
The parseSessionPayload function ensures that if a viewer is not present, the result contains at least one error, falling back to a generic "Request failed" message if necessary to avoid exposing internal server details.
Sources: assets/src/routes/auth/actions.ts:125-133, assets/src/routes/auth/actions.ts:175-185
Error Handling & Validation
Authentication errors are structured to allow field-level feedback in the UI. The MutationError type includes a code, an optional field, and a message.
Token Lifecycles & Windows
The backend enforces strict validity windows for different types of authentication tokens:
| Token Type | Validity Period |
|---|---|
| Access / Session | 14 days |
| Refresh Token | 30 days |
| Email Magic Link | 15 minutes |
| Email Verification | 7 days |
The system uses single-use refresh rotation, where an old refresh token is consumed atomically and a new access/refresh pair is issued.
Sources: backend_starting_guide.md:144-148, backend_starting_guide.md:154-156
Conclusion
The Authentication & Session Flow provides a secure, Relay-first mechanism for managing user identity. By centralizing the auth contract in GraphQL while retaining Phoenix as the session authority, the project ensures a consistent security model across browser and API interactions. The use of SHA3 hashing for tokens and single-use rotation strategies reflects a robust approach to session integrity.
Sources: ARCHITECTURE.md:12-15, backend_starting_guide.md:154-159