gmail-agentic / backend /app /auth /security.py
Kushal14680
Add files via upload
e280b6e unverified
Raw
History Blame Contribute Delete
2.97 kB
import base64
from datetime import datetime, timedelta
from typing import Optional
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt
from passlib.context import CryptContext
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from cryptography.fernet import Fernet
from app.config import settings
from app.database.postgres import get_db, User
# OAuth2 scheme
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="api/auth/login")
# Derive symmetric encryption key from JWT Secret
def get_fernet() -> Fernet:
secret_bytes = settings.JWT_SECRET.encode()
# Fernet key needs to be 32 bytes and urlsafe-base64 encoded
derived_key = base64.urlsafe_b64encode(secret_bytes[:32].ljust(32, b'-'))
return Fernet(derived_key)
def encrypt_token(token: str) -> str:
"""Encrypt a token before database storage."""
if not token:
return ""
fernet = get_fernet()
return fernet.encrypt(token.encode()).decode()
def decrypt_token(encrypted_token: str) -> str:
"""Decrypt a token retrieved from database."""
if not encrypted_token:
return ""
try:
fernet = get_fernet()
return fernet.decrypt(encrypted_token.encode()).decode()
except Exception:
return ""
import bcrypt
def verify_password(plain_password: str, hashed_password: str) -> bool:
try:
return bcrypt.checkpw(plain_password.encode('utf-8'), hashed_password.encode('utf-8'))
except Exception:
return False
def get_password_hash(password: str) -> str:
salt = bcrypt.gensalt()
return bcrypt.hashpw(password.encode('utf-8'), salt).decode('utf-8')
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, settings.JWT_SECRET, algorithm=settings.JWT_ALGORITHM)
return encoded_jwt
async def get_current_user(
token: str = Depends(oauth2_scheme),
db: AsyncSession = Depends(get_db)
) -> User:
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_id_str: str = payload.get("sub")
if user_id_str is None:
raise credentials_exception
except JWTError:
raise credentials_exception
# Query Postgres for the user
result = await db.execute(select(User).where(User.id == user_id_str))
user = result.scalars().first()
if user is None:
raise credentials_exception
return user