Origin validation, every rate limiter and the password-strength check each treated an unset PUBLIC_BUILD_ENV as "relaxed", so a deployment that forgot the variable silently ran with CORS reflecting any origin, no brute-force limit on the sole account, and no password rules. setup.sh writes it, but .env.example never mentioned it. The three now share IS_DEV_BUILD, which is true only when PUBLIC_BUILD_ENV is explicitly "dev" or "development". Anything else, including unset, is hardened. Documented in .env.example. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
33 lines
1.1 KiB
TypeScript
33 lines
1.1 KiB
TypeScript
import type { Handler } from 'hono';
|
|
import { getUserById, updateUser } from 'officerdb';
|
|
import argon2 from 'argon2';
|
|
import { sign } from '@@/jwt';
|
|
import * as errors from '@@/custom-errors';
|
|
import { validatePassword } from './validate-password';
|
|
import { IS_DEV_BUILD } from '@@/build-env';
|
|
|
|
export const changePasswordHandler: Handler = async function (ctx) {
|
|
const { password, newPassword } = ctx.get('body');
|
|
if (!IS_DEV_BUILD) validatePassword(newPassword);
|
|
const reqUser = ctx.get('user');
|
|
|
|
const dbUser = await getUserById(reqUser.id);
|
|
|
|
if (!dbUser) throw errors.UNAUTHORIZED();
|
|
|
|
const isCorrectOldPassword = await argon2.verify(dbUser.password!, password);
|
|
if (!isCorrectOldPassword) {
|
|
throw errors.createError(403, 'Incorrect old password');
|
|
}
|
|
|
|
const newPasswordHash = await argon2.hash(newPassword);
|
|
const passwordChangedAt = new Date();
|
|
await updateUser(reqUser.id, { password: newPasswordHash, passwordChangedAt });
|
|
|
|
const { id, email, name, role } = reqUser;
|
|
const username = dbUser.username ?? reqUser.username;
|
|
const token = await sign({ id, email, name, username, role });
|
|
|
|
return ctx.json({ token });
|
|
};
|