fastify-xauth-local
local.password-reset
Built-in POST/PUT routes for requesting and completing a password reset flow.
local.password-reset
Two built-in routes registered at {loginPath}/password-reset when local.enabled is true:
POST {loginPath}/password-reset— initiate a reset (send email, generate token, etc.) via thepasswordReset(email)callback.PUT {loginPath}/password-reset— complete the reset by supplying a reset token and new password via thepasswordReset(null, token, hashedPassword)callback.
Both routes are automatically excluded from JWT verification.
Default paths: {prefix}/local/password-reset (e.g. /api/local/password-reset)
Signatures
POST — Request a password reset
POST {loginPath}/password-reset
Content-Type: application/json
{
"email": string // required, valid email format
}
PUT — Complete a password reset
PUT {loginPath}/password-reset
Content-Type: application/json
{
"token": string, // required, the reset token from the email
"password": string // required, new password (min 8 chars); hashed before callback
}
Params
POST params:
| Name | Type | Required | Description |
|---|---|---|---|
email | string | Yes | The account email to reset |
PUT params:
| Name | Type | Required | Description |
|---|---|---|---|
token | string | Yes | The reset token (from email link or other delivery) |
password | string | Yes | New plaintext password (min 8 chars) |
Returns
POST 200:
{ "message": "If an account with that email exists, a password reset link has been sent" }
Always returns 200 regardless of whether the email exists (prevents email enumeration).
PUT 200:
{ "message": "Password has been reset successfully" }
Throws
| Status | Message | Reason |
|---|---|---|
| 400 | Invalid or expired reset token | passwordReset threw an error on PUT |
Examples
Basic: request and complete a password reset
# Step 1: request a reset link
curl -X POST http://localhost:3000/api/local/password-reset \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com"}'
# Step 2: complete the reset with the token from the email
curl -X PUT http://localhost:3000/api/local/password-reset \
-H "Content-Type: application/json" \
-d '{"token":"abc123","password":"NewSecurePass1"}'
Advanced: implementing the passwordReset callback
The plugin hashes the new password before calling your passwordReset function on PUT. Your callback decides how to validate the token and store the new hash.
await fastify.register(xAuthLocal, {
configs: [
{
name: "api",
prefix: "/api",
secret: process.env.JWT_SECRET,
local: {
enabled: true,
userLookup: async (email) => db.users.findByEmail(email),
/**
* passwordReset is called in two ways:
* POST: passwordReset(email) — initiate, send link
* PUT: passwordReset(null, token, hash) — complete, validate + store
*/
passwordReset: async (email, token, hashedPassword) => {
if (email) {
// Initiate: generate a token and send an email
const resetToken = crypto.randomUUID();
await db.resetTokens.create({
email,
token: resetToken,
expiresAt: new Date(Date.now() + 15 * 60 * 1000),
});
await mailer.send({
to: email,
subject: "Reset your password",
body: `Click here: https://app.example.com/reset?token=${resetToken}`,
});
} else {
// Complete: validate token and update password
const record = await db.resetTokens.findByToken(token);
if (!record || record.expiresAt < new Date()) {
throw new Error("Invalid or expired reset token");
}
await db.users.updatePassword(record.email, hashedPassword);
await db.resetTokens.delete(token);
}
},
},
},
],
});
See Also
- local.login — authenticate after the password is reset
- password.hash — hash passwords manually
AI Context
package: "@xenterprises/fastify-xauth-local"
routes: POST {loginPath}/password-reset (request reset) | PUT {loginPath}/password-reset (complete reset)
use-when: Built-in password reset flow — POST requests a reset token, PUT completes with the new password
requires: local.enabled: true, local.passwordReset function
