41 lines
1.3 KiB
Python
41 lines
1.3 KiB
Python
from datetime import datetime, timezone
|
|
|
|
from core.security import (
|
|
AuthContext,
|
|
create_access_token,
|
|
get_auth_context,
|
|
verify_password,
|
|
)
|
|
from db.auth import get_user_by_username, revoke_token
|
|
from fastapi import APIRouter, Depends, HTTPException, Response, status
|
|
from models.auth import LoginInput, TokenResponse
|
|
|
|
router = APIRouter(
|
|
prefix="/auth",
|
|
tags=["Auth"],
|
|
)
|
|
|
|
|
|
@router.post("/login", response_model=TokenResponse)
|
|
async def login_handler(payload: LoginInput):
|
|
user = await get_user_by_username(payload.username)
|
|
if user is None or not verify_password(payload.password, user.password_hash):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail="Invalid username or password",
|
|
headers={"WWW-Authenticate": "Bearer"},
|
|
)
|
|
|
|
token = create_access_token(subject=user.username)
|
|
return TokenResponse(access_token=token)
|
|
|
|
|
|
@router.post("/logout", status_code=status.HTTP_204_NO_CONTENT)
|
|
async def logout_handler(
|
|
auth_context: AuthContext = Depends(get_auth_context),
|
|
) -> Response:
|
|
expires_at = datetime.fromtimestamp(auth_context.payload.exp, tz=timezone.utc)
|
|
if expires_at > datetime.now(timezone.utc):
|
|
await revoke_token(jti=auth_context.payload.jti, expires_at=expires_at)
|
|
return Response(status_code=status.HTTP_204_NO_CONTENT)
|