6

I'm trying to get service-to-service authentication working in Azure AD. I have it working when the two services (webapps) are running locally against an Azure AD app registration. But when the webapps are running in Azure (against the same Azure AD app registration), it doesn't work.

I've created two Azure App Service webapps (which are ASP.NET Core Web API projects) - one acting as the client and the other the server. The bit that breaks is the client webapp, where it requests an access token from AzureAD. It only breaks when requested a token with a custom scope. This same 'custom scope' token request works fine from a local webapp though.

In Azure, I've created an 'App Registration', and given it a custom scope called 'bob'...

enter image description here

In the client ASP.NET Core webapi app, I've added the Azure.Identity nuget package, and am getting a token like this...

var tokenRequestContext = new TokenRequestContext(new[] {scope});

token = await new DefaultAzureCredential().GetTokenAsync(tokenRequestContext, cancellationToken);

(where the scope variable matches the name from the screenshot above)

Locally, this works fine and returns a token.

In Azure, I get this exception:

Azure.Identity.AuthenticationFailedException: DefaultAzureCredential failed to retrieve a token from the included credentials.
- ClientSecretCredential authentication failed: AADSTS70011: The provided request must include a 'scope' input parameter. The provided value for the input parameter 'scope' is not valid. The scope api://<guid>/bob is not valid.

(I've removed the GUID from that snippet)

When running this locally and in Azure, I'm setting the AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID environment variables to the same value. I don't understand why it would work differently locally than in Azure, given these environment variables are the same.

Note that my intention is to use a Managed Service Identity instead of setting those environment variables. But for the time being, by using those environment variables both locally and in Azure - both should be using the same "ClientSecretCredential" authentication - so I'd expect the same results.

Dan
  • 5,692
  • 3
  • 35
  • 66

2 Answers2

6

There are 2 parts of the answer:

Azure.Identity is currently meant to be ONLY for the native Azure Services SDK using new Azure.* client libraries. In your case, you are trying to use it for your custom web app auth with custom scope which is not officially supported or documented. For regular auth flow (like in this case Client Credential Grant for example) You should be still using MSAL, OR for easier integration with asp.net core apps, Microsoft Identity Web.

NOTE: Specifically for Client Credential Flow, it only allows .default scope. There is an open UserVoice and another thread for workaround.

Now coming to your problem of why it's is not working in Azure.Identity DefaultAzureCredential (regardless of the fact it's not officially supported). I suspect at your deployed Web App, it's probably one or more context missing (IDENTITY_ENDPOINT or AZURE_AUTHORITY_HOST would be my wild guess looking at code). But no idea unless we do little trial and error (again, I would only want to do it for experimental purpose, not in a real production application due to lack of official support). The DefaultAzureCredential attempts to authenticate via the following mechanisms in order. In your local machine, it might be able to get the required context from one of mechanisms down in the chain (like VS or CLI) whereas in the deployed app, those are simply non-existent.

