After migrating a Blazor Server App project from dotnet 5 to dotnet 6, I'm encountering a weird problem with authentication.
The app is using local username and password for authentication.
In ConfigureServices(), we have
services.AddDefaultIdentity<IdentityUser>
(options =>
{
options.SignIn.RequireConfirmedAccount = true;
// attempt to force name claim to "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"
options.ClaimsIdentity.UserNameClaimType = System.Security.Claims.ClaimTypes.Name;
})
.AddRoles<IdentityRole>()
.AddUserManager<UserManager<IdentityUser>>()
.AddRoleManager<AspNetRoleManager<IdentityRole>>()
.AddEntityFrameworkStores<ApplicationDbContext>();
Now, this has been working fine in .net 5, both in the dev environment, and also in Azure App Services (windows / iis )
After upgrading to .net 6, the following is observed:
- Dev environment (launched using "Project" ): Everything works as before (including authentication)
- Prod environment (Azure App Service) : Authentication is broken. When someone logs in, 'Identity.IsAuthenticated' is true, but 'Identity.Name' is null.
I traced the problem to the following:
- in Dev, the claims are as follows:
2022-05-17 12:46:54.190 +12:00 [INF] Claim http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier = 7f041757-3aad-4221-971a-729598c1d81e
2022-05-17 12:46:54.190 +12:00 [INF] Claim http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name = superadmin@local
2022-05-17 12:46:54.190 +12:00 [INF] Claim http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress = someone@somewhere.com
2022-05-17 12:46:54.190 +12:00 [INF] Claim AspNet.Identity.SecurityStamp = OJLEYFWG4JE4ND5NRPTRT4JVCVEE2ANR
2022-05-17 12:46:54.191 +12:00 [INF] Claim http://schemas.microsoft.com/ws/2008/06/identity/claims/role = SuperAdmin
2022-05-17 12:46:54.191 +12:00 [INF] Claim amr = pwd
But in Prod (Azure App services), the claims are as follows
2022-05-17 01:16:12.467 +00:00 [INF] Claim http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier = 7f041757-3aad-4221-971a-729598c1d81e
2022-05-17 01:16:12.468 +00:00 [INF] **Claim name = superadmin@local**
2022-05-17 01:16:12.471 +00:00 [INF] Claim http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress = someone@somewhere.com
2022-05-17 01:16:12.472 +00:00 [INF] Claim AspNet.Identity.SecurityStamp = OJLEYFWG4JE4ND5NRPTRT4JVCVEE2ANR
2022-05-17 01:16:12.473 +00:00 [INF] Claim http://schemas.microsoft.com/ws/2008/06/identity/claims/role = SuperAdmin
2022-05-17 01:16:12.475 +00:00 [INF] Claim http://schemas.microsoft.com/claims/authnmethodsreferences = pwd
2022-05-17 01:16:12.477 +00:00 [INF] Claim nameid = 7f041757-3aad-4221-971a-729598c1d81e
2022-05-17 01:16:12.478 +00:00 [INF] Claim email = someone@somewhere.com
2022-05-17 01:16:12.480 +00:00 [INF] Claim role = SuperAdmin
2022-05-17 01:16:12.481 +00:00 [INF] Claim amr = pwd
The claim http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name is MISSING. Instead we can see the claim called name holding the username value.
I tried to force the app to use the correct claim name
options.ClaimsIdentity.UserNameClaimType = System.Security.Claims.ClaimTypes.Name;
but that didn't work. Its still using the wrong name.
Pretty sure it is a simple configuration missing somewhere - if someone can point it out?
UPDATE 1
I updated the code as follows
if (IsProduction)
{
options.ClaimsIdentity.UserNameClaimType = 'name';
}
This actually worked and allowed the user to log in. Identity.IsAuthenticated == true and Identity.Name != null. Hooray! ... But.. why??
Also, the following works in DEV on ASPNET 6 :
[Inject]
IHttpContextAccessor HCA { get; set; }
if (HCA?.HttpContext?.User?.Claims?.Any() == true)
{
foreach (var claim in HCA.HttpContext.User.Claims)
{
Logger.Log("HCA Type = {t} Value = {v}", claim.Type, claim.Value);
}
}
But in Prod, on Azure App Services, the check HCA?.HttpContext?.User?.Claims?.Any() == true FAILS. Meaning that there are no claims in HttpContext. How is that possible???
ALSO
I installed the ASP.NET core 6.0 (x64) Extension to Azure Web App. It installed fine, but did not make a difference to authentication error.
UPDATE 2
I found the source of the problem, but not the solution, yet. It turns out that Azure Signal R is messing things up.
In Startup.cs I have this code:
if (IsProduction)
{
services.AddSignalR().AddAzureSignalR(options =>
{
options.ServerStickyMode = Microsoft.Azure.SignalR.ServerStickyMode.Required;
});
}
If I remove this, then the claims go back to normal, meaning:
- Name claim is now
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name - HttpContext works again
But of course I lose Azure SignalR which is no good.
What is even weirder is this: I created a fresh ASPNET 6 Blazor Server App project using the "Local Authentication" template, and deployed that to Azure App Services. Now, the claims behaviour is slightly different:
- Name claim is correct
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name - A new claim called
unique_nameis added to the ClaimsPrincipal - HttpContext is broken.
| State | Name Claim | HttpContext |
|---|---|---|
| Upgraded ASPNET 5 Project; No Azure Signal R | OK | Working |
| Upgraded ASPNET 5 Project; with Azure Signal R | Broken | Broken |
| Fresh ASPNET 6 Project; with Azure Signal R | Working | Broken |
So, the upshot is: Azure SignalR is definitely screwing up authentication for me. But why? And how do I fix it?
Update 3
So, it turns out that the version of System.IdentityModel.Tokens.Jwt matters when using Azure Signal R.
In the experiments above,
- the upgraded NET5>NET6 project was using System.IdentityModel.Tokens.Jwt 6.10.0.20330 (name claim wrong, unique_name claim missing)
- the Fresh NET 6 project was using System.IdentityModel.Tokens.Jwt 6.8.0.11012 (a lower version - strangely enough) (name claim OK, unique_name claim exists)
To fix my problem, I simply upgraded to the latest System.IdentityModel.Tokens.Jwt 6.18 and now authentication for my upgraded NET5 project works again.
To understand more, see the github issue
PS. Kudos to the Azure SignalR team for pointing me in the right direction