# Data Model: Authentication & API Security **Feature**: 001-auth-security **Date**: 2026-01-09 **Phase**: 1 - Design ## Overview This document defines the data entities and their relationships for the authentication and API security feature. The primary entity is the User, which will be extended to support password-based authentication. ## Entities ### User (Modified) **Purpose**: Represents a registered user account with authentication credentials. **Table**: `users` **Fields**: | Field | Type | Constraints | Description | |-------|------|-------------|-------------| | id | Integer | PRIMARY KEY, AUTO_INCREMENT | Unique user identifier | | email | String(255) | UNIQUE, NOT NULL, INDEX | User's email address (used for login) | | name | String(100) | NOT NULL | User's display name | | password_hash | String(255) | NOT NULL | Bcrypt-hashed password (NEW) | | created_at | DateTime | NOT NULL, DEFAULT NOW() | Account creation timestamp | | updated_at | DateTime | NOT NULL, DEFAULT NOW() | Last update timestamp | **Indexes**: - PRIMARY KEY on `id` - UNIQUE INDEX on `email` - INDEX on `created_at` (for sorting/filtering) **Relationships**: - One-to-Many with Task (one user has many tasks) **Validation Rules**: - Email must be valid RFC 5322 format - Email must be unique (enforced at database level) - Password must be hashed with bcrypt before storage - Name must be 1-100 characters - password_hash must be exactly 60 characters (bcrypt output length) **State Transitions**: None (users don't have state in this spec) **Security Considerations**: - Password is never stored in plain text - Password hash uses bcrypt with cost factor 12 - Email is indexed for fast lookup during authentication - created_at and updated_at track account lifecycle --- ### Task (Existing - No Changes) **Purpose**: Represents a to-do item owned by a user. **Table**: `tasks` **Fields**: | Field | Type | Constraints | Description | |-------|------|-------------|-------------| | id | Integer | PRIMARY KEY, AUTO_INCREMENT | Unique task identifier | | user_id | Integer | FOREIGN KEY(users.id), NOT NULL, INDEX | Owner of the task | | title | String(200) | NOT NULL | Task title | | description | String(1000) | NULLABLE | Task description | | completed | Boolean | NOT NULL, DEFAULT FALSE, INDEX | Completion status | | created_at | DateTime | NOT NULL, DEFAULT NOW(), INDEX | Creation timestamp | | updated_at | DateTime | NOT NULL, DEFAULT NOW() | Last update timestamp | **Relationships**: - Many-to-One with User (many tasks belong to one user) **Security Note**: All task queries MUST filter by authenticated user_id to enforce data isolation. --- ### JWT Token (Virtual Entity - Not Stored) **Purpose**: Represents an authentication token issued by Better Auth and verified by the backend. **Storage**: Not persisted in database (stateless authentication) **Structure** (JWT Payload): | Claim | Type | Description | |-------|------|-------------| | sub | String | User ID (subject) | | email | String | User's email address | | iat | Integer | Issued at timestamp (Unix epoch) | | exp | Integer | Expiration timestamp (Unix epoch, iat + 7 days) | | iss | String | Issuer (Better Auth) | **Validation Rules**: - Token must be signed with BETTER_AUTH_SECRET using HS256 - Token must not be expired (exp > current time) - Token must contain valid sub (user ID) - Token signature must be valid **Lifecycle**: 1. Issued by Better Auth upon successful authentication 2. Included in Authorization header for API requests 3. Verified by backend on every protected endpoint 4. Expires after 7 days (no refresh in this spec) --- ## Database Migrations ### Migration 002: Add User Password Field **File**: `backend/alembic/versions/002_add_user_password.py` **Changes**: - Add `password_hash` column to `users` table - Column is NOT NULL (existing users will need password set) **Upgrade**: ```sql ALTER TABLE users ADD COLUMN password_hash VARCHAR(255) NOT NULL; ``` **Downgrade**: ```sql ALTER TABLE users DROP COLUMN password_hash; ``` **Data Migration Note**: If existing users exist without passwords, they will need to be handled separately (e.g., force password reset on first login, or seed with temporary passwords). --- ## Entity Relationships Diagram ``` ┌─────────────────────────────────────┐ │ User │ ├─────────────────────────────────────┤ │ id (PK) │ │ email (UNIQUE) │ │ name │ │ password_hash (NEW) │ │ created_at │ │ updated_at │ └─────────────────────────────────────┘ │ │ 1:N │ ▼ ┌─────────────────────────────────────┐ │ Task │ ├─────────────────────────────────────┤ │ id (PK) │ │ user_id (FK → User.id) │ │ title │ │ description │ │ completed │ │ created_at │ │ updated_at │ └─────────────────────────────────────┘ ``` --- ## Data Access Patterns ### Authentication Flow 1. User submits email + password to Better Auth 2. Better Auth verifies credentials against users table 3. Better Auth issues JWT token with user_id in `sub` claim 4. Frontend stores token in httpOnly cookie ### API Request Flow 1. Frontend includes JWT in Authorization header 2. Backend extracts token from header 3. Backend verifies token signature and expiration 4. Backend extracts user_id from `sub` claim 5. Backend filters data by user_id ### Task Query Pattern ```sql -- All task queries MUST include user_id filter SELECT * FROM tasks WHERE user_id = :authenticated_user_id; -- Example: Get user's completed tasks SELECT * FROM tasks WHERE user_id = :authenticated_user_id AND completed = true ORDER BY created_at DESC; ``` --- ## Validation Summary ### User Entity - ✅ Email format validation (RFC 5322) - ✅ Email uniqueness (database constraint) - ✅ Password strength (minimum 8 chars, complexity rules) - ✅ Password hashing (bcrypt, cost 12) - ✅ Name length (1-100 characters) ### JWT Token - ✅ Signature validation (HS256 with shared secret) - ✅ Expiration validation (exp claim) - ✅ Required claims present (sub, email, iat, exp) - ✅ User ID extraction (from sub claim) ### Task Entity (Security) - ✅ User ownership validation (user_id matches token) - ✅ Query filtering (all queries include user_id) - ✅ Authorization checks (prevent cross-user access) --- ## Performance Considerations ### Indexes - `users.email` - UNIQUE INDEX for fast authentication lookups - `tasks.user_id` - INDEX for fast user task queries - `tasks.completed` - INDEX for filtering by status - `tasks.created_at` - INDEX for sorting ### Query Optimization - JWT verification is stateless (no database lookup) - User lookup by email is O(1) with index - Task queries filtered by indexed user_id - Pagination supported for large task lists --- ## Security Checklist - [x] Passwords never stored in plain text - [x] Bcrypt hashing with appropriate cost factor - [x] Email uniqueness enforced at database level - [x] JWT tokens contain minimal claims (no sensitive data) - [x] Token expiration enforced (7 days) - [x] User ID extracted from validated token only - [x] All task queries filtered by authenticated user - [x] Foreign key constraints prevent orphaned tasks