From afbf5fee7a4094e8e88bbe40b9322ff389049f10 Mon Sep 17 00:00:00 2001 From: Taha Yassine Kraiem Date: Mon, 10 Mar 2025 11:25:18 +0100 Subject: [PATCH] fix(chalice): fix refresh token --- api/chalicelib/core/authorizers.py | 18 ++---------------- api/chalicelib/core/users.py | 17 +++++++++++++---- api/routers/core_dynamic.py | 24 ++++++++++-------------- ee/api/chalicelib/core/users.py | 26 +++++++++++++++++++------- 4 files changed, 44 insertions(+), 41 deletions(-) diff --git a/api/chalicelib/core/authorizers.py b/api/chalicelib/core/authorizers.py index e96fca0d5..c0e874d86 100644 --- a/api/chalicelib/core/authorizers.py +++ b/api/chalicelib/core/authorizers.py @@ -40,8 +40,7 @@ def jwt_authorizer(scheme: str, token: str, leeway=0) -> dict | None: logger.debug("! JWT Expired signature") return None except BaseException as e: - logger.warning("! JWT Base Exception") - logger.debug(e) + logger.warning("! JWT Base Exception", exc_info=e) return None return payload @@ -50,10 +49,6 @@ def jwt_refresh_authorizer(scheme: str, token: str): if scheme.lower() != "bearer": return None try: - logger.warning("Checking JWT REF token: %s", token) - logger.warning("Against REF: %s", - config("JWT_REFRESH_SECRET") if not is_spot_token(token) else config("JWT_SPOT_REFRESH_SECRET")) - logger.warning(get_supported_audience()) payload = jwt.decode(jwt=token, key=config("JWT_REFRESH_SECRET") if not is_spot_token(token) \ else config("JWT_SPOT_REFRESH_SECRET"), @@ -63,8 +58,7 @@ def jwt_refresh_authorizer(scheme: str, token: str): logger.debug("! JWT-refresh Expired signature") return None except BaseException as e: - logger.warning("! JWT-refresh Base Exception") - logger.debug(e) + logger.error("! JWT-refresh Base Exception", exc_info=e) return None return payload @@ -83,10 +77,6 @@ def generate_jwt(user_id, tenant_id, iat, aud, for_spot=False): key=config("JWT_SECRET") if not for_spot else config("JWT_SPOT_SECRET"), algorithm=config("JWT_ALGORITHM") ) - logger.warning("Generated JWT token: %s", token) - logger.warning("For spot: %s", for_spot) - logger.warning("Using: %s", config("JWT_SECRET") if not for_spot else config("JWT_SPOT_SECRET")) - logger.warning(aud) return token @@ -105,10 +95,6 @@ def generate_jwt_refresh(user_id, tenant_id, iat, aud, jwt_jti, for_spot=False): key=config("JWT_REFRESH_SECRET") if not for_spot else config("JWT_SPOT_REFRESH_SECRET"), algorithm=config("JWT_ALGORITHM") ) - logger.warning("Generated JWT REF token: %s", token) - logger.warning("For spot REF: %s", for_spot) - logger.warning("Using: %s", config("JWT_REFRESH_SECRET") if not for_spot else config("JWT_SPOT_REFRESH_SECRET")) - logger.warning(aud) return token diff --git a/api/chalicelib/core/users.py b/api/chalicelib/core/users.py index 6e3de7282..a02a2241c 100644 --- a/api/chalicelib/core/users.py +++ b/api/chalicelib/core/users.py @@ -3,7 +3,7 @@ import secrets from decouple import config from fastapi import BackgroundTasks -from pydantic import BaseModel +from pydantic import BaseModel, model_validator import schemas from chalicelib.core import authorizers @@ -284,7 +284,7 @@ def edit_member(user_id_to_update, tenant_id, changes: schemas.EditMemberSchema, if editor_id != user_id_to_update: admin = get_user_role(tenant_id=tenant_id, user_id=editor_id) if not admin["superAdmin"] and not admin["admin"]: - return {"errors": ["unauthorized"]} + return {"errors": ["unauthorized, you must have admin privileges"]} if admin["admin"] and user["superAdmin"]: return {"errors": ["only the owner can edit his own details"]} else: @@ -554,12 +554,21 @@ def refresh_auth_exists(user_id, jwt_jti=None): class ChangeJwt(BaseModel): jwt_iat: int - jwt_refresh_jti: int + jwt_refresh_jti: str jwt_refresh_iat: int spot_jwt_iat: int - spot_jwt_refresh_jti: int + spot_jwt_refresh_jti: str spot_jwt_refresh_iat: int + @model_validator(mode="before") + @classmethod + def _transform_data(cls, values): + if values.get("jwt_refresh_jti") is not None: + values["jwt_refresh_jti"] = str(values["jwt_refresh_jti"]) + if values.get("jwt_refresh_jti") is not None: + values["spot_jwt_refresh_jti"] = str(values["spot_jwt_refresh_jti"]) + return values + def change_jwt_iat_jti(user_id): with pg_client.PostgresClient() as cur: diff --git a/api/routers/core_dynamic.py b/api/routers/core_dynamic.py index 4ab135035..6df4bfff4 100644 --- a/api/routers/core_dynamic.py +++ b/api/routers/core_dynamic.py @@ -7,27 +7,30 @@ from fastapi import HTTPException, status from starlette.responses import RedirectResponse, FileResponse, JSONResponse, Response import schemas -from chalicelib.core import scope from chalicelib.core import assist, signup, feature_flags -from chalicelib.core.metrics import heatmaps -from chalicelib.core.errors import errors, errors_details -from chalicelib.core.sessions import sessions, sessions_notes, sessions_replay, sessions_favorite, sessions_viewed, \ - sessions_assignments, unprocessed_sessions, sessions_search +from chalicelib.core import scope from chalicelib.core import tenants, users, projects, license from chalicelib.core import webhook from chalicelib.core.collaborations.collaboration_slack import Slack +from chalicelib.core.errors import errors, errors_details +from chalicelib.core.metrics import heatmaps +from chalicelib.core.sessions import sessions, sessions_notes, sessions_replay, sessions_favorite, sessions_viewed, \ + sessions_assignments, unprocessed_sessions, sessions_search from chalicelib.utils import captcha, smtp +from chalicelib.utils import contextual_validators from chalicelib.utils import helper from chalicelib.utils.TimeUTC import TimeUTC from or_dependencies import OR_context, OR_role from routers.base import get_routers from routers.subs import spot -from chalicelib.utils import contextual_validators logger = logging.getLogger(__name__) public_app, app, app_apikey = get_routers() -COOKIE_PATH = "/api/refresh" +if config("LOCAL_DEV", cast=bool, default=False): + COOKIE_PATH = "/refresh" +else: + COOKIE_PATH = "/api/refresh" @public_app.get('/signup', tags=['signup']) @@ -68,18 +71,11 @@ def __process_authentication_response(response: JSONResponse, data: dict) -> dic max_age=refresh_token_max_age, secure=True, httponly=True) response.set_cookie(key="spotRefreshToken", value=spot_refresh_token, path=spot.COOKIE_PATH, max_age=spot_refresh_token_max_age, secure=True, httponly=True) - logger.warning("cookie refreshToken: %s", refresh_token) - logger.warning("cookie spotRefreshToken: %s", spot_refresh_token) return data @public_app.post('/login', tags=["authentication"]) def login_user(response: JSONResponse, data: schemas.UserLoginSchema = Body(...)): - # if data.email != 'tahay@asayer.io': - # raise HTTPException( - # status_code=status.HTTP_401_UNAUTHORIZED, - # detail="Enforced testing mode is active." - # ) if helper.allow_captcha() and not captcha.is_valid(data.g_recaptcha_response): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, diff --git a/ee/api/chalicelib/core/users.py b/ee/api/chalicelib/core/users.py index fea9f522b..8f322826d 100644 --- a/ee/api/chalicelib/core/users.py +++ b/ee/api/chalicelib/core/users.py @@ -4,7 +4,7 @@ import secrets from decouple import config from fastapi import BackgroundTasks, HTTPException -from pydantic import BaseModel +from pydantic import BaseModel, model_validator from starlette import status import schemas @@ -659,12 +659,21 @@ def refresh_auth_exists(user_id, tenant_id, jwt_jti=None): class ChangeJwt(BaseModel): jwt_iat: int - jwt_refresh_jti: int + jwt_refresh_jti: str jwt_refresh_iat: int spot_jwt_iat: int - spot_jwt_refresh_jti: int + spot_jwt_refresh_jti: str spot_jwt_refresh_iat: int + @model_validator(mode="before") + @classmethod + def _transform_data(cls, values): + if values.get("jwt_refresh_jti") is not None: + values["jwt_refresh_jti"] = str(values["jwt_refresh_jti"]) + if values.get("jwt_refresh_jti") is not None: + values["spot_jwt_refresh_jti"] = str(values["spot_jwt_refresh_jti"]) + return values + def change_jwt_iat_jti(user_id): with pg_client.PostgresClient() as cur: @@ -759,9 +768,12 @@ def authenticate(email, password, for_change_password=False) -> dict | bool | No response = { "jwt": authorizers.generate_jwt(user_id=r['userId'], tenant_id=r['tenantId'], iat=j_r.jwt_iat, aud=AUDIENCE), - "refreshToken": authorizers.generate_jwt_refresh(user_id=r['userId'], tenant_id=r['tenantId'], - iat=j_r.jwt_refresh_iat, aud=AUDIENCE, - jwt_jti=j_r.jwt_refresh_jti), + "refreshToken": authorizers.generate_jwt_refresh(user_id=r['userId'], + tenant_id=r['tenantId'], + iat=j_r.jwt_refresh_iat, + aud=AUDIENCE, + jwt_jti=j_r.jwt_refresh_jti, + for_spot=False), "refreshTokenMaxAge": config("JWT_REFRESH_EXPIRATION", cast=int), "email": email, "spotJwt": authorizers.generate_jwt(user_id=r['userId'], tenant_id=r['tenantId'], @@ -856,7 +868,7 @@ def logout(user_id: int): cur.execute(query) -def refresh(user_id: int, tenant_id: int) -> dict: +def refresh(user_id: int, tenant_id: int = -1) -> dict: jwt_iat, jwt_r_jti, jwt_r_iat = refresh_jwt_iat_jti(user_id=user_id) return { "jwt": authorizers.generate_jwt(user_id=user_id, tenant_id=tenant_id, iat=jwt_iat,