Skip to content

Implement UserFIC: IByUserFederatedIdentityCredential interface and user_fic grant type#5802

Merged
neha-bhargava merged 13 commits intomainfrom
copilot/add-federated-credential-provider
Mar 18, 2026
Merged

Implement UserFIC: IByUserFederatedIdentityCredential interface and user_fic grant type#5802
neha-bhargava merged 13 commits intomainfrom
copilot/add-federated-credential-provider

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 3, 2026

Adds the User Federated Identity Credential (UserFIC) flow — a new grant_type=user_fic token acquisition path that allows a confidential client to obtain user tokens by presenting a federated identity credential assertion instead of a password.

New public surface

  • IByUserFederatedIdentityCredential — interface on IConfidentialClientApplication exposing AcquireTokenByUserFederatedIdentityCredential(scopes, username, assertion) where assertion is a plain JWT string
  • AcquireTokenByUserFederatedIdentityCredentialParameterBuilder — fluent builder with WithForceRefresh, WithSendX5C

Wire protocol (new internal plumbing)

  • OAuth2GrantType.UserFic = "user_fic", OAuth2Parameter.UserFederatedIdentityCredential = "user_federated_identity_credential"
  • UserFederatedIdentityCredentialRequest — modeled after UsernamePasswordRequest; sets user_federated_identity_credential + username on the token request body, merges OIDC scopes, sends client_info=1, caches response in user token cache

Usage

Developers acquire the assertion themselves (from a Managed Identity, Confidential Client, or any other source) and pass the access token string directly:

// Acquire assertion first (e.g. via Managed Identity or Confidential Client)
var assertionResult = await app
    .AcquireTokenForClient(new[] { "api://AzureADTokenExchange/.default" })
    .ExecuteAsync();

// Pass the assertion string to AcquireTokenByUserFederatedIdentityCredential
var result = await (app as IByUserFederatedIdentityCredential)
    .AcquireTokenByUserFederatedIdentityCredential(
        scopes, "user@contoso.com", assertionResult.AccessToken)
    .ExecuteAsync();

Testing

6 unit tests in UserFederatedIdentityCredentialTests.cs covering: correct OAuth2 wire parameters (grant_type, username, user_federated_identity_credential), token cache storage, ForceRefresh behavior, and null/empty-argument guards on the builder.

Performance impact

None — new code path only; no changes to existing hot paths.

Documentation

  • All relevant documentation is updated.
Original prompt

Implement UserFIC: Add FederatedCredentialProvider helper factory and extended callback signature for assertion providers

Closes #5766 (sub-issue)

Overview

Implement the User Federated Identity Credential (UserFIC) flow in MSAL .NET, including a new IByUserFederatedIdentityCredential interface, FederatedCredentialProvider static factory, and all necessary plumbing to support token acquisition via grant_type=user_fic.


API to implement

1. IByUserFederatedIdentityCredential interface

namespace Microsoft.Identity.Client
{
    public interface IByUserFederatedIdentityCredential
    {
        AcquireTokenByUserFederatedIdentityCredentialParameterBuilder AcquireTokenByUserFederatedIdentityCredential(
            IEnumerable<string> scopes,
            string username,
            Func<AssertionRequestOptions, Task<string>> assertionProvider);
    }
}
  • AssertionRequestOptions is the existing type in Microsoft.Identity.Client — it is passed directly by users (not constructed by the library internally for this flow), to allow for extensibility and future requirements. Do not add new properties to AssertionRequestOptions for this feature.

2. FederatedCredentialProvider static factory

Create a new file: src/client/Microsoft.Identity.Client/AppConfig/FederatedCredentialProvider.cs

namespace Microsoft.Identity.Client
{
    public static class FederatedCredentialProvider
    {
        // Builds MI app internally from ManagedIdentityId
        public static Func<AssertionRequestOptions, Task<string>> FromManagedIdentity(
            ManagedIdentityId managedIdentityId,
            string audience = "api://AzureADTokenExchange/.default");

        // Uses Confidential Client with client credentials
        public static Func<AssertionRequestOptions, Task<string>> FromConfidentialClient(
            IConfidentialClientApplication cca,
            string audience = "api://AzureADTokenExchange/.default");
    }
}
  • FromManagedIdentity(ManagedIdentityId, audience?): Eagerly builds a ManagedIdentityApplication from the provided ManagedIdentityId (via ManagedIdentityApplicationBuilder.Create(...).Build()), then returns a delegate that calls AcquireTokenForManagedIdentity(audience).ExecuteAsync(ct) and returns result.AccessToken.
  • FromConfidentialClient(IConfidentialClientApplication, audience?): Returns a delegate that calls cca.AcquireTokenForClient(new[] { audience }).ExecuteAsync(ct) and returns result.AccessToken.
  • Both methods must throw ArgumentNullException on null arguments.
  • The audience defaults to "api://AzureADTokenExchange/.default".
  • The returned Func<AssertionRequestOptions, Task<string>> must propagate options.CancellationToken to the inner ExecuteAsync call.

3. Internal parameter class

namespace Microsoft.Identity.Client.ApiConfig.Parameters
{
    internal class AcquireTokenByUserFederatedIdentityCredentialParameters : IAcquireTokenParameters
    {
        public string Username { get; set; }
        public Func<AssertionRequestOptions, Task<string>> AssertionProvider { get; set; }
        public bool? SendX5C { get; set; }
        public bool ForceRefresh { get; set; }
    }
}

File: src/client/Microsoft.Identity.Client/ApiConfig/Parameters/AcquireTokenByUserFederatedIdentityCredentialParameters.cs

4. Parameter builder

