Add User Federated Identity Credential (user_fic) grant type support#1026
Add User Federated Identity Credential (user_fic) grant type support#1026Avery-Dunn wants to merge 8 commits into
user_fic) grant type support#1026Conversation
neha-bhargava
left a comment
There was a problem hiding this comment.
Overall looks good. I would like to see some end to end tests as well where an assertion is built and passed to user fic. The test setup is up and working and used in MISE PR.
user_fic) grant type support
c94ca99 to
07bdbb7
Compare
07bdbb7 to
ebf788b
Compare
ebf788b to
17cdda5
Compare
neha-bhargava
left a comment
There was a problem hiding this comment.
Minor comment, otherwise looks good
| for (IAccount account : accounts) { | ||
| // Match by OID — homeAccountId format is "oid.tid" | ||
| if (userObjectId != null && account.homeAccountId() != null | ||
| && account.homeAccountId().startsWith(userObjectId.toString())) { |
There was a problem hiding this comment.
UPN match below is equalsIgnoreCase but OID match here is case-sensitive startsWith on oid.tid. Should be case-insensitive equality on the OID segment, no?
There was a problem hiding this comment.
The code now splits homeAccountId on "." and compares the OID segment with equalsIgnoreCase, consistent with the UPN matching.
| class UserFicIT { | ||
|
|
||
| // Same config as AgenticIT | ||
| private static final String BLUEPRINT_CLIENT_ID = "aab5089d-e764-47e3-9f28-cc11c2513821"; |
There was a problem hiding this comment.
All these constants are duplicated accross test files. If there is a change in tenant or app id these will need to be updated in all places. Can we have these in a single constants file?
There was a problem hiding this comment.
Moved all shared agentic/FMI/FIC constants to TestConstants.java. All three integration test classes (FmiIT, AgenticIT, UserFicIT) now reference TestConstants.AGENTIC_* instead of local declarations.
There was a problem hiding this comment.
Pull request overview
Adds MSAL4J support for the user_fic (User Federated Identity Credential) grant type in ConfidentialClientApplication, enabling agent identity “Leg 3” user-scoped token acquisition with built-in cache-then-network behavior.
Changes:
- Introduces new public API
acquireToken(UserFederatedIdentityCredentialParameters)plus the corresponding parameters/request types anduser_ficgrant constants/telemetry. - Adds
AcquireTokenByUserFederatedIdentityCredentialSupplierto orchestrate cache lookup (silent) and fallback to network (user_fic) acquisition. - Adds unit + integration tests covering request construction, caching semantics, and multi-user isolation.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| msal4j-sdk/src/test/java/com/microsoft/aad/msal4j/UserFederatedIdentityCredentialTest.java | New unit tests validating body params, scope augmentation, caching, and multi-user behavior. |
| msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/UserFederatedIdentityCredentialRequest.java | New request type building the user_fic OAuth grant parameters. |
| msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/UserFederatedIdentityCredentialParameters.java | New public parameters + builder for user_fic requests. |
| msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/PublicApi.java | Adds telemetry enum for the new public API. |
| msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/IConfidentialClientApplication.java | Adds new public interface method for user_fic token acquisition. |
| msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/GrantConstants.java | Adds user_fic grant type + parameter constants. |
| msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/ConfidentialClientApplication.java | Routes new API call into request execution. |
| msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AcquireTokenByUserFederatedIdentityCredentialSupplier.java | Implements cache-then-network orchestration for the new flow. |
| msal4j-sdk/src/main/java/com/microsoft/aad/msal4j/AbstractApplicationBase.java | Wires request type to new supplier. |
| msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/UserFicIT.java | New integration tests for end-to-end user_fic agent identity scenarios. |
| msal4j-sdk/src/integrationtest/java/com/microsoft/aad/msal4j/AgenticIT.java | Expands agent identity integration coverage to include user_fic flows and cache isolation. |
17cdda5 to
09ea0f5
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
msal4j-sdk/src/test/java/com/microsoft/aad/msal4j/UserFederatedIdentityCredentialTest.java:530
- Local variables in this test use snake_case (e.g., alice_oid/alice_upn/alice_token, bob_oid/...). MSAL4J tests generally use Java-style camelCase for local variables; consider renaming to improve consistency and readability.
String alice_oid = "oid-alice-1111";
String alice_upn = "alice@contoso.com";
String alice_token = "access-token-alice";
String bob_oid = "oid-bob-2222";
This PR adds support for the
user_fic(User Federated Identity Credential) grant type to MSAL Java, enabling Leg 3 of the agent identity protocol. This allows an agent application to exchange a federated identity credential (obtained from Leg 2's instance token) for a user-scoped access token, enabling the agent to act on behalf of a specific user.What's included
New public APIs:
IConfidentialClientApplication.acquireToken(UserFederatedIdentityCredentialParameters)— acquires a user-scoped token using theuser_ficgrant typeUserFederatedIdentityCredentialParameters— parameter object with builder pattern, supporting:.builder(scopes, username, assertion)— identify user by UPN.builder(scopes, userObjectId, assertion)— identify user by Object ID (UUID).forceRefresh(boolean)— bypass cache and hit IdP.tenant(String)— override tenant.claims(ClaimsRequest)— claims challenge support.extraHttpHeaders(Map)— additional HTTP headers.extraQueryParameters(Map)— (deprecated) present only to satisfyIAcquireTokenParametersinterface; scheduled for removalToken request behavior:
grant_type=user_ficin the POST bodyuser_federated_identity_credential=<T2>(the instance token from Leg 2)user_id=<OID>orusername=<UPN>(mutually exclusive, enforced by API design)openid,profile,offline_access(user-flow behavior, not app-flow)client_info=1to receive account information in the responseOAuthAuthorizationGrantconstructor, ensuring correct merging withclientCapabilitiesinTokenRequestExecutor(rather than being silently overwritten)Cache behavior:
forceRefresh=true)findCachedAccount()which matches by OID (homeAccountId prefix, case-insensitive) or UPN (case-insensitive)tokenCache().getAccounts(clientId)to avoid potential deadlock when the app runs on a single-thread executor (the previous approach ofgetAccounts().get()submitted work to the same executor)claims,tenant,extraHttpHeaders, andextraQueryParametersfrom the original parameters are propagated to theSilentParametersInternal changes:
AcquireTokenByUserFederatedIdentityCredentialSupplier— orchestrates cache-then-network logic, including synchronousfindCachedAccount()with case-insensitive OID/UPN matching and parameter propagation to silent requestsUserFederatedIdentityCredentialRequest— constructs the OAuth2 grant with correct parameters; uses 3-argOAuthAuthorizationGrantconstructor for proper claims handlingGrantConstants.USER_FIC— new grant type constantConfidentialClientApplication.acquireToken(UserFederatedIdentityCredentialParameters)— routes to the new supplierPublicApi.ACQUIRE_TOKEN_BY_USER_FEDERATED_IDENTITY_CREDENTIAL— telemetry enumTests:
UserFederatedIdentityCredentialTest.java— 21 unit tests covering:UserFicIT.java— 5 integration tests covering:AgenticIT.java— 6 integration tests (shared with FMI), including 2 FIC-specific tests:Alignment with MSAL .NET
This implementation matches the
user_ficbehavior on MSAL .NET'smainbranch. Key equivalences:AcquireTokenByUserFederatedIdentityCredential(scopes, username, assertion)acquireToken(UserFederatedIdentityCredentialParameters.builder(scopes, username, assertion).build())WithForceRefresh(true).forceRefresh(true)WithExtraQueryParameters(inherited from base builder, old overload deprecated).extraQueryParameters(Map)(deprecated on parameters, satisfies interface)findCachedAccount()grant_type=user_fic+ scope augmentation +client_info=1OAuthAuthorizationGrantfor proper mergingDifferences from .NET (intentional)
username(UPN)usernameanduserObjectId(UUID)AcquireTokenSilentseparatelytokenCache().getAccounts(clientId)extraQueryParametersdeprecationDictionary<string,string>overload deprecated; newIDictionary<string,(string,bool)>availableextraQueryParametersAPI deprecated (matching Java-wide pattern)