AzureAD / microsoft-authentication-library-for-dotnet

Microsoft Authentication Library (MSAL) for .NET
https://aka.ms/msal-net
MIT License
1.36k stars 332 forks source link

[Bug] Unable to authenticate with error "Unable to determine the caller." when using DefaultAzureCredential() and obtained an access token with custom scope. #4490

Closed GuangMo closed 5 months ago

GuangMo commented 6 months ago

Library version used

1.10.4

.NET version

.NET Framework 4.6.2

Scenario

ManagedIdentityClient - managed identity

Is this a new or an existing app?

This is a new app or experiment

Issue description and reproduction steps

I am writing code for a client application, where it is hosted on Azure App Service with managed identity on, this client needs to authenticate with an App Registration/Enterprise App, which has a customed scope. I use "DefaultAzureCredential()" to perform authentication, I have specified many different ways for "TokenRequestContext", some to include tenantId, some to use resourceId +"/.default" as scope, some to specify the full customed scope, I am always able to get the access token, but once I used the token to authenticate header and make the actual request, it fails with 401 "Unable to determine the caller." This is the only error message, and there is no document and nothing on the internet I can debug further.

Do note that I have done the same auth against this same app registration/enterprise app with certificate or user/password auth via ConfidentialClientApplicationBuilder() and PublicClientApplicationBuilder(), it works just fine, this only fails with I use managed identity to auth.

Relevant code snippets

// TokenRequestContext tokenRequestContext = new TokenRequestContext(new string[1] { this.Resource + "/.default" }, null, null, "<xxxx Tenant ID> xxxx", isCaeEnabled: false);
                       // TokenRequestContext tokenRequestContext = new TokenRequestContext(new string[] { "xxxxx customed scope xxxx " });
                        var tokenRequestContext = new TokenRequestContext(
                            scopes: new[]
                            {
                                "xxxxx customed scope xxxx",
                            },
                            tenantId: "xxxxx tenant id xxxxxxx");

                        var authToken = await credential.GetTokenAsync(tokenRequestContext).ConfigureAwait(false);

                        return new AuthenticationHeaderValue("Bearer", authToken.Token);

Expected behavior

should authenticate success and give me response since the enterprise application has granted the client permissions.

Identity provider

Microsoft Entra ID (Work and School accounts and Personal Microsoft accounts)

Regression

No response

Solution and workarounds

No response

neha-bhargava commented 6 months ago

Looks like you are using Microsoft.Identity.Web. Can you open the issue here

GuangMo commented 6 months ago

Seriously? How is this identity.web? It is Azure.identity.core lib, and this behavior exhibits in all stack, whether it is a website or console app. I am going to reopen

bgavrilMS commented 6 months ago

You are right @GuangMo , this is Azure.Identity, which internally uses MSAL (this lib) for most of its operations.

Managed Identity can be seen as "certificate-less" version of client_credentials flow, i.e. service to service authentication. Or in Azure.Identity terms, ClientCertificateCredential. As such, the access token is for a service principal, not for a user.

I would expect that the access tokens obtained via Managed Identity (e.g. via ManagedIdentityCredential) and via Certificate or Secret to be identical. Is this not the case? You can have a look at the tokens using https://jwt.ms/

Second, you say this client needs to authenticate with an App Registration/Enterprise App, which has a customed scope - do you own this app? Or is it one of Microsoft's ? How did you expose this custom scope? Note that the "Expose an API" tab in the EntraID App Configuration portal controls only delegated scopes, i.e. user scopes.

image
GuangMo commented 6 months ago

hey @bgavrilMS , I am able to get access token with managed identity, there is no issue at all. the problem is it returns the above error once I performed authentication with the token. The error is not documented anywhere across the internet, it is not so good for customers and users.

I do not own this enterprise app, and yes, there is where the scope is exposed. It is not using "api//", which it is what I considered as default scope, it is customed such as "http://xxxx/user_impersonate". Note that the moment I am able to obtained the access token, it means the authority/tenant has been able to locate this customed scope, and issued a token on behalf of this scope, the problem is the auth failure, and this only happens to managed identity (Azure Arc, and Azure managed identity/system identity).

bgavrilMS commented 6 months ago

The error comes from the downstream API. It's most likely expecting a user token, but you are sending a service principal (S2S) token.