File size: 8,081 Bytes
9eafd9f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# 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