feat(backend): Implement password reset flow (Phase 3)
Complete implementation of secure password reset functionality per DAY7-PRD.md specifications. Changes: - Domain: PasswordResetToken entity with 1-hour expiration and single-use constraint - Domain Events: PasswordResetRequestedEvent and PasswordResetCompletedEvent - Repository: IPasswordResetTokenRepository with token management and invalidation - Commands: ForgotPasswordCommand and ResetPasswordCommand with handlers - Security: MemoryRateLimitService (3 requests/hour) and IRateLimitService interface - API: POST /api/Auth/forgot-password and POST /api/Auth/reset-password endpoints - Infrastructure: EF Core configuration and database migration for password_reset_tokens table - Features: Email enumeration prevention, SHA-256 token hashing, refresh token revocation on password reset - Test: PowerShell test script for password reset flow verification Security Enhancements: - Rate limiting: 3 forgot-password requests per hour per email - Token security: SHA-256 hashing, 1-hour expiration, single-use only - Privacy: Always return success message to prevent email enumeration - Audit trail: IP address and User Agent logging for security monitoring - Session revocation: All refresh tokens revoked after successful password reset Database: - New table: password_reset_tokens with indexes for performance - Columns: id, user_id, token_hash, expires_at, used_at, ip_address, user_agent, created_at 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
77
colaflow-api/test-password-reset.ps1
Normal file
77
colaflow-api/test-password-reset.ps1
Normal file
@@ -0,0 +1,77 @@
|
||||
# Test Password Reset Flow
|
||||
# This script tests the complete password reset functionality
|
||||
|
||||
$baseUrl = "http://localhost:5266/api"
|
||||
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host "Testing Password Reset Flow" -ForegroundColor Cyan
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# Step 1: Request password reset
|
||||
Write-Host "[Step 1] Requesting password reset for test user..." -ForegroundColor Yellow
|
||||
$forgotPasswordRequest = @{
|
||||
email = "test@example.com"
|
||||
tenantSlug = "acme"
|
||||
} | ConvertTo-Json
|
||||
|
||||
try {
|
||||
$forgotPasswordResponse = Invoke-RestMethod -Uri "$baseUrl/Auth/forgot-password" `
|
||||
-Method Post `
|
||||
-Body $forgotPasswordRequest `
|
||||
-ContentType "application/json" `
|
||||
-ErrorAction Stop
|
||||
|
||||
Write-Host "SUCCESS: Password reset email requested" -ForegroundColor Green
|
||||
Write-Host "Response: $($forgotPasswordResponse.message)" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
} catch {
|
||||
Write-Host "FAILED: Password reset request failed" -ForegroundColor Red
|
||||
Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Step 2: Note about email
|
||||
Write-Host "[Step 2] Check email for reset token" -ForegroundColor Yellow
|
||||
Write-Host "In a real scenario, you would:" -ForegroundColor Gray
|
||||
Write-Host " 1. Check your email inbox" -ForegroundColor Gray
|
||||
Write-Host " 2. Click the password reset link" -ForegroundColor Gray
|
||||
Write-Host " 3. Enter a new password" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
Write-Host "Since we're using MockEmailService, check the console logs." -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
|
||||
# Step 3: Test with invalid token (for security verification)
|
||||
Write-Host "[Step 3] Testing with invalid reset token (security check)..." -ForegroundColor Yellow
|
||||
$invalidResetRequest = @{
|
||||
token = "invalid-token-12345"
|
||||
newPassword = "NewPassword123!"
|
||||
} | ConvertTo-Json
|
||||
|
||||
try {
|
||||
$invalidResetResponse = Invoke-RestMethod -Uri "$baseUrl/Auth/reset-password" `
|
||||
-Method Post `
|
||||
-Body $invalidResetRequest `
|
||||
-ContentType "application/json" `
|
||||
-ErrorAction Stop
|
||||
|
||||
Write-Host "UNEXPECTED: Invalid token should have been rejected" -ForegroundColor Red
|
||||
} catch {
|
||||
$statusCode = $_.Exception.Response.StatusCode.value__
|
||||
if ($statusCode -eq 400) {
|
||||
Write-Host "SUCCESS: Invalid token correctly rejected (400 Bad Request)" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "FAILED: Unexpected status code: $statusCode" -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host "Test Summary" -ForegroundColor Cyan
|
||||
Write-Host "========================================" -ForegroundColor Cyan
|
||||
Write-Host "1. Forgot password request: SUCCESS" -ForegroundColor Green
|
||||
Write-Host "2. Invalid token handling: SUCCESS" -ForegroundColor Green
|
||||
Write-Host "3. To complete the test:" -ForegroundColor Yellow
|
||||
Write-Host " - Extract the token from email logs" -ForegroundColor Gray
|
||||
Write-Host " - Call POST /api/Auth/reset-password with valid token" -ForegroundColor Gray
|
||||
Write-Host ""
|
||||
Reference in New Issue
Block a user