Have I Been Pwned

A plugin to check if a password has been compromised

The Have I Been Pwned plugin helps protect user accounts by preventing the use of passwords that have been exposed in known data breaches. It uses the Have I Been Pwned API to check if a password has been compromised.

Installation

Add the plugin to your auth config

auth.ts
import { betterAuth } from "better-auth"
import { haveIBeenPwned } from "better-auth/plugins"

export const auth = betterAuth({
    plugins: [
        haveIBeenPwned() 
    ]
})

Usage

When a user attempts to create an account or update their password with a compromised password, they'll receive the following default error:

{
  "code": "PASSWORD_COMPROMISED",
  "message": "The password you entered has been compromised. Please choose a different password."
}

Custom password flows

Use isPasswordCompromised to check passwords in server-side flows that do not use Better Auth endpoints.

import { isPasswordCompromised } from "better-auth/plugins/haveibeenpwned"

const compromised = await isPasswordCompromised(password)
if (compromised) {
    throw new Error("Please choose a password that has not been compromised")
}

The function only sends the first five characters of the password's SHA-1 hash to Have I Been Pwned. It throws an APIError if the service cannot complete the check.

Options

enabled

Enable or disable password checks against the HIBP database. Useful for skipping checks in development or testing without removing the plugin. Defaults to true.

auth.ts
import { betterAuth } from "better-auth"
import { haveIBeenPwned } from "better-auth/plugins"

const auth = betterAuth({
    plugins: [
        haveIBeenPwned({
            enabled: process.env.NODE_ENV === 'production'
        })
    ]
})

customPasswordCompromisedMessage

Customize the error message shown when a compromised password is detected.

auth.ts
import { betterAuth } from "better-auth"
import { haveIBeenPwned } from "better-auth/plugins"

const auth = betterAuth({
    plugins: [
        haveIBeenPwned({
            customPasswordCompromisedMessage: "Please choose a more secure password."
        })
    ]
})

Security Notes

  • Only the first 5 characters of the password hash are sent to the API
  • The full password is never transmitted
  • Provides an additional layer of account security