diff --git a/src/Microsoft.Identity.Web.TokenAcquisition/TokenAcquisition.cs b/src/Microsoft.Identity.Web.TokenAcquisition/TokenAcquisition.cs index 28b32b7fa..9b45a442f 100644 --- a/src/Microsoft.Identity.Web.TokenAcquisition/TokenAcquisition.cs +++ b/src/Microsoft.Identity.Web.TokenAcquisition/TokenAcquisition.cs @@ -820,6 +820,11 @@ private async Task BuildConfidentialClientApplic builder.WithRedirectUri(currentUri); } + // ClientCapabilities are applied once during CCA construction + // (see UpdateConfidentialClientApplicationOptionsFromMergedOptions). + // We rely on that path. if it ever regresses the unit test + // (CrossCloudFicUnitTest) will fail. + string authority; if (mergedOptions.PreserveAuthority && !string.IsNullOrEmpty(mergedOptions.Authority)) diff --git a/tests/E2E Tests/OidcIdPSignedAssertionProviderTests/OidCIdPSignedAssertionProviderExtensibilityTests.cs b/tests/E2E Tests/OidcIdPSignedAssertionProviderTests/OidCIdPSignedAssertionProviderExtensibilityTests.cs index 6f587feea..75c3ff2c2 100644 --- a/tests/E2E Tests/OidcIdPSignedAssertionProviderTests/OidCIdPSignedAssertionProviderExtensibilityTests.cs +++ b/tests/E2E Tests/OidcIdPSignedAssertionProviderTests/OidCIdPSignedAssertionProviderExtensibilityTests.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; @@ -60,6 +62,19 @@ public async Task CrossCloudFicIntegrationTest() // Assert Assert.NotNull(result); Assert.StartsWith("Bearer", result, StringComparison.Ordinal); + + // Decode token & verify xms_cc + string jwt = result["Bearer ".Length..].Trim(); + + var handler = new JwtSecurityTokenHandler(); + var token = handler.ReadJwtToken(jwt); + + var xmsCcValues = token.Claims + .Where(c => c.Type == "xms_cc") + .Select(c => c.Value) + .ToArray(); + + Assert.Contains("cp1", xmsCcValues); } //[Fact(Skip ="Does not run if run with the E2E test")] @@ -96,6 +111,7 @@ public async Task CrossCloudFicUnitTest() options.Instance = "https://login.microsoftonline.com/"; options.TenantId = "t2"; options.ClientId = "c2"; + options.ClientCapabilities = ["cp1"]; options.ExtraQueryParameters = null; options.ClientCredentials = [ new CredentialDescription() { SourceType = CredentialSource.CustomSignedAssertion, @@ -121,6 +137,28 @@ public async Task CrossCloudFicUnitTest() Assert.Equal("c2", tokenRequestHttpHandler.ActualRequestPostData["client_id"]); Assert.Equal("https://login.microsoftonline.com/t2/oauth2/v2.0/token", tokenRequestHttpHandler.ActualRequestMessage?.RequestUri?.AbsoluteUri); + // First request (credential exchange) – should have *no* "claims" + Assert.False(credentialRequestHttpHandler.ActualRequestPostData + .ContainsKey("claims")); + + // Second request (real token acquisition) – must carry "claims" + Assert.True(tokenRequestHttpHandler.ActualRequestPostData + .ContainsKey("claims")); + + // Extract and inspect the JSON payload + string claimsJson = tokenRequestHttpHandler.ActualRequestPostData["claims"]; + + using JsonDocument doc = JsonDocument.Parse(claimsJson); + + string? cp = doc.RootElement + .GetProperty("access_token") + .GetProperty("xms_cc") + .GetProperty("values")[0] + .GetString(); + + // Ensure that the client capabilities are passed in the claims + Assert.Equal("cp1", cp); + string? accessTokenFromRequest1; using (JsonDocument document = JsonDocument.Parse(credentialRequestHttpHandler.ResponseString)) { diff --git a/tests/E2E Tests/OidcIdPSignedAssertionProviderTests/appsettings.json b/tests/E2E Tests/OidcIdPSignedAssertionProviderTests/appsettings.json index 07aeb14f0..2d7bd7b7d 100644 --- a/tests/E2E Tests/OidcIdPSignedAssertionProviderTests/appsettings.json +++ b/tests/E2E Tests/OidcIdPSignedAssertionProviderTests/appsettings.json @@ -3,8 +3,9 @@ "AzureAd": { "Instance": "https://login.microsoftonline.com/", "TenantId": "msidlab4.onmicrosoft.com", - "ExtraQueryParameters": { "dc": "ESTS-PUB-WEULR1-AZ1-FD000-TEST1" }, + "ExtraQueryParameters": { "dc": "ESTS-PUB-WEULR1-AZ1-FD000-TEST1" }, "ClientId": "5e71875b-ae52-4a3c-8b82-f6fdc8e1dbe1", // this app is configured to trust credentials (tokens) from f6b698c0-140c-448f-8155-4aa9bf77ceba + "ClientCapabilities": [ "cp1" ], "ClientCredentials": [ { "SourceType": "CustomSignedAssertion",