feat(auth): accept trusted IdP tokens without local provisioning in trust mode - #6750
Open
jonpspri wants to merge 5 commits into
Open
feat(auth): accept trusted IdP tokens without local provisioning in trust mode#6750jonpspri wants to merge 5 commits into
jonpspri wants to merge 5 commits into
Conversation
jonpspri
added this pull request to stack #6729
September 10, 2026 02:58
jonpspri
marked this pull request as ready for review
September 10, 2026 02:58
jonpspri
requested review from
Lang-Akshay,
brian-hussey,
crivetimihai,
ja8zyjits and
msureshkumar88
as code owners
September 10, 2026 02:58
jonpspri
force-pushed
the
feat/5903-external-idp-trust-root
branch
from
September 10, 2026 06:23
8920db4 to
d058fb3
Compare
jonpspri
removed this pull request from stack #6729
September 12, 2026 08:50
jonpspri
force-pushed
the
feat/5903-external-idp-trust-root
branch
from
September 12, 2026 09:07
d058fb3 to
1770eb7
Compare
jonpspri
added this pull request to stack #6798
September 12, 2026 09:08
jonpspri
force-pushed
the
feat/5903-external-idp-trust-root
branch
2 times, most recently
from
September 12, 2026 09:48
1a75ab1 to
5b2727c
Compare
jonpspri
force-pushed
the
feat/5903-external-idp-trust-root
branch
from
September 12, 2026 09:52
5b2727c to
874b77b
Compare
jonpspri
force-pushed
the
feat/5903-external-idp-trust-root
branch
from
September 12, 2026 16:45
874b77b to
9b15009
Compare
jonpspri
force-pushed
the
feat/5903-external-idp-trust-root
branch
from
September 12, 2026 17:20
9b15009 to
55a8cdf
Compare
jonpspri
force-pushed
the
feat/5903-external-idp-trust-root
branch
2 times, most recently
from
September 12, 2026 17:54
03ef307 to
2a7ced6
Compare
jonpspri
force-pushed
the
feat/5903-external-idp-trust-root
branch
from
September 12, 2026 18:09
2a7ced6 to
a46ea83
Compare
jonpspri
force-pushed
the
feat/5903-external-idp-trust-root
branch
from
September 12, 2026 18:33
a46ea83 to
faf453c
Compare
jonpspri
force-pushed
the
feat/5903-external-idp-trust-root
branch
from
September 12, 2026 18:52
faf453c to
7101c19
Compare
jonpspri
force-pushed
the
feat/5903-external-idp-trust-root
branch
from
September 12, 2026 19:12
7101c19 to
f8c768e
Compare
…rust mode build_external_identity branches on trust mode: when jwt_trust_mode is "jwt-trust" and the issuer is a configured trust root, the identity is built from verified claims via extract_trusted_principal. No INSERT or SELECT against email_users. The revocation claim gate, the three overage policies, and the token-hash identity cache apply to the claims-derived path. The B.1 dispatch-matrix branch-(b) row flips from strict-xfail to green. Default mode is unchanged. Signed-off-by: Jonathan Springer <jps@s390x.com>
…he authentication choke point In JWT trust mode, get_current_user() now consults the external-IdP verifier before the internal one. Dispatch is decided by the token's unverified iss claim (read only for routing): a configured trust root authenticates via the existing verify_external_idp_token -> build_trusted_external_identity chain and the payload flows into the token_use=trusted branch; a trust-root token that fails verification definitively raises fail-closed (401) and never falls through to the internal funnel; any other issuer falls through exactly as before. The fail-closed distinction is expressed by ExternalIssuerVerificationError and an opt-in fail_closed flag on _maybe_verify_external; existing callers keep the legacy fall-through-on-any-failure behavior. Signed-off-by: Jonathan Springer <jps@s390x.com>
…s claim With non-Entra trust roots live (#5903), the claim-collision guard in validate_security_combinations now rejects jwt_claim_teams aliasing ANY of sso_entra_groups_claim, sso_keycloak_groups_claim, or sso_generic_groups_claim, compared case-insensitively, naming both JWT_CLAIM_TEAMS and the offending variable. Signed-off-by: Jonathan Springer <jps@s390x.com>
… matrix The harness serves OIDC discovery and JWKS over TLS from a generated RSA key (uvicorn in a thread, ephemeral port) plus a stub A2A agent endpoint, with a mint_token helper and an idempotent ensure-material CLI for the gateway's SSL_CERT_FILE. The black-box matrix seeds a trust root, team, team-visible agent, and group mapping through the admin API and pins the ingress contract: mapped user -> 200, unmapped user -> 403 (RBAC deny), wrong audience -> 401, missing revocation claim -> 401, nonexistent agent with role -> 404. The two rows blocked by the TokenScopingMiddleware membership check are xfail(strict=False) pending the trusted-team exemption in #5904. Signed-off-by: Jonathan Springer <jps@s390x.com>
With the ingress dispatch in place, the barrier scenario's 401 for the valid Entra token is re-pinned as the correct by-design outcome: no providers are seeded, so the Entra issuer is not a configured trust root and the token falls through to the internal verifier. The seeded-issuer denial paths (403 without mapping, 404 with role) are covered by the external-issuer ingress matrix. Fake-token control unchanged. Signed-off-by: Jonathan Springer <jps@s390x.com>
jonpspri
force-pushed
the
feat/5903-external-idp-trust-root
branch
from
September 12, 2026 21:11
b3db2f8 to
e4e1929
Compare
This was referenced Sep 13, 2026
Collaborator
Author
|
Requirement note (remediation) — the ingress fix This PR now wires the dispatch that the review found missing (finding F1):
Also on this PR: the collision guard widened to all three provider claims; the local OIDC issuer harness; the five-row live matrix; the barrier test re-pinned to the fall-through contract. Live results: mapped 200, unmapped 403, wrong audience 401, missing revocation claim 401, nonexistent agent 404. See the notes on #5896 and #5903. |
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.
build_external_identityand_maybe_verify_externalnow branch on trust mode: whenjwt_trust_mode="jwt-trust"and the issuer is a configured trust root (SSOProvider.trusted_for_api_auth+api_audience), the identity builds from verified claims viaextract_trusted_principal—token_use="trusted",authenticate_or_create_userprovisioning skipped, noget_user_by_email, no DBis_admin. JWKS validation and iss/aud checks are unchanged; default-mode provisioning is untouched. External group IDs map throughresolve_external_groups_to_teams; overage tokens dispatch onjwt_trust_overage_policy(graph_lookup uses the #5977 app-only client). A trust-eligible token missing the configured revocation claim (jtidefault,utisupported) is rejected.invalidate_external_identity_cachecovers the claims-derived path (token-hash key, tested).The B.1 dispatch-matrix external-IdP row flips from strict-xfail to green in this commit — the matrix file now contains zero xfail markers. The flip required one added line (
jwt_trust_modemonkeypatch) so the row exercises what its own contract states.Tested with:
uv run pytest tests/unit/mcpgateway/utils/test_external_idp_trust_mode.py -q— 9 passed (TDD: 8 failed first — the provisioning path ran). Includes the SQLAlchemybefore_cursor_executerecorder proof: ZEROINSERTand ZEROSELECTagainstemail_userson the trust path; teams/roles from seeded mapping rows with no user row;is_adminfrom the mapped claim.uv run pytest tests -k "external_idp or sso_token" -q— 86 tests, 0 failuresuv run pytest tests/unit/mcpgateway/test_token_dispatch_matrix.py -q— 6 passed, zero xfailmake ruff— all checks passed;make test— 23343 passed, 879 skipped, 2 xfailed (the two pre-existing plugin rows — the last trust xfails are gone)Acceptance criteria of #5903 are met. Risk to existing users: none — default mode byte-identical.
Stack: B.11 of epic #5885 (base: #6749).
Closes #5903