HelloJohn / docs
Security

Password Policy

Configure minimum password strength requirements, breach detection, and password change policies in HelloJohn.

Password Policy

HelloJohn lets you configure password requirements globally and override them per tenant.

Default policy

Out of the box, HelloJohn requires:

  • Minimum 8 characters
  • No character class requirements (uppercase, number, symbol are not forced)
  • Passwords checked against the HaveIBeenPwned breach database

This follows NIST SP 800-63B guidelines, which recommend length over complexity and breach checking over forced character classes.

Global configuration

Set in environment variables or the admin dashboard under Settings → Security → Password Policy:

VariableDefaultDescription
PASSWORD_MIN_LENGTH8Minimum password length
PASSWORD_MAX_LENGTH128Maximum password length
PASSWORD_REQUIRE_UPPERCASEfalseRequire at least one uppercase letter
PASSWORD_REQUIRE_LOWERCASEfalseRequire at least one lowercase letter
PASSWORD_REQUIRE_NUMBERfalseRequire at least one digit
PASSWORD_REQUIRE_SYMBOLfalseRequire at least one special character
PASSWORD_BREACH_CHECKtrueCheck against HaveIBeenPwned
PASSWORD_BREACH_CHECK_THRESHOLD1Reject if found this many times or more
PASSWORD_HISTORY_COUNT0Prevent reuse of last N passwords (0 = disabled)
PASSWORD_EXPIRY_DAYS0Force password change after N days (0 = never)
PASSWORD_ZXCVBN_MIN_SCORE0Minimum zxcvbn strength score (0–4; 0 = disabled)

Per-tenant override

Individual tenants can have stricter (but not looser) password policies:

PATCH /v1/tenants/{tenant_id}/config
Authorization: Bearer <admin_api_key>
Content-Type: application/json

{
  "password_policy": {
    "min_length": 12,
    "require_uppercase": true,
    "require_number": true,
    "require_symbol": true,
    "breach_check": true,
    "zxcvbn_min_score": 3,
    "history_count": 5
  }
}

HaveIBeenPwned breach check

When enabled, HelloJohn uses the HIBP Passwords API with the k-anonymity model:

  1. The password is hashed with SHA-1
  2. Only the first 5 characters of the hash are sent to HIBP
  3. HIBP returns all hashes starting with those 5 characters
  4. HelloJohn checks the full hash locally — the full password and hash are never sent to HIBP

This is zero-knowledge: HIBP cannot learn your users' passwords.

# Example: how k-anonymity works
# Password: "hunter2"
# SHA-1: F3BBBD8...

# Request sent to HIBP:
GET https://api.pwnedpasswords.com/range/F3BBB

# HIBP returns all hashes starting with F3BBB
# HelloJohn checks if the remaining suffix matches locally

To disable breach checking (not recommended):

PASSWORD_BREACH_CHECK=false

zxcvbn strength scoring

zxcvbn is a password strength estimator that models realistic attack scenarios (dictionary attacks, common patterns, keyboard walks, etc.) rather than character class rules.

ScoreStrengthCrack time estimate
0Very weak< 1 second
1Weak< 1 minute
2Fair< 1 hour
3Strong< 1 day
4Very strong> 1 year

Recommended: PASSWORD_ZXCVBN_MIN_SCORE=2 for consumer apps, 3 for business apps.

Password history

Prevent users from cycling back to old passwords:

PASSWORD_HISTORY_COUNT=5  # reject if matches any of last 5 passwords

HelloJohn stores password history hashes (Argon2id) — not the passwords themselves.

Password expiry

Force users to change their password periodically:

PASSWORD_EXPIRY_DAYS=90  # require change every 90 days

Note: NIST SP 800-63B no longer recommends periodic password rotation without evidence of compromise. Only enable this if required by your compliance framework (e.g., PCI DSS).

When a password expires:

  1. The user's access token contains password_expired: true
  2. Your app should redirect to a password change flow
  3. After changing, the flag is cleared

Communicating requirements to users

Use the password policy endpoint to show requirements in your UI:

GET /v1/tenants/{tenant_id}/config/password-policy
# Returns the effective policy for this tenant
{
  "min_length": 12,
  "require_uppercase": true,
  "require_number": true,
  "require_symbol": false,
  "breach_check": true,
  "zxcvbn_min_score": 2
}

Build a real-time strength indicator using the policy settings and a zxcvbn client library:

import { zxcvbn } from '@zxcvbn-ts/core';

function PasswordStrengthIndicator({ password, policy }) {
  const result = zxcvbn(password);
  const meetsLength = password.length >= policy.min_length;
  const meetsStrength = result.score >= policy.zxcvbn_min_score;

  return (
    <div>
      <progress value={result.score} max={4} />
      {!meetsLength && <p>At least {policy.min_length} characters required</p>}
      {!meetsStrength && <p>{result.feedback.suggestions[0]}</p>}
    </div>
  );
}

On this page