-
Notifications
You must be signed in to change notification settings - Fork 2
feat: email support #296
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
feat: email support #296
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
ad1b58f
feat: email support
gusfcarvalho 17ed06f
fix: copilot review 1
gusfcarvalho 5fe4c03
fix: copilot review 2
gusfcarvalho 47d65f2
fix: copilot review 2
gusfcarvalho 34c1b80
chore: refactor config so its clearer
gusfcarvalho eaa0eb0
fix: add docs
gusfcarvalho File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,232 @@ | ||
| # Forgot Password Flow | ||
|
|
||
| This document describes the forgot password implementation for the Compliance Framework API. | ||
|
|
||
| ## Overview | ||
|
|
||
| The forgot password flow consists of two endpoints: | ||
|
|
||
| 1. `POST /auth/forgot-password` - Initiates password reset by sending an email | ||
| 2. `POST /auth/password-reset` - Completes password reset using a JWT token | ||
|
|
||
| ## Security Features | ||
|
|
||
| - **15-minute token expiry**: Password reset tokens expire after 15 minutes | ||
| - **Auth method validation**: Only users with `authMethod=password` can reset passwords | ||
| - **Email enumeration protection**: Same response is returned regardless of whether email exists | ||
| - **JWT token validation**: Tokens are cryptographically signed and verified | ||
| - **Token-bound email**: Password reset is performed for the email encoded in the JWT token; no separate email is accepted in the request | ||
|
|
||
| ## Configuration | ||
|
|
||
| ### Environment Variables | ||
|
|
||
| Add these to your environment or `.env` file: | ||
|
|
||
| ```bash | ||
| # Web base URL for password reset links (required) | ||
| WEB_BASE_URL=http://localhost:3000 | ||
|
|
||
| # Email configuration (optional but recommended) | ||
| CCF_EMAIL_ENABLED=true | ||
| CCF_EMAIL_PROVIDER=smtp | ||
| CCF_EMAIL_HOST=smtp.gmail.com | ||
| CCF_EMAIL_PORT=587 | ||
| CCF_EMAIL_USERNAME=your-email@gmail.com | ||
| CCF_EMAIL_PASSWORD=your-app-password | ||
| CCF_EMAIL_FROM=your-email@gmail.com | ||
| CCF_EMAIL_FROM_NAME="Compliance Framework" | ||
| CCF_EMAIL_USE_TLS=true | ||
| ``` | ||
|
|
||
| ### Email Configuration File | ||
|
|
||
| Create `email.yaml`: | ||
|
|
||
| ```yaml | ||
| enabled: true | ||
| provider: "smtp" | ||
| providers: | ||
| smtp: | ||
| name: "SMTP" | ||
| provider: "smtp" | ||
| enabled: true | ||
| host: "smtp.gmail.com" | ||
| port: 587 | ||
| username: "your-email@gmail.com" | ||
| password: "your-app-password" | ||
| from: "your-email@gmail.com" | ||
| from_name: "Compliance Framework" | ||
| use_tls: true | ||
| use_ssl: false | ||
| ``` | ||
|
|
||
| ## API Endpoints | ||
|
|
||
| ### 1. Forgot Password | ||
|
|
||
| **Endpoint:** `POST /auth/forgot-password` | ||
|
|
||
| **Request Body:** | ||
| ```json | ||
| { | ||
| "email": "user@example.com" | ||
| } | ||
| ``` | ||
|
|
||
| **Response:** | ||
| ```json | ||
| { | ||
| "data": "If an account with this email exists, a password reset link has been sent." | ||
| } | ||
| ``` | ||
|
|
||
| **Behavior:** | ||
| - Validates email format | ||
| - Checks if user exists and has `authMethod=password` | ||
| - Generates a JWT token valid for 15 minutes | ||
| - Sends password reset email if email service is configured | ||
| - Returns same response regardless of user existence (security) | ||
|
|
||
| ### 2. Password Reset | ||
|
|
||
| **Endpoint:** `POST /auth/password-reset` | ||
|
|
||
| **Request Body:** | ||
| ```json | ||
| { | ||
| "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...", | ||
| "password": "newSecurePassword123" | ||
| } | ||
| ``` | ||
|
|
||
| **Response:** | ||
| ```json | ||
| { | ||
| "data": "Password has been reset successfully" | ||
| } | ||
| ``` | ||
|
|
||
| **Behavior:** | ||
| - Validates JWT token signature and expiry | ||
| - Uses email encoded in JWT token (no email field in request) | ||
| - Updates user password with bcrypt hashing | ||
| - Requires minimum 8 character password | ||
|
|
||
| ## Email Templates | ||
|
|
||
| ### HTML Email Template | ||
| ```html | ||
| <h2>Password Reset Request</h2> | ||
| <p>Hello {{FirstName}},</p> | ||
| <p>You requested a password reset for your Compliance Framework account.</p> | ||
| <p>Click the link below to reset your password:</p> | ||
| <p><a href="{{ResetURL}}">Reset Password</a></p> | ||
| <p>This link will expire in 15 minutes.</p> | ||
| <p>If you didn't request this password reset, you can safely ignore this email.</p> | ||
| <p>Thank you,<br/>Compliance Framework Team</p> | ||
| ``` | ||
|
|
||
| ### Text Email Template | ||
| ``` | ||
| Password Reset Request | ||
|
|
||
| Hello {{FirstName}}, | ||
|
|
||
| You requested a password reset for your Compliance Framework account. | ||
| Visit the following link to reset your password: | ||
| {{ResetURL}} | ||
|
|
||
| This link will expire in 15 minutes. | ||
|
|
||
| If you didn't request this password reset, you can safely ignore this email. | ||
|
|
||
| Thank you, | ||
| Compliance Framework Team | ||
| ``` | ||
|
|
||
| ## Frontend Integration | ||
|
|
||
| ### Password Reset Form | ||
|
|
||
| Your frontend should: | ||
|
|
||
| 1. Collect email from user | ||
| 2. Call `POST /auth/forgot-password` | ||
| 3. Show success message regardless of result | ||
| 4. When user clicks email link, extract token from URL query parameter | ||
| 5. Show password reset form with the token pre-filled (no email input needed) | ||
| 6. Call `POST /auth/password-reset` with the token and new password | ||
| 7. Redirect to login on success | ||
|
|
||
| ### Example URL Format | ||
| ``` | ||
| http://localhost:3000/reset-password?token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9... | ||
| ``` | ||
|
|
||
| ## Error Handling | ||
|
|
||
| ### Common Error Responses | ||
|
|
||
| **400 Bad Request:** | ||
| ```json | ||
| { | ||
| "error": "invalid email format" | ||
| } | ||
| ``` | ||
|
|
||
| **401 Unauthorized:** | ||
| ```json | ||
| { | ||
| "error": "invalid or expired token" | ||
| } | ||
| ``` | ||
|
|
||
| **404 Not Found:** | ||
| ```json | ||
| { | ||
| "error": "user not found" | ||
| } | ||
| ``` | ||
|
|
||
| **500 Internal Server Error:** | ||
| ```json | ||
| { | ||
| "error": "failed to generate password reset token" | ||
| } | ||
| ``` | ||
|
|
||
| ## Testing | ||
|
|
||
| Run the forgot password tests: | ||
|
|
||
| ```bash | ||
| go test ./internal/api/handler/auth/ -v -run TestForgotPassword | ||
| ``` | ||
|
|
||
| ## Security Considerations | ||
|
|
||
| 1. **Rate Limiting**: Consider implementing rate limiting on forgot password endpoint | ||
| 2. **Email Security**: Ensure email service is properly secured with TLS | ||
| 3. **Token Storage**: Tokens are not stored in database (stateless JWT) | ||
| 4. **Password Policy**: Enforce strong password requirements | ||
| 5. **Logging**: All password reset attempts are logged for security monitoring | ||
| 6. **Account Lockout**: Consider implementing account lockout after failed attempts | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| ### Email Not Sending | ||
| - Check email service configuration | ||
| - Verify SMTP credentials and connectivity | ||
| - Check application logs for email errors | ||
| - Ensure `CCF_EMAIL_ENABLED=true` | ||
|
|
||
| ### Token Invalid/Expired | ||
| - Tokens expire after 15 minutes | ||
| - Check system time synchronization | ||
| - Verify JWT public/private key configuration | ||
|
|
||
| ### User Cannot Reset Password | ||
| - Verify user has `authMethod=password` | ||
| - Check if user account is active | ||
| - Verify email exists in database |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.