Have I Been Pwned is a data breach search service run by security researcher Troy Hunt. Its Pwned Passwords corpus collects passwords exposed in real data breaches, and its range API lets services check a password against it without sending the password.
How does the integration work?
Breached password detection is a password policy in Appwrite Auth that checks your users' passwords against Have I Been Pwned. Appwrite runs the check when a user signs up, signs in with email and password, changes their password, or completes a password recovery, and stores the result on the user as passwordPwned.
The check uses k-anonymity. Appwrite hashes the password with SHA-1 and sends only the first five characters of the hash to the Pwned Passwords API. The API returns every known hash suffix for that prefix, and Appwrite compares them on its own servers, so neither the password nor its full hash leaves Appwrite.
On top of recording the result, you can turn on two enforcement options:
- Reject breached passwords. A breached password can't be set at sign-up, password change, or password recovery, including through the server-side Users API. The request fails with the
password_pwnederror. - Block sign-in with a breached password. An email and password sign-in with a breached password fails with the
user_password_reset_requirederror until the user resets their password.
If the service can't be reached, Appwrite doesn't treat the password as safe. The request fails with the general_pwned_passwords_unavailable error and can be retried.
On Appwrite Cloud, checking and recording are on by default for every project, and both enforcement options are off. Self-hosted Appwrite 2.3 and later needs the _APP_PWNED_PASSWORDS_DSN environment variable set before any password is actually checked.
How to implement
To implement breached password detection in Appwrite Auth, there are several steps you must complete:
Step 1: Connect your self-hosted instance to Have I Been Pwned
Skip this step on Appwrite Cloud. If you self-host Appwrite, the default value of _APP_PWNED_PASSWORDS_DSN, none://localhost, reports every password as safe. Point it at the public Pwned Passwords range API in the .env file of your Appwrite installation:
_APP_PWNED_PASSWORDS_DSN=hibp://localhostThen recreate your Appwrite stack and confirm the variable was applied:
docker compose up -ddocker compose exec appwrite varsStep 2: Configure the breached passwords policy
For this step, you must create an account on Appwrite Cloud or have a self-hosted instance configured as in the previous step.
- Open your project in the Appwrite Console.
- Navigate to Auth in the sidebar.
- Open the Policies tab and select Passwords.
- In the Breached passwords card, turn on Check passwords against known data breaches.
- Optionally, check Reject breached passwords and Block sign-in with a breached password.
- Click Update.
The users table then shows the latest result for each user as a shield icon. A red shield means the password was found in a breach, a green one means it wasn't, and a dash means it has never been checked.
Step 3: Handle breached password errors in your app
When enforcement is on, catch the two error types in your sign-up and sign-in flows. On password_pwned, ask the user to choose a different password. On user_password_reset_required, send the user a password recovery email so they can set a new password.
import { Client, Account, ID } from "appwrite";
const client = new Client() .setEndpoint('https://<REGION>.cloud.appwrite.io/v1') .setProject('<PROJECT_ID>');
const account = new Account(client);
// Sign-up rejected by "Reject breached passwords"try { await account.create({ userId: ID.unique(), email: 'email@example.com', password: '<PASSWORD>' });} catch (error) { if (error.type === 'password_pwned') { // Ask the user to choose a different password }}
// Sign-in refused by "Block sign-in with a breached password"try { await account.createEmailPasswordSession({ email: 'email@example.com', password: '<PASSWORD>' });} catch (error) { if (error.type === 'user_password_reset_required') { await account.createRecovery({ email: 'email@example.com', url: 'https://example.com/recovery' }); }}Step 4: Find users with breached passwords
To notify affected users before you turn on enforcement, query the passwordPwned attribute with the server-side Users API:
import { Client, Users, Query } from 'node-appwrite';
const client = new Client() .setEndpoint('https://<REGION>.cloud.appwrite.io/v1') .setProject('<PROJECT_ID>') .setKey('<YOUR_API_KEY>');
const users = new Users(client);
const result = await users.list({ queries: [Query.equal('passwordPwned', true)]});In a client app, the passwordPwned field on the account returned by account.get() tells you whether the signed-in user's password was flagged.
Read more about Have I Been Pwned and Appwrite Auth
If you would like to learn more about Have I Been Pwned and Appwrite Auth, we have some resources that you should visit:
