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/__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..fad4b6788 100644 --- a/descope/auth.py +++ b/descope/auth.py @@ -11,9 +11,11 @@ from jwt.exceptions import ExpiredSignatureError from descope.common import ( + COOKIE_DATA_NAME, DEFAULT_BASE_URL, PHONE_REGEX, - SESSION_COOKIE_NAME, + REFRESH_SESSION_TOKEN_NAME, + SESSION_TOKEN_NAME, DeliveryMethod, EndpointsV1, ) @@ -264,54 +266,48 @@ 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 - - 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), + def _generate_auth_info(self, response_body: dict, refresh_cookie: str) -> dict: + jwt_response = {} + st_jwt = response_body.get("sessionJwt", "") + if st_jwt: + jwt_response[SESSION_TOKEN_NAME] = self._validate_and_load_tokens( + st_jwt, None + ) + rt_jwt = response_body.get("refreshJwt", "") + if rt_jwt: + jwt_response[REFRESH_SESSION_TOKEN_NAME] = self._validate_and_load_tokens( + rt_jwt, None + ) + + if refresh_cookie: + jwt_response[REFRESH_SESSION_TOKEN_NAME] = self._validate_and_load_tokens( + refresh_cookie, None + ) + + jwt_response[COOKIE_DATA_NAME] = { + "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, 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) + 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"] = user_id + jwt_response["user"] = response_body.get("user", {}) + jwt_response["firstSeen"] = response_body.get("firstSeen", True) 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/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 4980d69ca..2997cd789 100644 --- a/descope/common.py +++ b/descope/common.py @@ -8,6 +8,10 @@ SESSION_COOKIE_NAME = "DS" REFRESH_SESSION_COOKIE_NAME = "DSR" +SESSION_TOKEN_NAME = "sessionToken" +REFRESH_SESSION_TOKEN_NAME = "refreshSessionToken" +COOKIE_DATA_NAME = "cookieData" + 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..31b571512 100644 --- a/samples/decorators/flask_decorators.py +++ b/samples/decorators/flask_decorators.py @@ -11,14 +11,17 @@ 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, + 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 +29,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 +104,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 +112,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(COOKIE_DATA_NAME, None): + set_cookie_on_response( + response, + jwt_response[SESSION_TOKEN_NAME], + jwt_response[COOKIE_DATA_NAME], + ) return response return decorated @@ -147,9 +153,16 @@ 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[COOKIE_DATA_NAME], + ) + set_cookie_on_response( + response, + jwt_response[REFRESH_SESSION_TOKEN_NAME], + jwt_response[COOKIE_DATA_NAME], + ) return response @@ -183,9 +196,16 @@ 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[COOKIE_DATA_NAME], + ) + set_cookie_on_response( + response, + jwt_response[REFRESH_SESSION_TOKEN_NAME], + jwt_response[COOKIE_DATA_NAME], + ) return response @@ -219,9 +239,16 @@ 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[COOKIE_DATA_NAME], + ) + set_cookie_on_response( + response, + jwt_response[REFRESH_SESSION_TOKEN_NAME], + jwt_response[COOKIE_DATA_NAME], + ) return response @@ -302,9 +329,16 @@ 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[COOKIE_DATA_NAME], + ) + set_cookie_on_response( + response, + jwt_response[REFRESH_SESSION_TOKEN_NAME], + jwt_response[COOKIE_DATA_NAME], + ) 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..168fd2b0b 100644 --- a/samples/magiclink_web_sample_app.py +++ b/samples/magiclink_web_sample_app.py @@ -6,6 +6,9 @@ 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, descope_validate_auth, descope_verify_magiclink_token, @@ -100,9 +103,15 @@ 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[COOKIE_DATA_NAME] + ) + set_cookie_on_response( + response, + jwt_response[REFRESH_SESSION_TOKEN_NAME], + jwt_response[COOKIE_DATA_NAME], + ) 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..80e1db3fe 100644 --- a/samples/otp_web_sample_app.py +++ b/samples/otp_web_sample_app.py @@ -8,8 +8,11 @@ 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, + SESSION_TOKEN_NAME, AuthException, DeliveryMethod, DescopeClient, @@ -23,8 +26,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 +35,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 +119,20 @@ 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[COOKIE_DATA_NAME] + ) + set_cookie_on_response( + response, + jwt_response[REFRESH_SESSION_TOKEN_NAME], + jwt_response[COOKIE_DATA_NAME], + ) return response @@ -135,7 +144,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 +154,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(COOKIE_DATA_NAME, None): + set_cookie_on_response( + response, jwt_response[SESSION_TOKEN_NAME], jwt_response[COOKIE_DATA_NAME] + ) 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,