Add FIC two-leg exchange over mTLS Proof-of-Possession (follow-up to #938)#939
Open
Robbie-Microsoft wants to merge 3 commits into
Open
Add FIC two-leg exchange over mTLS Proof-of-Possession (follow-up to #938)#939Robbie-Microsoft wants to merge 3 commits into
Robbie-Microsoft wants to merge 3 commits into
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Adds support for the optional two-leg Federated Identity Credential (FIC) token exchange over mTLS Proof-of-Possession, where a leg-1 mtls_pop token is presented as a jwt-pop client assertion over an mTLS connection bound to the same certificate to obtain the final token.
Changes:
- Introduces the
jwt-popclient assertion type constant and uses it for FIC leg-2 requests. - Extends
ConfidentialClientApplicationto detectclient_credential["mtls_binding_certificate"](FIC leg 2) and route leg-2 traffic over the mTLS token endpoint (including implicit Bearer-over-mTLS). - Adds unit/E2E coverage, docs, and a gated sample for the two-leg flow.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
msal/oauth2cli/oauth2.py |
Adds jwt-pop client assertion type constant for OAuth2 client assertions. |
msal/application.py |
Implements FIC leg-2 detection and mTLS routing; sets client_assertion_type to jwt-pop for leg-2. |
tests/test_application.py |
Adds unit tests validating jwt-pop assertion type and Bearer-over-mTLS behavior for FIC leg-2. |
tests/test_e2e.py |
Adds an E2E test for the two-leg FIC exchange over mTLS PoP (self-skipping when not configured). |
docs/index.rst |
Documents the FIC leg-2 mtls_binding_certificate configuration and jwt-pop behavior. |
sample/confidential_client_mtls_pop_sample.py |
Adds a gated sample function demonstrating the two-leg FIC flow over mTLS PoP. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
5339d9e to
dd4f13a
Compare
772ef58 to
614acc3
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
msal/application.py:892
- The new FIC leg-2 configuration requires passing a leg-1 access token as a string
client_assertion, but the constructor currently emits a DeprecationWarning for any static string/bytesclient_assertion. This makes the documented/expected FIC usage noisy (and can fail callers running with warnings-as-errors). Consider suppressing this warning whenmtls_binding_certificateis present (FIC leg 2).
# Warn if using a static string/bytes client_assertion (discouraged for long-running apps)
if isinstance(client_credential, dict) and isinstance(
client_credential.get("client_assertion"), (str, bytes)):
warnings.warn(
"Passing a static string/bytes 'client_assertion' is "
Stacked on top of the SN/I mTLS PoP base support. Enables a Federated Identity Credential (FIC) two-leg exchange over mutual-TLS: a leg-1 cert-bound mtls_pop token is presented as a jwt-pop client assertion over an mTLS connection bound by the same certificate to obtain the final token. - Detect the client_credential mtls_binding_certificate sub-key (FIC leg 2) and carry the leg-1 assertion as a jwt-pop client assertion over mTLS - Route every FIC leg-2 request over the mTLS endpoint, so even a Bearer token travels the cert-bound connection (implicit Bearer-over-mTLS) - Add unit + E2E coverage, docs, and a two-leg sample Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
A confidential client configured for a Federated Identity Credential (FIC) leg-2 exchange (a client_assertion plus an mtls_binding_certificate) is only meaningful through acquire_token_for_client, which presents the binding certificate over mutual-TLS and carries the leg-1 assertion as jwt-pop. If such an app is instead used with any other flow (authorization code, on-behalf-of, refresh token, username/password, or user-FIC), MSAL would send that cert-bound leg-1 assertion as an ordinary jwt-bearer over a non-mTLS connection. ESTS rejects it, but with a confusing server error. Add a small _reject_if_mtls_binding_cert guard and call it at the top of those flows so the misuse fails fast with a clear ValueError that points the caller to acquire_token_for_client. The guard is a no-op for every non-FIC-leg-2 app, so public clients and ordinary confidential clients are unaffected. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The _reject_if_mtls_binding_cert guard already covered the six non-client-credential acquisition flows, but not acquire_token_silent / acquire_token_silent_with_error. With a shared token cache holding user refresh tokens, a FIC leg-2 app (client_assertion + mtls_binding_certificate) could silently redeem one of those RTs, putting the cert-bound leg-1 assertion on a non-mTLS jwt-bearer request -- which ESTS rejects with a confusing invalid_client rather than a clear message. Add the guard to both public silent entry points. Both call the private _acquire_token_silent_with_error directly (not each other), and acquire_token_for_client also uses that private helper, so the guard lives on the two public wrappers -- never on the private method -- to keep the legitimate mTLS client-credential flow working. No-op for normal apps. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
614acc3 to
acd78ed
Compare
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.
Summary
Follow-up to #938. Adds the optional two-leg Federated Identity Credential (FIC) exchange over mTLS PoP: the leg-2 confidential client presents the leg-1 (cert-bound
mtls_pop) token as ajwt-popclient assertion over an mTLS connection bound by the same certificate, to obtain the final token.Usage
What changed (delta over #938)
msal/oauth2cli/oauth2.py—jwt-popclient-assertion-type constant.msal/application.py— detect themtls_binding_certificatecredential sub-key (FIC leg 2); carry the leg-1 assertion asjwt-pop; route every leg-2 request over the mTLS endpoint (even a plain Bearer travels the cert-bound connection); reject a leg-2 app on non–client-credential / silent flows and reject a string assertion without a binding cert; FIC-aware error messages.test_application.py; a self-skipping E2E case intest_e2e.py. Docstrings +docs/index.rstFIC bullet;fic_two_leg_over_mtls_pop()sample.Full unit suite passes (base + FIC); no regressions.