nexusbert commited on
Commit
c4ac9a1
Β·
1 Parent(s): 78928e0
Files changed (1) hide show
  1. FORGOT_PASSWORD_FLOW.md +336 -0
FORGOT_PASSWORD_FLOW.md ADDED
@@ -0,0 +1,336 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Forgot Password Flow
2
+
3
+ This document explains the complete password reset flow implemented in the Zurri API.
4
+
5
+ ## Overview
6
+
7
+ The forgot password flow consists of three main steps:
8
+ 1. **Request Password Reset** - User requests a password reset
9
+ 2. **Receive Reset Link** - User receives a reset token (via email in production)
10
+ 3. **Reset Password** - User uses the token to set a new password
11
+
12
+ ## Flow Diagram
13
+
14
+ ```
15
+ User β†’ Forgot Password Request β†’ Server generates token β†’ User receives email
16
+ ↓
17
+ User ← Reset Link (with token) ← Email sent ← Token stored in DB
18
+ ↓
19
+ User submits token + new password β†’ Server validates β†’ Password reset
20
+ ```
21
+
22
+ ## Step-by-Step Process
23
+
24
+ ### 1. Request Password Reset
25
+
26
+ **Endpoint:** `POST /api/auth/forgot-password`
27
+
28
+ **Request:**
29
+ ```json
30
+ {
31
+ "email": "user@example.com"
32
+ }
33
+ ```
34
+
35
+ **Process:**
36
+ 1. User submits their email address
37
+ 2. Server validates email format
38
+ 3. Server checks if user exists (for security, always returns success message)
39
+ 4. If user exists:
40
+ - Generates a secure random token (32 bytes, hex-encoded)
41
+ - Hashes the token using bcrypt (10 rounds)
42
+ - Stores hashed token in database
43
+ - Sets expiration time (1 hour from now)
44
+ - **In Development:** Returns token and reset URL in response
45
+ - **In Production:** Sends email with reset link (email service not yet implemented)
46
+
47
+ **Response (Success):**
48
+ ```json
49
+ {
50
+ "message": "If an account with that email exists, a password reset link has been sent.",
51
+ "resetToken": "abc123...", // Only in development mode
52
+ "resetUrl": "http://localhost:3000/reset-password?token=abc123...&email=user@example.com" // Only in development mode
53
+ }
54
+ ```
55
+
56
+ **Security Features:**
57
+ - βœ… Always returns same message (prevents email enumeration)
58
+ - βœ… Token is hashed before storage
59
+ - βœ… Token expires after 1 hour
60
+ - βœ… Rate limited: 3 requests per hour per IP
61
+ - βœ… Email sanitization and validation
62
+
63
+ ### 2. User Receives Reset Link
64
+
65
+ **In Development Mode:**
66
+ - Token and URL are returned in the API response
67
+ - Frontend can use this directly for testing
68
+
69
+ **In Production Mode:**
70
+ - Email service should send an email with:
71
+ - Reset link: `{FRONTEND_URL}/reset-password?token={TOKEN}&email={EMAIL}`
72
+ - Token expires in 1 hour
73
+ - Clear instructions
74
+
75
+ **Reset Link Format:**
76
+ ```
77
+ https://yourdomain.com/reset-password?token=abc123def456...&email=user@example.com
78
+ ```
79
+
80
+ ### 3. Reset Password
81
+
82
+ **Endpoint:** `POST /api/auth/reset-password`
83
+
84
+ **Request:**
85
+ ```json
86
+ {
87
+ "email": "user@example.com",
88
+ "token": "abc123def456...",
89
+ "password": "NewSecurePassword123!"
90
+ }
91
+ ```
92
+
93
+ **Process:**
94
+ 1. Server validates email format
95
+ 2. Server validates password strength (8+ chars, uppercase, lowercase, number, special char)
96
+ 3. Server finds user by email
97
+ 4. Server checks if reset token exists and hasn't expired
98
+ 5. Server verifies token by comparing with hashed token in database
99
+ 6. If valid:
100
+ - Hashes new password (bcrypt, 12 rounds)
101
+ - Updates user password
102
+ - Clears reset token and expiration
103
+ - Resets failed login attempts
104
+ - Unlocks account if locked
105
+ 7. Returns success message
106
+
107
+ **Response (Success):**
108
+ ```json
109
+ {
110
+ "message": "Password has been reset successfully"
111
+ }
112
+ ```
113
+
114
+ **Response (Error - Expired Token):**
115
+ ```json
116
+ {
117
+ "error": "Reset token has expired"
118
+ }
119
+ ```
120
+
121
+ **Response (Error - Invalid Token):**
122
+ ```json
123
+ {
124
+ "error": "Invalid reset token"
125
+ }
126
+ ```
127
+
128
+ **Response (Error - Weak Password):**
129
+ ```json
130
+ {
131
+ "error": "Password does not meet requirements",
132
+ "details": [
133
+ "Password must be at least 8 characters long",
134
+ "Password must contain at least one uppercase letter",
135
+ ...
136
+ ]
137
+ }
138
+ ```
139
+
140
+ **Security Features:**
141
+ - βœ… Token can only be used once (deleted after use)
142
+ - βœ… Token expires after 1 hour
143
+ - βœ… Strong password requirements enforced
144
+ - βœ… Token verification using secure comparison
145
+ - βœ… Rate limited: 3 requests per hour per IP
146
+ - βœ… Account automatically unlocked on successful reset
147
+
148
+ ## Password Requirements
149
+
150
+ The new password must meet all of these requirements:
151
+ - βœ… Minimum 8 characters
152
+ - βœ… At least one uppercase letter (A-Z)
153
+ - βœ… At least one lowercase letter (a-z)
154
+ - βœ… At least one number (0-9)
155
+ - βœ… At least one special character (!@#$%^&*()_+-=[]{}|;':",./<>?)
156
+
157
+ ## Database Schema
158
+
159
+ The `users` table includes these fields for password reset:
160
+
161
+ ```typescript
162
+ passwordResetToken?: string; // Hashed reset token
163
+ passwordResetExpires?: Date; // Token expiration time
164
+ failedLoginAttempts: number; // Reset to 0 on successful reset
165
+ lockedUntil?: Date; // Cleared on successful reset
166
+ ```
167
+
168
+ ## Security Considerations
169
+
170
+ ### Token Security
171
+ - Tokens are cryptographically random (32 bytes)
172
+ - Tokens are hashed before storage (bcrypt)
173
+ - Tokens are single-use (deleted after successful reset)
174
+ - Tokens expire after 1 hour
175
+
176
+ ### Rate Limiting
177
+ - **Forgot Password:** 3 requests per hour per IP
178
+ - **Reset Password:** 3 requests per hour per IP
179
+ - Prevents abuse and brute force attacks
180
+
181
+ ### Email Enumeration Prevention
182
+ - Always returns same success message
183
+ - Doesn't reveal if email exists or not
184
+ - Prevents attackers from discovering valid emails
185
+
186
+ ### Token Storage
187
+ - Tokens are hashed (not stored in plaintext)
188
+ - Even if database is compromised, tokens can't be extracted
189
+ - Uses secure bcrypt comparison
190
+
191
+ ## Frontend Integration
192
+
193
+ ### Step 1: Request Reset
194
+ ```javascript
195
+ const response = await fetch('/api/auth/forgot-password', {
196
+ method: 'POST',
197
+ headers: { 'Content-Type': 'application/json' },
198
+ body: JSON.stringify({ email: 'user@example.com' })
199
+ });
200
+
201
+ const data = await response.json();
202
+ // Show success message (always the same)
203
+ alert(data.message);
204
+ ```
205
+
206
+ ### Step 2: Handle Reset Link
207
+ ```javascript
208
+ // Extract token and email from URL query params
209
+ const urlParams = new URLSearchParams(window.location.search);
210
+ const token = urlParams.get('token');
211
+ const email = urlParams.get('email');
212
+
213
+ // Show password reset form
214
+ ```
215
+
216
+ ### Step 3: Submit New Password
217
+ ```javascript
218
+ const response = await fetch('/api/auth/reset-password', {
219
+ method: 'POST',
220
+ headers: { 'Content-Type': 'application/json' },
221
+ body: JSON.stringify({
222
+ email: email,
223
+ token: token,
224
+ password: 'NewSecurePassword123!'
225
+ })
226
+ });
227
+
228
+ const data = await response.json();
229
+ if (response.ok) {
230
+ // Redirect to login
231
+ window.location.href = '/login';
232
+ } else {
233
+ // Show error (expired token, weak password, etc.)
234
+ alert(data.error);
235
+ }
236
+ ```
237
+
238
+ ## Error Handling
239
+
240
+ ### Common Errors
241
+
242
+ 1. **Invalid Email Format**
243
+ - Status: 400
244
+ - Message: "Invalid email format"
245
+
246
+ 2. **Expired Token**
247
+ - Status: 400
248
+ - Message: "Reset token has expired"
249
+ - Action: User must request a new reset
250
+
251
+ 3. **Invalid Token**
252
+ - Status: 400
253
+ - Message: "Invalid reset token"
254
+ - Action: Check token is correct or request new one
255
+
256
+ 4. **Weak Password**
257
+ - Status: 400
258
+ - Message: "Password does not meet requirements"
259
+ - Details: Array of specific requirements not met
260
+
261
+ 5. **Rate Limit Exceeded**
262
+ - Status: 429
263
+ - Message: "Too many password reset requests. Please try again later."
264
+ - Action: Wait 1 hour
265
+
266
+ ## Testing
267
+
268
+ ### Development Mode
269
+ - Reset token and URL are returned in API response
270
+ - Useful for testing without email service
271
+ - Can test full flow locally
272
+
273
+ ### Production Mode
274
+ - Requires email service integration
275
+ - Token only sent via email
276
+ - More secure (no token in API response)
277
+
278
+ ## Email Service Integration (TODO)
279
+
280
+ To complete the flow in production, integrate an email service:
281
+
282
+ 1. **Recommended Services:**
283
+ - SendGrid
284
+ - AWS SES
285
+ - Mailgun
286
+ - Nodemailer with SMTP
287
+
288
+ 2. **Email Template Should Include:**
289
+ - Reset link with token
290
+ - Expiration time (1 hour)
291
+ - Security warning
292
+ - Instructions
293
+
294
+ 3. **Example Integration:**
295
+ ```typescript
296
+ // In forgot-password handler
297
+ if (user) {
298
+ // ... generate token ...
299
+
300
+ // Send email (example with Nodemailer)
301
+ await sendPasswordResetEmail({
302
+ to: user.email,
303
+ resetUrl: resetUrl,
304
+ expiresIn: '1 hour'
305
+ });
306
+ }
307
+ ```
308
+
309
+ ## Additional Endpoint: Change Password
310
+
311
+ For authenticated users who want to change their password:
312
+
313
+ **Endpoint:** `POST /api/auth/change-password`
314
+ **Authentication:** Required (Bearer token)
315
+
316
+ **Request:**
317
+ ```json
318
+ {
319
+ "currentPassword": "CurrentPassword123!",
320
+ "newPassword": "NewSecurePassword123!"
321
+ }
322
+ ```
323
+
324
+ This allows users to change their password while logged in, without needing a reset token.
325
+
326
+ ## Summary
327
+
328
+ The forgot password flow is secure, user-friendly, and follows security best practices:
329
+ - βœ… Secure token generation and storage
330
+ - βœ… Time-limited tokens (1 hour)
331
+ - βœ… Strong password requirements
332
+ - βœ… Rate limiting
333
+ - βœ… Email enumeration prevention
334
+ - βœ… Single-use tokens
335
+ - βœ… Automatic account unlock
336
+