language-learning-app/api/app/auth.py

67 lines
1.7 KiB
Python
Raw Normal View History

from datetime import datetime, timedelta, timezone
2026-03-18 20:55:02 +00:00
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import jwt
from passlib.context import CryptContext
2026-03-18 20:55:02 +00:00
from .config import settings
security = HTTPBearer()
pwd_context = CryptContext(
schemes=["pbkdf2_sha256"],
deprecated="auto",
)
TOKEN_EXPIRY_HOURS = 24
def hash_password(password: str) -> str:
return pwd_context.hash(password)
def verify_password(plain: str, hashed: str) -> bool:
return pwd_context.verify(plain, hashed)
def create_access_token(user_id: str, email: str, is_admin: bool = False) -> str:
payload = {
"sub": user_id,
"email": email,
"is_admin": is_admin,
"exp": datetime.now(timezone.utc) + timedelta(hours=TOKEN_EXPIRY_HOURS),
}
return jwt.encode(payload, settings.jwt_secret, algorithm="HS256")
2026-03-18 20:55:02 +00:00
def verify_token(
credentials: HTTPAuthorizationCredentials = Depends(security),
) -> dict:
try:
payload = jwt.decode(
credentials.credentials,
settings.jwt_secret,
algorithms=["HS256"],
)
return payload
except jwt.InvalidTokenError:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid or expired token",
)
2026-03-27 10:36:43 +00:00
def _admin_emails() -> frozenset[str]:
return frozenset(
e.strip() for e in settings.admin_user_emails.split(",") if e.strip()
)
def require_admin(token_data: dict = Depends(verify_token)) -> dict:
if not token_data.get("is_admin"):
2026-03-27 10:36:43 +00:00
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Admin access required",
)
return token_data