Olympus Docs
SecurityIdentity protection

Password policy

Olympus's defaults and how to tune them

Olympus uses Kratos's Argon2id hashing plus the HIBP breach check. Strong defaults; tune to your audience.

Defaults

SettingDefaultSource
Min length8 charactersKratos default
HashingArgon2id (memory=64MB, iterations=3, parallelism=4)Kratos default
Breach checkHaveIBeenPwned k-anonymityOlympus addition
Composition rulesNone (no "must include digit + upper" by default)Kratos default
Password historyOff (no remembering of previous passwords)Kratos default
ExpirationNoneKratos default

Modern guidance (NIST SP 800-63B)

The 2017 NIST guidance (still current in 2024):

  • Length ≥ 8 chars. NIST allows up to 64+.
  • No composition rules (don't require digit/symbol).
  • Breach checks (HIBP-style).
  • No rotation without cause.

Olympus matches this.

Tuning length

selfservice:
  methods:
    password:
      config:
        min_password_length: 12   # stricter than default

For admin (IAM) accounts, recommend 14+ characters.

Composition rules (anti-pattern)

Resist the temptation to add "must contain digit/symbol/upper" rules. They drive predictable patterns ("Password1!", "Spring2024!") and increase phishing-substitute risk.

If your auditor insists, add via a custom hook:

selfservice:
  flows:
    registration:
      after:
        password:
          hooks:
            - hook: web_hook
              config:
                url: https://your-backend/internal/validate-password
                # Reject if too simple

Breach check

Olympus's HIBP integration is enabled by default. Disable only if:

  • You're in an air-gapped environment.
  • Your audience hates the friction.
// hera/src/lib/breach-check.ts disabled via env
HIBP_DISABLED=true

Password history

To prevent reuse, store SHA-256 hashes of the last N passwords. On password change:

const newHash = sha256(newPassword);
const recent = await db`SELECT old_password_hash FROM password_history WHERE identity_id = ${id} ORDER BY changed_at DESC LIMIT 10`;
if (recent.some(r => r.old_password_hash === newHash)) {
  throw new Error("Cannot reuse a recent password");
}
await db`INSERT INTO password_history (identity_id, old_password_hash, changed_at) VALUES (${id}, ${newHash}, NOW())`;

Tradeoff: storing hashes of old passwords creates a small new risk surface. Not currently shipped in Olympus.

Rotation

NIST says: only rotate on evidence of compromise. Routine rotation (every 90 days) is now considered counter-productive.

If your compliance regime requires rotation (some HIPAA / PCI interpretations), enforce via a hook that checks password_changed_at against a threshold and forces a settings flow.

Hashing tuning

Argon2id defaults: 64MB memory, 3 iterations, 4 parallelism. Adjust in kratos.yml:

hashers:
  algorithm: argon2id
  argon2:
    memory: 131072        # 128 MB
    iterations: 3
    parallelism: 4
    salt_length: 16
    key_length: 32

Higher memory makes brute-force expensive. Test on your VPS, too high and login latency increases.

On this page