mirror of
https://github.com/turnstonelabs/turnstone.git
synced 2026-08-12 23:12:23 -06:00
fix(oidc): trust Entra's graph.microsoft.com userinfo out of the box
Microsoft Entra's discovery document advertises userinfo_endpoint on graph.microsoft.com — a host distinct from the login.microsoftonline.com issuer — so discover_oidc's cross-host guard rejected it and disabled OIDC unless the operator set trusted_endpoint_hosts. Add login.microsoftonline.com to the built-in KNOWN_TRUSTED_OAUTH_ENDPOINT_HOSTS allow-list (mirroring the Google entry) so Azure AD OIDC works with no extra configuration. Surfaced by the live obo integration test.
This commit is contained in:
+9
-9
@@ -77,17 +77,17 @@ IdP from redirecting the token-exchange POST (which carries
|
||||
being aimed at internal services.
|
||||
|
||||
A few public IdPs legitimately split endpoints across hostnames. Google
|
||||
is the canonical example:
|
||||
and Microsoft Entra ID are the canonical examples:
|
||||
|
||||
| Field | Hostname |
|
||||
|-------|----------|
|
||||
| issuer | `accounts.google.com` |
|
||||
| token_endpoint | `oauth2.googleapis.com` |
|
||||
| jwks_uri | `www.googleapis.com` |
|
||||
| userinfo_endpoint | `openidconnect.googleapis.com` |
|
||||
| IdP | Issuer host | Cross-host endpoint(s) |
|
||||
|-----|-------------|------------------------|
|
||||
| Google | `accounts.google.com` | `oauth2.googleapis.com`, `www.googleapis.com`, `openidconnect.googleapis.com` |
|
||||
| Microsoft Entra | `login.microsoftonline.com` | `graph.microsoft.com` (userinfo) |
|
||||
|
||||
Google's set is built in — operators using `https://accounts.google.com`
|
||||
need no extra configuration.
|
||||
Both sets are built in — operators using `https://accounts.google.com` or
|
||||
`https://login.microsoftonline.com/<tenant>/v2.0` need no extra
|
||||
configuration. (Entra's discovery document advertises `userinfo_endpoint`
|
||||
on `graph.microsoft.com`, distinct from the issuer host.)
|
||||
|
||||
For other IdPs whose discovery document references a non-issuer host,
|
||||
extend the allow-list explicitly:
|
||||
|
||||
@@ -910,6 +910,40 @@ class TestValidateDiscoveredEndpoint:
|
||||
|
||||
asyncio.run(_run())
|
||||
|
||||
def test_discover_accepts_entra_userinfo_on_graph(self):
|
||||
"""discover_oidc accepts Entra's cross-host userinfo on graph.microsoft.com
|
||||
via the built-in allow-list, so Azure AD OIDC works out of the box with no
|
||||
trusted_endpoint_hosts override."""
|
||||
tenant = "11111111-1111-1111-1111-111111111111"
|
||||
config = _make_config(
|
||||
issuer=f"https://login.microsoftonline.com/{tenant}/v2.0",
|
||||
authorization_endpoint="",
|
||||
token_endpoint="",
|
||||
userinfo_endpoint="",
|
||||
jwks_uri="",
|
||||
)
|
||||
discovery_doc = {
|
||||
"authorization_endpoint": f"https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize",
|
||||
"token_endpoint": f"https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token",
|
||||
"userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo",
|
||||
"jwks_uri": f"https://login.microsoftonline.com/{tenant}/discovery/v2.0/keys",
|
||||
}
|
||||
mock_response = MagicMock()
|
||||
mock_response.json.return_value = discovery_doc
|
||||
mock_response.raise_for_status = MagicMock()
|
||||
|
||||
async def _run():
|
||||
client = _mock_async_client(lambda url: _async_return(mock_response))
|
||||
with (
|
||||
patch("socket.getaddrinfo", return_value=self._PUBLIC_ADDR),
|
||||
patch("httpx.AsyncClient", return_value=client),
|
||||
):
|
||||
result = await discover_oidc(config)
|
||||
assert result.enabled is True
|
||||
assert result.userinfo_endpoint == "https://graph.microsoft.com/oidc/userinfo"
|
||||
|
||||
asyncio.run(_run())
|
||||
|
||||
def test_discover_rejects_http_endpoint(self):
|
||||
"""discover_oidc returns enabled=False when an endpoint is http:// in prod."""
|
||||
config = _make_config(
|
||||
|
||||
@@ -42,6 +42,20 @@ KNOWN_TRUSTED_OAUTH_ENDPOINT_HOSTS: dict[str, frozenset[str]] = {
|
||||
"openidconnect.googleapis.com",
|
||||
}
|
||||
),
|
||||
# Microsoft Entra ID (commercial cloud). The tenant issuer is
|
||||
# login.microsoftonline.com/<tenant>/v2.0, but its discovery document
|
||||
# advertises userinfo_endpoint on graph.microsoft.com — a distinct host.
|
||||
# Without this entry, discovery rejects the cross-host userinfo and
|
||||
# disables OIDC, so every Azure AD deployment would need a manual
|
||||
# trusted_endpoint_hosts override to log in. (Sovereign clouds —
|
||||
# login.microsoftonline.us / *.chinacloudapi.cn — use their own graph
|
||||
# hosts and can be added the same way.)
|
||||
"login.microsoftonline.com": frozenset(
|
||||
{
|
||||
"login.microsoftonline.com",
|
||||
"graph.microsoft.com",
|
||||
}
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user