krishg
  • 5,935
  • 2
  • 12
  • 19
  • Thankyou @krishnendughosh-msft. It feels like if it's only meant for the native Azure Services SDK - this should be the first things mentioned in the documentation. This would have saved myself and a colleague a lot of time yesterday. Both my local hosted webapp and the Azure hosted webpp are using the "Environment" box in your diagram (ie. "ClientSecretCredential"). I can see that in the error I pasted into my initial post for the Azure side, and then locally if I comment out the env variables, it falls back to interactive browser. If I don't, then it doesn't do the interactive browser. (1/2) – Dan Sep 08 '20 at 05:46
  • Looking at the microsoft-identity-web library - for what I want, it looks like it works in a similar way with a call to `GetAccessTokenForAppAsync` to get the token. Does this have the same fallback mechanism - were it can use a MSI in Azure, but when running locally, fall back to the things in your diagram? Will have a try later this morning, then accept your answer. Thanks again :) (2/2) – Dan Sep 08 '20 at 05:52
  • @Dan, thanks for the feedback. Though in the first line of [Azure.Identity](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/identity/Azure.Identity/README.md) already says "The Azure Identity library provides Azure Active Directory token authentication support across the Azure SDK. It provides a set of TokenCredential implementations which can be used to construct Azure SDK clients which support AAD token authentication.". But I will definitely take this feedback to see if we can update to highlight it in more pronounced way (1/2) – krishg Sep 08 '20 at 05:55
  • microsoft-identity-web cannot do MSI. It neither has fallback mechanism like Azure.Identity. For MSI, please check [this](https://docs.microsoft.com/azure/app-service/overview-managed-identity?tabs=dotnet#obtain-tokens-for-azure-resources) or use Azure.Identity like you were doing. But problem here is you are trying 'custom' scope for your own resource. [MSI](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview) is **officially** for Azure native resources so far. Though people have written blog posts for own API using MSI you can search for (2/2) – krishg Sep 08 '20 at 06:39
  • Thanks, that's really useful. This is just a spike, and just trying to work out the best way we should be doing this. We probably don't even need to use custom scopes - just trying to understand them, as they seem a key part of registering custom applications. As in, adding a client id in the "Expose an API" section REQUIRES to you create a scope. But neither Azure.Identity, MSAL.NET, or Microsoft.Identity.Web then allow me to specify that scope when trying to get the access token. – Dan Sep 08 '20 at 09:42
  • To be specific you meant neither allows custom scope for MSI, right? In regular case (non-MSI), MSAL and Microsoft.Identity.Web definitely do allow custom scope.. – krishg Sep 08 '20 at 10:09
  • No, it's not working even without MSI. Eg. currently trying `AcquireTokenForClient` from MSAL .NET (using client secret). This works if I pass in the scope as "api:///.default". But if I pass in as the scope "api:///", it says the scope is not valid. I get similar errors using all three libraries mentioned. I'm sure there's something dumb I'm doing - it's just not obvious what :( – Dan Sep 08 '20 at 10:21
  • Ahh I see. Scope should be like "https://.onmicrosoft.com//". AAD portal will show you directly, just copy paste. Let me update the answer to include a sample screenshot – krishg Sep 08 '20 at 10:31
  • Thanks for the screenshot. In the exact same place in my portal, it's in the format "api:///". – Dan Sep 08 '20 at 10:44
  • I'm basically seeing the exact same issue described here: https://stackoverflow.com/questions/57147646/daemon-apps-and-scopes. Except that the accepted answer is what I'm trying to do, but doesn't work. The other answer shows the exact problem I'm getting. – Dan Sep 08 '20 at 10:45
  • Infact, looking at the screenshot in the accepted answer in my previous link, it's also in the "api://guid/scope" format there too - so matches what I'm seeing. – Dan Sep 08 '20 at 10:47
  • Ok. That's interesting. I think that's because you have an explicit app URI assigned, so format is different than my screenshot. – krishg Sep 08 '20 at 10:57
  • 1
    I think I was wrong in that point. Client Credential Flow specifically only allows ***.default*** scope. There is an open [UserVoice](https://feedback.azure.com/forums/169401-azure-active-directory/suggestions/38363230-v2-0-client-credentials-implement-scopes) and another [thread](https://stackoverflow.com/a/58717318/13791953). But for sure, for user login case like Authorization Grant Flow, it surely supports custom scopes which I used myself. – krishg Sep 08 '20 at 11:10
  • 1
    Thank you. That makes sense. So custom scopes are only for user logins, and you can't use them for server-to-server. The link you posted explains nicely how to add the appRoles to the manifest - this is working well. Thanks again for all your help with this. – Dan Sep 08 '20 at 11:43
  • Thank you. One thing which is still puzzling me is you mentioned in the original question **"This same 'custom scope' token request works fine from a local webapp though"**. So I am trying to understand now how it is. Or something I am missing. – krishg Sep 08 '20 at 12:45
  • Sorry for my slow reply. Nope, you weren't missing anything - I saw it work using custom scope locally using the Azure.Identity library and the "client secret" environment variable method, but exactly the same environment variables didn't work when hosted in Azure. – Dan Sep 10 '20 at 06:00
  • 1
    It's fine though - we didn't actually need to use scopes - was just trying to work out how it works, and why scopes weren't working. I'm now just using app roles instead, which works perfectly :) – Dan Sep 10 '20 at 06:01
0

While explicitly stated - you can use the .default claim as hinted at here: https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#the-default-scope.

This will let you generate an access token for your API but it will not have any other scopes for you to verify.

Travis Sharp
  • 821
  • 11
  • 26