69 lines
2.1 KiB
Python
69 lines
2.1 KiB
Python
|
|
"""Auth router: login, token refresh, current user profile."""
|
||
|
|
|
||
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||
|
|
|
||
|
|
from app.database import get_db
|
||
|
|
from app.dependencies import get_current_user
|
||
|
|
from app.models.user import User
|
||
|
|
from app.schemas.user import (
|
||
|
|
LoginRequest,
|
||
|
|
RefreshRequest,
|
||
|
|
TokenResponse,
|
||
|
|
UserResponse,
|
||
|
|
)
|
||
|
|
from app.services.auth_service import (
|
||
|
|
authenticate_user,
|
||
|
|
generate_token_pair,
|
||
|
|
refresh_access_token,
|
||
|
|
)
|
||
|
|
|
||
|
|
router = APIRouter(prefix="/auth", tags=["auth"])
|
||
|
|
|
||
|
|
|
||
|
|
@router.post("/login", response_model=TokenResponse, status_code=status.HTTP_200_OK)
|
||
|
|
async def login(
|
||
|
|
body: LoginRequest,
|
||
|
|
db: AsyncSession = Depends(get_db),
|
||
|
|
):
|
||
|
|
"""Authenticate user and return JWT access + refresh tokens."""
|
||
|
|
user = await authenticate_user(db, body.email, body.password)
|
||
|
|
if user is None:
|
||
|
|
raise HTTPException(
|
||
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||
|
|
detail={
|
||
|
|
"error": {
|
||
|
|
"code": "INVALID_CREDENTIALS",
|
||
|
|
"message": "Invalid email or password",
|
||
|
|
}
|
||
|
|
},
|
||
|
|
)
|
||
|
|
tokens = generate_token_pair(user)
|
||
|
|
return TokenResponse(**tokens)
|
||
|
|
|
||
|
|
|
||
|
|
@router.post("/refresh", response_model=TokenResponse, status_code=status.HTTP_200_OK)
|
||
|
|
async def refresh_token(
|
||
|
|
body: RefreshRequest,
|
||
|
|
db: AsyncSession = Depends(get_db),
|
||
|
|
):
|
||
|
|
"""Exchange a valid refresh token for a new access + refresh token pair."""
|
||
|
|
tokens = await refresh_access_token(db, body.refresh_token)
|
||
|
|
if tokens is None:
|
||
|
|
raise HTTPException(
|
||
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||
|
|
detail={
|
||
|
|
"error": {
|
||
|
|
"code": "INVALID_REFRESH_TOKEN",
|
||
|
|
"message": "Invalid or expired refresh token",
|
||
|
|
}
|
||
|
|
},
|
||
|
|
)
|
||
|
|
return TokenResponse(**tokens)
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/me", response_model=UserResponse, status_code=status.HTTP_200_OK)
|
||
|
|
async def get_me(current_user: User = Depends(get_current_user)):
|
||
|
|
"""Return the currently authenticated user's profile."""
|
||
|
|
return UserResponse.model_validate(current_user)
|