# modules/auth/api.py from fastapi import APIRouter, Cookie, Depends, HTTPException, status, Request, Response from fastapi.security import OAuth2PasswordRequestForm from jose import JWTError from modules.auth.models import User from modules.auth.schemas import UserCreate, UserResponse, Token from modules.auth.services import create_user from modules.auth.security import TokenType, get_current_user, oauth2_scheme, create_access_token, create_refresh_token, verify_token, authenticate_user, blacklist_tokens from sqlalchemy.orm import Session from typing import Annotated, Optional from core.database import get_db from datetime import timedelta from core.config import settings # Assuming settings is defined in core.config from core.exceptions import unauthorized_exception router = APIRouter(prefix="/auth", tags=["auth"]) @router.post("/register", response_model=UserResponse, status_code=status.HTTP_201_CREATED) def register(user: UserCreate, db: Annotated[Session, Depends(get_db)]): return create_user(user.username, user.password, user.name, db) @router.post("/login", response_model=Token) def login(response: Response, form_data: Annotated[OAuth2PasswordRequestForm, Depends()], db: Annotated[Session, Depends(get_db)]): """ Authenticate user and return JWT token. """ user = authenticate_user(form_data.username, form_data.password, db) if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password", ) access_token = create_access_token(data={"sub": user.username}, expires_delta=timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)) refresh_token = create_refresh_token(data={"sub": user.username}) max_age = settings.REFRESH_TOKEN_EXPIRE_DAYS * 24 * 60 * 60 response.set_cookie( key="refresh_token", value=refresh_token, httponly=True, secure=True, samesite="Lax", max_age=max_age ) return {"access_token": access_token, "token_type": "bearer"} @router.post("/refresh") def refresh_token(request: Request, db: Annotated[Session, Depends(get_db)]): refresh_token = request.cookies.get("refresh_token") if not refresh_token: raise unauthorized_exception("Refresh token missing") user_data = verify_token(refresh_token, expected_token_type=TokenType.REFRESH, db=db) if not user_data: raise unauthorized_exception("Invalid refresh token") new_access_token = create_access_token(data={"sub": user_data.username}) return {"access_token": new_access_token, "token_type": "bearer"} @router.post("/logout") def logout(response: Response, db: Annotated[Session, Depends(get_db)], current_user: Annotated[User, Depends(get_current_user)], access_token: str = Depends(oauth2_scheme), refresh_token: Optional[str] = Cookie(None, alias="refresh_token")): try: if not refresh_token: raise unauthorized_exception("Refresh token not found") blacklist_tokens( access_token=access_token, refresh_token=refresh_token, db=db ) response.delete_cookie(key="refresh_token") return {"message": "Logged out successfully"} except JWTError: raise unauthorized_exception("Invalid token")