diff --git a/docker/compose-dev.yaml b/docker/compose-dev.yaml index 0ed58b9..2f23a7d 100644 --- a/docker/compose-dev.yaml +++ b/docker/compose-dev.yaml @@ -1,4 +1,4 @@ -name: excel-project +name: disexcel services: backend-dev: diff --git a/src/service/auth/auth.py b/src/service/auth/auth.py index 746e454..9a81b12 100644 --- a/src/service/auth/auth.py +++ b/src/service/auth/auth.py @@ -4,6 +4,7 @@ from uuid import UUID from fastapi import Request +from src.cache.redis_client import redis_client from src.database.auth.refresh_tokens import JwtCrudActions from src.database.users.crud import UsersCrudActions 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: payload= await self.jwt_service.jwt_decode(token) + 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: sub=UUID(sub) 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''' - 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: - raise self.error.credentials_error(detail="Jwt token is incorrect") + '''decode current access token''' + 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: - jti=UUID(jti) + jti_refresh=UUID(jti_refresh) + jti_access=UUID(jti_access) except (ValueError, TypeError, AttributeError) as 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''' - if await self.jwt_db_actions.logout(jti): + if await self.jwt_db_actions.logout(jti_refresh): return True else: raise self.error.not_found_error(detail="Refresh Token Not Found") diff --git a/src/service/auth/jwt.py b/src/service/auth/jwt.py index 25368c8..a8dff5e 100644 --- a/src/service/auth/jwt.py +++ b/src/service/auth/jwt.py @@ -35,14 +35,17 @@ class JwtService: if not (data.get("sub")) or data.get("sub") == "": raise self.error.credentials_error(detail="Jwt token is incorrect") + async def create_access_token(self, data:dict)->str: user_info=data.copy() + jti=str(uuid4()) await self._validate_sub(user_info) 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) diff --git a/src/web/protected_routes/auth_routes.py b/src/web/protected_routes/auth_routes.py index 503c6b6..44d74c4 100644 --- a/src/web/protected_routes/auth_routes.py +++ b/src/web/protected_routes/auth_routes.py @@ -62,10 +62,12 @@ async def get_refresh_token(request:Request, @router.get("/logout") async def logout(response:Response, refresh_token: str = Cookie(), + access_token: str = Depends(oauth2_schema), auth:CurrentUserService=Depends(auth_service), current_user:UserOut=Depends(require_permissions()))->bool: + response.delete_cookie("refresh_token") - return await auth.logout(refresh_token) + return await auth.logout(refresh_token, access_token) @router.get("") async def protected(current_user:UserOut=Depends(require_permissions()))->dict: