{
"$type": "site.standard.document",
"canonicalUrl": "https://johnnyreilly.com/posts/azure-container-apps-easy-auth-and-dotnet-authentication",
"description": "Azure Container Apps support Easy Auth. However, .NET applications run in ACAs do not recognise Easy Auth authentication. This post explains the issue and solves it.",
"path": "/posts/azure-container-apps-easy-auth-and-dotnet-authentication",
"publishedAt": "2023-06-11T00:00:00.000Z",
"site": "at://did:plc:yy3apqjlms24kso7ahn7lbmb/site.standard.publication/3mova7c4nho2b",
"tags": [
"azure",
"azure container apps",
"easy auth",
"asp.net",
"auth"
],
"textContent": "Easy Auth is a great way to authenticate your users. However, when used in the context of Azure Container Apps, .NET applications do not, by default, recognise that Easy Auth is in place. You might be authenticated but .NET will still act as if you aren't. builder.Services.AddAuthentication() and app.UseAuthentication() doesn't change that. This post explains the issue and solves it through the implementation of an AuthenticationHandler.\n\n\n\nIf you're looking for information about Easy Auth and roles with .NET and Azure App Service, you can find it here:\n\n- Azure App Service, Easy Auth and Roles with .NET\n- Azure App Service, Easy Auth and Roles with .NET and Microsoft.Identity.Web\n\nUpdated 09/04/2025 On-behalf-of auth and X-MS-TOKEN-AAD-ACCESS-TOKEN\n\nYou'll see later in the post that I berate the lack of a X-MS-TOKEN-AAD-ACCESS-TOKEN in Azure Container Apps with EasyAuth. Great news! Whilst when I first wrote this, it wasn't available. As of the microsoft.app/2025-01-01/containerapps release, it is.\n\nFor details on how to use it, read David's post.\n\nUser.Identity.IsAuthenticated == false\n\nWhen I'm building an application, I want to focus on the problem I'm solving. I don't want to think about how to implement my own authentication system. Rather, I lean on an auth provider for that, and if I'm working in the Azure ecosystem, that often means Easy Auth, usually with Azure AD.\n\nI recently started building a .NET application using Easy Auth and deploying to Azure Container Apps. One thing that surprised me when I tested it out was that, whilst I was being authenticated, my app didn't seem to be aware of it. When I inspected the User.Identity.IsAuthenticated property in my application, it was false. The reason why lies in the documentation:\n\n> For all language frameworks, Container Apps makes the claims in the incoming token available to your application code. The claims are injected into the request headers, which are present whether from an authenticated end user or a client application. External requests aren't allowed to set these headers, so they're present only if set by Container Apps. Some example headers include:\n>\n> X-MS-CLIENT-PRINCIPAL-NAME\n>\n> X-MS-CLIENT-PRINCIPAL-ID\n>\n> _Code that is written in any language or framework can get the information that it needs from these headers._\n\nThe emphasis above is mine. What it's saying here is this: you need to implement this yourself.\n\nEasy Auth Azure Container App headers\n\nSure enough, when I inspected the headers in my application, I could see these:\n\nThe X-MS-CLIENT-PRINCIPAL header is particularly interesting. From the appearance, you might assume it's a JWT. It's not. It's actually a base 64 encoded JSON string that represents the signed in user and their claims. It's actually super easy to decode in your browser devtools:\n\nIf you decode it, you'll see something like this:\n\nGiven that this information is present, what we can do is tell .NET about it.\n\nBut before we do that, let's pause to talk about a current limitation with Easy Auth on Azure Container Apps; JWT support.\n\nLack of JWT / Token support\n\nThe problem is, there's not JWT token that we can make use of in the headers of an Azure Container App. This is supported in App Service which has a token store and supports the following headers:\n\nIf there was a populated X-MS-TOKEN-AAD-ACCESS-TOKEN then it would unlock all manner of possibilities. Let's say I want to make use of the Graph API on behalf of my logged in user. I cannot.\n\nReading the docs I believe I should be trying to use the \"on behalf of\" approach:\n\nBut because we have no token to exchange, we can't make use of delegated permissions in the Graph API. This is a current limitation of using Easy Auth and Azure Container Apps. Keep an eye on this GitHub issue if this is interesting to you.\n\nImplementing AddAzureContainerAppsEasyAuth()\n\nWe're going to implement an AuthenticationHandler that takes the information from the X-MS-CLIENT-PRINCIPAL header and uses it to create a ClaimsPrincipal:\n\nThere's a few things to note from the above:\n\n- EasyAuthAuthenticationHandler is an AuthenticationHandler that takes the information from the X-MS-CLIENT-PRINCIPAL header and uses it to create a ClaimsPrincipal.\n- The MsClientPrincipal class is a representation of the decoded X-MS-CLIENT-PRINCIPAL header.\n- AddAzureContainerAppsEasyAuth is an extension method for the AuthenticationBuilder object - this allows users to make use of the handler in their application.\n- With Easy Auth, role claims arrive in the custom \"roles\" claim. This is somewhat non-standard and so we remap \"roles\" claims to be ClaimTypes.Role / \"http://schemas.microsoft.com/ws/2008/06/identity/claims/role\" claims as well. This should ensure that anything built with the expectation of that type of claim behaves in the way you'd expect.\n\nUsing AddAzureContainerAppsEasyAuth()\n\nNow we've written our handler, we can use it in our application. We do this by calling AddAzureContainerAppsEasyAuth() in our Program.cs:\n\nNow when we run our application, we'll see that User.Identity.IsAuthenticated is true when we're authenticated in Azure Container Apps!\n\nEasy Auth differs Azure App Service, Azure Container Apps, Azure Static Web Apps and Azure Functions\n\nOne thing that became very clear to me as I worked on this, is that Easy Auth is implemented differently in Azure App Service, Azure Container Apps, Azure Static Web Apps and Azure Functions. Whilst the authentication appears to be the same, the headers are different across the services. So the code above will work in Azure Container Apps; for other Azure services I can't vouch for it.\n\nThe code in this post is very similar to that in MaximeRouiller.Azure.AppService.EasyAuth. But it's not quite the same, as that library depends upon a WEBSITE_AUTH_ENABLED environment variable, which isn't present in Azure Container Apps.\n\nLikewise, the Microsoft.Identity.Web package supports Easy Auth, but for Azure App Service. If you take a look at the code, you'll see it is powered by environment variables and headers, which aren't present in Azure Container Apps.\n\nSo whilst it would be tremendous if this was built into .NET, or in a NuGet package somewhere. I'm not aware of one at the time of writing, so I made this. Perhaps this should become a NuGet package? Let me know if you think so!\n\nI've also raised a feature request in the Microsoft.Identity.Web repo to support Azure Container Apps. If you'd like to see this, please upvote it!",
"title": "Azure Container Apps, Easy Auth and .NET authentication"
}