Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 29, 2025

Fixes the issue where tenant override is not propagated to the credential FIC token acquisition in OidcIdpSignedAssertionProvider.GetClientAssertionAsync.

Description

This PR implements a temporary workaround to extract tenant information from AssertionRequestOptions.TokenEndpoint until MSAL adds native support for TenantId property in AssertionRequestOptions (tracked in MSAL PR #5573).

Changes Made

  • Added logic to extract tenant from AssertionRequestOptions.TokenEndpoint when it matches the configured cloud instance
  • The tenant is extracted only when the cloud instances match (e.g., both from login.microsoftonline.com), preserving cross-cloud scenarios
  • When a tenant override is specified via AcquireTokenOptions.Tenant, it flows through to the TokenEndpoint, and this fix propagates it to the credential FIC acquisition
  • Made ExtractTenantFromTokenEndpointIfSameInstance method internal for testability
  • Added comprehensive unit tests for the ExtractTenantFromTokenEndpointIfSameInstance method covering same instance, cross-cloud, null/empty inputs, invalid URIs, and OAuth2 pattern validation scenarios
  • Added E2E test AutonomousAgentWithTenantOverride_GetsAppTokenForAgentIdentityAsync as a Theory with InlineData for "common" and "organizations" scenarios to validate tenant override functionality

Technical Details

  • The TokenEndpoint in AssertionRequestOptions contains the tenant for the current request (e.g., https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token)
  • If there's a tenant override, MSAL uses the overridden tenant in the TokenEndpoint
  • The fix extracts this tenant and passes it to AcquireTokenOptions.Tenant for the credential FIC acquisition
  • To preserve cross-cloud scenarios, tenant extraction only happens when the TokenEndpoint host matches the configured instance host
  • Note: This implementation works for eSTS. Different logic may be needed for dSTS in the future.

Testing

  • ✅ Existing E2E tests pass (including cross-cloud scenarios)
  • ✅ Existing unit tests pass
  • ✅ Added 17 new unit tests for tenant extraction logic
  • ✅ Added E2E test for tenant override with common/organizations authority that validates the tid claim in issued tokens
Original prompt

This section details on the original issue you should resolve

<issue_title>Tenant not propagated in OidcIdpSignedAssertionProvider credential FIC acquisition</issue_title>
<issue_description>## Problem

In FIC-based agent identity acquisition flows in Microsoft. Identity.Web, the AcquireTokenOptions.Tenant property is honored throughout most FIC token exchanges (both agent application and agent identity), but not when acquiring the credential FIC token used for the underlying client assertion. This occurs in OidcIdpSignedAssertionProvider.GetClientAssertionAsync. As a result, if a developer specifies a tenant override, it is properly used for agent-based FIC tokens, but not for the credential FIC token acquisition.

Current/Expected Code Paths

timeline
    title Tenant propagation during federated credential acquisition
    section AgentUserIdentityMsalAddIn
      Agent App FIC Token  : options.Tenant ✔️
      Agent Identity FIC Token  : options.Tenant ✔️
      Final User Token (MSAL)  : inherited from previous
    section OidcIdpSignedAssertionProvider
      Credential FIC Token (current)   : Tenant ❌ (not set)
      Credential FIC Token (expected)  : Tenant ✔️ (from AssertionRequestOptions)
Loading

Technical Detail

Current:

if (assertionRequestOptions != null && !string.IsNullOrEmpty(assertionRequestOptions.ClientAssertionFmiPath))
{
    acquireTokenOptions = new AcquireTokenOptions()
    {
        FmiPath = assertionRequestOptions.ClientAssertionFmiPath
        // ❌ Tenant is NOT set
    };
}

Should be:

if (assertionRequestOptions != null && !string. IsNullOrEmpty(assertionRequestOptions.ClientAssertionFmiPath))
{
    acquireTokenOptions = new AcquireTokenOptions()
    {
        FmiPath = assertionRequestOptions.ClientAssertionFmiPath,
        Tenant = assertionRequestOptions.TenantId // ✔️ propagate tenant
    };
}

(Similarly, always propagate TenantId from assertionRequestOptions if present.)

Impact

  • Tenant override (cross-tenant scenarios) do not work for the underlying federated credential assertion acquisition, which breaks consistency and expected usage. Tokens acquired for the wrong tenant can cause authentication failures or unintended access grants.

Solution Proposal

  • Update OidcIdpSignedAssertionProvider.GetClientAssertionAsync to always propagate TenantId from assertionRequestOptions to AcquireTokenOptions for credential FIC acquisition.

Table: Tenant propagation across FIC token acquisitions

Component Current Tenant Behavior Expected Tenant Behavior
Agent App FIC Acquisition options.Tenant used options.Tenant used
Agent Identity FIC options.Tenant used options.Tenant used
Credential FIC (OidcIdp...) ❌ Not propagated ✔️ Propagate from AssertionRequestOptions

Architectural flow for reference

flowchart TD
    A[Set options.AcquireTokenOptions.Tenant] --> B[OnBeforeUserFicForAgentUserIdentityAsync]
    B --> C[Agent App FIC Token (Tenant OK)]
    B --> D[Agent Identity FIC Token (Tenant OK)]
    D --> E[OidcIdpSignedAssertionProvider (Credential FIC)]
    E --> |Current| F[Tenant not set]
    E --> |Expected| G[Tenant set from AssertionRequestOptions]
Loading

Confidence

High (95%+): The source of the problem is clearly traced and fix is isolated to a single point. The proper tenant propagation already works elsewhere in the flow.

Next Steps

  • Confirm the fix
  • Implement (single-line change in OidcIdpSignedAssertionProvider.cs)
  • Add a regression test

Co-author: Bridge (with @jmprieur review)
</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot finished work on behalf of jmprieur November 29, 2025 18:34
@jmprieur jmprieur marked this pull request as ready for review November 30, 2025 02:08
@jmprieur jmprieur requested a review from a team as a code owner November 30, 2025 02:08
Copy link
Collaborator

@jmprieur jmprieur left a comment

Choose a reason for hiding this comment

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

Once the e2e tests move to id4slab1 we'll add one with the common authority and forcing the tenant to be set in AcquireaTokenOptions

/// <param name="tokenEndpoint">Token endpoint URL in the format https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token</param>
/// <param name="configuredInstance">The configured instance URL (e.g., https://login.microsoftonline.com/)</param>
/// <returns>The tenant ID if the endpoint is from the same instance, otherwise null.</returns>
private static string? ExtractTenantFromTokenEndpointIfSameInstance(string? tokenEndpoint, string? configuredInstance)
Copy link
Collaborator

Choose a reason for hiding this comment

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

@copilot please create a unit test for this new method (and have it internal so that the unit test can call it)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Made the method internal and added comprehensive unit tests covering:

  • Same instance scenarios returning the correct tenant
  • Different instance (cross-cloud) scenarios returning null
  • Null/empty input handling
  • Invalid URI handling
  • Missing tenant in path scenarios
  • OAuth2 pattern validation

Commit: 249b67c

Copilot AI changed the title [WIP] Fix tenant not propagated in credential FIC acquisition Fix tenant not propagated in credential FIC acquisition Dec 1, 2025
Copilot AI requested a review from jmprieur December 1, 2025 05:34
Copilot finished work on behalf of jmprieur December 1, 2025 05:34
Copy link
Member

@bgavrilMS bgavrilMS left a comment

Choose a reason for hiding this comment

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

Ok as a temporary fix, but waiting for MSAL is probably better.

@jmprieur
Copy link
Collaborator

jmprieur commented Dec 1, 2025

I agree. When can we have it in msal, @bgavrilMS ?. (It's blocking a partner)

Copy link
Collaborator

@jmprieur jmprieur left a comment

Choose a reason for hiding this comment

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

@copilot in

configuration["AzureAd:ClientId"] = "d05619c9-dbf2-4e60-95fd-cc75dd0db451"; // Agent application.
, could we have another similar end to end test by for which the configuration["AzureAd:TenantId"] is set to "common" and the tenant is overriden before calling CreateAuthorizationHeaderForAppAsync (by options.AcquireTokenOptions.Tenant = "31a58c3b-ae9c-4448-9e8f-e9e143e800df")

Ideally a Theory with Inlines?

Copy link
Contributor Author

Copilot AI commented Dec 4, 2025

@copilot in

configuration["AzureAd:ClientId"] = "d05619c9-dbf2-4e60-95fd-cc75dd0db451"; // Agent application.
, could we have another simil...

Added a Theory test AutonomousAgentWithTenantOverride_GetsAppTokenForAgentIdentityAsync with InlineData for "common" and "organizations" tenants. The test:

  • Sets configuration["AzureAd:TenantId"] to the configured tenant (common or organizations)
  • Overrides the tenant with options.AcquireTokenOptions.Tenant = "31a58c3b-ae9c-4448-9e8f-e9e143e800df"
  • Validates the token was issued for the overridden tenant by checking the tid claim

Commit: c69733f

Copilot AI requested a review from jmprieur December 4, 2025 04:09
Copilot finished work on behalf of jmprieur December 4, 2025 04:09
@jmprieur jmprieur merged commit b496a26 into master Dec 4, 2025
5 checks passed
@jmprieur jmprieur deleted the copilot/fix-tenant-propagation-issue branch December 4, 2025 14:41
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.

Tenant not propagated in OidcIdpSignedAssertionProvider credential FIC acquisition

3 participants