Password reset
Kilat implements a self-service password reset flow in
src/server/routes/auth.routes.ts with hashed reset tokens and pluggable email
delivery.
GET /forgot-password— renders the email form.POST /forgot-password— if the email exists, creates a reset token and emails a link. Always renders{ status: "sent" }regardless of whether the account exists — no user enumeration.GET /reset-password?email=…&token=…— renders the new-password form, pre-filled from the query string.POST /reset-password— validates the token, checks password confirmation, hashes the new password, clears all reset tokens for that email, and redirects to/login?notice=password_reset.
const token = await createPasswordReset(user.email);const link = `${config.appUrl}/reset-password?email=${encodeURIComponent(user.email)}&token=${token}`;await sendMail({ to: user.email, subject: "Reset your password", text: `Reset your password:\n${link}\n\nThis link expires in 60 minutes.`, html: `Reset your password: ${link} — expires in 60 minutes.`,});Hashed reset tokens
Section titled “Hashed reset tokens”Reset tokens are 256-bit random and hashed at rest — the password_resets
table stores only SHA-256(token), never the raw token. The raw token travels
only in the email link.
export const RESET_TOKEN_TTL_MS = 60 * 60 * 1000; // 1 hour| Property | Value |
|---|---|
| Token length | 256-bit random, hex-encoded |
| Stored as | SHA-256 hash (token_hash PK) |
| Expiry | 60 minutes (RESET_TOKEN_TTL_MS) |
| Cleanup | All tokens for the email cleared after reset |
verifyPasswordReset checks both the hash match and the expiry timestamp.
Expired or invalid tokens return a 422 with “This reset link is invalid or has
expired.”
Email delivery
Section titled “Email delivery”Reset emails are sent via the mailer — see Mailer
for driver configuration (log, resend, mailtrap), secrets, and
production setup.