Use pwdlib with Argon2 by default, adding logic (and tests) to autoupdate old passwords using Bcrypt (#2104)

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Sebastián Ramírez
2026-01-22 07:24:19 -08:00
committed by GitHub
parent a0fe8a236f
commit 730c6e9ebb
8 changed files with 304 additions and 47 deletions

View File

@@ -104,7 +104,8 @@ def update_password_me(
"""
Update own password.
"""
if not verify_password(body.current_password, current_user.hashed_password):
verified, _ = verify_password(body.current_password, current_user.hashed_password)
if not verified:
raise HTTPException(status_code=400, detail="Incorrect password")
if body.current_password == body.new_password:
raise HTTPException(

View File

@@ -2,11 +2,18 @@ from datetime import datetime, timedelta, timezone
from typing import Any
import jwt
from passlib.context import CryptContext
from pwdlib import PasswordHash
from pwdlib.hashers.argon2 import Argon2Hasher
from pwdlib.hashers.bcrypt import BcryptHasher
from app.core.config import settings
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
password_hash = PasswordHash(
(
Argon2Hasher(),
BcryptHasher(),
)
)
ALGORITHM = "HS256"
@@ -19,9 +26,11 @@ def create_access_token(subject: str | Any, expires_delta: timedelta) -> str:
return encoded_jwt
def verify_password(plain_password: str, hashed_password: str) -> bool:
return pwd_context.verify(plain_password, hashed_password)
def verify_password(
plain_password: str, hashed_password: str
) -> tuple[bool, str | None]:
return password_hash.verify_and_update(plain_password, hashed_password)
def get_password_hash(password: str) -> str:
return pwd_context.hash(password)
return password_hash.hash(password)

View File

@@ -41,8 +41,14 @@ def authenticate(*, session: Session, email: str, password: str) -> User | None:
db_user = get_user_by_email(session=session, email=email)
if not db_user:
return None
if not verify_password(password, db_user.hashed_password):
verified, updated_password_hash = verify_password(password, db_user.hashed_password)
if not verified:
return None
if updated_password_hash:
db_user.hashed_password = updated_password_hash
session.add(db_user)
session.commit()
session.refresh(db_user)
return db_user