Password Reset Flow
This document explains how the password reset pipeline in the auth module is implemented and what a frontend client must do to integrate with it. The flow is split into three RPCs: one to request a password reset email with a magic link, one to validate the reset token from the magic link, and one to finalize the password reset. The sections below describe the data contracts, validation rules, and expected UX behavior at each step so that UI engineers can wire the screens without re-reading the Rust implementation.
Step 1. Request a password reset email
Use the UserAuth.SendPasswordResetEmail RPC with the user’s email address.
| Field | Notes |
|---|---|
email | Raw email string entered by the user. |
Backend behavior
- The service first validates the email format. Requests with invalid email addresses return
INVALID_EMAILimmediately. - If the email doesn’t map to any existing account with email login, the call returns
NOT_FOUNDso the UI can inform the user appropriately. - When rate limiting is triggered (same address requested again before
resend_intervalelapses, default 30 seconds), the server returnsTOO_FREQUENT. The frontend should display a message asking the user to wait before requesting another reset email. - For a valid request, the service creates a password reset link record containing a 32-character
auth_key, queues an email via RabbitMQ, and responds withSENT:
#[derive(Debug, Clone, PartialEq, Eq, sqlx::FromRow)]
pub struct EmailVerifyLink {
pub id: i64,
pub email: String,
pub auth_key: String,
pub send_at: PrimitiveDateTime,
pub reason: EmailVerifyReason,
pub user_id: Option<Uuid>,
pub is_unused: bool,
}
const AUTH_KEY_LENGTH: usize = 32;
Frontend expectations
- Present a single email field and an action button. On submission, call
SendPasswordResetEmailand branch on the enum:PWD_RESET_EMAIL_RESULT_SENT: Show success UI (“Check your inbox for a password reset link”) and inform the user to click the magic link in their email.PWD_RESET_EMAIL_RESULT_INVALID_EMAIL: Highlight the field with a validation error.PWD_RESET_EMAIL_RESULT_NOT_FOUND: Inform the user that no account exists with this email address.PWD_RESET_EMAIL_RESULT_TOO_FREQUENT: Display a message asking the user to wait before requesting another reset email (default 30 seconds).
- Add a visible countdown using the configured
resend_interval(default 30 seconds) before enabling a “Resend” button. - The email template contains a clickable magic link that embeds the
auth_keyin the URL. When clicked, it should route the user directly to the password reset page (Step 2/3) with the token automatically populated from URL query parameters.
Step 2. Validate the reset token (optional but recommended)
Use the UserAuth.CheckPasswordResetToken RPC to validate the token before allowing the user to enter a new password. This step is optional but provides better user experience by catching expired or invalid tokens early.
| Field | Notes |
|---|---|
token | The 32-character auth_key extracted from the URL query parameter |
Backend behavior
- The service looks up the token in the database and validates:
- The token exists
- The token is for password reset (not registration or other purposes)
- The token hasn’t been used yet
- The token hasn’t expired (default expiry is 5 minutes, controlled by
link_expire_after)
- If all validations pass, it returns
valid: trueand theexpire_attimestamp (Unix timestamp in seconds). - If any validation fails, it returns
valid: falsewith no expiration time.
pub struct CheckPasswordResetToken {
pub token: String,
}
pub enum CheckPasswordResetTokenResult {
Valid(PrimitiveDateTime),
Invalid,
}
Frontend expectations
- Extract the
auth_keyfrom the URL query parameters when the user lands on the password reset page via the magic link. - Call this RPC with the extracted token to validate it before showing the password reset form.
- If
validistrue:- Display the password reset form
- Optionally show a countdown timer based on the
expire_attimestamp to inform the user how much time they have left
- If
validisfalse:- Display an error message that the reset link is invalid or has expired
- Offer a link to request a new password reset email
- This validation step helps prevent the user from filling out a new password only to discover the token is invalid when they submit.
Step 3. Collect the new password
The magic link is time-limited (default 5 minutes). After that, reset attempts fail with INVALID_LINK.
On the frontend, the password reset page should:
- Use the token (
auth_key) extracted from the URL query parameters (Step 2). - Collect a new password that satisfies the platform’s password policy.
Step 4. Finalize password reset
Call UserAuth.ResetPassword with the collected data:
pub struct ResetPassword {
pub auth_key: String,
pub new_password: String,
}
| Field | Required | Notes |
|---|---|---|
auth_key | ✓ | Token from Step 1. Each token is single-use. |
new_password | ✓ | Plain password; hashing happens server-side. |
Response handling
ResetPasswordReply can return three results based on the service outcome:
pub enum ResetPasswordResult {
Success,
InvalidLink,
AccountNotFound,
}
RESET_PASSWORD_RESULT_SUCCESS: Password was successfully reset. All active sessions for this user have been terminated for security. Direct the user to the login page with a success message.RESET_PASSWORD_RESULT_INVALID_LINK: Token was unknown, already used, expired, or not for password reset. Show an error and offer to resend a reset email.RESET_PASSWORD_RESULT_ACCOUNT_NOT_FOUND: The account associated with this reset token no longer exists. This is rare but can happen if an account was deleted between steps. Display an appropriate error message.
Security considerations
When the password reset succeeds, the backend automatically:
- Hashes the new password using the configured password hashing algorithm
- Updates the password in the database
- Terminates all active sessions for this user account to prevent any potentially compromised sessions from remaining active
- Emits a
PasswordResetEventfor audit logging and downstream processing
Users will need to log in again with their new password after a successful reset.
Retrying after failures
If the user encounters an INVALID_LINK error, require them to restart from Step 1. Once a token has been consumed or expired, it cannot be retried. The single-use nature of tokens prevents replay attacks.
UI checklist
- Provide separate screens for email submission and password reset completion.
- When the user lands on the password reset page via magic link, automatically extract the
auth_keyfrom the URL query parameters—no manual token entry is needed. - Show a visible countdown for link expiration (5 minutes by default, based on
link_expire_afterconfig). - Use
CheckPasswordResetTokenbefore showing the password reset form to provide early feedback about token validity and show an expiration countdown. - After successful password reset, clearly direct the user to the login screen and inform them that all their sessions have been terminated for security.
- Consider implementing the token validation (Step 2) to improve user experience by catching invalid links before the user enters a new password.
Typical UX flow
A recommended user experience flow:
- Forgot Password Page: User enters email → call
SendPasswordResetEmail - Check Email Page: Show success message and instructions to click the magic link in their email
- Reset Password Page (accessed by clicking the magic link in the email):
- Extract
auth_keyfrom URL query parameters - On page load: call
CheckPasswordResetTokenwith the extracted token - If valid: show password form with expiration timer
- If invalid: show error and link back to Step 1
- Extract
- Submit New Password: call
ResetPasswordwith the token from URL and new password - Success Page: Inform user their password was reset and all sessions were terminated → redirect to login
This flow mirrors the backend implementation and should keep the frontend in sync with the server-side invariants without re-reading the Rust code every time changes are made.