feat: add validation

This commit is contained in:
2026-05-19 22:29:02 +03:00
parent 666f2f67cb
commit caf7f003fa
6 changed files with 110 additions and 41 deletions

View File

@@ -2,43 +2,21 @@ from fastapi import APIRouter, Request, Depends
from fastapi.responses import ORJSONResponse
from starlette import status
from src.application.commands import JwtRefreshCommand
from src.application.domain.exceptions import ApplicationException
from src.application.domain.exceptions import ApplicationException, RefreshConcurrentException
from src.infrastructure.config import settings
from src.presentation.decorators import csrf_protect,rate_limit
from src.presentation.decorators import csrf_protect, rate_limit
from src.presentation.dependencies import get_jwt_refresh_command
jwt_router = APIRouter(prefix='/jwt', tags=['Jwt'])
@jwt_router.post('/refresh', response_class=ORJSONResponse, status_code=status.HTTP_200_OK)
@rate_limit(limit=settings.RATE_LIMIT_REQUESTS, window_seconds=settings.RATE_LIMIT_WINDOW, scope='ip')
@csrf_protect()
async def refresh_tokens(
request: Request,
command: JwtRefreshCommand = Depends(get_jwt_refresh_command)
):
refresh_token = request.cookies.get('refresh_token')
def _clear_auth_cookies(response: ORJSONResponse) -> None:
response.delete_cookie('access_token', path='/', domain=settings.AUTH_COOKIE_DOMAIN)
response.delete_cookie('refresh_token', path='/', domain=settings.AUTH_COOKIE_DOMAIN)
if not refresh_token:
response = ORJSONResponse({'ok': False, 'error': 'No refresh token'}, status_code=401)
response.delete_cookie('access_token', path='/', domain=settings.AUTH_COOKIE_DOMAIN)
response.delete_cookie('refresh_token', path='/', domain=settings.AUTH_COOKIE_DOMAIN)
return response
ip = request.client.host if request.client else None
user_agent = request.headers.get('user-agent')
try:
access, refresh = await command(refresh_token=refresh_token, ip=ip, user_agent=user_agent)
except ApplicationException:
response = ORJSONResponse({'result': False}, status_code=401)
response.delete_cookie('access_token', path='/', domain=settings.AUTH_COOKIE_DOMAIN)
response.delete_cookie('refresh_token', path='/', domain=settings.AUTH_COOKIE_DOMAIN)
return response
response = ORJSONResponse({'result': True})
def _set_auth_cookies(response: ORJSONResponse, access: str, refresh: str) -> None:
response.set_cookie(
key='access_token',
value=access,
@@ -59,9 +37,37 @@ async def refresh_tokens(
domain=settings.AUTH_COOKIE_DOMAIN,
max_age=int(settings.JWT_REFRESH_TTL_SECONDS),
)
return response
# Usage
# @jwt_router.get("/test")
# async def profile(auth: AuthContext = Depends(require_access_token)):
# return 'ok'
@jwt_router.post('/refresh', response_class=ORJSONResponse, status_code=status.HTTP_200_OK)
@rate_limit(limit=settings.RATE_LIMIT_REQUESTS, window_seconds=settings.RATE_LIMIT_WINDOW, scope='ip')
@csrf_protect()
async def refresh_tokens(
request: Request,
command: JwtRefreshCommand = Depends(get_jwt_refresh_command),
):
refresh_token = request.cookies.get('refresh_token')
if not refresh_token:
response = ORJSONResponse({'ok': False, 'error': 'No refresh token'}, status_code=401)
_clear_auth_cookies(response)
return response
ip = request.client.host if request.client else None
user_agent = request.headers.get('user-agent')
try:
tokens = await command(refresh_token=refresh_token, ip=ip, user_agent=user_agent)
except RefreshConcurrentException:
return ORJSONResponse({'result': True, 'concurrent': True}, status_code=status.HTTP_200_OK)
except ApplicationException as exc:
if exc.status_code == status.HTTP_401_UNAUTHORIZED:
response = ORJSONResponse({'result': False}, status_code=401)
_clear_auth_cookies(response)
return response
raise
access, refresh = tokens
response = ORJSONResponse({'result': True})
_set_auth_cookies(response, access, refresh)
return response