Skip to content

Commit

Permalink
Use bcrypt instead of passlib
Browse files Browse the repository at this point in the history
  • Loading branch information
Brzuszek Maël committed Oct 17, 2023
1 parent 61ace97 commit 0e1b25c
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 15 deletions.
33 changes: 19 additions & 14 deletions app/core/security.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import secrets
from datetime import datetime, timedelta

import bcrypt
from fastapi.security import OAuth2AuthorizationCodeBearer
from jose import jwt
from passlib.context import CryptContext
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.config import Settings
from app.cruds import cruds_users
from app.models import models_core
from app.schemas import schemas_auth

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto", bcrypt__rounds=13)
"""
In order to salt and hash password, we use a *passlib* [CryptContext](https://passlib.readthedocs.io/en/stable/narr/quickstart.html) object.
Expand All @@ -36,12 +35,23 @@
"""


def generate_token(nbytes=32) -> str:
"""
Generate a `nbytes` bytes cryptographically strong random urlsafe token using the *secrets* library.
By default, a 32 bytes token is generated.
"""
# We use https://docs.python.org/3/library/secrets.html#secrets.token_urlsafe to generate the activation secret token
return secrets.token_urlsafe(nbytes)


def get_password_hash(password: str) -> str:
"""
Return a salted hash computed from password. The function use a bcrypt based *passlib* CryptContext.
Both the salt and the algorithm identifier are included in the hash.
"""
return pwd_context.hash(password)
hashed = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt(rounds=13))
return hashed.decode("utf-8")


def verify_password(plain_password: str, hashed_password: str | None) -> bool:
Expand All @@ -50,17 +60,12 @@ def verify_password(plain_password: str, hashed_password: str | None) -> bool:
Pass hashed_password=None to simulate the delay a real verification would have taken. This is useful to limit timing attacks
"""
return pwd_context.verify(plain_password, hashed_password)


def generate_token(nbytes=32) -> str:
"""
Generate a `nbytes` bytes cryptographically strong random urlsafe token using the *secrets* library.
By default, a 32 bytes token is generated.
"""
# We use https://docs.python.org/3/library/secrets.html#secrets.token_urlsafe to generate the activation secret token
return secrets.token_urlsafe(nbytes)
fake_hash = bcrypt.hashpw(generate_token(12).encode("utf-8"), bcrypt.gensalt(13))
if hashed_password is None:
return bcrypt.checkpw(plain_password.encode("utf-8"), fake_hash)
return bcrypt.checkpw(
plain_password.encode("utf-8"), hashed_password.encode("utf-8")
)


async def authenticate_user(
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
alembic==1.11.3 # database migrations
fastapi==0.99.0
Jinja2==3.1.2 # template engine for html files
passlib[bcrypt]==1.7.4 # password hashing using bcrypt
bcrytpt==4.0.1 # password hashing
python-dotenv==1.0.0 # load environment variables from .env file
python-jose[cryptography]==3.3.0 # generate and verify the JWT tokens
python-multipart==0.0.6 # a form data parser, as oauth flow requires form-data parameters
Expand Down

0 comments on commit 0e1b25c

Please sign in to comment.