From 608494b8d672daf090ab98b49026299ca63b2730 Mon Sep 17 00:00:00 2001 From: aviadl Date: Thu, 1 Sep 2022 12:35:01 +0300 Subject: [PATCH 1/3] Change all endpoints to the correct one Change name of webauthn update functions fix all tests related to https://github.com/descope/etc/issues/476 --- descope/auth.py | 21 ++++++++++ descope/authmethod/exchanger.py | 31 --------------- descope/authmethod/oauth.py | 11 ++++-- descope/authmethod/saml.py | 10 ++++- descope/authmethod/webauthn.py | 16 ++++---- descope/common.py | 49 ++++++++++++----------- samples/webauthn_web_sample_app.py | 8 ++-- tests/test_exchanger.py | 63 ------------------------------ tests/test_magiclink.py | 8 ++-- tests/test_oauth.py | 38 ++++++++++++++++++ tests/test_otp.py | 24 ++++++------ tests/test_saml.py | 38 ++++++++++++++++++ tests/test_webauthn.py | 44 ++++++++++----------- 13 files changed, 186 insertions(+), 175 deletions(-) delete mode 100644 descope/authmethod/exchanger.py delete mode 100644 tests/test_exchanger.py diff --git a/descope/auth.py b/descope/auth.py index 3639735ec..806f0ce96 100644 --- a/descope/auth.py +++ b/descope/auth.py @@ -13,6 +13,7 @@ COOKIE_DATA_NAME, DEFAULT_BASE_URL, PHONE_REGEX, + REFRESH_SESSION_COOKIE_NAME, REFRESH_SESSION_TOKEN_NAME, SESSION_TOKEN_NAME, DeliveryMethod, @@ -98,6 +99,26 @@ def do_post(self, uri: str, body: dict, pswd: str = None) -> requests.Response: ) return response + def exchange_token(self, uri, code: str) -> dict: + if not code: + raise AuthException( + 400, + ERROR_TYPE_INVALID_ARGUMENT, + "exchange code is empty", + ) + + params = Auth._compose_exchange_params(code) + response = self.do_get(uri, params, False) + resp = response.json() + jwt_response = self.generate_jwt_response( + resp, response.cookies.get(REFRESH_SESSION_COOKIE_NAME, None) + ) + return jwt_response + + @staticmethod + def _compose_exchange_params(code: str) -> dict: + return {"code": code} + @staticmethod def verify_delivery_method( method: DeliveryMethod, identifier: str, user: dict diff --git a/descope/authmethod/exchanger.py b/descope/authmethod/exchanger.py deleted file mode 100644 index f4924339b..000000000 --- a/descope/authmethod/exchanger.py +++ /dev/null @@ -1,31 +0,0 @@ -from descope.auth import Auth -from descope.common import REFRESH_SESSION_COOKIE_NAME, EndpointsV1 -from descope.exceptions import ERROR_TYPE_INVALID_ARGUMENT, AuthException - - -class Exchanger: - _auth: Auth = None - - def __init__(self, auth: Auth): - self._auth = auth - - def exchange_token(self, code: str) -> dict: - if not code: - raise AuthException( - 400, - ERROR_TYPE_INVALID_ARGUMENT, - "exchange code is empty", - ) - - uri = EndpointsV1.exchangeTokenPath - params = Exchanger._compose_exchange_params(code) - response = self._auth.do_get(uri, params, False) - resp = response.json() - jwt_response = self._auth.generate_jwt_response( - resp, response.cookies.get(REFRESH_SESSION_COOKIE_NAME, None) - ) - return jwt_response - - @staticmethod - def _compose_exchange_params(code: str) -> dict: - return {"code": code} diff --git a/descope/authmethod/oauth.py b/descope/authmethod/oauth.py index 855c5cc56..fe5c81da3 100644 --- a/descope/authmethod/oauth.py +++ b/descope/authmethod/oauth.py @@ -1,12 +1,13 @@ from descope.auth import Auth -from descope.authmethod.exchanger import Exchanger # noqa: F401 from descope.common import EndpointsV1, OAuthProviders from descope.exceptions import ERROR_TYPE_INVALID_ARGUMENT, AuthException -class OAuth(Exchanger): +class OAuth: + _auth: Auth + def __init__(self, auth: Auth): - super().__init__(auth) + self._auth = auth def start(self, provider: str, return_url: str = "") -> dict: """ """ @@ -23,6 +24,10 @@ def start(self, provider: str, return_url: str = "") -> dict: return response.json() + def exchange_token(self, code: str) -> dict: + uri = EndpointsV1.oauthExchangeTokenPath + return self._auth.exchange_token(uri, code) + @staticmethod def _verify_provider(oauth_provider: str) -> str: if oauth_provider == "" or oauth_provider is None: diff --git a/descope/authmethod/saml.py b/descope/authmethod/saml.py index 6c116b237..6f368f5f3 100644 --- a/descope/authmethod/saml.py +++ b/descope/authmethod/saml.py @@ -4,9 +4,11 @@ from descope.exceptions import ERROR_TYPE_INVALID_ARGUMENT, AuthException -class SAML(Exchanger): +class SAML: + _auth: Auth + def __init__(self, auth: Auth): - super().__init__(auth) + self._auth = auth def start(self, tenant: str, return_url: str = None) -> dict: """ @@ -28,6 +30,10 @@ def start(self, tenant: str, return_url: str = None) -> dict: return response.json() + def exchange_token(self, code: str) -> dict: + uri = EndpointsV1.samlExchangeTokenPath + return self._auth.exchange_token(uri, code) + @staticmethod def _compose_start_params(tenant: str, return_url: str) -> dict: res = {"tenant": tenant} diff --git a/descope/authmethod/webauthn.py b/descope/authmethod/webauthn.py index f6f03c1ad..8c6274d20 100644 --- a/descope/authmethod/webauthn.py +++ b/descope/authmethod/webauthn.py @@ -97,7 +97,7 @@ def sign_in_finish(self, transaction_id: str, response: str) -> dict: ) return jwt_response - def add_device_start(self, identifier: str, refresh_token: str, origin: str): + def update_start(self, identifier: str, refresh_token: str, origin: str): """ Docs """ @@ -111,13 +111,13 @@ def add_device_start(self, identifier: str, refresh_token: str, origin: str): 400, ERROR_TYPE_INVALID_ARGUMENT, "Refresh token cannot be empty" ) - uri = EndpointsV1.deviceAddAuthWebauthnStart - body = WebauthN._compose_add_device_start_body(identifier, origin) + uri = EndpointsV1.updateAuthWebauthnStart + body = WebauthN._compose_update_start_body(identifier, origin) response = self._auth.do_post(uri, body, refresh_token) return response.json() - def add_device_finish(self, transaction_id: str, response: str) -> None: + def update_finish(self, transaction_id: str, response: str) -> None: """ Docs """ @@ -131,8 +131,8 @@ def add_device_finish(self, transaction_id: str, response: str) -> None: 400, ERROR_TYPE_INVALID_ARGUMENT, "Response cannot be empty" ) - uri = EndpointsV1.deviceAddAuthWebauthnFinish - body = WebauthN._compose_sign_up_in_finish_body(transaction_id, response) + uri = EndpointsV1.updateAuthWebauthnFinish + body = WebauthN._compose_update_finish_body(transaction_id, response) self._auth.do_post(uri, body) @staticmethod @@ -155,12 +155,12 @@ def _compose_sign_up_in_finish_body(transaction_id: str, response: str) -> dict: return {"transactionId": transaction_id, "response": response} @staticmethod - def _compose_add_device_start_body(identifier: str, origin: str) -> dict: + def _compose_update_start_body(identifier: str, origin: str) -> dict: body = {"externalId": identifier} if origin: body["origin"] = origin return body @staticmethod - def _compose_add_device_finish_body(transaction_id: str, response: str) -> dict: + def _compose_update_finish_body(transaction_id: str, response: str) -> dict: return {"transactionId": transaction_id, "response": response} diff --git a/descope/common.py b/descope/common.py index dbbdcbb5d..7d52ee89d 100644 --- a/descope/common.py +++ b/descope/common.py @@ -20,43 +20,42 @@ class EndpointsV1: logoutPath = "/v1/auth/logoutall" # otp - signUpAuthOTPPath = "/v1/auth/signup/otp" - signInAuthOTPPath = "/v1/auth/signin/otp" - signUpOrInAuthOTPPath = "/v1/auth/sign-up-or-in/otp" - verifyCodeAuthPath = "/v1/auth/code/verify" - updateUserEmailOTPPath = "/v1/user/update/email/otp" - updateUserPhoneOTPPath = "/v1/user/update/phone/otp" + signUpAuthOTPPath = "/v1/auth/otp/signup" + signInAuthOTPPath = "/v1/auth/otp/signin" + signUpOrInAuthOTPPath = "/v1/auth/otp/signup-in" + verifyCodeAuthPath = "/v1/auth/otp/verify" + updateUserEmailOTPPath = "/v1/auth/otp/update/email" + updateUserPhoneOTPPath = "/v1/auth/otp/update/phone" # magiclink - signUpAuthMagicLinkPath = "/v1/auth/signup/magiclink" - signInAuthMagicLinkPath = "/v1/auth/signin/magiclink" - signUpOrInAuthMagicLinkPath = "/v1/auth/sign-up-or-in/magiclink" + signUpAuthMagicLinkPath = "/v1/auth/magiclink/signup" + signInAuthMagicLinkPath = "/v1/auth/magiclink/signin" + signUpOrInAuthMagicLinkPath = "/v1/auth/magiclink/signup-in" verifyMagicLinkAuthPath = "/v1/auth/magiclink/verify" - getSessionMagicLinkAuthPath = "/v1/auth/magiclink/session" - updateUserEmailMagicLinkPath = "/v1/user/update/email/magiclink" - updateUserPhoneMagicLinkPath = "/v1/user/update/phone/magiclink" + getSessionMagicLinkAuthPath = "/v1/auth/magiclink/pending-session" + updateUserEmailMagicLinkPath = "/v1/auth/magiclink/update/email" + updateUserPhoneMagicLinkPath = "/v1/auth/magiclink/update/phone" # oauth - oauthStart = "/v1/oauth/authorize" + oauthStart = "/v1/auth/oauth/authorize" + oauthExchangeTokenPath = "/v1/auth/oauth/exchange" # saml authSAMLStart = "/v1/auth/saml/authorize" - - # exchange (saml + oauth) - exchangeTokenPath = "/v1/auth/exchange" + samlExchangeTokenPath = "/v1/auth/saml/exchange" # totp - signUpAuthTOTPPath = "/v1/auth/signup/totp" - verifyTOTPPath = "/v1/auth/verify/totp" - updateTOTPPath = "/v1/user/update/totp" + signUpAuthTOTPPath = "/v1/auth/totp/signup" + verifyTOTPPath = "/v1/auth/totp/verify" + updateTOTPPath = "/v1/auth/totp/update" # webauthn - signUpAuthWebauthnStart = "/v1/webauthn/signup/start" - signUpAuthWebauthnFinish = "/v1/webauthn/signup/finish" - signInAuthWebauthnStart = "/v1/webauthn/signin/start" - signInAuthWebauthnFinish = "/v1/webauthn/signin/finish" - deviceAddAuthWebauthnStart = "/v1/webauthn/device/add/start" - deviceAddAuthWebauthnFinish = "/v1/webauthn/device/add/finish" + signUpAuthWebauthnStart = "/v1/auth/webauthn/signup/start" + signUpAuthWebauthnFinish = "/v1/auth/webauthn/signup/finish" + signInAuthWebauthnStart = "/v1/auth/webauthn/signin/start" + signInAuthWebauthnFinish = "/v1/auth/webauthn/signin/finish" + updateAuthWebauthnStart = "/v1/auth/webauthn/update/start" + updateAuthWebauthnFinish = "/v1/auth/webauthn/update/finish" class DeliveryMethod(Enum): diff --git a/samples/webauthn_web_sample_app.py b/samples/webauthn_web_sample_app.py index 55820ba9b..e0b105792 100644 --- a/samples/webauthn_web_sample_app.py +++ b/samples/webauthn_web_sample_app.py @@ -74,19 +74,19 @@ def webauthn_signin_finish(): @APP.route("/webauthn/device/add/start", methods=["POST"]) -def webauthn_add_device_start(): +def webauthn_update_start(): data = request.get_json() refresh_token = request.cookies.get("DSR") - response = descope_client.webauthn.add_device_start( + response = descope_client.webauthn.update_start( data["externalId"], refresh_token, data["origin"] ) return response @APP.route("/webauthn/device/add/finish", methods=["POST"]) -def webauthn_add_device_finish(): +def webauthn_update_finish(): data = request.get_json() - descope_client.webauthn.add_device_finish(data["transactionId"], data["response"]) + descope_client.webauthn.update_finish(data["transactionId"], data["response"]) return jsonify("{}") diff --git a/tests/test_exchanger.py b/tests/test_exchanger.py deleted file mode 100644 index 58b3ae9df..000000000 --- a/tests/test_exchanger.py +++ /dev/null @@ -1,63 +0,0 @@ -import json -import unittest -from unittest import mock -from unittest.mock import patch - -from descope import AuthException -from descope.auth import Auth -from descope.authmethod.exchanger import Exchanger -from descope.common import DEFAULT_BASE_URL, EndpointsV1 - - -class TestExchanger(unittest.TestCase): - def setUp(self) -> None: - self.dummy_project_id = "dummy" - self.public_key_dict = { - "alg": "ES384", - "crv": "P-384", - "kid": "2Bt5WLccLUey1Dp7utptZb3Fx9K", - "kty": "EC", - "use": "sig", - "x": "8SMbQQpCQAGAxCdoIz8y9gDw-wXoyoN5ILWpAlBKOcEM1Y7WmRKc1O2cnHggyEVi", - "y": "N5n5jKZA5Wu7_b4B36KKjJf-VRfJ-XqczfCSYy9GeQLqF-b63idfE0SYaYk9cFqg", - } - - def test_compose_exchange_params(self): - self.assertEqual(Exchanger._compose_exchange_params("c1"), {"code": "c1"}) - - def test_exchange_token(self): - exchanger = Exchanger(Auth(self.dummy_project_id, self.public_key_dict)) - - # Test failed flows - self.assertRaises(AuthException, exchanger.exchange_token, "") - self.assertRaises(AuthException, exchanger.exchange_token, None) - - with patch("requests.get") as mock_get: - mock_get.return_value.ok = False - self.assertRaises(AuthException, exchanger.exchange_token, "c1") - - # Test success flow - with patch("requests.get") as mock_get: - my_mock_response = mock.Mock() - my_mock_response.ok = True - my_mock_response.cookies = {} - data = json.loads( - """{"jwts": ["eyJhbGciOiJFUzM4NCIsImtpZCI6IjJCdDVXTGNjTFVleTFEcDd1dHB0WmIzRng5SyIsInR5cCI6IkpXVCJ9.eyJjb29raWVEb21haW4iOiIiLCJjb29raWVFeHBpcmF0aW9uIjoxNjYwMzg4MDc4LCJjb29raWVNYXhBZ2UiOjI1OTE5OTksImNvb2tpZU5hbWUiOiJEU1IiLCJjb29raWVQYXRoIjoiLyIsImV4cCI6MTY2MDIxNTI3OCwiaWF0IjoxNjU3Nzk2MDc4LCJpc3MiOiIyQnQ1V0xjY0xVZXkxRHA3dXRwdFpiM0Z4OUsiLCJzdWIiOiIyQnRFSGtnT3UwMmxtTXh6UElleGRNdFV3MU0ifQ.oAnvJ7MJvCyL_33oM7YCF12JlQ0m6HWRuteUVAdaswfnD4rHEBmPeuVHGljN6UvOP4_Cf0559o39UHVgm3Fwb-q7zlBbsu_nP1-PRl-F8NJjvBgC5RsAYabtJq7LlQmh"], "user": {"externalIds": ["guyp@descope.com"], "name": "", "email": "guyp@descope.com", "phone": "", "verifiedEmail": true, "verifiedPhone": false}, "firstSeen": false}""" - ) - my_mock_response.json.return_value = data - mock_get.return_value = my_mock_response - exchanger.exchange_token("c1") - mock_get.assert_called_with( - f"{DEFAULT_BASE_URL}{EndpointsV1.exchangeTokenPath}", - headers={ - "Content-Type": "application/json", - "Authorization": f"Bearer {self.dummy_project_id}", - }, - params={"code": "c1"}, - allow_redirects=False, - verify=True, - ) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_magiclink.py b/tests/test_magiclink.py index de6be97e1..3cea55d8d 100644 --- a/tests/test_magiclink.py +++ b/tests/test_magiclink.py @@ -25,20 +25,20 @@ def setUp(self) -> None: def test_compose_urls(self): self.assertEqual( MagicLink._compose_signin_url(DeliveryMethod.PHONE), - "/v1/auth/signin/magiclink/sms", + "/v1/auth/magiclink/signin/sms", ) self.assertEqual( MagicLink._compose_signup_url(DeliveryMethod.WHATSAPP), - "/v1/auth/signup/magiclink/whatsapp", + "/v1/auth/magiclink/signup/whatsapp", ) self.assertEqual( MagicLink._compose_sign_up_or_in_url(DeliveryMethod.EMAIL), - "/v1/auth/sign-up-or-in/magiclink/email", + "/v1/auth/magiclink/signup-in/email", ) self.assertEqual( MagicLink._compose_update_phone_url(DeliveryMethod.PHONE), - "/v1/user/update/phone/magiclink/sms", + "/v1/auth/magiclink/update/phone/sms", ) def test_compose_body(self): diff --git a/tests/test_oauth.py b/tests/test_oauth.py index 251cc2fdd..878f8408c 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -1,4 +1,6 @@ +import json import unittest +from unittest import mock from unittest.mock import patch from descope import AuthException @@ -77,6 +79,42 @@ def test_oauth_start(self): verify=True, ) + def test_compose_exchange_params(self): + self.assertEqual(Auth._compose_exchange_params("c1"), {"code": "c1"}) + + def test_exchange_token(self): + oauth = OAuth(Auth(self.dummy_project_id, self.public_key_dict)) + + # Test failed flows + self.assertRaises(AuthException, oauth.exchange_token, "") + self.assertRaises(AuthException, oauth.exchange_token, None) + + with patch("requests.get") as mock_get: + mock_get.return_value.ok = False + self.assertRaises(AuthException, oauth.exchange_token, "c1") + + # Test success flow + with patch("requests.get") as mock_get: + my_mock_response = mock.Mock() + my_mock_response.ok = True + my_mock_response.cookies = {} + data = json.loads( + """{"jwts": ["eyJhbGciOiJFUzM4NCIsImtpZCI6IjJCdDVXTGNjTFVleTFEcDd1dHB0WmIzRng5SyIsInR5cCI6IkpXVCJ9.eyJjb29raWVEb21haW4iOiIiLCJjb29raWVFeHBpcmF0aW9uIjoxNjYwMzg4MDc4LCJjb29raWVNYXhBZ2UiOjI1OTE5OTksImNvb2tpZU5hbWUiOiJEU1IiLCJjb29raWVQYXRoIjoiLyIsImV4cCI6MTY2MDIxNTI3OCwiaWF0IjoxNjU3Nzk2MDc4LCJpc3MiOiIyQnQ1V0xjY0xVZXkxRHA3dXRwdFpiM0Z4OUsiLCJzdWIiOiIyQnRFSGtnT3UwMmxtTXh6UElleGRNdFV3MU0ifQ.oAnvJ7MJvCyL_33oM7YCF12JlQ0m6HWRuteUVAdaswfnD4rHEBmPeuVHGljN6UvOP4_Cf0559o39UHVgm3Fwb-q7zlBbsu_nP1-PRl-F8NJjvBgC5RsAYabtJq7LlQmh"], "user": {"externalIds": ["guyp@descope.com"], "name": "", "email": "guyp@descope.com", "phone": "", "verifiedEmail": true, "verifiedPhone": false}, "firstSeen": false}""" + ) + my_mock_response.json.return_value = data + mock_get.return_value = my_mock_response + oauth.exchange_token("c1") + mock_get.assert_called_with( + f"{DEFAULT_BASE_URL}{EndpointsV1.oauthExchangeTokenPath}", + headers={ + "Content-Type": "application/json", + "Authorization": f"Bearer {self.dummy_project_id}", + }, + params={"code": "c1"}, + allow_redirects=False, + verify=True, + ) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_otp.py b/tests/test_otp.py index 6225576fc..b4f024c7c 100644 --- a/tests/test_otp.py +++ b/tests/test_otp.py @@ -24,57 +24,57 @@ def setUp(self) -> None: def test_compose_signin_url(self): self.assertEqual( OTP._compose_signin_url(DeliveryMethod.EMAIL), - "/v1/auth/signin/otp/email", + "/v1/auth/otp/signin/email", ) self.assertEqual( OTP._compose_signin_url(DeliveryMethod.PHONE), - "/v1/auth/signin/otp/sms", + "/v1/auth/otp/signin/sms", ) self.assertEqual( OTP._compose_signin_url(DeliveryMethod.WHATSAPP), - "/v1/auth/signin/otp/whatsapp", + "/v1/auth/otp/signin/whatsapp", ) def test_compose_verify_code_url(self): self.assertEqual( OTP._compose_verify_code_url(DeliveryMethod.EMAIL), - "/v1/auth/code/verify/email", + "/v1/auth/otp/verify/email", ) self.assertEqual( OTP._compose_verify_code_url(DeliveryMethod.PHONE), - "/v1/auth/code/verify/sms", + "/v1/auth/otp/verify/sms", ) self.assertEqual( OTP._compose_verify_code_url(DeliveryMethod.WHATSAPP), - "/v1/auth/code/verify/whatsapp", + "/v1/auth/otp/verify/whatsapp", ) def test_compose_update_phone_url(self): self.assertEqual( OTP._compose_update_phone_url(DeliveryMethod.EMAIL), - "/v1/user/update/phone/otp/email", + "/v1/auth/otp/update/phone/email", ) self.assertEqual( OTP._compose_update_phone_url(DeliveryMethod.PHONE), - "/v1/user/update/phone/otp/sms", + "/v1/auth/otp/update/phone/sms", ) self.assertEqual( OTP._compose_update_phone_url(DeliveryMethod.WHATSAPP), - "/v1/user/update/phone/otp/whatsapp", + "/v1/auth/otp/update/phone/whatsapp", ) def test_compose_sign_up_or_in_url(self): self.assertEqual( OTP._compose_sign_up_or_in_url(DeliveryMethod.EMAIL), - "/v1/auth/sign-up-or-in/otp/email", + "/v1/auth/otp/signup-in/email", ) self.assertEqual( OTP._compose_sign_up_or_in_url(DeliveryMethod.PHONE), - "/v1/auth/sign-up-or-in/otp/sms", + "/v1/auth/otp/signup-in/sms", ) self.assertEqual( OTP._compose_sign_up_or_in_url(DeliveryMethod.WHATSAPP), - "/v1/auth/sign-up-or-in/otp/whatsapp", + "/v1/auth/otp/signup-in/whatsapp", ) def test_compose_update_user_phone_body(self): diff --git a/tests/test_saml.py b/tests/test_saml.py index b3432e7be..d9039164a 100644 --- a/tests/test_saml.py +++ b/tests/test_saml.py @@ -1,4 +1,6 @@ +import json import unittest +from unittest import mock from unittest.mock import patch from descope import AuthException @@ -59,6 +61,42 @@ def test_saml_start(self): verify=True, ) + def test_compose_exchange_params(self): + self.assertEqual(Auth._compose_exchange_params("c1"), {"code": "c1"}) + + def test_exchange_token(self): + saml = SAML(Auth(self.dummy_project_id, self.public_key_dict)) + + # Test failed flows + self.assertRaises(AuthException, saml.exchange_token, "") + self.assertRaises(AuthException, saml.exchange_token, None) + + with patch("requests.get") as mock_get: + mock_get.return_value.ok = False + self.assertRaises(AuthException, saml.exchange_token, "c1") + + # Test success flow + with patch("requests.get") as mock_get: + my_mock_response = mock.Mock() + my_mock_response.ok = True + my_mock_response.cookies = {} + data = json.loads( + """{"jwts": ["eyJhbGciOiJFUzM4NCIsImtpZCI6IjJCdDVXTGNjTFVleTFEcDd1dHB0WmIzRng5SyIsInR5cCI6IkpXVCJ9.eyJjb29raWVEb21haW4iOiIiLCJjb29raWVFeHBpcmF0aW9uIjoxNjYwMzg4MDc4LCJjb29raWVNYXhBZ2UiOjI1OTE5OTksImNvb2tpZU5hbWUiOiJEU1IiLCJjb29raWVQYXRoIjoiLyIsImV4cCI6MTY2MDIxNTI3OCwiaWF0IjoxNjU3Nzk2MDc4LCJpc3MiOiIyQnQ1V0xjY0xVZXkxRHA3dXRwdFpiM0Z4OUsiLCJzdWIiOiIyQnRFSGtnT3UwMmxtTXh6UElleGRNdFV3MU0ifQ.oAnvJ7MJvCyL_33oM7YCF12JlQ0m6HWRuteUVAdaswfnD4rHEBmPeuVHGljN6UvOP4_Cf0559o39UHVgm3Fwb-q7zlBbsu_nP1-PRl-F8NJjvBgC5RsAYabtJq7LlQmh"], "user": {"externalIds": ["guyp@descope.com"], "name": "", "email": "guyp@descope.com", "phone": "", "verifiedEmail": true, "verifiedPhone": false}, "firstSeen": false}""" + ) + my_mock_response.json.return_value = data + mock_get.return_value = my_mock_response + saml.exchange_token("c1") + mock_get.assert_called_with( + f"{DEFAULT_BASE_URL}{EndpointsV1.samlExchangeTokenPath}", + headers={ + "Content-Type": "application/json", + "Authorization": f"Bearer {self.dummy_project_id}", + }, + params={"code": "c1"}, + allow_redirects=False, + verify=True, + ) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_webauthn.py b/tests/test_webauthn.py index 98996e843..a042dbfff 100644 --- a/tests/test_webauthn.py +++ b/tests/test_webauthn.py @@ -45,17 +45,17 @@ def test_compose_signin_body(self): {"externalId": "dummy@dummy.com", "origin": "https://example.com"}, ) - def test_compose_add_device_start_body(self): + def test_compose_update_start_body(self): self.assertEqual( - WebauthN._compose_add_device_start_body( + WebauthN._compose_update_start_body( "dummy@dummy.com", "https://example.com" ), {"externalId": "dummy@dummy.com", "origin": "https://example.com"}, ) - def test_compose_add_device_finish_body(self): + def test_compose_update_finish_body(self): self.assertEqual( - WebauthN._compose_add_device_finish_body("t01", "response01"), + WebauthN._compose_update_finish_body("t01", "response01"), {"transactionId": "t01", "response": "response01"}, ) @@ -234,27 +234,27 @@ def test_sign_in_finish(self): ) self.assertIsNotNone(webauthn.sign_up_finish("t01", "response01")) - def test_add_device_start(self): + def test_update_start(self): valid_jwt_token = "eyJhbGciOiJFUzM4NCIsImtpZCI6IjJCdDVXTGNjTFVleTFEcDd1dHB0WmIzRng5SyIsInR5cCI6IkpXVCJ9.eyJhdXRob3JpemVkVGVuYW50cyI6eyIiOm51bGx9LCJjb29raWVEb21haW4iOiIiLCJjb29raWVFeHBpcmF0aW9uIjoxNjYwNjc5MjA4LCJjb29raWVNYXhBZ2UiOjI1OTE5OTksImNvb2tpZU5hbWUiOiJEU1IiLCJjb29raWVQYXRoIjoiLyIsImV4cCI6MjA5MDA4NzIwOCwiaWF0IjoxNjU4MDg3MjA4LCJpc3MiOiIyQnQ1V0xjY0xVZXkxRHA3dXRwdFpiM0Z4OUsiLCJzdWIiOiIyQzU1dnl4dzBzUkw2RmRNNjhxUnNDRGRST1YifQ.cWP5up4R5xeIl2qoG2NtfLH3Q5nRJVKdz-FDoAXctOQW9g3ceZQi6rZQ-TPBaXMKw68bijN3bLJTqxWW5WHzqRUeopfuzTcMYmC0wP2XGJkrdF6A8D5QW6acSGqglFgu" webauthn = WebauthN(Auth(self.dummy_project_id, self.public_key_dict)) # Test failed flows self.assertRaises( - AuthException, webauthn.add_device_start, "", "", "https://example.com" + AuthException, webauthn.update_start, "", "", "https://example.com" ) self.assertRaises( - AuthException, webauthn.add_device_start, None, "", "https://example.com" + AuthException, webauthn.update_start, None, "", "https://example.com" ) self.assertRaises( AuthException, - webauthn.add_device_start, + webauthn.update_start, "dummy@dummy.com", "", "https://example.com", ) self.assertRaises( AuthException, - webauthn.add_device_start, + webauthn.update_start, "dummy@dummy.com", None, "https://example.com", @@ -264,7 +264,7 @@ def test_add_device_start(self): mock_post.return_value.ok = False self.assertRaises( AuthException, - webauthn.add_device_start, + webauthn.update_start, "dummy@dummy.com", valid_jwt_token, "https://example.com", @@ -274,7 +274,7 @@ def test_add_device_start(self): with patch("requests.post") as mock_post: mock_post.return_value.ok = True self.assertIsNotNone( - webauthn.add_device_start( + webauthn.update_start( "dummy@dummy.com", valid_jwt_token, "https://example.com" ) ) @@ -285,10 +285,10 @@ def test_add_device_start(self): my_mock_response.ok = True my_mock_response.json.return_value = valid_response mock_post.return_value = my_mock_response - res = webauthn.add_device_start( + res = webauthn.update_start( "dummy@dummy.com", "asdasd", "https://example.com" ) - expected_uri = f"{DEFAULT_BASE_URL}{EndpointsV1.deviceAddAuthWebauthnStart}" + expected_uri = f"{DEFAULT_BASE_URL}{EndpointsV1.updateAuthWebauthnStart}" mock_post.assert_called_with( expected_uri, headers={ @@ -302,19 +302,19 @@ def test_add_device_start(self): ) self.assertEqual(res, valid_response) - def test_add_device_finish(self): + def test_update_finish(self): webauthn = WebauthN(Auth(self.dummy_project_id, self.public_key_dict)) # Test failed flows - self.assertRaises(AuthException, webauthn.add_device_finish, "", "response01") - self.assertRaises(AuthException, webauthn.add_device_finish, None, "response01") - self.assertRaises(AuthException, webauthn.add_device_finish, "t01", "") - self.assertRaises(AuthException, webauthn.add_device_finish, "t01", None) + self.assertRaises(AuthException, webauthn.update_finish, "", "response01") + self.assertRaises(AuthException, webauthn.update_finish, None, "response01") + self.assertRaises(AuthException, webauthn.update_finish, "t01", "") + self.assertRaises(AuthException, webauthn.update_finish, "t01", None) with patch("requests.post") as mock_post: mock_post.return_value.ok = False self.assertRaises( - AuthException, webauthn.add_device_finish, "t01", "response01" + AuthException, webauthn.update_finish, "t01", "response01" ) # Test success flow @@ -327,10 +327,8 @@ def test_add_device_finish(self): ) my_mock_response.json.return_value = data mock_post.return_value = my_mock_response - expected_uri = ( - f"{DEFAULT_BASE_URL}{EndpointsV1.deviceAddAuthWebauthnFinish}" - ) - webauthn.add_device_finish("t01", "response01") + expected_uri = f"{DEFAULT_BASE_URL}{EndpointsV1.updateAuthWebauthnFinish}" + webauthn.update_finish("t01", "response01") mock_post.assert_called_with( expected_uri, headers={ From b2c0775184747e303d545e09d9f6b768097868d3 Mon Sep 17 00:00:00 2001 From: aviadl Date: Thu, 1 Sep 2022 12:49:03 +0300 Subject: [PATCH 2/3] Remove some more exchanger leftovers --- descope/authmethod/saml.py | 1 - descope/descope_client.py | 1 - 2 files changed, 2 deletions(-) diff --git a/descope/authmethod/saml.py b/descope/authmethod/saml.py index 6f368f5f3..b615c4dba 100644 --- a/descope/authmethod/saml.py +++ b/descope/authmethod/saml.py @@ -1,5 +1,4 @@ from descope.auth import Auth -from descope.authmethod.exchanger import Exchanger # noqa: F401 from descope.common import EndpointsV1 from descope.exceptions import ERROR_TYPE_INVALID_ARGUMENT, AuthException diff --git a/descope/descope_client.py b/descope/descope_client.py index 6c2e612ea..384e7c7d3 100644 --- a/descope/descope_client.py +++ b/descope/descope_client.py @@ -1,7 +1,6 @@ import requests from descope.auth import Auth # noqa: F401 -from descope.authmethod.exchanger import Exchanger # noqa: F401 from descope.authmethod.magiclink import MagicLink # noqa: F401 from descope.authmethod.oauth import OAuth # noqa: F401 from descope.authmethod.otp import OTP # noqa: F401 From 2fa00eeb0fab652d69d11306e04e422c95979e40 Mon Sep 17 00:00:00 2001 From: aviadl Date: Thu, 1 Sep 2022 14:31:08 +0300 Subject: [PATCH 3/3] CR changes --- descope/auth.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/descope/auth.py b/descope/auth.py index 806f0ce96..04c715259 100644 --- a/descope/auth.py +++ b/descope/auth.py @@ -115,10 +115,6 @@ def exchange_token(self, uri, code: str) -> dict: ) return jwt_response - @staticmethod - def _compose_exchange_params(code: str) -> dict: - return {"code": code} - @staticmethod def verify_delivery_method( method: DeliveryMethod, identifier: str, user: dict @@ -227,6 +223,10 @@ def refresh_token(self, refresh_token: str) -> dict: auth_info = self._generate_auth_info(resp, refresh_token) return auth_info + @staticmethod + def _compose_exchange_params(code: str) -> dict: + return {"code": code} + @staticmethod def _validate_and_load_public_key(public_key) -> Tuple[str, jwt.PyJWK, str]: if isinstance(public_key, str):