Files
devops-portal/backend/auth.py
2026-04-07 23:48:47 +03:00

68 lines
2.0 KiB
Python

from __future__ import annotations
import os
from datetime import datetime, timedelta, timezone
from fastapi import Depends, HTTPException
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from jose import JWTError, jwt
from passlib.context import CryptContext
from sqlalchemy.orm import Session
from backend.db import get_db
from backend.models import User
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
bearer = HTTPBearer(auto_error=False)
def _env(name: str, default: str) -> str:
value = os.getenv(name)
return value if value else default
JWT_SECRET = _env("JWT_SECRET", "change-me")
JWT_ALG = _env("JWT_ALG", "HS256")
JWT_EXPIRES_MINUTES = int(_env("JWT_EXPIRES_MINUTES", "480")) # 8h
def hash_password(password: str) -> str:
return pwd_context.hash(password)
def verify_password(password: str, password_hash: str) -> bool:
return pwd_context.verify(password, password_hash)
def create_access_token(*, user_id: str) -> str:
now = datetime.now(timezone.utc)
payload = {
"sub": user_id,
"iat": int(now.timestamp()),
"exp": int((now + timedelta(minutes=JWT_EXPIRES_MINUTES)).timestamp()),
}
return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALG)
def get_current_user(
creds: HTTPAuthorizationCredentials | None = Depends(bearer),
db: Session = Depends(get_db),
) -> User:
if creds is None or not creds.credentials:
raise HTTPException(status_code=401, detail="Not authenticated")
token = creds.credentials
try:
payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALG])
user_id = payload.get("sub")
if not user_id:
raise HTTPException(status_code=401, detail="Invalid token")
except JWTError:
raise HTTPException(status_code=401, detail="Invalid token")
user = db.get(User, user_id)
if not user or not user.is_active:
raise HTTPException(status_code=401, detail="User inactive or not found")
return user