revoke access_tokens

This commit is contained in:
2026-09-05 20:26:42 +03:00
parent da8284ae64
commit c4a6a88d05
4 changed files with 34 additions and 10 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
name: excel-project name: disexcel
services: services:
backend-dev: backend-dev:
+26 -7
View File
@@ -4,6 +4,7 @@ from uuid import UUID
from fastapi import Request from fastapi import Request
from src.cache.redis_client import redis_client
from src.database.auth.refresh_tokens import JwtCrudActions from src.database.auth.refresh_tokens import JwtCrudActions
from src.database.users.crud import UsersCrudActions from src.database.users.crud import UsersCrudActions
from src.errors.http_errors.errors import Errors from src.errors.http_errors.errors import Errors
@@ -52,8 +53,13 @@ class CurrentUserService:
async def get_current_user(self, token:str, *permissions: str)->UserOut: async def get_current_user(self, token:str, *permissions: str)->UserOut:
payload= await self.jwt_service.jwt_decode(token) payload= await self.jwt_service.jwt_decode(token)
sub=payload.get("sub") sub=payload.get("sub")
jti_access=payload.get("jti")
if jti_access and await redis_client.get(f"revoked_access_token:{jti_access}"):
raise self.error.credentials_error(detail="Token has been revoked")
try: try:
sub=UUID(sub) sub=UUID(sub)
except (ValueError, TypeError) as e: except (ValueError, TypeError) as e:
@@ -167,21 +173,34 @@ class CurrentUserService:
async def logout(self, refresh_token:str)->bool: async def logout(self, refresh_token:str, access_token:str)->bool:
'''decode current refresh token''' '''decode current refresh token'''
payload=await self.jwt_service.jwt_decode(refresh_token) payload_refresh=await self.jwt_service.jwt_decode(refresh_token)
if (jti:=payload.get("jti")) is None: '''decode current access token'''
raise self.error.credentials_error(detail="Jwt token is incorrect") payload_access=await self.jwt_service.jwt_decode(access_token)
if (jti_refresh:=payload_refresh.get("jti")) is None:
raise self.error.credentials_error(detail="Jwt refresh token is incorrect")
if (jti_access:=payload_access.get("jti")) is None or (exp_access:=payload_access.get("exp")) is None:
raise self.error.credentials_error(detail="Jwt access token is incorrect")
try: try:
jti=UUID(jti) jti_refresh=UUID(jti_refresh)
jti_access=UUID(jti_access)
except (ValueError, TypeError, AttributeError) as e: except (ValueError, TypeError, AttributeError) as e:
raise self.error.credentials_error(detail="Jwt token is incorrect") from e raise self.error.credentials_error(detail="Jwt token is incorrect") from e
if jti_access and exp_access:
exp_datetime = datetime.fromtimestamp(exp_access, tz=UTC)
remaining_ttl = max(int((exp_datetime - datetime.now(UTC)).total_seconds()), 1)
await redis_client.set(f"revoked_access_token:{jti_access}", "1", ex=remaining_ttl) #revoke tokens and set them to redis until their exp ends
'''logout by assigning revoked flag''' '''logout by assigning revoked flag'''
if await self.jwt_db_actions.logout(jti): if await self.jwt_db_actions.logout(jti_refresh):
return True return True
else: else:
raise self.error.not_found_error(detail="Refresh Token Not Found") raise self.error.not_found_error(detail="Refresh Token Not Found")
+4 -1
View File
@@ -35,14 +35,17 @@ class JwtService:
if not (data.get("sub")) or data.get("sub") == "": if not (data.get("sub")) or data.get("sub") == "":
raise self.error.credentials_error(detail="Jwt token is incorrect") raise self.error.credentials_error(detail="Jwt token is incorrect")
async def create_access_token(self, data:dict)->str: async def create_access_token(self, data:dict)->str:
user_info=data.copy() user_info=data.copy()
jti=str(uuid4())
await self._validate_sub(user_info) await self._validate_sub(user_info)
user_info.update({"exp": datetime.now(UTC)+timedelta(minutes=env_settings.ACCESS_TOKEN_EXPIRE_MINUTES), user_info.update({"exp": datetime.now(UTC)+timedelta(minutes=env_settings.ACCESS_TOKEN_EXPIRE_MINUTES),
"token_type":"access"}) "token_type":"access",
"jti":jti})
return jwt.encode(user_info, env_settings.SECRET_KEY, env_settings.ALGORITHM) return jwt.encode(user_info, env_settings.SECRET_KEY, env_settings.ALGORITHM)
+3 -1
View File
@@ -62,10 +62,12 @@ async def get_refresh_token(request:Request,
@router.get("/logout") @router.get("/logout")
async def logout(response:Response, async def logout(response:Response,
refresh_token: str = Cookie(), refresh_token: str = Cookie(),
access_token: str = Depends(oauth2_schema),
auth:CurrentUserService=Depends(auth_service), auth:CurrentUserService=Depends(auth_service),
current_user:UserOut=Depends(require_permissions()))->bool: current_user:UserOut=Depends(require_permissions()))->bool:
response.delete_cookie("refresh_token") response.delete_cookie("refresh_token")
return await auth.logout(refresh_token) return await auth.logout(refresh_token, access_token)
@router.get("") @router.get("")
async def protected(current_user:UserOut=Depends(require_permissions()))->dict: async def protected(current_user:UserOut=Depends(require_permissions()))->dict: