Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/otdf-python/src/otdf_python/sdk_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,9 @@ def _discover_token_endpoint_from_platform(self) -> None:
config_doc = response.json()
# Newer platform versions return the fields directly at the top
# level instead of nested under a "configuration" key.
configuration = config_doc.get("configuration") or config_doc
configuration = config_doc.get("configuration")
if configuration is None:
configuration = config_doc

# Try to get token endpoint from IDP configuration
idp_config = configuration.get("idp", {})
Expand Down
88 changes: 87 additions & 1 deletion tests/test_sdk_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import tempfile
from pathlib import Path
from unittest.mock import MagicMock, patch
from unittest.mock import MagicMock, call, patch

import pytest
from otdf_python.sdk import SDK
Expand Down Expand Up @@ -193,6 +193,92 @@ def get_side_effect(url, **kwargs):
assert "Token request failed: 401" in str(excinfo.value)


@pytest.mark.parametrize(
"outer_fields",
[
{"idp": {"token_endpoint": "https://outer.example.com/token"}},
{"platform_issuer": "https://outer.example.com"},
],
ids=["idp", "platform-issuer"],
)
def test_empty_platform_configuration_ignores_outer_fields(outer_fields):
"""An empty nested configuration must not expose unrelated outer fields."""
builder = SDKBuilder()
builder.set_platform_endpoint("https://platform.example.com")
builder.client_secret("client", "secret")
response = MagicMock(status_code=200)
response.json.return_value = {"configuration": {}, **outer_fields}

with (
patch("otdf_python.sdk_builder.httpx.get", return_value=response) as get,
pytest.raises(AutoConfigureException, match="No platform_issuer found"),
):
builder._discover_token_endpoint_from_platform()

get.assert_called_once_with(
"https://platform.example.com/.well-known/opentdf-configuration",
verify=True,
)
assert builder.oauth_config is not None
assert builder.oauth_config.token_endpoint is None


@pytest.mark.parametrize("wrapper", ["missing", "null", "nested"])
@pytest.mark.parametrize(
"configuration",
[
{"idp": {"token_endpoint": "https://issuer.example.com/token"}},
{"platform_issuer": "https://issuer.example.com"},
],
ids=["idp", "platform-issuer"],
)
def test_platform_discovery_configuration_formats(configuration, wrapper):
"""Keep top-level and nested discovery formats and nested-field precedence."""
if wrapper == "missing":
config_doc = configuration
elif wrapper == "null":
config_doc = {"configuration": None, **configuration}
else:
config_doc = {
"configuration": configuration,
"idp": {"token_endpoint": "https://outer.example.com/token"},
"platform_issuer": "https://outer.example.com",
}

platform_response = MagicMock(status_code=200)
platform_response.json.return_value = config_doc
issuer_response = MagicMock(status_code=200)
issuer_response.json.return_value = {
"token_endpoint": "https://issuer.example.com/token"
}
builder = SDKBuilder()
builder.set_platform_endpoint("https://platform.example.com")
builder.client_secret("client", "secret")

with patch(
"otdf_python.sdk_builder.httpx.get",
side_effect=[platform_response, issuer_response],
) as get:
builder._discover_token_endpoint_from_platform()

expected_calls = [
call(
"https://platform.example.com/.well-known/opentdf-configuration",
verify=True,
)
]
if "platform_issuer" in configuration:
expected_calls.append(
call(
"https://issuer.example.com/.well-known/openid-configuration",
verify=True,
)
)
assert get.call_args_list == expected_calls
assert builder.oauth_config is not None
assert builder.oauth_config.token_endpoint == "https://issuer.example.com/token"


def test_build_without_platform_endpoint():
"""Test building SDK without platform endpoint."""
builder = SDKBuilder()
Expand Down