Skip to content
GitHub
Build Errors

passlib crashes with bcrypt 5.x — use bcrypt directly

passlib crashes with bcrypt 5.x — use bcrypt directly

Severity: P2 (runtime crash on any password-handling endpoint when bcrypt 4.1+ resolves) · Applies to: any service that uses passlib[bcrypt] Related: ADR-039 — Supabase Auth confirmation and hardening

Problem

Using passlib[bcrypt] for password hashing crashes at runtime when bcrypt>=4.1 is installed. Passlib’s internal version detection tries to access bcrypt.__about__.__version__, which was removed in newer bcrypt releases.

Symptoms:

  • AttributeError: module 'bcrypt' has no attribute '__about__'
  • ValueError: password cannot be longer than 72 bytes (from passlib’s bug detection routine failing)
  • Server returns 500 on any endpoint that hashes or verifies passwords

Root Cause

Passlib (last release: 1.7.4, Jan 2020) relies on internal bcrypt APIs that changed in bcrypt 4.1+. Passlib is effectively unmaintained — no release in 6 years. The version detection code at passlib/handlers/bcrypt.py:620 accesses _bcrypt.__about__.__version__ which no longer exists.

Solution

Use bcrypt directly instead of through passlib. The bcrypt API is simple enough that the wrapper adds no value.

Dependency

pyproject.toml
dependencies = [
"bcrypt>=4.0.0", # NOT passlib[bcrypt]
]

Code

import bcrypt as _bcrypt
# Hash a password (registration)
password_hash = _bcrypt.hashpw(
password.encode(), _bcrypt.gensalt(rounds=12)
).decode()
# Verify a password (login)
is_valid = _bcrypt.checkpw(
password.encode(), stored_hash.encode()
)

Key details

  • .encode() / .decode() — bcrypt works with bytes, but store as string in DB
  • rounds=12 — production-grade cost factor (passlib defaults to 8)
  • Store the full hash string including algorithm prefix ($2b$12$...)

Prevention

  • Don’t depend on passlib for new projects — use bcrypt directly
  • Pin bcrypt>=4.0.0 to avoid pre-4.x API differences
  • If you must use passlib, pin bcrypt<4.1 alongside it