File size: 11,208 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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
# Authentication & API Security - Testing Guide

**Feature**: Authentication & API Security (Spec 001)
**Date**: 2026-01-09
**Status**: Ready for Testing

## Prerequisites

Before testing, ensure:

1. **Backend is running**:
   ```bash
   cd backend
   python -m uvicorn src.main:app --reload
   # Should be running at http://localhost:8000
   ```

2. **Database migrations applied**:
   ```bash
   cd backend
   python -m alembic upgrade head
   ```

3. **Frontend is running**:
   ```bash
   cd frontend
   npm run dev
   # Should be running at http://localhost:3000
   ```

4. **Environment variables configured**:
   - `backend/.env` has `BETTER_AUTH_SECRET`
   - `frontend/.env.local` has same `BETTER_AUTH_SECRET`
   - Both secrets match exactly

## Test Suite

### T048: Test Signup Flow End-to-End

**Objective**: Verify new users can create accounts and data is stored correctly in database

**Steps**:

1. **Navigate to signup page**:
   - Open browser to `http://localhost:3000/auth/signup`
   - Verify signup form is displayed with email, password, and name fields

2. **Test validation errors**:
   - Try submitting with empty fields → Should show validation errors
   - Try weak password (e.g., "pass") → Should show "Password must be at least 8 characters"
   - Try invalid email (e.g., "notanemail") → Should show email format error

3. **Create valid account**:
   - Email: `test1@example.com`
   - Password: `SecurePass123`
   - Name: `Test User 1`
   - Click "Sign Up"
   - **Expected**: Success message, redirect to signin page

4. **Verify in database**:
   ```bash
   # Connect to your database and run:
   SELECT id, email, name, password_hash, created_at FROM users WHERE email = 'test1@example.com';
   ```
   - **Expected**: User record exists
   - **Expected**: `password_hash` is bcrypt hash (starts with `$2b$`)
   - **Expected**: `created_at` timestamp is recent

5. **Test duplicate email**:
   - Try signing up again with `test1@example.com`
   - **Expected**: 409 Conflict error "Email already registered"

**Pass Criteria**:
- ✅ Form validation works correctly
- ✅ Valid signup creates user in database
- ✅ Password is hashed (not stored in plain text)
- ✅ Duplicate email is rejected with 409 error
- ✅ User is redirected to signin after successful signup

---

### T049: Test Signin Flow End-to-End

**Objective**: Verify users can authenticate and receive valid JWT tokens

**Steps**:

1. **Navigate to signin page**:
   - Open browser to `http://localhost:3000/auth/signin`
   - Verify signin form is displayed

2. **Test invalid credentials**:
   - Email: `test1@example.com`
   - Password: `WrongPassword123`
   - Click "Sign In"
   - **Expected**: 401 error "Invalid email or password"

3. **Test valid credentials**:
   - Email: `test1@example.com`
   - Password: `SecurePass123`
   - Click "Sign In"
   - **Expected**: Success, redirect to home page (`/`)

4. **Verify JWT token**:
   - Open browser DevTools → Application → Local Storage → `http://localhost:3000`
   - Find `auth_session` key
   - **Expected**: JSON object with `token` and `user` fields
   - Copy the token value

5. **Decode JWT token** (use jwt.io or command line):
   ```bash
   # Using Python
   python -c "import jwt; print(jwt.decode('YOUR_TOKEN_HERE', options={'verify_signature': False}))"
   ```
   - **Expected payload**:
     ```json
     {
       "sub": "1",  // User ID
       "email": "test1@example.com",
       "iat": 1704067200,  // Issued at timestamp
       "exp": 1704672000,  // Expiration (7 days later)
       "iss": "better-auth"
     }
     ```

6. **Verify session persistence**:
   - Refresh the page
   - **Expected**: Still logged in (no redirect to signin)
   - **Expected**: User name displayed in header

