TypeScript · Password breach checks
Check breached passwords in TypeScript
A password check in TypeScript works best as a small function that returns a verdict type your signup handler can switch on. The Finowl endpoint replies with breached and count, which the example maps to a discriminated union: ok, or breached with the number of sightings. That keeps the policy decision, such as rejecting any password seen at least once, in one obvious place.
Finowl hashes the submitted password with SHA-1 and searches by a 5-character prefix with padding, and the password is processed transiently, never logged, stored or returned. On your side, the function never interpolates the password into errors and builds the request body inline. The demo reads the candidate from an environment variable so it never appears in shell history or the process list.
Steps
- 1
Install the dev dependencies and set FINOWL_API_KEY on your server.
- 2
Copy checkPassword and the Verdict type into your auth module.
- 3
Call it after local validation passes and before you hash and store the new password.
- 4
Switch on verdict.status to accept the password or return a form error.
npm install --save-dev typescript tsx @types/node// check-password.ts (run with: CANDIDATE_PASSWORD=... npx tsx check-password.ts)
type PasswordResult = { breached: boolean; count: number };
type Verdict = { status: "ok" } | { status: "breached"; count: number };
type ErrorBody = { error?: { code?: string } };
const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));
// Never log the password, the request body or this function's arguments.
export async function checkPassword(password: string, attempts = 3): Promise<Verdict> {
for (let i = 0; i < attempts; i++) {
const res = await fetch("https://api.finowl.co/v1/breaches/password", {
method: "POST",
headers: {
Authorization: "Bearer " + process.env.FINOWL_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({ password }),
signal: AbortSignal.timeout(5_000),
});
if (res.ok) {
const data = (await res.json()) as PasswordResult;
return data.breached ? { status: "breached", count: data.count } : { status: "ok" };
}
const code = ((await res.json().catch(() => ({}))) as ErrorBody).error?.code ?? "unknown_error";
if (res.status === 400) throw new Error("Password failed validation (" + code + ")");
if (res.status === 401) throw new Error("Unauthorized: check FINOWL_API_KEY");
if (res.status === 429 && code === "rate_limited") {
await sleep((Number(res.headers.get("retry-after")) || 1) * 1000);
continue;
}
throw new Error("Password check failed: " + res.status + " " + code);
}
throw new Error("Password check still rate limited");
}
async function main(): Promise<void> {
const candidate = process.env.CANDIDATE_PASSWORD;
if (!candidate) throw new Error("Set CANDIDATE_PASSWORD to the password to check");
const verdict = await checkPassword(candidate);
switch (verdict.status) {
case "breached":
console.log("Rejected: seen " + verdict.count + " times in breach data.");
break;
case "ok":
console.log("Accepted: not found in breach data.");
break;
}
}
main().catch((err: unknown) => {
console.error(err instanceof Error ? err.message : err);
process.exitCode = 1;
});IN PRODUCTION
Production tips
Mark the password parameter as sensitive in your observability setup. OpenTelemetry fetch instrumentation and error trackers can capture bodies, so exclude this URL from body capture.
Enforce your own minimum length and format rules in TypeScript first, then call the API only for plausible candidates, so obviously unacceptable input never costs a round trip or a credit.
Return a generic message to the client, like this password has appeared in a data breach, and keep the count for internal metrics or for tuning a stricter threshold later.
Each lookup costs 1 credit, with 100 free credits every month. See pricing and error codes.
A FEW GOOD QUESTIONS
Common questions
Related guides
- Check email breaches in TypeScript with typed results
- Batch gender prediction in TypeScript
- Check breached passwords in Python at signup
- Screen breached passwords in Node.js
- Check breached passwords in Go before storing them
- Check breached passwords in Java at registration
- Breached password checks for fintech accounts
- Breached password screening for e-commerce accounts
- Breached password checks for SaaS signup and resets
Check breached passwords in TypeScript
Grab an API key, paste the example and run it. 100 free credits every month.