Add User Federated Identity Credential (user_fic) grant type support#918
Open
Avery-Dunn wants to merge 2 commits into
Open
Add User Federated Identity Credential (user_fic) grant type support#918Avery-Dunn wants to merge 2 commits into
Avery-Dunn wants to merge 2 commits into
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds user federated identity credential (user_fic) token acquisition support for confidential clients and extends client assertion callbacks with request context for FMI scenarios.
Changes:
- Adds
acquire_token_by_user_federated_identity_credential()and low-levelobtain_token_by_user_fic(). - Adds context-aware client assertion callback dispatch.
- Updates token cache extended-key exclusions and adds protocol/cache/validation tests.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
msal/application.py |
Adds public confidential-client API and telemetry ID for user FIC acquisition. |
msal/oauth2cli/oauth2.py |
Adds assertion callback context handling and low-level user_fic grant request construction. |
msal/token_cache.py |
Excludes user FIC request fields from extended cache key calculation. |
tests/test_application.py |
Adds unit tests for user FIC protocol behavior, cache behavior, validation, and assertion callback context. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+825
to
+828
| data.update( | ||
| scope=scope, | ||
| user_federated_identity_credential=assertion, | ||
| client_info="1", |
Comment on lines
+179
to
+192
| """Check if a callable accepts at least one positional argument.""" | ||
| try: | ||
| sig = inspect.signature(func) | ||
| params = [ | ||
| p for p in sig.parameters.values() | ||
| if p.kind in ( | ||
| inspect.Parameter.POSITIONAL_ONLY, | ||
| inspect.Parameter.POSITIONAL_OR_KEYWORD, | ||
| ) | ||
| ] | ||
| return len(params) >= 1 | ||
| except (ValueError, TypeError): | ||
| return False # Signature not inspectable; treat as zero-arg | ||
|
|
| }) | ||
|
|
||
|
|
||
| import base64 |
Comment on lines
+70
to
+72
| "user_federated_identity_credential", | ||
| "user_id", | ||
| "client_info", |
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.
This PR adds the
user_fic(User Federated Identity Credential) grant type and related APIs to MSAL Go's confidential client, matching what was done in other MSALs.In addition, this PR also adds a new piece for FMI required by an (eventual) support for agent identity scenarios: passing
fmi_pathand other context to assertion callbacks so they can acquire the correct credentials.What's included
FMI completion — assertion callback context:
client_id,token_endpoint, andfmi_path(when set on the request)inspect.signature)BaseClient._accepts_context()/_invoke_assertion_callable()— new internal methods for dispatchFIC — new public API:
ConfidentialClientApplication.acquire_token_by_user_federated_identity_credential(scopes, assertion, username=None, user_object_id=None, claims_challenge=None)— acquires a user-scoped token using theuser_ficgrant typeFIC — token request behavior:
grant_type=user_ficin the POST bodyuser_federated_identity_credential=<assertion>(the instance token from Leg 2)user_id=<OID>orusername=<UPN>(mutually exclusive, validated at call time)openid,profile,offline_accessvia existing_decorate_scope()client_info=1to receive account information in the responseFIC — cache behavior:
AccessTokencredential type (notatext) — noext_cache_keyacquire_token_silent()with the returned account for subsequent cached accessSupporting changes:
user_federated_identity_credential,user_id, andclient_infoadded to_EXT_CACHE_KEY_EXCLUDED_FIELDSintoken_cache.py, ensuring FIC body parameters don't pollute the extended cache key hashACQUIRE_TOKEN_BY_USER_FIC_ID = "950"Internal changes:
BaseClient._accepts_context()— inspects callable signature to determine if it accepts contextBaseClient._invoke_assertion_callable()— dispatches to context-aware or zero-arg callableClient.obtain_token_by_user_fic()— new oauth2cli method that builds theuser_ficPOST bodyBaseClient._obtain_token()— updated to use_invoke_assertion_callable()for assertion dispatchTests (16 total):
FIC protocol tests (4):
test_sends_correct_grant_type_and_params— verifiesgrant_type=user_fic, credential param, client_info, client_idtest_scope_includes_oidc_scopes— verifies openid/offline_access/profile augmentationtest_with_username_sends_username_not_user_id— username param sent, user_id absenttest_with_oid_sends_user_id_not_username— user_id param sent, username absentFIC cache behavior tests (3):
test_token_stored_in_user_cache_with_account— token stored with populated account (home_account_id)test_token_not_stored_as_atext— FIC tokens use standard AccessToken type, no ext_cache_keytest_acquire_token_silent_returns_cached_fic_token—acquire_token_silentreturns cached FIC tokenFIC validation tests (5):
test_empty_assertion_raises— empty assertion rejectedtest_none_assertion_raises— None assertion rejectedtest_no_user_identifier_raises— missing user identification rejectedtest_both_user_identifiers_raises— both OID and UPN rejected (mutually exclusive)test_reserved_scopes_rejected— reserved OIDC scopes in input rejected (consistent with other APIs)Assertion callback context tests (4):
test_context_aware_callback_receives_fmi_path— one-arg callback receives client_id, token_endpoint, fmi_pathtest_context_aware_callback_omits_fmi_path_when_not_set— fmi_path absent from context when not on requesttest_legacy_zero_arg_callback_still_works— zero-arg callable invoked normallytest_context_callback_type_error_not_swallowed— internal TypeError in one-arg callback propagates (not masked by dispatch)