ayn / auth.py
AndreaNotes1's picture
Upload 2 files
19fab8e verified
Raw History Blame Contribute Delete
1.96 kB
"""
Handles password hashing and JWT issuing/validation for user login.
"""
from datetime import datetime, timedelta, timezone
from typing import Optional
import bcrypt
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
from config import settings
from database import users_collection
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="api/auth/login")
# bcrypt has a hard 72-byte limit on the input; longer passwords are truncated
# before hashing (this matches bcrypt's own behavior, so it's safe either way).
_MAX_PASSWORD_BYTES = 72
def hash_password(password: str) -> str:
pw_bytes = password.encode("utf-8")[:_MAX_PASSWORD_BYTES]
hashed = bcrypt.hashpw(pw_bytes, bcrypt.gensalt())
return hashed.decode("utf-8")
def verify_password(plain: str, hashed: str) -> bool:
pw_bytes = plain.encode("utf-8")[:_MAX_PASSWORD_BYTES]
return bcrypt.checkpw(pw_bytes, hashed.encode("utf-8"))
def create_access_token(subject: str) -> str:
expire = datetime.now(timezone.utc) + timedelta(minutes=settings.JWT_EXPIRE_MINUTES)
payload = {"sub": subject, "exp": expire}
return jwt.encode(payload, settings.JWT_SECRET, algorithm=settings.JWT_ALGORITHM)
async def get_current_user(token: str = Depends(oauth2_scheme)) -> dict:
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, settings.JWT_SECRET, algorithms=[settings.JWT_ALGORITHM])
user_email: Optional[str] = payload.get("sub")
if user_email is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = await users_collection().find_one({"email": user_email})
if user is None:
raise credentials_exception
return user