|
|
@@ -9,7 +9,7 @@ from typing import Optional
|
|
|
from fastapi import Depends, HTTPException, status, Request, Response
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
from sqlalchemy import select
|
|
|
-from passlib.hash import bcrypt
|
|
|
+import bcrypt
|
|
|
from cryptography.fernet import Fernet
|
|
|
from itsdangerous import URLSafeTimedSerializer, BadSignature, SignatureExpired
|
|
|
import base64
|
|
|
@@ -27,12 +27,19 @@ SESSION_MAX_AGE = 60 * 60 * 24 * 30 # 30 days
|
|
|
|
|
|
def get_password_hash(password: str) -> str:
|
|
|
"""Hash a password using bcrypt."""
|
|
|
- return bcrypt.hash(password)
|
|
|
+ # Bcrypt requires bytes
|
|
|
+ password_bytes = password.encode('utf-8')
|
|
|
+ salt = bcrypt.gensalt()
|
|
|
+ hashed = bcrypt.hashpw(password_bytes, salt)
|
|
|
+ # Return as string for storage
|
|
|
+ return hashed.decode('utf-8')
|
|
|
|
|
|
|
|
|
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
|
|
"""Verify a password against its hash."""
|
|
|
- return bcrypt.verify(plain_password, hashed_password)
|
|
|
+ password_bytes = plain_password.encode('utf-8')
|
|
|
+ hashed_bytes = hashed_password.encode('utf-8')
|
|
|
+ return bcrypt.checkpw(password_bytes, hashed_bytes)
|
|
|
|
|
|
|
|
|
def get_fernet_key() -> bytes:
|
|
|
@@ -92,6 +99,7 @@ def set_session_cookie(response: Response, user_id: int):
|
|
|
key=SESSION_COOKIE_NAME,
|
|
|
value=token,
|
|
|
max_age=SESSION_MAX_AGE,
|
|
|
+ path="/",
|
|
|
httponly=True,
|
|
|
samesite="lax",
|
|
|
# Set secure=True in production with HTTPS
|
|
|
@@ -101,7 +109,7 @@ def set_session_cookie(response: Response, user_id: int):
|
|
|
|
|
|
def clear_session_cookie(response: Response):
|
|
|
"""Clear session cookie."""
|
|
|
- response.delete_cookie(key=SESSION_COOKIE_NAME)
|
|
|
+ response.delete_cookie(key=SESSION_COOKIE_NAME, path="/")
|
|
|
|
|
|
|
|
|
async def get_current_user(
|