Backend Context: Accounts & Security
Relevant source files
The following files were used as context for generating this wiki page:
Introduction
The Accounts & Security context in the ProductCompare backend manages user identity, reputation, and secure access control. It serves as the foundation for multi-user collaboration, ensuring that actions such as product claims, reviews, and discussions are attributed to verified actors. The system is built as a modular monolith within Phoenix, utilizing PostgreSQL 18 for durable storage of user records and security tokens.
Security is governed by a GraphQL-first approach for browser-facing auth flows, while maintaining Phoenix as the primary authority for cookie-backed sessions. The architecture enforces strict token handling, SHA3 hashing for secrets, and global ID resolution via Relay to maintain a robust security posture across the API boundary.
Sources: backend_starting_guide.md:6-14, ARCHITECTURE.md:12-15, AGENTS.md:27-32
Identity and Access Management Architecture
The backend implements a clear separation between transport-level authentication and internal context logic. All browser-based authentication flows—including registration, login, and password resets—are migrated from legacy REST endpoints to GraphQL mutations.
Auth Contract and Session Authority
While GraphQL serves as the interface, Phoenix remains the session authority. GraphQL auth mutations (e.g., login, register) are responsible for setting or clearing the Phoenix session cookie. These mutations do not return bearer tokens for browser use; instead, they rely on the cookie-backed session for subsequent requests.
Sources: AGENTS.md:27-32, ARCHITECTURE.md:12-15
Authentication Flow
The following diagram illustrates the sequence for a standard GraphQL-based login and session establishment.
Data Modeling & Persistence
The ProductCompare.Accounts context utilizes specific schema conventions to ensure high precision and security.
Database Conventions
- Primary Keys: Tables use
bigintfor internal IDs. - Entropy IDs: Every relational table includes an
entropy_idusinguuidv7()generated by Postgres for public-facing identifiers. - Timestamps: All tables must use
:utc_datetime_usecfor microsecond precision. - User Reputation: Separate tables track
user_reputationandreputation_eventsto audit user standing.
Sources: backend_starting_guide.md:12-16, 95-101, 2026-03-03-product-compare-backend-mvp-plan.md:104-110
Security Token Storage
Tokens (for sessions, password resets, or email verification) follow a strict storage contract to prevent raw secret exposure.
| Field | Type | Description |
|---|---|---|
id | uuidv7 | DB-generated primary key (UUID exception table). |
secret_hash | binary | SHA3-256 hash of the raw token secret. |
context | enum | Purpose (e.g., session, reset_password). |
sent_to | string | Target identifier (optional). |
authenticated_at | datetime | When the token was last used. |
Sources: backend_starting_guide.md:113-122, 2026-03-03-product-compare-backend-mvp-plan.md:107-108
Authentication Semantics & Security Policy
Token Lifecycle
The system enforces specific validity windows and rotation policies:
- Session Tokens: 14 days.
- Refresh Tokens: 30 days (single-use rotation).
- Magic Links: 15 minutes.
- Email Verification: 7 days.
Secrets are never persisted in raw form. They are hashed using :sha3_256 before insertion. Validation is performed using Plug.Crypto.secure_compare/2 to prevent timing attacks.
Sources: backend_starting_guide.md:19-21, 131-143
GraphQL Authorization Precedence
The GraphQL layer injects context via a dedicated plug with the following priority:
Authorization: Bearer ...headers are processed first.- Session cookies are used as a fallback only if the bearer header is absent.
- If a bearer header is present but malformed, the system must not fall back to the session.
Sources: backend_starting_guide.md:150-155
GraphQL Interface (Accounts)
The viewer and authentication mutations define the frontend-backend security contract.
Key Auth Mutations
| Mutation | Input Fields | Output Payload |
|---|---|---|
login | email, password | viewer, errors |
register | email, password | viewer, errors |
forgotPassword | email | ok (boolean), errors |
resetPassword | token, password | ok (boolean), errors |
verifyEmail | token | ok (boolean), errors |
Sources: assets/tests/e2e/auth.spec.ts:114-190, AGENTS.md:29-32
Error Handling
Errors are returned as structured payloads rather than generic booleans. Common error codes include INVALID_CREDENTIALS, INVALID_TOKEN, EXPIRED_TOKEN, and REVOKED_TOKEN. The frontend renders these based on the field and message attributes in the GraphQL response.
Sources: backend_starting_guide.md:137-140, assets/src/routes/auth/tests/session.route.test.tsx:90-112
Implementation Invariants
To maintain the integrity of the security context, developers must adhere to the following logic:
- Write Scoping: Write operations (mutations) must derive the actor from the authenticated
viewerscope, never from client-supplied IDs. - Hashing: Use
argon2_elixirfor passwords andSHA3-256for tokens. - Audit Trails: Significant auth lifecycle actions must be recorded in an append-only
auth_eventstable.
Sources: backend_starting_guide.md:46, 145, 172-173
Summary
The Accounts & Security module provides a high-assurance identity layer for ProductCompare. By combining the robustness of Argon2 and SHA3 hashing with a GraphQL-first authentication strategy, the system ensures secure collaborative workflows. The transition to a cookie-backed session authority managed via GraphQL mutations aligns modern frontend requirements with proven backend security patterns.