From ec34845c50dbae9f17979f8c88e755c605532a3a Mon Sep 17 00:00:00 2001 From: Zach Waterfield Date: Wed, 16 Sep 2020 10:24:18 -0400 Subject: [PATCH 1/9] Add MagicLink support through passwordless module --- tests/test_passwordless.py | 50 ++++++++++++++++++++++++++++++++ workos/passwordless.py | 59 ++++++++++++++++++++++++++++++++++++++ workos/utils/validation.py | 2 ++ 3 files changed, 111 insertions(+) create mode 100644 tests/test_passwordless.py create mode 100644 workos/passwordless.py diff --git a/tests/test_passwordless.py b/tests/test_passwordless.py new file mode 100644 index 00000000..1f3064b5 --- /dev/null +++ b/tests/test_passwordless.py @@ -0,0 +1,50 @@ +import json +from requests import Response + +import pytest + +import workos +from workos.passwordless import Passwordless + + +class TestPasswordless(object): + @pytest.fixture(autouse=True) + def setup(self, set_api_key_and_project_id): + self.passwordless = Passwordless() + + @pytest.fixture + def mock_passwordless_session(self): + return { + "id": "passwordless_session_01EHDAK2BFGWCSZXP9HGZ3VK8C", + "email": "demo@workos-okta.com", + "expires_at": "2020-08-13T05:50:00.000Z", + "link": "https://auth.workos.com/passwordless/4TeRexuejWCKs9rrFOIuLRYEr/confirm", + "object": "passwordless_session" + } + + def test_create_session_succeeds(self, mock_passwordless_session, mock_request_method): + mock_response = Response() + mock_response.status_code = 201 + mock_response.response_dict = mock_passwordless_session + mock_request_method("post", mock_response, 201) + + session_options = { + "email": "demo@workos-okta.com", + "session_type": "MagicLink", + } + response = self.passwordless.create_session(session_options) + + assert response.status_code == 201 + assert response.response_dict == mock_passwordless_session + + def test_get_send_session_succeeds(self, mock_request_method): + + response = { + "success": True, + } + mock_request_method("post", response, 200) + + response = self.passwordless.send_session( + "passwordless_session_01EHDAK2BFGWCSZXP9HGZ3VK8C" + ) + assert response["success"] == True diff --git a/workos/passwordless.py b/workos/passwordless.py new file mode 100644 index 00000000..1ff58691 --- /dev/null +++ b/workos/passwordless.py @@ -0,0 +1,59 @@ +import workos +from workos.utils.request import RequestHelper, REQUEST_METHOD_POST +from workos.utils.validation import PASSWORDLESS_MODULE, validate_settings + + +class Passwordless(object): + """Offers methods through the WorkOS Passwordless service.""" + + @validate_settings(PASSWORDLESS_MODULE) + def __init__(self): + pass + + @property + def request_helper(self): + if not getattr(self, "_request_helper", None): + self._request_helper = RequestHelper() + return self._request_helper + + def create_session(self, session_options): + """Create an Passwordless Session. + + Args: + session_options (dict) - An session options object + session_options[email] (str): The email of the user to authenticate. + session_options[state] (str): Optional parameter that the redirect + URI received from WorkOS will contain. The state parameter + can be used to encode arbitrary information to help + restore application state between redirects. + session_options[type] (str): The type of Passwordless Session to + create. Currently, the only supported value is 'MagicLink'. + + Returns: + dict: Passwordless Session + """ + + return self.request_helper.request( + "passwordless/sessions", + method=REQUEST_METHOD_POST, + params=session_options, + token=workos.api_key, + ) + + def send_session(self, session_id): + """Send a Passwordless Session via email. + + Note, either 'directory' or 'user' must be provided. + + Args: + session_id (str): The unique identifier of the Passwordless + Session to send an email for. + + Returns: + dict: {"success": true} if successful + """ + return self.request_helper.request( + "passwordless/sessions/{session_id}/send", + method=REQUEST_METHOD_POST, + token=workos.api_key, + ) diff --git a/workos/utils/validation.py b/workos/utils/validation.py index abacb531..7f4699e5 100644 --- a/workos/utils/validation.py +++ b/workos/utils/validation.py @@ -5,12 +5,14 @@ AUDIT_TRAIL_MODULE = "AuditTrail" DIRECTORY_SYNC_MODULE = "DirectorySync" +PASSWORDLESS_MODULE = "Passwordless" PORTAL_MODULE = "Portal" SSO_MODULE = "SSO" REQUIRED_SETTINGS_FOR_MODULE = { AUDIT_TRAIL_MODULE: ["api_key",], DIRECTORY_SYNC_MODULE: ["api_key",], + PASSWORDLESS_MODULE: ["api_key",], PORTAL_MODULE: ["api_key",], SSO_MODULE: ["api_key", "project_id",], } From e885f8e3aef3accefc8151122a15c432fce538c6 Mon Sep 17 00:00:00 2001 From: Zach Waterfield Date: Wed, 16 Sep 2020 10:37:35 -0400 Subject: [PATCH 2/9] Update test_passwordless.py spacing --- tests/test_passwordless.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_passwordless.py b/tests/test_passwordless.py index 1f3064b5..7631f676 100644 --- a/tests/test_passwordless.py +++ b/tests/test_passwordless.py @@ -15,11 +15,11 @@ def setup(self, set_api_key_and_project_id): @pytest.fixture def mock_passwordless_session(self): return { - "id": "passwordless_session_01EHDAK2BFGWCSZXP9HGZ3VK8C", - "email": "demo@workos-okta.com", - "expires_at": "2020-08-13T05:50:00.000Z", - "link": "https://auth.workos.com/passwordless/4TeRexuejWCKs9rrFOIuLRYEr/confirm", - "object": "passwordless_session" + "id": "passwordless_session_01EHDAK2BFGWCSZXP9HGZ3VK8C", + "email": "demo@workos-okta.com", + "expires_at": "2020-08-13T05:50:00.000Z", + "link": "https://auth.workos.com/passwordless/4TeRexuejWCKs9rrFOIuLRYEr/confirm", + "object": "passwordless_session" } def test_create_session_succeeds(self, mock_passwordless_session, mock_request_method): @@ -45,6 +45,6 @@ def test_get_send_session_succeeds(self, mock_request_method): mock_request_method("post", response, 200) response = self.passwordless.send_session( - "passwordless_session_01EHDAK2BFGWCSZXP9HGZ3VK8C" + "passwordless_session_01EHDAK2BFGWCSZXP9HGZ3VK8C" ) assert response["success"] == True From c43414a9cd18f70af920f8578fe5a23dddbbc747 Mon Sep 17 00:00:00 2001 From: Zach Waterfield Date: Wed, 16 Sep 2020 11:02:36 -0400 Subject: [PATCH 3/9] Update test_passwordless.py spacing --- tests/test_passwordless.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_passwordless.py b/tests/test_passwordless.py index 7631f676..6bca4ea7 100644 --- a/tests/test_passwordless.py +++ b/tests/test_passwordless.py @@ -19,10 +19,12 @@ def mock_passwordless_session(self): "email": "demo@workos-okta.com", "expires_at": "2020-08-13T05:50:00.000Z", "link": "https://auth.workos.com/passwordless/4TeRexuejWCKs9rrFOIuLRYEr/confirm", - "object": "passwordless_session" + "object": "passwordless_session", } - def test_create_session_succeeds(self, mock_passwordless_session, mock_request_method): + def test_create_session_succeeds( + self, mock_passwordless_session, mock_request_method + ): mock_response = Response() mock_response.status_code = 201 mock_response.response_dict = mock_passwordless_session @@ -38,7 +40,6 @@ def test_create_session_succeeds(self, mock_passwordless_session, mock_request_m assert response.response_dict == mock_passwordless_session def test_get_send_session_succeeds(self, mock_request_method): - response = { "success": True, } From 417f66c43d69bf547f02e77af35aab3df717214f Mon Sep 17 00:00:00 2001 From: Zach Waterfield Date: Wed, 16 Sep 2020 11:16:19 -0400 Subject: [PATCH 4/9] Update session type key --- tests/test_passwordless.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_passwordless.py b/tests/test_passwordless.py index 6bca4ea7..d00e2946 100644 --- a/tests/test_passwordless.py +++ b/tests/test_passwordless.py @@ -32,7 +32,7 @@ def test_create_session_succeeds( session_options = { "email": "demo@workos-okta.com", - "session_type": "MagicLink", + "type": "MagicLink", } response = self.passwordless.create_session(session_options) From f2b7b49f0c2ef9191bcc3847ce15a40b88ebfa09 Mon Sep 17 00:00:00 2001 From: Zach Waterfield Date: Wed, 16 Sep 2020 11:16:54 -0400 Subject: [PATCH 5/9] Update string interoplation for send session post request --- workos/passwordless.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workos/passwordless.py b/workos/passwordless.py index 1ff58691..fa544669 100644 --- a/workos/passwordless.py +++ b/workos/passwordless.py @@ -53,7 +53,7 @@ def send_session(self, session_id): dict: {"success": true} if successful """ return self.request_helper.request( - "passwordless/sessions/{session_id}/send", + "passwordless/sessions/{session_id}/send".format(session_id=session_id), method=REQUEST_METHOD_POST, token=workos.api_key, ) From 5e229709503b5760be4163cf72ee4716541d670e Mon Sep 17 00:00:00 2001 From: Zach Waterfield Date: Wed, 16 Sep 2020 13:23:00 -0400 Subject: [PATCH 6/9] Return True instead of a dict when sending a session --- tests/test_passwordless.py | 2 +- workos/passwordless.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_passwordless.py b/tests/test_passwordless.py index d00e2946..e8308b18 100644 --- a/tests/test_passwordless.py +++ b/tests/test_passwordless.py @@ -48,4 +48,4 @@ def test_get_send_session_succeeds(self, mock_request_method): response = self.passwordless.send_session( "passwordless_session_01EHDAK2BFGWCSZXP9HGZ3VK8C" ) - assert response["success"] == True + assert response == True diff --git a/workos/passwordless.py b/workos/passwordless.py index fa544669..4bb3b5c5 100644 --- a/workos/passwordless.py +++ b/workos/passwordless.py @@ -43,8 +43,6 @@ def create_session(self, session_options): def send_session(self, session_id): """Send a Passwordless Session via email. - Note, either 'directory' or 'user' must be provided. - Args: session_id (str): The unique identifier of the Passwordless Session to send an email for. @@ -52,8 +50,10 @@ def send_session(self, session_id): Returns: dict: {"success": true} if successful """ - return self.request_helper.request( + self.request_helper.request( "passwordless/sessions/{session_id}/send".format(session_id=session_id), method=REQUEST_METHOD_POST, token=workos.api_key, ) + + return True From 6de1e501848a35cc9dc19bdf502735deed3d4f73 Mon Sep 17 00:00:00 2001 From: Zach Waterfield Date: Wed, 16 Sep 2020 13:23:48 -0400 Subject: [PATCH 7/9] Update send_session return comment --- workos/passwordless.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workos/passwordless.py b/workos/passwordless.py index 4bb3b5c5..3c7cb24d 100644 --- a/workos/passwordless.py +++ b/workos/passwordless.py @@ -48,7 +48,7 @@ def send_session(self, session_id): Session to send an email for. Returns: - dict: {"success": true} if successful + boolean: Returns True """ self.request_helper.request( "passwordless/sessions/{session_id}/send".format(session_id=session_id), From 2170e5a3cd63a10ed06f7e77ff0d1f53e89cf956 Mon Sep 17 00:00:00 2001 From: Zach Waterfield Date: Wed, 16 Sep 2020 14:59:17 -0400 Subject: [PATCH 8/9] Grammar fix --- workos/passwordless.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workos/passwordless.py b/workos/passwordless.py index 3c7cb24d..44ef7634 100644 --- a/workos/passwordless.py +++ b/workos/passwordless.py @@ -17,7 +17,7 @@ def request_helper(self): return self._request_helper def create_session(self, session_options): - """Create an Passwordless Session. + """Create a Passwordless Session. Args: session_options (dict) - An session options object From 854ed40e1323aa1ef605b73eed068ac867ddb366 Mon Sep 17 00:00:00 2001 From: Zach Waterfield Date: Thu, 17 Sep 2020 10:06:35 -0400 Subject: [PATCH 9/9] Fix comment spacing --- workos/passwordless.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workos/passwordless.py b/workos/passwordless.py index 44ef7634..cc220b78 100644 --- a/workos/passwordless.py +++ b/workos/passwordless.py @@ -45,7 +45,7 @@ def send_session(self, session_id): Args: session_id (str): The unique identifier of the Passwordless - Session to send an email for. + Session to send an email for. Returns: boolean: Returns True