57 lines
1.9 KiB
TypeScript
57 lines
1.9 KiB
TypeScript
/**
|
|
* Resets forgotten passwords. The client's `/login` page asks for a reset
|
|
* link, which the email takes to the `/reset-password` page, whose token
|
|
* names the reset.
|
|
*
|
|
* - `POST /api/password-resets` — public. Emails a reset link to the account
|
|
* with that `email`, if any; always answers 200.
|
|
* - `PUT /api/password-resets/:token` — public, with a reset token. Sets the
|
|
* `newPassword`.
|
|
*
|
|
* @module
|
|
*/
|
|
|
|
import { Router } from "@oak/oak";
|
|
|
|
import { type APIResponse } from "../../shared/api.ts";
|
|
import { APIException } from "../lib/api-exception.ts";
|
|
import { fieldOf } from "../validators/user.ts";
|
|
import {
|
|
requestPasswordReset,
|
|
resetPassword,
|
|
} from "../services/password-reset-service.ts";
|
|
|
|
/**
|
|
* Mounted by `api/main.ts` behind the error, Open Graph and CORS middleware.
|
|
* An `APIException` thrown by a handler becomes the JSON error response there.
|
|
*/
|
|
const router: Router = new Router({ prefix: "/api/password-resets" });
|
|
|
|
// Answers the same whatever happens, so it can't tell which addresses have an
|
|
// account.
|
|
router.post("/", async (ctx) => {
|
|
const body: unknown = await ctx.request.body.json();
|
|
const email = fieldOf(body, "email");
|
|
const trimmed = typeof email === "string" ? email.trim() : "";
|
|
if (trimmed) {
|
|
await requestPasswordReset(trimmed).catch((err) =>
|
|
console.error("[password-resets]", err)
|
|
);
|
|
}
|
|
const responseBody: APIResponse<null> = { success: true, data: null };
|
|
ctx.response.body = responseBody;
|
|
});
|
|
|
|
router.put("/:token", async (ctx) => {
|
|
const body: unknown = await ctx.request.body.json();
|
|
const newPassword = fieldOf(body, "newPassword");
|
|
if (typeof newPassword !== "string") {
|
|
throw new APIException(400, "Invalid request");
|
|
}
|
|
await resetPassword(ctx.params.token, newPassword);
|
|
const responseBody: APIResponse<null> = { success: true, data: null };
|
|
ctx.response.body = responseBody;
|
|
});
|
|
|
|
export default router;
|