fix: replace passlib with bcrypt directly (passlib incompatible with bcrypt>=4)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vadym Samoilenko 2026-03-26 13:10:24 +00:00
parent 8edf31edff
commit ca40d251d6
2 changed files with 6 additions and 7 deletions

View file

@ -6,6 +6,6 @@ alembic==1.14.0
pydantic[email]==2.10.3
pydantic-settings==2.6.1
python-jose[cryptography]==3.3.0
passlib[bcrypt]==1.7.4
bcrypt==4.2.1
python-multipart==0.0.20
httpx==0.28.1

View file

@ -2,10 +2,10 @@ import secrets
from datetime import datetime, timedelta, timezone
from typing import Annotated
import bcrypt
from fastapi import Depends, HTTPException, Security, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from jose import JWTError, jwt
from passlib.context import CryptContext
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
@ -13,7 +13,6 @@ from src.config import settings
from src.database import get_db
from src.models import ApiKey, User
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
bearer_scheme = HTTPBearer(auto_error=False)
ALGORITHM = "HS256"
@ -22,11 +21,11 @@ ALGORITHM = "HS256"
# ── Password ──────────────────────────────────────────────────────────────────
def hash_password(password: str) -> str:
return pwd_context.hash(password)
return bcrypt.hashpw(password.encode(), bcrypt.gensalt()).decode()
def verify_password(plain: str, hashed: str) -> bool:
return pwd_context.verify(plain, hashed)
return bcrypt.checkpw(plain.encode(), hashed.encode())
# ── JWT ───────────────────────────────────────────────────────────────────────
@ -60,7 +59,7 @@ def generate_api_key() -> tuple[str, str, str]:
"""Returns (raw_key, prefix, hash). raw_key shown once to user."""
raw = "cc_" + secrets.token_urlsafe(32)
prefix = raw[:11] # "cc_" + 8 chars
return raw, prefix, pwd_context.hash(raw)
return raw, prefix, hash_password(raw)
async def verify_api_key(raw_key: str, db: AsyncSession) -> User | None:
@ -75,7 +74,7 @@ async def verify_api_key(raw_key: str, db: AsyncSession) -> User | None:
)
keys = result.scalars().all()
for key in keys:
if pwd_context.verify(raw_key, key.key_hash):
if verify_password(raw_key, key.key_hash):
key.last_used_at = datetime.now(timezone.utc)
await db.commit()
return key.user