7. **Test signout**:
   - Click "Sign Out" button in header
   - **Expected**: Redirect to signin page
   - **Expected**: localStorage `auth_session` is cleared

**Pass Criteria**:
- ✅ Invalid credentials return 401 error
- ✅ Valid credentials return JWT token
- ✅ Token contains correct user_id, email, and expiration
- ✅ Token expiration is 7 days from issuance
- ✅ Session persists across page refreshes
- ✅ Signout clears session and redirects

---

### T050: Test Protected API Access

**Objective**: Verify API endpoints require valid JWT tokens and reject invalid tokens

**Steps**:

1. **Test unauthenticated request**:
   ```bash
   # Try to fetch tasks without token
   curl http://localhost:8000/api/tasks
   ```
   - **Expected**: 401 Unauthorized
   - **Expected**: Response body: `{"detail": "Not authenticated"}`

2. **Test with valid token**:
   - Sign in to get a valid token (from T049)
   - Copy token from localStorage
   ```bash
   # Replace YOUR_TOKEN with actual token
   curl http://localhost:8000/api/tasks \
     -H "Authorization: Bearer YOUR_TOKEN"
   ```
   - **Expected**: 200 OK
   - **Expected**: Returns task list (may be empty)

3. **Test with invalid token**:
   ```bash
   curl http://localhost:8000/api/tasks \
     -H "Authorization: Bearer invalid_token_here"
   ```
   - **Expected**: 401 Unauthorized
   - **Expected**: Error message about invalid token

4. **Test with expired token**:
   - Manually create an expired token or wait 7 days (not practical)
   - Alternative: Temporarily change `JWT_EXPIRATION_DAYS=0` in backend/.env, restart backend, get new token, wait 1 minute
   ```bash
   curl http://localhost:8000/api/tasks \
     -H "Authorization: Bearer EXPIRED_TOKEN"
   ```
   - **Expected**: 401 Unauthorized
   - **Expected**: Error code `TOKEN_EXPIRED`

5. **Test frontend automatic redirect**:
   - In browser, manually edit localStorage to set invalid token
   - Try to access home page (`/`)
   - **Expected**: Automatic redirect to `/auth/signin`

6. **Test all protected endpoints**:
   - With valid token, test:
     - `GET /api/tasks` → 200 OK
     - `POST /api/tasks` → 201 Created
     - `GET /api/tasks/{id}` → 200 OK
     - `PATCH /api/tasks/{id}` → 200 OK
     - `DELETE /api/tasks/{id}` → 204 No Content
     - `GET /api/auth/me` → 200 OK

**Pass Criteria**:
- ✅ Requests without token return 401
- ✅ Requests with valid token succeed
- ✅ Requests with invalid token return 401
- ✅ Requests with expired token return 401 with TOKEN_EXPIRED
- ✅ Frontend automatically redirects on 401
- ✅ All task endpoints require authentication

---

### T051: Test User Data Isolation

**Objective**: Verify users can only access their own tasks, not other users' tasks

**Steps**:

1. **Create two user accounts**:
   - User A: `usera@example.com` / `PasswordA123`
   - User B: `userb@example.com` / `PasswordB123`

2. **Sign in as User A**:
   - Navigate to `/auth/signin`
   - Sign in with User A credentials
   - Copy User A's JWT token from localStorage

3. **Create tasks as User A**:
   - Create 2-3 tasks through the UI
   - Note the task IDs (check Network tab or database)

4. **Sign out and sign in as User B**:
   - Click "Sign Out"
   - Sign in with User B credentials
   - Copy User B's JWT token

5. **Create tasks as User B**:
   - Create 2-3 different tasks through the UI

6. **Verify User B cannot see User A's tasks**:
   - Check the task list in UI
   - **Expected**: Only User B's tasks are visible
   - **Expected**: User A's tasks are NOT visible

