better-auth
Version:
The most comprehensive authentication framework for TypeScript.
55 lines (53 loc) • 2.14 kB
JavaScript
import { APIError } from "../../api/index.mjs";
import { getCurrentAuthContext } from "@better-auth/core/context";
import { defineErrorCodes } from "@better-auth/core/utils";
import { createHash } from "@better-auth/utils/hash";
import { betterFetch } from "@better-fetch/fetch";
//#region src/plugins/haveibeenpwned/index.ts
const ERROR_CODES = defineErrorCodes({ PASSWORD_COMPROMISED: "The password you entered has been compromised. Please choose a different password." });
async function checkPasswordCompromise(password, customMessage) {
if (!password) return;
const sha1Hash = (await createHash("SHA-1", "hex").digest(password)).toUpperCase();
const prefix = sha1Hash.substring(0, 5);
const suffix = sha1Hash.substring(5);
try {
const { data, error } = await betterFetch(`https://api.pwnedpasswords.com/range/${prefix}`, { headers: {
"Add-Padding": "true",
"User-Agent": "BetterAuth Password Checker"
} });
if (error) throw new APIError("INTERNAL_SERVER_ERROR", { message: `Failed to check password. Status: ${error.status}` });
if (data.split("\n").some((line) => line.split(":")[0].toUpperCase() === suffix.toUpperCase())) throw new APIError("BAD_REQUEST", {
message: customMessage || ERROR_CODES.PASSWORD_COMPROMISED,
code: "PASSWORD_COMPROMISED"
});
} catch (error) {
if (error instanceof APIError) throw error;
throw new APIError("INTERNAL_SERVER_ERROR", { message: "Failed to check password. Please try again later." });
}
}
const haveIBeenPwned = (options) => {
const paths = options?.paths || [
"/sign-up/email",
"/change-password",
"/reset-password"
];
return {
id: "haveIBeenPwned",
init(ctx) {
return { context: { password: {
...ctx.password,
async hash(password) {
const c = await getCurrentAuthContext();
if (!c.path || !paths.includes(c.path)) return ctx.password.hash(password);
await checkPasswordCompromise(password, options?.customPasswordCompromisedMessage);
return ctx.password.hash(password);
}
} } };
},
options,
$ERROR_CODES: ERROR_CODES
};
};
//#endregion
export { haveIBeenPwned };
//# sourceMappingURL=index.mjs.map