microsoft-identity-web
microsoft-identity-web copied to clipboard
Calling Downstream Web API fails when using ADFS.
Microsoft.Identity.Web Library
Microsoft.Identity.Web
Microsoft.Identity.Web version
2.15.1
Web app
Not Applicable
Web API
Protected web APIs call downstream web APIs
Token cache serialization
In-memory caches
Description
When using EnableTokenAcquisitionToCallDownstreamApi
with ADFS, an error occurs. (See Error Message section below).
An example application that reproduces this error can be found here.
The error is throw from this section of code in the BaseAbstractApplicationBuilder
class.
The error occurs when attempting to call the downstream Web API.
Reproduction steps
- Clone https://github.com/robv8r/example-react-app
- Setup ADFS with appropriate settings
- Replace appsettings values with those from ADFS.
Error message
MSAL.NetCore.4.56.0.0.MsalClientException:
ErrorCode: tenant_override_non_aad
Microsoft.Identity.Client.MsalClientException: Cannot use WithTenantId() in the application builder, because the authority Adfs doesn't support it
at Microsoft.Identity.Client.BaseAbstractApplicationBuilder`1.ResolveAuthority()
at Microsoft.Identity.Client.AbstractApplicationBuilder`1.BuildConfiguration()
at Microsoft.Identity.Client.ConfidentialClientApplicationBuilder.BuildConcrete()
at Microsoft.Identity.Client.ConfidentialClientApplicationBuilder.Build()
at Microsoft.Identity.Web.TokenAcquisition.BuildConfidentialClientApplication(MergedOptions mergedOptions)
at Microsoft.Identity.Web.TokenAcquisition.GetOrBuildConfidentialClientApplication(MergedOptions mergedOptions)
at Microsoft.Identity.Web.TokenAcquisition.GetAuthenticationResultForUserAsync(IEnumerable`1 scopes, String authenticationScheme, String tenantId, String userFlow, ClaimsPrincipal user, TokenAcquisitionOptions tokenAcquisitionOptions)
at Microsoft.Identity.Web.DefaultAuthorizationHeaderProvider.CreateAuthorizationHeaderForUserAsync(IEnumerable`1 scopes, AuthorizationHeaderProviderOptions downstreamApiOptions, ClaimsPrincipal claimsPrincipal, CancellationToken cancellationToken)
at Microsoft.Identity.Web.DownstreamApi.CallApiInternalAsync(String serviceName, DownstreamApiOptions effectiveOptions, Boolean appToken, HttpContent content, ClaimsPrincipal user, CancellationToken cancellationToken)
at Microsoft.Identity.Web.DownstreamApi.CallApiForUserAsync[TOutput](String serviceName, Action`1 downstreamApiOptionsOverride, ClaimsPrincipal user, CancellationToken cancellationToken)
at ExampleReactApp.Server.Controllers.DownstreamVersionController.Get(IDownstreamApi api, CancellationToken token) in C:\pj\git\example-react-app\src\server\Controllers\DownstreamVersionController.cs:line 14
at lambda_method6(Closure, Object)
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
HEADERS
=======
Accept: application/json, text/plain, */*
Connection: close
Host: localhost:5173
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.47
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Authorization: Bearer eyJ0...
Referer: https://localhost:5173/
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: same-origin
sec-ch-ua-platform: "Windows"
sec-ch-ua-mobile: ?0
DNT: 1
sec-ch-ua: "Microsoft Edge";v="117", "Not;A=Brand";v="8", "Chromium";v="117"
Id Web logs
No response
Relevant code snippets
[ApiController]
[Route("[controller]")]
public class DownstreamVersionController : ControllerBase
{
[HttpGet]
public async Task<DownstreamVersionInfo?> Get([FromServices] IDownstreamApi api, CancellationToken token)
{
return await api.CallApiForUserAsync<DownstreamVersionInfo>(
"api",
options =>
{
options.HttpMethod = HttpMethod.Get.Method;
options.RelativePath = $"api/Version/Get";
},
cancellationToken: token)
.ConfigureAwait(false);
}
}
Regression
No response
Expected behavior
Because ADFS has a fixed tenant (adfs), the Microsoft.Identity library should not throw an exception due to WithTenantId()
being unsupported.
Please note that the example application targets net8.0. The issue described above is also present in net7.0 using VS 2022 v17.7.4.
The ADFS server is running on Windows Server 2019.
@robv8r : did you try to specify the "Authority" directly in the configuration file?
@jmprieur - I've tried many different configuration options. This morning, I attempted to specify the Authority in the appsettings.Development.json.
Old
"AzureAd": {
"Instance": "https://login.companyname.com/",
"ClientId": "Enter_the_Confidential_Client_Id_Here",
"ClientSecret": "Enter_the_Confidential_Client_Secret_here",
"TenantId": "adfs"
}
New
"AzureAd": {
"Authority": "https://login.companyname.com/adfs",
"ClientId": "Enter_the_Confidential_Client_Id_Here",
"ClientSecret": "Enter_the_Confidential_Client_Secret_here"
}
This results in an invalid "v2.0" URL segment when attempting to retrieve metadata.
Message: IDX20803: Unable to obtain configuration from: 'https://login.companyname.com/adfs/v2.0/.well-known/openid-configuration'.
The original error is still present.
Microsoft.Identity.Web.TokenAcquisition[300]
[MsIdWeb] An error occured during token acquisition: IDW10501: Exception acquiring token for a confidential client.
MSAL.NetCore.4.56.0.0.MsalClientException:
ErrorCode: tenant_override_non_aad
Microsoft.Identity.Client.MsalClientException: Cannot use WithTenantId() in the application builder, because the authority Adfs doesn't support it
at Microsoft.Identity.Client.BaseAbstractApplicationBuilder`1.ResolveAuthority()
at Microsoft.Identity.Client.AbstractApplicationBuilder`1.BuildConfiguration()
at Microsoft.Identity.Client.ConfidentialClientApplicationBuilder.BuildConcrete()
at Microsoft.Identity.Client.ConfidentialClientApplicationBuilder.Build()
at Microsoft.Identity.Web.TokenAcquisition.BuildConfidentialClientApplication(MergedOptions mergedOptions)
fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
MSAL.NetCore.4.56.0.0.MsalClientException:
ErrorCode: tenant_override_non_aad
Microsoft.Identity.Client.MsalClientException: Cannot use WithTenantId() in the application builder, because the authority Adfs doesn't support it
at Microsoft.Identity.Client.BaseAbstractApplicationBuilder`1.ResolveAuthority()
at Microsoft.Identity.Client.AbstractApplicationBuilder`1.BuildConfiguration()
at Microsoft.Identity.Client.ConfidentialClientApplicationBuilder.BuildConcrete()
at Microsoft.Identity.Client.ConfidentialClientApplicationBuilder.Build()
at Microsoft.Identity.Web.TokenAcquisition.BuildConfidentialClientApplication(MergedOptions mergedOptions)
at Microsoft.Identity.Web.TokenAcquisition.GetOrBuildConfidentialClientApplication(MergedOptions mergedOptions)
at Microsoft.Identity.Web.TokenAcquisition.GetAuthenticationResultForUserAsync(IEnumerable`1 scopes, String authenticationScheme, String tenantId, String userFlow, ClaimsPrincipal user, TokenAcquisitionOptions tokenAcquisitionOptions)
at Microsoft.Identity.Web.DefaultAuthorizationHeaderProvider.CreateAuthorizationHeaderForUserAsync(IEnumerable`1 scopes, AuthorizationHeaderProviderOptions downstreamApiOptions, ClaimsPrincipal claimsPrincipal, CancellationToken cancellationToken)
at Microsoft.Identity.Web.DownstreamApi.CallApiInternalAsync(String serviceName, DownstreamApiOptions effectiveOptions, Boolean appToken, HttpContent content, ClaimsPrincipal user, CancellationToken cancellationToken)
at Microsoft.Identity.Web.DownstreamApi.CallApiForUserAsync[TOutput](String serviceName, Action`1 downstreamApiOptionsOverride, ClaimsPrincipal user, CancellationToken cancellationToken)
at ExampleReactApp.Server.Controllers.DownstreamVersionController.Get(IDownstreamApi api, CancellationToken token) in C:\pj\git\example-react-app\src\server\Controllers\DownstreamVersionController.cs:line 14
at lambda_method6(Closure, Object)
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
Please let me know if I've misunderstood your recommendation, or if there are other steps I should try.
Thank you, Rob
I'm still unable to use EnableTokenAcquisitionToCallDownstreamApi with ADFS.
Is ADFS officially supported? If so, what's the best way to address this issue?
Thank you, Rob
My previous comment may have been missed.
Does this repository officially support ADFS? If so, what's the best way to address this issue?
Thank you.
Not officially, but we know some customers made it work. We have not prioritized work for Microsoft.Identity.Web to support ADFS (Microsoft.Identity.Web is about supporting Azure AD = Microsoft Entra ID, Azure AD B2C, and Microsoft Entra external IDs
Per the documentation, ADFS is supported. Can you point me to documentation listing officially supported products?
[Edit]
The documentation includes instructions for connecting to ADFS directly from MSAL.
@robv8r I came acorss exact same issue as you today and found a workaround. Just specify the Instance to "https://{your adfs service name}/adfs" in ConfidentialClientApplicationOptions . During debug, I can see the "TenantId" will be set to null which will not trigger the error anymore.
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(options =>
{
Configuration.Bind("Adfs", options);
options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = false }; // This is to disable the tenant ID verification in ADFS login scenario
options.SaveTokens = true; // This is to support sending "id_token_hint" in log out redirect to ADFS
})
.EnableTokenAcquisitionToCallDownstreamApi(
options =>
{
Configuration.GetSection("TodoList:Scopes").Get<string[]>();
options.Instance = "https://fs.contoso.com/adfs";
}
)
.AddInMemoryTokenCaches();
@jmprieur @jennyf19 - I think we should fix this in MSAL ? We can just have WithTenantId
be no-op for ADFS. This avoids higher level APIs having to do "if ADFS ... do stuff".
Sure @bgavrilMS Let's do that/
@bgavrilMS do you have an ETA on when this will be fixed in MSAL?
Will be fixed by adopting MSAL 4.61.1+ (not yet released)
https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/pull/4753