7. **Test API-level isolation**:
   ```bash
   # Get User A's task ID from database
   # Try to access it with User B's token
   curl http://localhost:8000/api/tasks/USER_A_TASK_ID \
     -H "Authorization: Bearer USER_B_TOKEN"
   ```
   - **Expected**: 404 Not Found (task doesn't exist for User B)

8. **Test cross-user modification attempt**:
   ```bash
   # Try to update User A's task with User B's token
   curl -X PATCH http://localhost:8000/api/tasks/USER_A_TASK_ID \
     -H "Authorization: Bearer USER_B_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"completed": true}'
   ```
   - **Expected**: 404 Not Found

9. **Test cross-user deletion attempt**:
   ```bash
   # Try to delete User A's task with User B's token
   curl -X DELETE http://localhost:8000/api/tasks/USER_A_TASK_ID \
     -H "Authorization: Bearer USER_B_TOKEN"
   ```
   - **Expected**: 404 Not Found

10. **Verify in database**:
    ```sql
    -- Check that tasks are correctly associated with users
    SELECT id, user_id, title FROM tasks ORDER BY user_id, id;
    ```
    - **Expected**: User A's tasks have `user_id = 1`
    - **Expected**: User B's tasks have `user_id = 2`
    - **Expected**: No cross-contamination

**Pass Criteria**:
- ✅ User A can only see their own tasks
- ✅ User B can only see their own tasks
- ✅ User B cannot access User A's tasks via API (404)
- ✅ User B cannot modify User A's tasks (404)
- ✅ User B cannot delete User A's tasks (404)
- ✅ Database correctly associates tasks with user_id
- ✅ All queries are filtered by authenticated user

---

## Test Results Summary

After completing all tests, fill in the results:

| Test | Status | Notes |
|------|--------|-------|
| T048: Signup Flow | ⬜ Pass / ⬜ Fail | |
| T049: Signin Flow | ⬜ Pass / ⬜ Fail | |
| T050: Protected API | ⬜ Pass / ⬜ Fail | |
| T051: User Isolation | ⬜ Pass / ⬜ Fail | |

## Common Issues & Troubleshooting

### Issue: "BETTER_AUTH_SECRET not found"
- **Cause**: Environment variable not set
- **Fix**: Ensure both `backend/.env` and `frontend/.env.local` have `BETTER_AUTH_SECRET`
- **Verify**: Secrets must be identical in both files

### Issue: "Token signature verification failed"
- **Cause**: Frontend and backend have different secrets
- **Fix**: Copy exact same secret to both .env files
- **Verify**: Run `grep BETTER_AUTH_SECRET backend/.env frontend/.env.local`

### Issue: "401 Unauthorized" on all requests
- **Cause**: Token not being sent or invalid
- **Fix**: Check localStorage has valid token, check Authorization header in Network tab

### Issue: "User can see other users' tasks"
- **Cause**: Missing user_id filter in queries
- **Fix**: Verify `get_current_user` dependency is applied to all task endpoints
- **Check**: `backend/src/api/routes/tasks.py` should use `current_user_id = Depends(get_current_user)`

### Issue: Database migration errors
- **Cause**: Migration not applied or database out of sync
- **Fix**: Run `python -m alembic upgrade head` in backend directory

## Security Checklist

After testing, verify:

- [ ] Passwords are hashed (never stored in plain text)
- [ ] JWT tokens expire after 7 days
- [ ] Invalid tokens are rejected with 401
- [ ] Expired tokens are rejected with 401
- [ ] Users cannot access other users' data
- [ ] All task endpoints require authentication
- [ ] BETTER_AUTH_SECRET is not committed to git
- [ ] Error messages don't leak sensitive information
- [ ] Token signature is verified on every request

## Next Steps

Once all tests pass:

1. Mark tasks T048-T051 as complete in `tasks.md`
2. Create git commit with authentication implementation
3. Consider additional security enhancements:
   - Rate limiting on auth endpoints
   - Account lockout after failed attempts
   - Password reset functionality
   - Refresh token mechanism
   - Multi-factor authentication (MFA)