Skip to content

Add User Federated Identity Credential (user_fic) grant type support#1026

Open
Avery-Dunn wants to merge 3 commits intoavdunn/fmi-supportfrom
avdunn/fic-support
Open

Add User Federated Identity Credential (user_fic) grant type support#1026
Avery-Dunn wants to merge 3 commits intoavdunn/fmi-supportfrom
avdunn/fic-support

Conversation

@Avery-Dunn
Copy link
Copy Markdown
Contributor

@Avery-Dunn Avery-Dunn commented Apr 15, 2026

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 the user_fic grant type
  • UserFederatedIdentityCredentialParameters — 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) / .extraQueryParameters(Map) — extensibility

Token request behavior:

  • Sends grant_type=user_fic in the POST body
  • Sends user_federated_identity_credential=<T2> (the instance token from Leg 2)
  • Sends either user_id=<OID> or username=<UPN> (mutually exclusive, enforced by API design)
  • Augments scopes with openid, profile, offline_access (user-flow behavior, not app-flow)
  • Sends client_info=1 to receive account information in the response

Cache behavior:

  • Tokens are stored in the user token cache with full account information
  • Subsequent calls for the same user return cached tokens without hitting the IdP (unless forceRefresh=true)
  • Account lookup uses findCachedAccount() which matches by OID (homeAccountId prefix) or UPN (case-insensitive), with a single-account fallback for format mismatches
  • App-only tokens and user_fic tokens are correctly isolated — user_fic goes through the account-aware silent path, never the app-token lookup path

Internal changes:

  • AcquireTokenByUserFederatedIdentityCredentialSupplier — orchestrates cache-then-network logic, including findCachedAccount() for silent lookup
  • UserFederatedIdentityCredentialRequest — constructs the OAuth2 grant with correct parameters
  • GrantConstants.USER_FIC — new grant type constant
  • ConfidentialClientApplication.acquireToken(UserFederatedIdentityCredentialParameters) — routes to the new supplier
  • PublicApi.ACQUIRE_TOKEN_BY_USER_FEDERATED_IDENTITY_CREDENTIAL — telemetry enum

Tests:

  • UserFederatedIdentityCredentialTest.java — 19 unit tests covering:
    • Input validation (null/blank scopes, username, assertion, userObjectId)
    • Correct OAuth2 parameters sent in POST body (grant_type, user_fic, username/user_id, scopes)
    • Mutual exclusion of user_id vs username
    • ForceRefresh behavior (cache bypass)
    • Cache hit on second call (no network)
    • Multi-user cache isolation (two UPNs, two OIDs — correct token returned for each)
  • FicIT.java — 4 integration tests matching MSAL .NET's agentic FIC coverage (user_fic with UPN, user_fic with OID, cache isolation between app and user tokens, multi-user cache correctness)
  • AgenticIT.java — expanded with 2 additional FIC integration tests (agent acquiring user token for Graph, app+user token cache isolation)

Alignment with MSAL .NET

This implementation matches the user_fic behavior on MSAL .NET's main branch. Key equivalences:

.NET Java
AcquireTokenByUserFederatedIdentityCredential(scopes, username, assertion) acquireToken(UserFederatedIdentityCredentialParameters.builder(scopes, username, assertion).build())
WithForceRefresh(true) .forceRefresh(true)
User token cache storage Account-aware cache with findCachedAccount()
grant_type=user_fic + scope augmentation + client_info=1 Same

Differences from .NET (intentional)

Aspect .NET Java Rationale
User identification Only username (UPN) Both username and userObjectId (UUID) OID is needed for the full agent identity flow support, so Java added it with the rest of initial FIC support
Cache strategy Always hits IdP; developer must call AcquireTokenSilent separately Built-in cache-then-network in single call More convenient API; matches Java's OBO pattern
CCS routing header Sends datacenter routing hint Not sent Performance optimization only; can be added later

}

// ========================================================================
// §6: user_fic grant type
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strange that this starts from 6

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The agent wrote this and some other comments with references to sections in some docs and some behavior in .NET it was using as examples.

In the latest commit I've cleaned up all the comments to remove that sort of stuff.

Copy link
Copy Markdown
Contributor

@neha-bhargava neha-bhargava left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@Avery-Dunn Avery-Dunn changed the title First draft FIC support Add User Federated Identity Credential (user_fic) grant type support May 6, 2026
@Avery-Dunn Avery-Dunn marked this pull request as ready for review May 6, 2026 20:07
@Avery-Dunn Avery-Dunn requested a review from a team as a code owner May 6, 2026 20:07
@Avery-Dunn Avery-Dunn force-pushed the avdunn/fic-support branch from c94ca99 to 07bdbb7 Compare May 6, 2026 21:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants