'Persist custom claims with windows authentication ASP.NET Core 6
I've created a new mvc app with windows authentication with dotnet new mvc --auth windows using Visual Studio Code.
If I start the application using VS Code and Kestrel, everything works as expected (I see my domain name at the top of the page). Now I want to add custom claims using the IClaimsTransformation.
We have a REST API that takes the domain name of the user and the application name and returns the role of the current user for this application. After receiving the response, I'm adding new claims to the current identity.
However, I don't know how to persist those custom claims.
Here is my code:
Program.cs with startup code:
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Negotiate;
using WindowsAuth.Utils;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
.AddNegotiate();
builder.Services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the default policy.
options.FallbackPolicy = options.DefaultPolicy;
});
builder.Services.AddTransient<IClaimsTransformation, CustomClaimsTransformer>();
builder.Services.AddRazorPages();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
CustomClaimsTransformer.cs:
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
namespace WindowsAuth.Utils;
public class CustomClaimsTransformer : IClaimsTransformation
{
public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
if (principal.Identity == null || string.IsNullOrEmpty(principal.Identity.Name))
return principal;
var userData = await GetUserDataFromRestAPI(principal.Identity.Name); //This is currently a dummy method, but will be called on every request
if (userData == null || string.IsNullOrEmpty(userData.Role))
return principal;
var clone = principal.Clone();
Claim[] claims = new Claim[]{
new Claim(ClaimTypes.Name, principal.Identity.Name),
new Claim("CustomProviderAccountId", userData.AccountId.ToString()),
new Claim(ClaimTypes.Role, userData.Role)
};
ClaimsIdentity identity = new ClaimsIdentity(claims);
clone.AddIdentity(identity); //Alternativly I cloud add the claims to the existing identity, but those won't persist either
return clone;
}
private Task<AuthProviderResponse> GetUserDataFromRestAPI(string username)
{
return Task.FromResult(new AuthProviderResponse
{
AccountId = 42,
Role = "Admin"
});
}
}
So as said: This is working fine as I can add the custom claims and they are getting passed to the rest of the application, but I don't know how to temporally persist these claims. Maybe in a cookie or cache or session or what else. What is the best practice for such a scenario?
Everything else in this application is the default from the mvc template.
Solution 1:[1]
Finally I found a solution, but I'm not sure if this is even close to best practise:
In the CustomClaimsTransformer I'm writing the claims inside a cookie and I'm encrypting that cookie with the IDataProtector. So on every Request, I first try to read the cookie value, and if it doesn't exist, I'm doing the API call. After doing the call I save the claims encrypted inside the cookie.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | MarvinS |