File: src/client/Microsoft.Identity.Client/ApiConfig/AcquireTokenByUserFederatedIdentityCredentialParameterBuilder.cs

  • Inherit from AbstractConfidentialClientAcquireTokenParameterBuilder<AcquireTokenByUserFederatedIdentityCredentialParameterBuilder>
  • Include WithForceRefresh(bool), WithSendX5C(bool), WithClaims(string), WithCorrelationId(Guid), ExecuteAsync(CancellationToken) methods
  • A static internal Create(IConfidentialClientApplicationExecutor executor, IEnumerable<string> scopes, string username, Func<AssertionRequestOptions, Task<string>> assertionProvider) factory method

5. UserFederatedIdentityCredentialRequest — internal request class

File: src/client/Microsoft.Identity.Client/Internal/Requests/UserFederatedIdentityCredentialRequest.cs

Model this after the existing UsernamePasswordRequest. Key differences:

  • grant_type = user_fic (add UserFic constant to OAuth2GrantType)
  • Replace the password parameter with user_federated_identity_credential (add constant to OAuth2Parameter)
  • Invoke AssertionProvider(assertionRequestOptions) to get the assertion string, then set it as user_federated_identity_credential on the OAuth2 request body
  • Include openid, offline_access, profile in scopes (same as ROPC)
  • Send client_info=1 (same as ROPC)
  • Cache response in user token cache, return IAccount

6. Wire up ConfidentialClientApplication

  • ConfidentialClientApplication should implement IByUserFederatedIdentityCredential
  • IConfidentialClientApplication should extend `IByUserF...

This pull request was created from Copilot chat.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 2 commits March 3, 2026 20:02
…dentityCredential interface, and UserFederatedIdentityCredentialRequest

Co-authored-by: neha-bhargava <61847233+neha-bhargava@users.noreply.github.com>
… test

Co-authored-by: neha-bhargava <61847233+neha-bhargava@users.noreply.github.com>
Copilot AI changed the title [WIP] Implement FederatedCredentialProvider helper factory and interface Implement UserFIC: FederatedCredentialProvider factory, IByUserFederatedIdentityCredential interface, and user_fic grant type Mar 3, 2026
@neha-bhargava
Copy link
Copy Markdown
Contributor

@copilot Can you add integration tests using username ficuser@msidlabtse4.onmicrosoft.com and confidential client app with app id 979a25aa-0daf-41a5-bcad-cebec5c7c254. Same app id can be used for creating assertion as well as getting the token.

…crosoft.com

Co-authored-by: neha-bhargava <61847233+neha-bhargava@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 4, 2026

@copilot Can you add integration tests using username ficuser@msidlabtse4.onmicrosoft.com and confidential client app with app id 979a25aa-0daf-41a5-bcad-cebec5c7c254. Same app id ca...

Added integration tests in UserFederatedIdentityCredentialIntegrationTests.cs (commit 7654c5e) covering all three scenarios:

  1. Initial acquisition — calls IdP, verifies TokenSource == IdentityProvider and account username matches ficuser@msidlabtse4.onmicrosoft.com
  2. Silent cache hit — calls AcquireTokenSilent with the returned account, verifies TokenSource == Cache
  3. Force refresh — calls AcquireTokenByUserFederatedIdentityCredential with .WithForceRefresh(true), verifies TokenSource == IdentityProvider

All tests use app ID 979a25aa-0daf-41a5-bcad-cebec5c7c254 for both the assertion (via FederatedCredentialProvider.FromConfidentialClient) and the final user token, with the lab auth cert loaded via CertificateHelper.FindCertificateByName(TestConstants.AutomationTestCertName).

@neha-bhargava neha-bhargava marked this pull request as ready for review March 4, 2026 20:02
@neha-bhargava neha-bhargava requested a review from a team as a code owner March 4, 2026 20:02
…on in UserFIC integration tests

Co-authored-by: neha-bhargava <61847233+neha-bhargava@users.noreply.github.com>
@neha-bhargava
Copy link
Copy Markdown
Contributor

@copilot Update the user fix dev ex to remove the helper methods and class FederatedCredentialProvider.cs. The dev ex will require the developer to create an assertion and pass to AcquireTokenByUserFederatedIdentityCredential as a plain string. Update methods as well

…ain string assertion

Co-authored-by: neha-bhargava <61847233+neha-bhargava@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 5, 2026

@copilot Update the user fix dev ex to remove the helper methods and class [FederatedCredentialProvider.cs](https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/pul...

Done in commit 6093243. FederatedCredentialProvider is removed entirely. The AcquireTokenByUserFederatedIdentityCredential signature now accepts a plain string assertion. Developers acquire the assertion themselves (e.g. via AcquireTokenForClient) and pass the access token string directly. All tests updated accordingly.

Copilot AI changed the title Implement UserFIC: FederatedCredentialProvider factory, IByUserFederatedIdentityCredential interface, and user_fic grant type Implement UserFIC: IByUserFederatedIdentityCredential interface and user_fic grant type Mar 5, 2026
@neha-bhargava
Copy link
Copy Markdown
Contributor

@copilot remove the class UserFederatedIdentityCredentialIntegrationTests.cs

Co-authored-by: neha-bhargava <61847233+neha-bhargava@users.noreply.github.com>
auto-merge was automatically disabled March 18, 2026 21:37

Head branch was pushed to by a user without write access

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 18, 2026

@copilot remove the class UserFederatedIdentityCredentialIntegrationTests.cs

Removed in commit bd330b3.

@neha-bhargava neha-bhargava merged commit 3d45a47 into main Mar 18, 2026
12 checks passed
@neha-bhargava neha-bhargava deleted the copilot/add-federated-credential-provider branch March 18, 2026 23:49
This was referenced Mar 30, 2026
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.

[Feature Request] Add MSAL .NET API for User FIC

5 participants