Spaces:
Sleeping
Sleeping
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)
|