Cybersecurity-Projects/PROJECTS/bug-bounty-platform/backend/app/auth/routes.py

152 lines
3.3 KiB
Python

"""
ⒸAngelaMos | 2025
routes.py
"""
from typing import Annotated
from fastapi import (
APIRouter,
Cookie,
Depends,
Request,
Response,
status,
)
from fastapi.security import (
OAuth2PasswordRequestForm,
)
from config import settings
from core.dependencies import (
ClientIP,
CurrentUser,
)
from core.security import (
clear_refresh_cookie,
set_refresh_cookie,
)
from core.rate_limit import limiter
from core.exceptions import TokenError
from .schemas import (
PasswordChange,
TokenResponse,
TokenWithUserResponse,
)
from user.schemas import UserResponse
from .dependencies import AuthServiceDep
from user.dependencies import UserServiceDep
from core.responses import AUTH_401
router = APIRouter(prefix = "/auth", tags = ["auth"])
@router.post(
"/login",
response_model = TokenWithUserResponse,
responses = {**AUTH_401}
)
@limiter.limit(settings.RATE_LIMIT_AUTH)
async def login(
request: Request,
response: Response,
auth_service: AuthServiceDep,
ip: ClientIP,
form_data: Annotated[OAuth2PasswordRequestForm,
Depends()],
) -> TokenWithUserResponse:
"""
Login with email and password
"""
result, refresh_token = await auth_service.login(
email=form_data.username,
password=form_data.password,
ip_address=ip,
)
set_refresh_cookie(response, refresh_token)
return result
@router.post(
"/refresh",
response_model = TokenResponse,
responses = {**AUTH_401}
)
async def refresh_token(
auth_service: AuthServiceDep,
ip: ClientIP,
refresh_token: str | None = Cookie(None),
) -> TokenResponse:
"""
Refresh access token
"""
if not refresh_token:
raise TokenError("Refresh token required")
return await auth_service.refresh_tokens(
refresh_token,
ip_address = ip
)
@router.post(
"/logout",
status_code = status.HTTP_204_NO_CONTENT,
responses = {**AUTH_401}
)
async def logout(
response: Response,
auth_service: AuthServiceDep,
refresh_token: str | None = Cookie(None),
) -> None:
"""
Logout current session
"""
if not refresh_token:
raise TokenError("Refresh token required")
await auth_service.logout(refresh_token)
clear_refresh_cookie(response)
@router.post("/logout-all", responses = {**AUTH_401})
async def logout_all(
response: Response,
auth_service: AuthServiceDep,
current_user: CurrentUser,
) -> dict[str,
int]:
"""
Logout from all devices
"""
count = await auth_service.logout_all(current_user)
clear_refresh_cookie(response)
return {"revoked_sessions": count}
@router.get("/me", response_model = UserResponse, responses = {**AUTH_401})
async def get_current_user(current_user: CurrentUser) -> UserResponse:
"""
Get current authenticated user
"""
return UserResponse.model_validate(current_user)
@router.post(
"/change-password",
status_code = status.HTTP_204_NO_CONTENT,
responses = {**AUTH_401}
)
async def change_password(
user_service: UserServiceDep,
current_user: CurrentUser,
data: PasswordChange,
) -> None:
"""
Change current user password
"""
await user_service.change_password(
current_user,
data.current_password,
data.new_password,
)