Skip to content

Commit 493fe38

Browse files
committed
Resolve token_audience from default_oidc_audience in host metadata
Signed-off-by: Tanmay Rustagi <tanmay.rustagi@databricks.com>
1 parent 142c169 commit 493fe38

3 files changed

Lines changed: 78 additions & 1 deletion

File tree

databricks/sdk/config.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -671,10 +671,12 @@ def _resolve_host_metadata(self) -> None:
671671
if resolved is not None:
672672
logger.debug(f"Resolved host_type from host metadata: {meta.host_type}")
673673
self._resolved_host_type = resolved
674+
if not self.token_audience and meta.default_oidc_audience:
675+
logger.debug(f"Resolved token_audience from host metadata default_oidc_audience: {meta.default_oidc_audience}")
676+
self.token_audience = meta.default_oidc_audience
674677
# Account hosts use account_id as the OIDC token audience instead of the token endpoint.
675678
# This is a special case: when the metadata has no workspace_id, the host is acting as an
676679
# account-level endpoint and the audience must be scoped to the account.
677-
# TODO: Add explicit audience to the metadata discovery endpoint.
678680
if not self.token_audience and not meta.workspace_id and self.account_id:
679681
logger.debug(f"Setting token_audience to account_id for account host: {self.account_id}")
680682
self.token_audience = self.account_id

databricks/sdk/oauth.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,7 @@ class HostMetadata:
449449
workspace_id: Optional[str] = None
450450
cloud: Optional[Cloud] = None
451451
host_type: Optional[str] = None
452+
default_oidc_audience: Optional[str] = None
452453

453454
@staticmethod
454455
def from_dict(d: dict) -> "HostMetadata":
@@ -458,6 +459,7 @@ def from_dict(d: dict) -> "HostMetadata":
458459
workspace_id=d.get("workspace_id"),
459460
cloud=Cloud.parse(d.get("cloud", "")),
460461
host_type=d.get("host_type"),
462+
default_oidc_audience=d.get("default_oidc_audience"),
461463
)
462464

463465
def as_dict(self) -> dict:
@@ -467,6 +469,7 @@ def as_dict(self) -> dict:
467469
"workspace_id": self.workspace_id,
468470
"cloud": self.cloud.value if self.cloud else None,
469471
"host_type": self.host_type,
472+
"default_oidc_audience": self.default_oidc_audience,
470473
}
471474

472475

tests/test_config.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,3 +1097,75 @@ def test_resolve_host_metadata_resolved_host_type_unknown_string(mocker):
10971097
)
10981098
config = Config(host=_DUMMY_WS_HOST, token="t")
10991099
assert config._resolved_host_type is None
1100+
1101+
1102+
# ---------------------------------------------------------------------------
1103+
# default_oidc_audience resolution from host metadata
1104+
# ---------------------------------------------------------------------------
1105+
1106+
1107+
def test_resolve_host_metadata_sets_token_audience_from_default_oidc_audience(mocker):
1108+
"""token_audience is set from default_oidc_audience in host metadata."""
1109+
mocker.patch(
1110+
"databricks.sdk.config.get_host_metadata",
1111+
return_value=HostMetadata.from_dict(
1112+
{
1113+
"oidc_endpoint": f"{_DUMMY_WS_HOST}/oidc",
1114+
"account_id": _DUMMY_ACCOUNT_ID,
1115+
"workspace_id": _DUMMY_WORKSPACE_ID,
1116+
"default_oidc_audience": f"{_DUMMY_WS_HOST}/oidc/v1/token",
1117+
}
1118+
),
1119+
)
1120+
config = Config(host=_DUMMY_WS_HOST, token="t")
1121+
assert config.token_audience == f"{_DUMMY_WS_HOST}/oidc/v1/token"
1122+
1123+
1124+
def test_resolve_host_metadata_default_oidc_audience_takes_priority_over_account_id_fallback(mocker):
1125+
"""default_oidc_audience takes priority over the account_id fallback."""
1126+
mocker.patch(
1127+
"databricks.sdk.config.get_host_metadata",
1128+
return_value=HostMetadata.from_dict(
1129+
{
1130+
"oidc_endpoint": f"{_DUMMY_ACC_HOST}/oidc/accounts/{_DUMMY_ACCOUNT_ID}",
1131+
"account_id": _DUMMY_ACCOUNT_ID,
1132+
"default_oidc_audience": "custom-audience-from-server",
1133+
}
1134+
),
1135+
)
1136+
config = Config(host=_DUMMY_ACC_HOST, token="t", account_id=_DUMMY_ACCOUNT_ID)
1137+
# default_oidc_audience should take priority over the account_id fallback
1138+
assert config.token_audience == "custom-audience-from-server"
1139+
1140+
1141+
def test_resolve_host_metadata_default_oidc_audience_does_not_override_existing_token_audience(mocker):
1142+
"""An explicitly set token_audience is not overwritten by default_oidc_audience."""
1143+
mocker.patch(
1144+
"databricks.sdk.config.get_host_metadata",
1145+
return_value=HostMetadata.from_dict(
1146+
{
1147+
"oidc_endpoint": f"{_DUMMY_WS_HOST}/oidc",
1148+
"account_id": _DUMMY_ACCOUNT_ID,
1149+
"workspace_id": _DUMMY_WORKSPACE_ID,
1150+
"default_oidc_audience": f"{_DUMMY_WS_HOST}/oidc/v1/token",
1151+
}
1152+
),
1153+
)
1154+
config = Config(host=_DUMMY_WS_HOST, token="t", token_audience="my-custom-audience")
1155+
assert config.token_audience == "my-custom-audience"
1156+
1157+
1158+
def test_resolve_host_metadata_falls_back_to_account_id_when_no_default_oidc_audience(mocker):
1159+
"""When no default_oidc_audience and no workspace_id, falls back to account_id."""
1160+
mocker.patch(
1161+
"databricks.sdk.config.get_host_metadata",
1162+
return_value=HostMetadata.from_dict(
1163+
{
1164+
"oidc_endpoint": f"{_DUMMY_ACC_HOST}/oidc/accounts/{_DUMMY_ACCOUNT_ID}",
1165+
"account_id": _DUMMY_ACCOUNT_ID,
1166+
}
1167+
),
1168+
)
1169+
config = Config(host=_DUMMY_ACC_HOST, token="t", account_id=_DUMMY_ACCOUNT_ID)
1170+
# No default_oidc_audience and no workspace_id → falls back to account_id
1171+
assert config.token_audience == _DUMMY_ACCOUNT_ID

0 commit comments

Comments
 (0)