From cae69256b0c17c43827718dfb7a178d059ffc248 Mon Sep 17 00:00:00 2001 From: Doron Sharon Date: Wed, 13 May 2026 14:18:22 +0000 Subject: [PATCH 1/2] feat: add email and sso_id parameters to generate_sso_configuration_link Update the generate_sso_configuration_link method to include all optional parameters from the API specification: - Make tenant_id and expire_time optional (as per API docs) - Add email parameter for admin email address - Add sso_id parameter for SSO identifier This ensures full compatibility with the /v2/mgmt/tenant/adminlinks/sso/generate API endpoint as documented in the Descope API reference. Changes: - Update method signature to include all optional parameters - Implement conditional parameter inclusion in request body - Add comprehensive tests for all parameter combinations - Add test for minimal parameters (no arguments) Resolves: #0F83 Co-Authored-By: Claude Sonnet 4.5 --- descope/management/tenant.py | 27 ++++++++---- tests/management/test_tenant.py | 74 +++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 8 deletions(-) diff --git a/descope/management/tenant.py b/descope/management/tenant.py index 92f797573..9dc117543 100644 --- a/descope/management/tenant.py +++ b/descope/management/tenant.py @@ -328,15 +328,19 @@ def search_all( def generate_sso_configuration_link( self, - tenant_id: str, - expire_time: int, + tenant_id: Optional[str] = None, + expire_time: Optional[int] = None, + email: Optional[str] = None, + sso_id: Optional[str] = None, ) -> str: """ Generate a tenant admin self-service link for SSO configuration. Args: - tenant_id (str): The ID of the tenant to generate the link for. - expire_time (int): The expiration duration in seconds. For a link valid for 6 hours, use 21600. + tenant_id (str): Optional tenant ID to generate the link for. + expire_time (int): Optional expiration duration in seconds. For a link valid for 6 hours, use 21600. + email (str): Optional email address associated with the admin. + sso_id (str): Optional SSO identifier for the tenant. Return value (str): Returns the admin SSO configuration link as a string. @@ -344,12 +348,19 @@ def generate_sso_configuration_link( Raise: AuthException: raised if generation operation fails """ + body: dict[str, Any] = {} + if tenant_id is not None: + body["tenantId"] = tenant_id + if expire_time is not None: + body["expireTime"] = expire_time + if email is not None: + body["email"] = email + if sso_id is not None: + body["ssoId"] = sso_id + response = self._http.post( MgmtV1.tenant_generate_sso_configuration_link_path, - body={ - "tenantId": tenant_id, - "expireTime": expire_time, - }, + body=body, ) result = response.json() return result.get("adminSSOConfigurationLink", "") diff --git a/tests/management/test_tenant.py b/tests/management/test_tenant.py index 3d7e6eec4..956fd07cd 100644 --- a/tests/management/test_tenant.py +++ b/tests/management/test_tenant.py @@ -663,3 +663,77 @@ def test_generate_sso_configuration_link_failed(self): "t1", 21600, ) + + def test_generate_sso_configuration_link_with_all_params(self): + client = DescopeClient( + self.dummy_project_id, + self.public_key_dict, + False, + self.dummy_management_key, + ) + + # Test success flow with all parameters + with patch("httpx.post") as mock_post: + network_resp = mock.Mock() + network_resp.is_success = True + network_resp.json.return_value = json.loads( + """{"adminSSOConfigurationLink": "https://example.com/sso-config-link"}""" + ) + mock_post.return_value = network_resp + link = client.mgmt.tenant.generate_sso_configuration_link( + tenant_id="t1", + expire_time=21600, + email="admin@example.com", + sso_id="sso123", + ) + self.assertEqual(link, "https://example.com/sso-config-link") + mock_post.assert_called_with( + f"{common.DEFAULT_BASE_URL}{MgmtV1.tenant_generate_sso_configuration_link_path}", + headers={ + **common.default_headers, + "Authorization": f"Bearer {self.dummy_project_id}:{self.dummy_management_key}", + "x-descope-project-id": self.dummy_project_id, + }, + params=None, + json={ + "tenantId": "t1", + "expireTime": 21600, + "email": "admin@example.com", + "ssoId": "sso123", + }, + follow_redirects=False, + verify=SSLMatcher(), + timeout=DEFAULT_TIMEOUT_SECONDS, + ) + + def test_generate_sso_configuration_link_minimal_params(self): + client = DescopeClient( + self.dummy_project_id, + self.public_key_dict, + False, + self.dummy_management_key, + ) + + # Test success flow with minimal parameters + with patch("httpx.post") as mock_post: + network_resp = mock.Mock() + network_resp.is_success = True + network_resp.json.return_value = json.loads( + """{"adminSSOConfigurationLink": "https://example.com/sso-config-link"}""" + ) + mock_post.return_value = network_resp + link = client.mgmt.tenant.generate_sso_configuration_link() + self.assertEqual(link, "https://example.com/sso-config-link") + mock_post.assert_called_with( + f"{common.DEFAULT_BASE_URL}{MgmtV1.tenant_generate_sso_configuration_link_path}", + headers={ + **common.default_headers, + "Authorization": f"Bearer {self.dummy_project_id}:{self.dummy_management_key}", + "x-descope-project-id": self.dummy_project_id, + }, + params=None, + json={}, + follow_redirects=False, + verify=SSLMatcher(), + timeout=DEFAULT_TIMEOUT_SECONDS, + ) From f482db2959e8410346d491701f2ee4a6b479ef00 Mon Sep 17 00:00:00 2001 From: Doron Sharon Date: Wed, 13 May 2026 14:29:56 +0000 Subject: [PATCH 2/2] fix: make tenant_id required in generate_sso_configuration_link Make tenant_id a required parameter instead of optional, as it's needed for the API to function correctly. Update test to pass tenant_id when calling with minimal parameters. Co-Authored-By: Claude Sonnet 4.5 --- descope/management/tenant.py | 8 +++----- tests/management/test_tenant.py | 6 +++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/descope/management/tenant.py b/descope/management/tenant.py index 9dc117543..504a61907 100644 --- a/descope/management/tenant.py +++ b/descope/management/tenant.py @@ -328,7 +328,7 @@ def search_all( def generate_sso_configuration_link( self, - tenant_id: Optional[str] = None, + tenant_id: str, expire_time: Optional[int] = None, email: Optional[str] = None, sso_id: Optional[str] = None, @@ -337,7 +337,7 @@ def generate_sso_configuration_link( Generate a tenant admin self-service link for SSO configuration. Args: - tenant_id (str): Optional tenant ID to generate the link for. + tenant_id (str): Tenant ID to generate the link for. expire_time (int): Optional expiration duration in seconds. For a link valid for 6 hours, use 21600. email (str): Optional email address associated with the admin. sso_id (str): Optional SSO identifier for the tenant. @@ -348,9 +348,7 @@ def generate_sso_configuration_link( Raise: AuthException: raised if generation operation fails """ - body: dict[str, Any] = {} - if tenant_id is not None: - body["tenantId"] = tenant_id + body: dict[str, Any] = {"tenantId": tenant_id} if expire_time is not None: body["expireTime"] = expire_time if email is not None: diff --git a/tests/management/test_tenant.py b/tests/management/test_tenant.py index 956fd07cd..429683dfc 100644 --- a/tests/management/test_tenant.py +++ b/tests/management/test_tenant.py @@ -714,7 +714,7 @@ def test_generate_sso_configuration_link_minimal_params(self): self.dummy_management_key, ) - # Test success flow with minimal parameters + # Test success flow with only required parameter with patch("httpx.post") as mock_post: network_resp = mock.Mock() network_resp.is_success = True @@ -722,7 +722,7 @@ def test_generate_sso_configuration_link_minimal_params(self): """{"adminSSOConfigurationLink": "https://example.com/sso-config-link"}""" ) mock_post.return_value = network_resp - link = client.mgmt.tenant.generate_sso_configuration_link() + link = client.mgmt.tenant.generate_sso_configuration_link("t1") self.assertEqual(link, "https://example.com/sso-config-link") mock_post.assert_called_with( f"{common.DEFAULT_BASE_URL}{MgmtV1.tenant_generate_sso_configuration_link_path}", @@ -732,7 +732,7 @@ def test_generate_sso_configuration_link_minimal_params(self): "x-descope-project-id": self.dummy_project_id, }, params=None, - json={}, + json={"tenantId": "t1"}, follow_redirects=False, verify=SSLMatcher(), timeout=DEFAULT_TIMEOUT_SECONDS,