Validate issuer and audience of Azure AD id_tokens in FAB auth manager - #71735
Merged
Merged
Conversation
vincbeck
approved these changes
Aug 17, 2026
The Azure id_token signature is verified against Microsoft's key set, but the decode call passed no claims_options, so authlib's claims.validate() enforced neither the issuer nor the audience. The key set in use is the multi-tenant one (login.microsoftonline.com/common/discovery/keys), which serves signing keys for every Azure tenant. A correctly-signed token from any tenant therefore satisfied the signature check, and get_oauth_user_info() then read the login identity (oid, email, roles) straight out of it. Pin both claims: * iss must be the configured tenant, accepting the v1.0 (sts.windows.net/<tenant>/) and v2.0 (login.microsoftonline.com/<tenant>/v2.0) issuer forms. * aud must be this application's client_id. The tenant is taken from an explicit tenant_id in client_kwargs when set, and otherwise from the tenant segment of the configured endpoints, which is where the documented configuration already puts it. Deployments that follow the documented setup therefore need no configuration change. A configuration that identifies no single tenant - the common, organizations or consumers endpoints - now raises rather than accepting tokens it cannot attribute to an issuer. That is a behaviour change for those deployments: they need to set tenant_id explicitly. The existing test that asserted the verification branch is reached by default now supplies a tenant-bearing endpoint, since tenant resolution happens before the key set is fetched. Generated-by: Claude Opus 5 (1M context) following the guidelines at https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions
eladkal
force-pushed
the
fab-azure-validate-issuer-audience
branch
from
August 18, 2026 13:07
09ff60a to
75ecbdd
Compare
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The Azure
id_tokensignature is verified against Microsoft's key set, but the decode call passes noclaims_options, so authlib'sclaims.validate()enforces neither the issuer nor the audience.The key set in use is the multi-tenant one —
login.microsoftonline.com/common/discovery/keys— which serves signing keys for every Azure tenant. A correctly-signed token from any tenant therefore satisfies the signature check, andget_oauth_user_info()then reads the login identity (oid,email,roles) straight out of it.What this changes
Both claims are now pinned:
issmust be the configured tenant, accepting the v1.0 (sts.windows.net/<tenant>/) and v2.0 (login.microsoftonline.com/<tenant>/v2.0) issuer forms, since either may be returned depending on which endpoints are configured.audmust be this application'sclient_id.The tenant is resolved from an explicit
tenant_idinclient_kwargswhen set, and otherwise from the tenant segment of the configured endpoints — which is where the documented configuration already puts it:Deployments following the documented setup need no configuration change.
Behaviour change worth reviewing
A configuration that identifies no single tenant — the
common,organizationsorconsumersendpoints — now raisesAirflowConfigExceptionrather than accepting tokens whose issuer it cannot check. Those deployments must settenant_idexplicitly.I chose fail-closed deliberately: a silent fallback would leave exactly the behaviour this PR is removing. But it is a startup-time break for multi-tenant configurations, so it is the main thing I would like a second opinion on. The alternative is to log loudly and continue, which I think is worse but is a defensible call.
Three things I would like your view on
tenant_idcomes from. I added it as an optionalclient_kwargskey and fall back to parsing the endpoints. Parsing-only would mean no new config surface at all; explicit-only would be cleaner but breaks every existing deployment. The current shape tries to get both.sts.windows.netentry should go.Also worth noting
_validate_jwt()(the Authentik path, same file) has the identicalauthlib_jwt.decode(id_token, keyset)shape with noclaims_options. Its blast radius is smaller because the Authentik JWKS is deployment-specific rather than multi-tenant, so there is no cross-issuer concern — butaudis still unchecked there. I left it out to keep this diff reviewable; happy to fold it in here or do it separately, whichever you prefer.Testing
Added coverage for tenant resolution (explicit, from
api_base_url, fromaccess_token_url, and the tenant-agnostic endpoints), for the fail-closed path, and for the claim options actually passed todecode. The existingtest_decode_and_validate_azure_jwt_verifies_signature_by_defaultneeded updating: tenant resolution now happens before the key set is fetched, so the mock had to grow a tenant-bearing endpoint.All 42 tests in
test_override.pypass locally.Generated-by: Claude Opus 5 (1M context) following the guidelines at
https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions