From 091d1ab5335cce6337272075b5fe11cfdd0ce8a1 Mon Sep 17 00:00:00 2001 From: Guy Pilosof Date: Sun, 7 Aug 2022 21:09:48 +0300 Subject: [PATCH 1/2] restructure the jwt_response object --- descope/__init__.py | 2 + descope/auth.py | 93 +++++++++----------- descope/common.py | 3 + descope/descope_client.py | 16 +++- samples/decorators/flask_decorators.py | 73 ++++++++++----- samples/magiclink_cross_device_sample_app.py | 6 +- samples/magiclink_sample_app.py | 14 ++- samples/magiclink_web_sample_app.py | 12 ++- samples/oauth_sample_app.py | 4 +- samples/otp_sample_app.py | 20 ++--- samples/otp_web_sample_app.py | 35 +++++--- samples/totp_sample_app.py | 16 ++-- tests/test_descope_client.py | 3 +- 13 files changed, 166 insertions(+), 131 deletions(-) diff --git a/descope/__init__.py b/descope/__init__.py index 4b133085e..7bfa357c6 100644 --- a/descope/__init__.py +++ b/descope/__init__.py @@ -1,6 +1,8 @@ from descope.common import ( REFRESH_SESSION_COOKIE_NAME, + REFRESH_SESSION_TOKEN_NAME, SESSION_COOKIE_NAME, + SESSION_TOKEN_NAME, DeliveryMethod, ) from descope.descope_client import DescopeClient diff --git a/descope/auth.py b/descope/auth.py index 33c99830c..01c35a330 100644 --- a/descope/auth.py +++ b/descope/auth.py @@ -13,7 +13,8 @@ from descope.common import ( DEFAULT_BASE_URL, PHONE_REGEX, - SESSION_COOKIE_NAME, + REFRESH_SESSION_TOKEN_NAME, + SESSION_TOKEN_NAME, DeliveryMethod, EndpointsV1, ) @@ -264,54 +265,49 @@ def _fetch_public_keys(self) -> None: # just continue to the next key pass - def _extractToken(self, jwt) -> dict: - if not jwt: - return None - token_claims = self._validate_and_load_tokens(jwt, None) - token_claims["projectId"] = token_claims.pop( - "iss" - ) # replace the key name from iss->projectId - token_claims["userId"] = token_claims.pop( - "sub" - ) # replace the key name from sub->userId - return token_claims - - def _generate_auth_info(self, response_body, cookie) -> dict: - tokens = {} - rt = response_body.get("refreshJwt", "") - rtoken = self._extractToken(rt) - if rtoken is not None: - tokens[rtoken["drn"]] = rtoken - stoken = self._extractToken(response_body.get("sessionJwt", "")) - if stoken is not None: - tokens[stoken["drn"]] = stoken + def _generate_auth_info(self, response_body: dict, cookie: str) -> dict: + jwt_response = {} + stJwt = response_body.get("sessionJwt", "") + if stJwt: + jwt_response[SESSION_TOKEN_NAME] = self._validate_and_load_tokens( + stJwt, None + ) + rtJwt = response_body.get("refreshJwt", "") + if rtJwt: + jwt_response[REFRESH_SESSION_TOKEN_NAME] = self._validate_and_load_tokens( + rtJwt, None + ) if cookie: - token_claims = self._validate_and_load_tokens(cookie, None) - token_claims["projectId"] = token_claims.pop( - "iss" - ) # replace the key name from iss->projectId - token_claims["userId"] = token_claims.pop( - "sub" - ) # replace the key name from sub->userId - tokens[token_claims["drn"]] = token_claims - - # collect all cookie attributed from response - tokens["cookieDomain"] = response_body.get("cookieDomain", "") - tokens["cookiePath"] = response_body.get("cookiePath", "/") - tokens["cookieMaxAge"] = response_body.get("cookieMaxAge", 0) - tokens["cookieExpiration"] = response_body.get("cookieExpiration", 0) - - return tokens - - def _generate_jwt_response(self, response_body, cookie) -> dict: - tokens = self._generate_auth_info(response_body, cookie) - jwt_response = { - "error": response_body.get("error", ""), - "jwts": tokens, - "user": response_body.get("user", ""), - "firstSeen": response_body.get("firstSeen", True), + jwt_response[REFRESH_SESSION_TOKEN_NAME] = self._validate_and_load_tokens( + cookie, None + ) + + jwt_response["cookieData"] = { + "exp": response_body.get("cookieExpiration", 0), + "maxAge": response_body.get("cookieMaxAge", 0), + "domain": response_body.get("cookieDomain", ""), + "path": response_body.get("cookiePath", "/"), } + + return jwt_response + + def _generate_jwt_response(self, response_body: dict, cookie: str) -> dict: + jwt_response = self._generate_auth_info(response_body, cookie) + + projectId = jwt_response.get(SESSION_TOKEN_NAME, {}).get( + "iss", None + ) or jwt_response.get(REFRESH_SESSION_TOKEN_NAME, {}).get("iss", None) + userId = jwt_response.get(SESSION_TOKEN_NAME, {}).get( + "sub", None + ) or jwt_response.get(REFRESH_SESSION_TOKEN_NAME, {}).get("sub", None) + + jwt_response["tenants"] = response_body.get("tenants", {}) + jwt_response["projectId"] = projectId + jwt_response["userId"] = userId + jwt_response["user"] = response_body.get("user", {}) + jwt_response["firstSeen"] = response_body.get("firstSeen", True) + jwt_response["error"] = response_body.get("error", "") return jwt_response def _get_default_headers(self, pswd: str = None): @@ -405,10 +401,7 @@ def _validate_and_load_tokens(self, session_token: str, refresh_token: str) -> d ) # Refresh token is valid now refresh the session token - auth_info = self._refresh_token(refresh_token) - - claims = auth_info[SESSION_COOKIE_NAME] - return claims + return self._refresh_token(refresh_token) # return jwt_response dict except Exception as e: raise AuthException( diff --git a/descope/common.py b/descope/common.py index 4980d69ca..7aa3d6712 100644 --- a/descope/common.py +++ b/descope/common.py @@ -8,6 +8,9 @@ SESSION_COOKIE_NAME = "DS" REFRESH_SESSION_COOKIE_NAME = "DSR" +SESSION_TOKEN_NAME = "sessionToken" +REFRESH_SESSION_TOKEN_NAME = "refreshSessionToken" + REDIRECT_LOCATION_COOKIE_NAME = "Location" diff --git a/descope/descope_client.py b/descope/descope_client.py index 4e55b2942..3494a627d 100644 --- a/descope/descope_client.py +++ b/descope/descope_client.py @@ -8,7 +8,7 @@ from descope.authmethod.saml import SAML # noqa: F401 from descope.authmethod.totp import TOTP # noqa: F401 from descope.authmethod.webauthn import WebauthN # noqa: F401 -from descope.common import EndpointsV1 +from descope.common import SESSION_TOKEN_NAME, EndpointsV1 from descope.exceptions import ERROR_TYPE_INVALID_ARGUMENT, AuthException @@ -73,10 +73,18 @@ def validate_session_request(self, session_token: str, refresh_token: str) -> di AuthException: for any case token is not valid means session is not authorized """ - token_claims = self._auth._validate_and_load_tokens( + res = self._auth._validate_and_load_tokens( session_token, refresh_token - ) - return {token_claims["drn"]: token_claims} + ) # return jwt_response dict + + # Check if we had to refresh the session token and got a new one + if res.get(SESSION_TOKEN_NAME, None) and session_token != res.get( + SESSION_TOKEN_NAME + ).get("jwt"): + return res + else: + # In such case we return only the data related to the session token + return {SESSION_TOKEN_NAME: res} def logout(self, refresh_token: str) -> requests.Response: """ diff --git a/samples/decorators/flask_decorators.py b/samples/decorators/flask_decorators.py index bac1ec622..7fff6e4b4 100644 --- a/samples/decorators/flask_decorators.py +++ b/samples/decorators/flask_decorators.py @@ -12,13 +12,15 @@ from descope import AuthException # noqa: E402 from descope import ( # noqa: E402 REFRESH_SESSION_COOKIE_NAME, + REFRESH_SESSION_TOKEN_NAME, SESSION_COOKIE_NAME, + SESSION_TOKEN_NAME, DeliveryMethod, ) -def set_cookie_on_response(response, data): - cookie_domain = data.get("cookieDomain", "") +def set_cookie_on_response(response: Response, token: dict, cookieData: dict): + cookie_domain = cookieData.get("domain", "") if cookie_domain == "": cookie_domain = None @@ -26,11 +28,11 @@ def set_cookie_on_response(response, data): expire_time = current_time + datetime.timedelta(days=30) return response.set_cookie( - key=data.get("cookieName", ""), - value=data.get("jwt", ""), - max_age=data.get("cookieMaxAge", int(expire_time.timestamp())), - expires=data.get("cookieExpiration", expire_time), - path=data.get("cookiePath", ""), + key=token.get("drn", ""), + value=token.get("jwt", ""), + max_age=cookieData.get("maxAge", int(expire_time.timestamp())), + expires=cookieData.get("exp", expire_time), + path=cookieData.get("path", "/"), domain=cookie_domain, secure=False, # True httponly=True, @@ -101,7 +103,7 @@ def decorated(*args, **kwargs): session_token = cookies.get(SESSION_COOKIE_NAME, None) refresh_token = cookies.get(REFRESH_SESSION_COOKIE_NAME, None) try: - claims = descope_client.validate_session_request( + jwt_response = descope_client.validate_session_request( session_token, refresh_token ) @@ -109,12 +111,15 @@ def decorated(*args, **kwargs): return Response("Access denied", 401) # Save the claims on the context execute the original API - _request_ctx_stack.top.claims = claims + _request_ctx_stack.top.claims = jwt_response response = f(*args, **kwargs) - tokens = claims - for _, data in tokens.items(): - set_cookie_on_response(response, data) + if jwt_response.get("cookieData", None): + set_cookie_on_response( + response, + jwt_response[SESSION_TOKEN_NAME], + jwt_response["cookieData"], + ) return response return decorated @@ -147,9 +152,14 @@ def decorated(*args, **kwargs): _request_ctx_stack.top.claims = jwt_response response = f(*args, **kwargs) - tokens = jwt_response["jwts"] - for _, data in tokens.items(): - set_cookie_on_response(response, data) + set_cookie_on_response( + response, jwt_response[SESSION_TOKEN_NAME], jwt_response["cookieData"] + ) + set_cookie_on_response( + response, + jwt_response[REFRESH_SESSION_TOKEN_NAME], + jwt_response["cookieData"], + ) return response @@ -183,9 +193,14 @@ def decorated(*args, **kwargs): _request_ctx_stack.top.claims = jwt_response response = f(*args, **kwargs) - tokens = jwt_response["jwts"] - for _, data in tokens.items(): - set_cookie_on_response(response, data) + set_cookie_on_response( + response, jwt_response[SESSION_TOKEN_NAME], jwt_response["cookieData"] + ) + set_cookie_on_response( + response, + jwt_response[REFRESH_SESSION_TOKEN_NAME], + jwt_response["cookieData"], + ) return response @@ -219,9 +234,14 @@ def decorated(*args, **kwargs): _request_ctx_stack.top.claims = jwt_response response = f(*args, **kwargs) - tokens = jwt_response["jwts"] - for _, data in tokens.items(): - set_cookie_on_response(response, data) + set_cookie_on_response( + response, jwt_response[SESSION_TOKEN_NAME], jwt_response["cookieData"] + ) + set_cookie_on_response( + response, + jwt_response[REFRESH_SESSION_TOKEN_NAME], + jwt_response["cookieData"], + ) return response @@ -302,9 +322,14 @@ def decorated(*args, **kwargs): _request_ctx_stack.top.claims = jwt_response response = f(*args, **kwargs) - tokens = jwt_response["jwts"] - for _, data in tokens.items(): - set_cookie_on_response(response, data) + set_cookie_on_response( + response, jwt_response[SESSION_TOKEN_NAME], jwt_response["cookieData"] + ) + set_cookie_on_response( + response, + jwt_response[REFRESH_SESSION_TOKEN_NAME], + jwt_response["cookieData"], + ) return response return decorated diff --git a/samples/magiclink_cross_device_sample_app.py b/samples/magiclink_cross_device_sample_app.py index e4a620aae..ea784846c 100644 --- a/samples/magiclink_cross_device_sample_app.py +++ b/samples/magiclink_cross_device_sample_app.py @@ -7,7 +7,7 @@ dir_name = os.path.dirname(__file__) sys.path.insert(0, os.path.join(dir_name, "../")) from descope import ( # noqa: E402 - REFRESH_SESSION_COOKIE_NAME, + REFRESH_SESSION_TOKEN_NAME, AuthException, DeliveryMethod, DescopeClient, @@ -61,9 +61,7 @@ def verify(): done = True if jwt_response: - refresh_token = ( - jwt_response["jwts"].get(REFRESH_SESSION_COOKIE_NAME).get("jwt") - ) + refresh_token = jwt_response.get(REFRESH_SESSION_TOKEN_NAME).get("jwt") descope_client.logout(refresh_token) logging.info("User logged out") diff --git a/samples/magiclink_sample_app.py b/samples/magiclink_sample_app.py index 15206cc11..467b99b74 100644 --- a/samples/magiclink_sample_app.py +++ b/samples/magiclink_sample_app.py @@ -5,8 +5,8 @@ dir_name = os.path.dirname(__file__) sys.path.insert(0, os.path.join(dir_name, "../")) from descope import ( # noqa: E402 - REFRESH_SESSION_COOKIE_NAME, - SESSION_COOKIE_NAME, + REFRESH_SESSION_TOKEN_NAME, + SESSION_TOKEN_NAME, AuthException, DeliveryMethod, DescopeClient, @@ -33,9 +33,7 @@ def main(): try: jwt_response = descope_client.magiclink.verify(token=token) logging.info("Code is valid") - refresh_token = ( - jwt_response["jwts"].get(REFRESH_SESSION_COOKIE_NAME).get("jwt") - ) + refresh_token = jwt_response.get(REFRESH_SESSION_TOKEN_NAME).get("jwt") logging.info(f"jwt_response: {jwt_response}") except AuthException as e: logging.info(f"Invalid code {e}") @@ -57,10 +55,8 @@ def main(): try: jwt_response = descope_client.magiclink.verify(token=token) logging.info("Code is valid") - session_token_1 = jwt_response["jwts"].get(SESSION_COOKIE_NAME).get("jwt") - refresh_token_1 = ( - jwt_response["jwts"].get(REFRESH_SESSION_COOKIE_NAME).get("jwt") - ) + session_token_1 = jwt_response.get(SESSION_TOKEN_NAME).get("jwt") + refresh_token_1 = jwt_response.get(REFRESH_SESSION_TOKEN_NAME).get("jwt") logging.info(f"jwt_response: {jwt_response}") except AuthException as e: logging.info(f"Invalid code {e}") diff --git a/samples/magiclink_web_sample_app.py b/samples/magiclink_web_sample_app.py index 42c2559be..c4d7a87de 100644 --- a/samples/magiclink_web_sample_app.py +++ b/samples/magiclink_web_sample_app.py @@ -6,6 +6,8 @@ dir_name = os.path.dirname(__file__) sys.path.insert(0, os.path.join(dir_name, "../")) from decorators.flask_decorators import ( # noqa: E402; + REFRESH_SESSION_TOKEN_NAME, + SESSION_TOKEN_NAME, descope_logout, descope_validate_auth, descope_verify_magiclink_token, @@ -100,9 +102,13 @@ def verify(): return Response("Unauthorized", 401) response = Response("Token verified", 200) - tokens = jwt_response["jwts"] - for _, data in tokens.items(): - set_cookie_on_response(response, data) + + set_cookie_on_response( + response, jwt_response[SESSION_TOKEN_NAME], jwt_response["cookieData"] + ) + set_cookie_on_response( + response, jwt_response[REFRESH_SESSION_TOKEN_NAME], jwt_response["cookieData"] + ) return response diff --git a/samples/oauth_sample_app.py b/samples/oauth_sample_app.py index d8cfdd075..4331e9c1b 100644 --- a/samples/oauth_sample_app.py +++ b/samples/oauth_sample_app.py @@ -5,7 +5,7 @@ dir_name = os.path.dirname(__file__) sys.path.insert(0, os.path.join(dir_name, "../")) from descope import ( # noqa: E402 - REFRESH_SESSION_COOKIE_NAME, + REFRESH_SESSION_TOKEN_NAME, AuthException, DescopeClient, ) @@ -26,7 +26,7 @@ def main(): jwt_response = descope_client.oauth.exchange_token(code) logging.info("oauth code valid") - refresh_token = jwt_response["jwts"].get(REFRESH_SESSION_COOKIE_NAME).get("jwt") + refresh_token = jwt_response.get(REFRESH_SESSION_TOKEN_NAME).get("jwt") descope_client.logout(refresh_token) logging.info("User logged out") diff --git a/samples/otp_sample_app.py b/samples/otp_sample_app.py index 32a955422..1ea21f360 100644 --- a/samples/otp_sample_app.py +++ b/samples/otp_sample_app.py @@ -5,8 +5,8 @@ dir_name = os.path.dirname(__file__) sys.path.insert(0, os.path.join(dir_name, "../")) from descope import ( # noqa: E402 - REFRESH_SESSION_COOKIE_NAME, - SESSION_COOKIE_NAME, + REFRESH_SESSION_TOKEN_NAME, + SESSION_TOKEN_NAME, AuthException, DeliveryMethod, DescopeClient, @@ -30,10 +30,8 @@ def main(): method=DeliveryMethod.EMAIL, identifier=email, code=value ) logging.info("Code is valid") - session_token = jwt_response["jwts"].get(SESSION_COOKIE_NAME).get("jwt") - refresh_token = ( - jwt_response["jwts"].get(REFRESH_SESSION_COOKIE_NAME).get("jwt") - ) + session_token = jwt_response[SESSION_TOKEN_NAME].get("jwt") + refresh_token = jwt_response[REFRESH_SESSION_TOKEN_NAME].get("jwt") logging.info(f"jwt_response: {jwt_response}") except AuthException as e: logging.info(f"Invalid code {e}") @@ -41,9 +39,7 @@ def main(): try: logging.info("going to validate session..") - claims = descope_client.validate_session_request( - session_token, refresh_token - ) + descope_client.validate_session_request(session_token, refresh_token) logging.info("Session is valid and all is OK") except AuthException as e: logging.info(f"Session is not valid {e}") @@ -55,10 +51,8 @@ def main(): "going to revalidate the session with the newly refreshed token.." ) - new_session_token = claims.get(SESSION_COOKIE_NAME).get("jwt") - claims = descope_client.validate_session_request( - new_session_token, refresh_token - ) + new_session_token = claims.get(SESSION_TOKEN_NAME).get("jwt") + descope_client.validate_session_request(new_session_token, refresh_token) logging.info("Session is valid also for the refreshed token.") except AuthException as e: logging.info(f"Session is not valid for the refreshed token: {e}") diff --git a/samples/otp_web_sample_app.py b/samples/otp_web_sample_app.py index 4cfd8853c..f19452b44 100644 --- a/samples/otp_web_sample_app.py +++ b/samples/otp_web_sample_app.py @@ -9,7 +9,9 @@ sys.path.insert(0, os.path.join(dir_name, "../")) from descope import ( # noqa: E402 REFRESH_SESSION_COOKIE_NAME, + REFRESH_SESSION_TOKEN_NAME, SESSION_COOKIE_NAME, + SESSION_TOKEN_NAME, AuthException, DeliveryMethod, DescopeClient, @@ -23,8 +25,8 @@ descope_client = DescopeClient(PROJECT_ID, skip_verify=True) -def set_cookie_on_response(response: Response, cookieAttributes, data): - cookie_domain = cookieAttributes.get("cookieDomain", "") +def set_cookie_on_response(response: Response, token: dict, cookieData: dict): + cookie_domain = cookieData.get("domain", "") if cookie_domain == "": cookie_domain = None @@ -32,11 +34,11 @@ def set_cookie_on_response(response: Response, cookieAttributes, data): expire_time = current_time + datetime.timedelta(days=30) return response.set_cookie( - key=data.get("drn", ""), - value=data.get("jwt", ""), - max_age=cookieAttributes.get("cookieMaxAge", int(expire_time.timestamp())), - expires=cookieAttributes.get("cookieExpiration", expire_time), - path=cookieAttributes.get("cookiePath", ""), + key=token.get("drn", ""), + value=token.get("jwt", ""), + max_age=cookieData.get("maxAge", int(expire_time.timestamp())), + expires=cookieData.get("exp", expire_time), + path=cookieData.get("path", "/"), domain=cookie_domain, secure=False, # True httponly=True, @@ -116,14 +118,18 @@ def verify(): jwt_response = descope_client.otp.verify_code(DeliveryMethod.EMAIL, email, code) except AuthException: return Response("Unauthorized", 401) - data = jwt_response["jwts"] + response = Response( f"This is Verify code API handling, info example: {json.dumps(jwt_response)}", 200, ) - set_cookie_on_response(response, data, data.get("DS")) - set_cookie_on_response(response, data, data.get("DSR")) + set_cookie_on_response( + response, jwt_response[SESSION_TOKEN_NAME], jwt_response["cookieData"] + ) + set_cookie_on_response( + response, jwt_response[REFRESH_SESSION_TOKEN_NAME], jwt_response["cookieData"] + ) return response @@ -135,7 +141,9 @@ def private(): session_token = cookies.get(SESSION_COOKIE_NAME, None) refresh_token = cookies.get(REFRESH_SESSION_COOKIE_NAME, None) try: - data = descope_client.validate_session_request(session_token, refresh_token) + jwt_response = descope_client.validate_session_request( + session_token, refresh_token + ) except AuthException: return Response("Access denied", 401) @@ -143,7 +151,10 @@ def private(): "This is a private API and you must be authenticated to see this", 200 ) - set_cookie_on_response(response, data, data.get("DS")) + if jwt_response.get("cookieData", None): + set_cookie_on_response( + response, jwt_response[SESSION_TOKEN_NAME], jwt_response["cookieData"] + ) return response diff --git a/samples/totp_sample_app.py b/samples/totp_sample_app.py index b46f3a526..5d360f382 100644 --- a/samples/totp_sample_app.py +++ b/samples/totp_sample_app.py @@ -6,8 +6,8 @@ dir_name = os.path.dirname(__file__) sys.path.insert(0, os.path.join(dir_name, "../")) from descope import ( # noqa: E402 - REFRESH_SESSION_COOKIE_NAME, - SESSION_COOKIE_NAME, + REFRESH_SESSION_TOKEN_NAME, + SESSION_TOKEN_NAME, AuthException, DeliveryMethod, DescopeClient, @@ -44,9 +44,7 @@ def main(): jwt_response = descope_client.otp.verify_code( DeliveryMethod.EMAIL, email, code ) - refresh_token = ( - jwt_response["jwts"].get(REFRESH_SESSION_COOKIE_NAME).get("jwt") - ) + refresh_token = jwt_response.get(REFRESH_SESSION_TOKEN_NAME).get("jwt") totp_info = descope_client.totp.update_user(email, refresh_token) logging.info("=== use this info in Authenticator app ===") logging.info(json.dumps(totp_info, indent=2, sort_keys=True)) @@ -61,16 +59,16 @@ def main(): logging.info("Code is valid") - session_token = jwt_response["jwts"].get(SESSION_COOKIE_NAME).get("jwt") - refresh_token = jwt_response["jwts"].get(REFRESH_SESSION_COOKIE_NAME).get("jwt") + session_token = jwt_response.get(SESSION_TOKEN_NAME).get("jwt") + refresh_token = jwt_response.get(REFRESH_SESSION_TOKEN_NAME).get("jwt") # validate session try: logging.info("going to validate session...") - claims = descope_client.validate_session_request( + jwt_response = descope_client.validate_session_request( session_token, refresh_token ) - logging.info(f"Session is valid and all is OK, claims: {claims}") + logging.info(f"Session is valid and all is OK, claims: {jwt_response}") except AuthException as e: logging.info(f"Session is not valid {e}") diff --git a/tests/test_descope_client.py b/tests/test_descope_client.py index aaa1d30fb..08e9486a5 100644 --- a/tests/test_descope_client.py +++ b/tests/test_descope_client.py @@ -5,6 +5,7 @@ from unittest.mock import patch from descope import SESSION_COOKIE_NAME, AuthException, DescopeClient +from descope.common import SESSION_TOKEN_NAME class TestDescopeClient(unittest.TestCase): @@ -256,7 +257,7 @@ def test_expired_token(self): mock_request.return_value.cookies = {} resp = client.validate_session_request(expired_token, valid_refresh_token) - new_session_token_from_request = resp[SESSION_COOKIE_NAME]["jwt"] + new_session_token_from_request = resp[SESSION_TOKEN_NAME]["jwt"] self.assertEqual( new_session_token_from_request, new_session_token, From 73fae6de22eb7a66ddc7a6b90fc1ca921b3fd5e5 Mon Sep 17 00:00:00 2001 From: Guy Pilosof Date: Sun, 7 Aug 2022 23:41:41 +0300 Subject: [PATCH 2/2] fix PR issue --- .pre-commit-config.yaml | 10 ++++----- descope/auth.py | 30 +++++++++++++------------- descope/authmethod/exchanger.py | 2 +- descope/authmethod/magiclink.py | 4 ++-- descope/authmethod/otp.py | 2 +- descope/authmethod/totp.py | 2 +- descope/authmethod/webauthn.py | 4 ++-- descope/common.py | 1 + samples/decorators/flask_decorators.py | 29 ++++++++++++++++--------- samples/magiclink_web_sample_app.py | 7 ++++-- samples/otp_web_sample_app.py | 11 ++++++---- 11 files changed, 59 insertions(+), 43 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ed1dd6e77..d41bd8610 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ exclude: 'docs/' repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.0.1 + rev: v4.3.0 hooks: - id: check-yaml - id: debug-statements @@ -14,21 +14,21 @@ repos: - id: isort args: ["--profile", "black"] - repo: https://github.com/psf/black - rev: 22.3.0 + rev: 22.6.0 hooks: - id: black language_version: python3 - repo: https://github.com/asottile/pyupgrade - rev: v2.31.1 + rev: v2.37.3 hooks: - id: pyupgrade args: [--py37-plus] - repo: https://gitlab.com/pycqa/flake8 - rev: 4.0.1 + rev: 3.9.2 hooks: - id: flake8 - repo: https://github.com/dhatim/python-license-check - rev: master + rev: 0.7.2 hooks: - id: liccheck language: system diff --git a/descope/auth.py b/descope/auth.py index 01c35a330..fad4b6788 100644 --- a/descope/auth.py +++ b/descope/auth.py @@ -11,6 +11,7 @@ from jwt.exceptions import ExpiredSignatureError from descope.common import ( + COOKIE_DATA_NAME, DEFAULT_BASE_URL, PHONE_REGEX, REFRESH_SESSION_TOKEN_NAME, @@ -265,25 +266,25 @@ def _fetch_public_keys(self) -> None: # just continue to the next key pass - def _generate_auth_info(self, response_body: dict, cookie: str) -> dict: + def _generate_auth_info(self, response_body: dict, refresh_cookie: str) -> dict: jwt_response = {} - stJwt = response_body.get("sessionJwt", "") - if stJwt: + st_jwt = response_body.get("sessionJwt", "") + if st_jwt: jwt_response[SESSION_TOKEN_NAME] = self._validate_and_load_tokens( - stJwt, None + st_jwt, None ) - rtJwt = response_body.get("refreshJwt", "") - if rtJwt: + rt_jwt = response_body.get("refreshJwt", "") + if rt_jwt: jwt_response[REFRESH_SESSION_TOKEN_NAME] = self._validate_and_load_tokens( - rtJwt, None + rt_jwt, None ) - if cookie: + if refresh_cookie: jwt_response[REFRESH_SESSION_TOKEN_NAME] = self._validate_and_load_tokens( - cookie, None + refresh_cookie, None ) - jwt_response["cookieData"] = { + jwt_response[COOKIE_DATA_NAME] = { "exp": response_body.get("cookieExpiration", 0), "maxAge": response_body.get("cookieMaxAge", 0), "domain": response_body.get("cookieDomain", ""), @@ -292,22 +293,21 @@ def _generate_auth_info(self, response_body: dict, cookie: str) -> dict: return jwt_response - def _generate_jwt_response(self, response_body: dict, cookie: str) -> dict: - jwt_response = self._generate_auth_info(response_body, cookie) + def generate_jwt_response(self, response_body: dict, refresh_cookie: str) -> dict: + jwt_response = self._generate_auth_info(response_body, refresh_cookie) projectId = jwt_response.get(SESSION_TOKEN_NAME, {}).get( "iss", None ) or jwt_response.get(REFRESH_SESSION_TOKEN_NAME, {}).get("iss", None) - userId = jwt_response.get(SESSION_TOKEN_NAME, {}).get( + user_id = jwt_response.get(SESSION_TOKEN_NAME, {}).get( "sub", None ) or jwt_response.get(REFRESH_SESSION_TOKEN_NAME, {}).get("sub", None) jwt_response["tenants"] = response_body.get("tenants", {}) jwt_response["projectId"] = projectId - jwt_response["userId"] = userId + jwt_response["userId"] = user_id jwt_response["user"] = response_body.get("user", {}) jwt_response["firstSeen"] = response_body.get("firstSeen", True) - jwt_response["error"] = response_body.get("error", "") return jwt_response def _get_default_headers(self, pswd: str = None): diff --git a/descope/authmethod/exchanger.py b/descope/authmethod/exchanger.py index 0b06226cc..7b896a4b3 100644 --- a/descope/authmethod/exchanger.py +++ b/descope/authmethod/exchanger.py @@ -22,7 +22,7 @@ def exchange_token(self, code: str) -> dict: params = Exchanger._compose_exchange_params(code) response = self._auth.do_get(uri, params, False) resp = response.json() - jwt_response = self._auth._generate_jwt_response( + jwt_response = self._auth.generate_jwt_response( resp, response.cookies.get(REFRESH_SESSION_COOKIE_NAME, None) ) return jwt_response diff --git a/descope/authmethod/magiclink.py b/descope/authmethod/magiclink.py index 2b67f30ff..e10af92c0 100644 --- a/descope/authmethod/magiclink.py +++ b/descope/authmethod/magiclink.py @@ -48,7 +48,7 @@ def get_session(self, pending_ref: str) -> dict: response = self._auth.do_post(uri, body) resp = response.json() - jwt_response = self._auth._generate_jwt_response( + jwt_response = self._auth.generate_jwt_response( resp, response.cookies.get(REFRESH_SESSION_COOKIE_NAME, None) ) return jwt_response @@ -58,7 +58,7 @@ def verify(self, token: str) -> dict: body = MagicLink._compose_verify_body(token) response = self._auth.do_post(uri, body) resp = response.json() - jwt_response = self._auth._generate_jwt_response( + jwt_response = self._auth.generate_jwt_response( resp, response.cookies.get(REFRESH_SESSION_COOKIE_NAME, None) ) return jwt_response diff --git a/descope/authmethod/otp.py b/descope/authmethod/otp.py index b42901d8c..be2777c2c 100644 --- a/descope/authmethod/otp.py +++ b/descope/authmethod/otp.py @@ -124,7 +124,7 @@ def verify_code(self, method: DeliveryMethod, identifier: str, code: str) -> dic response = self._auth.do_post(uri, body) resp = response.json() - jwt_response = self._auth._generate_jwt_response( + jwt_response = self._auth.generate_jwt_response( resp, response.cookies.get(REFRESH_SESSION_COOKIE_NAME, None) ) return jwt_response diff --git a/descope/authmethod/totp.py b/descope/authmethod/totp.py index e917e50bd..340671199 100644 --- a/descope/authmethod/totp.py +++ b/descope/authmethod/totp.py @@ -50,7 +50,7 @@ def sign_in_code(self, identifier: str, code: str) -> dict: response = self._auth.do_post(uri, body) resp = response.json() - jwt_response = self._auth._generate_jwt_response( + jwt_response = self._auth.generate_jwt_response( resp, response.cookies.get(REFRESH_SESSION_COOKIE_NAME, None) ) return jwt_response diff --git a/descope/authmethod/webauthn.py b/descope/authmethod/webauthn.py index e5b137ebc..ffc744f16 100644 --- a/descope/authmethod/webauthn.py +++ b/descope/authmethod/webauthn.py @@ -48,7 +48,7 @@ def sign_up_finish(self, transactionID: str, response: str) -> dict: response = self._auth.do_post(uri, body) resp = response.json() - jwt_response = self._auth._generate_jwt_response( + jwt_response = self._auth.generate_jwt_response( resp, response.cookies.get(REFRESH_SESSION_COOKIE_NAME, None) ) return jwt_response @@ -92,7 +92,7 @@ def sign_in_finish(self, transaction_id: str, response: str) -> dict: response = self._auth.do_post(uri, body) resp = response.json() - jwt_response = self._auth._generate_jwt_response( + jwt_response = self._auth.generate_jwt_response( resp, response.cookies.get(REFRESH_SESSION_COOKIE_NAME, None) ) return jwt_response diff --git a/descope/common.py b/descope/common.py index 7aa3d6712..2997cd789 100644 --- a/descope/common.py +++ b/descope/common.py @@ -10,6 +10,7 @@ SESSION_TOKEN_NAME = "sessionToken" REFRESH_SESSION_TOKEN_NAME = "refreshSessionToken" +COOKIE_DATA_NAME = "cookieData" REDIRECT_LOCATION_COOKIE_NAME = "Location" diff --git a/samples/decorators/flask_decorators.py b/samples/decorators/flask_decorators.py index 7fff6e4b4..31b571512 100644 --- a/samples/decorators/flask_decorators.py +++ b/samples/decorators/flask_decorators.py @@ -11,6 +11,7 @@ sys.path.insert(0, os.path.join(dir_name, "../")) from descope import AuthException # noqa: E402 from descope import ( # noqa: E402 + COOKIE_DATA_NAME, REFRESH_SESSION_COOKIE_NAME, REFRESH_SESSION_TOKEN_NAME, SESSION_COOKIE_NAME, @@ -114,11 +115,11 @@ def decorated(*args, **kwargs): _request_ctx_stack.top.claims = jwt_response response = f(*args, **kwargs) - if jwt_response.get("cookieData", None): + if jwt_response.get(COOKIE_DATA_NAME, None): set_cookie_on_response( response, jwt_response[SESSION_TOKEN_NAME], - jwt_response["cookieData"], + jwt_response[COOKIE_DATA_NAME], ) return response @@ -153,12 +154,14 @@ def decorated(*args, **kwargs): response = f(*args, **kwargs) set_cookie_on_response( - response, jwt_response[SESSION_TOKEN_NAME], jwt_response["cookieData"] + response, + jwt_response[SESSION_TOKEN_NAME], + jwt_response[COOKIE_DATA_NAME], ) set_cookie_on_response( response, jwt_response[REFRESH_SESSION_TOKEN_NAME], - jwt_response["cookieData"], + jwt_response[COOKIE_DATA_NAME], ) return response @@ -194,12 +197,14 @@ def decorated(*args, **kwargs): response = f(*args, **kwargs) set_cookie_on_response( - response, jwt_response[SESSION_TOKEN_NAME], jwt_response["cookieData"] + response, + jwt_response[SESSION_TOKEN_NAME], + jwt_response[COOKIE_DATA_NAME], ) set_cookie_on_response( response, jwt_response[REFRESH_SESSION_TOKEN_NAME], - jwt_response["cookieData"], + jwt_response[COOKIE_DATA_NAME], ) return response @@ -235,12 +240,14 @@ def decorated(*args, **kwargs): response = f(*args, **kwargs) set_cookie_on_response( - response, jwt_response[SESSION_TOKEN_NAME], jwt_response["cookieData"] + response, + jwt_response[SESSION_TOKEN_NAME], + jwt_response[COOKIE_DATA_NAME], ) set_cookie_on_response( response, jwt_response[REFRESH_SESSION_TOKEN_NAME], - jwt_response["cookieData"], + jwt_response[COOKIE_DATA_NAME], ) return response @@ -323,12 +330,14 @@ def decorated(*args, **kwargs): response = f(*args, **kwargs) set_cookie_on_response( - response, jwt_response[SESSION_TOKEN_NAME], jwt_response["cookieData"] + response, + jwt_response[SESSION_TOKEN_NAME], + jwt_response[COOKIE_DATA_NAME], ) set_cookie_on_response( response, jwt_response[REFRESH_SESSION_TOKEN_NAME], - jwt_response["cookieData"], + jwt_response[COOKIE_DATA_NAME], ) return response diff --git a/samples/magiclink_web_sample_app.py b/samples/magiclink_web_sample_app.py index c4d7a87de..168fd2b0b 100644 --- a/samples/magiclink_web_sample_app.py +++ b/samples/magiclink_web_sample_app.py @@ -6,6 +6,7 @@ dir_name = os.path.dirname(__file__) sys.path.insert(0, os.path.join(dir_name, "../")) from decorators.flask_decorators import ( # noqa: E402; + COOKIE_DATA_NAME, REFRESH_SESSION_TOKEN_NAME, SESSION_TOKEN_NAME, descope_logout, @@ -104,10 +105,12 @@ def verify(): response = Response("Token verified", 200) set_cookie_on_response( - response, jwt_response[SESSION_TOKEN_NAME], jwt_response["cookieData"] + response, jwt_response[SESSION_TOKEN_NAME], jwt_response[COOKIE_DATA_NAME] ) set_cookie_on_response( - response, jwt_response[REFRESH_SESSION_TOKEN_NAME], jwt_response["cookieData"] + response, + jwt_response[REFRESH_SESSION_TOKEN_NAME], + jwt_response[COOKIE_DATA_NAME], ) return response diff --git a/samples/otp_web_sample_app.py b/samples/otp_web_sample_app.py index f19452b44..80e1db3fe 100644 --- a/samples/otp_web_sample_app.py +++ b/samples/otp_web_sample_app.py @@ -8,6 +8,7 @@ dir_name = os.path.dirname(__file__) sys.path.insert(0, os.path.join(dir_name, "../")) from descope import ( # noqa: E402 + COOKIE_DATA_NAME, REFRESH_SESSION_COOKIE_NAME, REFRESH_SESSION_TOKEN_NAME, SESSION_COOKIE_NAME, @@ -125,10 +126,12 @@ def verify(): ) set_cookie_on_response( - response, jwt_response[SESSION_TOKEN_NAME], jwt_response["cookieData"] + response, jwt_response[SESSION_TOKEN_NAME], jwt_response[COOKIE_DATA_NAME] ) set_cookie_on_response( - response, jwt_response[REFRESH_SESSION_TOKEN_NAME], jwt_response["cookieData"] + response, + jwt_response[REFRESH_SESSION_TOKEN_NAME], + jwt_response[COOKIE_DATA_NAME], ) return response @@ -151,9 +154,9 @@ def private(): "This is a private API and you must be authenticated to see this", 200 ) - if jwt_response.get("cookieData", None): + if jwt_response.get(COOKIE_DATA_NAME, None): set_cookie_on_response( - response, jwt_response[SESSION_TOKEN_NAME], jwt_response["cookieData"] + response, jwt_response[SESSION_TOKEN_NAME], jwt_response[COOKIE_DATA_NAME] ) return response