diff --git a/descope/management/sso_settings.py b/descope/management/sso_settings.py index 1600854ec..4afbda289 100644 --- a/descope/management/sso_settings.py +++ b/descope/management/sso_settings.py @@ -584,7 +584,10 @@ def load_xaa_settings( Return value (dict): Containing the loaded XAA settings (ssoId, enabled, settings, groupsMapping, defaultSSORoles, - fgaMappings, groupsPriority, groupPriorityEnabled, allowOverrideRoles). + fgaMappings, groupsPriority, groupPriorityEnabled, allowOverrideRoles, audience). + audience is read-only: the project-level audience a requesting application must present in its + ID-JAG token. It carries no tenant segment - it equals the issuer the project publishes - so the + identity provider must send the tenant id in the token's aud_tenant claim. Raise: AuthException: raised if load operation fails diff --git a/descope/management/sso_settings_async.py b/descope/management/sso_settings_async.py index 37219da9e..d89e8e182 100644 --- a/descope/management/sso_settings_async.py +++ b/descope/management/sso_settings_async.py @@ -316,7 +316,10 @@ async def load_xaa_settings( sso_id (str): Optional, the SSO configuration id (for multi-SSO). Omit for the default SSO configuration. Return value (dict): - Containing the loaded XAA settings. + Containing the loaded XAA settings, including the read-only project-level audience a requesting + application must present in its ID-JAG token. The audience carries no tenant segment - it equals + the issuer the project publishes - so the identity provider must send the tenant id in the + token's aud_tenant claim. Raise: AuthException: raised if load operation fails diff --git a/tests/management/test_sso_settings.py b/tests/management/test_sso_settings.py index 9fdf5b7d6..6882b4871 100644 --- a/tests/management/test_sso_settings.py +++ b/tests/management/test_sso_settings.py @@ -482,7 +482,7 @@ async def test_load_xaa_settings(self, client_factory): # Test success flow (with ssoId) resp_data = json.loads( - """{"ssoId": "sso-1", "enabled": true, "settings": {"issuers": {"https://issuer.example.com": {"jwksUri": "https://issuer.example.com/jwks", "signAlgorithm": "RS256"}}}, "groupsMapping": [{"role": {"id": "r1", "name": "role1"}, "groups": ["g1"]}], "defaultSSORoles": ["aa"], "groupPriorityEnabled": true, "allowOverrideRoles": true}""" + """{"ssoId": "sso-1", "enabled": true, "settings": {"issuers": {"https://issuer.example.com": {"jwksUri": "https://issuer.example.com/jwks", "signAlgorithm": "RS256"}}}, "groupsMapping": [{"role": {"id": "r1", "name": "role1"}, "groups": ["g1"]}], "defaultSSORoles": ["aa"], "groupPriorityEnabled": true, "allowOverrideRoles": true, "audience": "https://api.descope.com/v1/apps/P1"}""" ) with client.mock_mgmt_get(make_response(resp_data)) as mock_get: resp = await client.invoke(client.mgmt.sso.load_xaa_settings("tenant-id", "sso-1")) @@ -493,6 +493,8 @@ async def test_load_xaa_settings(self, client_factory): == "https://issuer.example.com/jwks" ) assert resp.get("groupsMapping")[0]["role"]["name"] == "role1" + # Read-only, project-level: no tenant segment - the tenant travels in the aud_tenant claim. + assert resp.get("audience") == "https://api.descope.com/v1/apps/P1" assert_http_called( mock_get, client.mode,