Home > other >  User.Identity no longer set in Azure web app with Authentication enabled
User.Identity no longer set in Azure web app with Authentication enabled

Time:01-11

I'm migrating a .NET 4.8 application to .NET Core. The current app runs on Azure and uses Microsoft as identity provider for authentication (set up on the Authentication page of the azure app, nothing in code, see picture below), so that any user in the Azure AD can access it. The authentication works fine, when you're not already logged in with an Azure AD account you are redirect to the login.microsoftonline.com page.

azure settings

The app contains a simple piece of code that always worked in .NET 4.8

@if (Request.IsAuthenticated)
{
    <strong>@User.Identity.Name</strong>
}
else
{
    <div><i>Not logged in</i></div>
}

(developers can also use this app locally, in that case authentication is not required)

I am now ready to test the new app on Azure, but I see that User.Identity is not populated, I attached a remote debugger and saw that all properties are empty (no claims, an Identity with default values). I found a number of topics that also report this issue, but most of them were already using some code. I think I may be missing some piece of startup code, but I haven't able to figure out what it is. Currently I have

// ConfigureServices
services.AddAuthentication();
services.Configure<IdentityOptions>(options => 
            options.ClaimsIdentity.UserIdClaimType = ClaimTypes.NameIdentifier); // found this somewhere as solution, but didn't work so it's probably not required

// Configure
app.UseAuthentication();
app.UseAuthorization();

So to summarize:

  • Azure web app with Microsoft identity provider (Azure AD) set up in the Azure portal works fine in .NET 4.8 (MVC5)
  • Same app, same settings, but migrated to .NET Core no longer has the User.Identity property set

CodePudding user response:

After more digging around I found this post with the same issue. So it turns out that it is caused by ASP.NET Core that works in a different way. One of the answers contained a piece of code that solved the problem, I used that to create a new simple middleware:

public static class AzureAuthExtensions
{
    /// <summary>
    /// Enables support for Azure EasyAuth, so that User.Identity.Name is populated correctly.
    /// </summary>
    public static IApplicationBuilder UseAzureEasyAuth(this IApplicationBuilder app)
    {
        if (app == null)
        {
            throw new ArgumentNullException(nameof(app));
        }

        return app.UseMiddleware<AzureEasyAuthMiddleware>();
    }
}

public class AzureEasyAuthMiddleware
{
    private readonly RequestDelegate _next;

    public AzureEasyAuthMiddleware(RequestDelegate next)
    {
        _next = next ?? throw new ArgumentNullException(nameof(next));
    }

    /// <summary>
    /// Invoke the middleware.
    /// </summary>
    /// <param name="context">The <see cref="HttpContext"/>.</param>        
    public Task Invoke(HttpContext context)
    {
        // Create a user on current thread from provided header
        if (context.Request.Headers.ContainsKey("X-MS-CLIENT-PRINCIPAL-ID"))
        {
            // Read headers from Azure
            var azureAppServicePrincipalIdHeader = context.Request.Headers["X-MS-CLIENT-PRINCIPAL-ID"][0];
            var azureAppServicePrincipalNameHeader = context.Request.Headers["X-MS-CLIENT-PRINCIPAL-NAME"][0];

            // Create claims id
            var claims = new Claim[] {
                new System.Security.Claims.Claim("http://schemas.microsoft.com/identity/claims/objectidentifier", azureAppServicePrincipalIdHeader),
                new System.Security.Claims.Claim("name", azureAppServicePrincipalNameHeader)
            };

            // Set user in current context as claims principal
            var identity = new GenericIdentity(azureAppServicePrincipalNameHeader);
            identity.AddClaims(claims);

            // Set current thread user to identity
            context.User = new GenericPrincipal(identity, null);
        };

        return _next(context);
    }
}

This works like a charm!

  •  Tags:  
  • Related