I have a Blazor Server app that uses Windows Auth. Based on the group memberships of the user, I am adding role claims using IClaimsTransformation. The group-to-role mapping is fetched from a DB.
This is my implementation of IClaimsTransformation:
public class WindowsAuthClaimsTransformation : IClaimsTransformation
{
private readonly IDataService _dataService;
public WindowsAuthClaimsTransformation(IDataService dataService)
{
_dataService = dataService;
}
public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
{
if (!(principal.Identity is ClaimsIdentity))
return principal;
var identity = principal.Identity as ClaimsIdentity;
if (identity?.IsAuthenticated == false)
return principal;
var userGroupClaims = identity.FindAll("http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid").ToList();
var groupRoleMappings = await _dataService.GetList<GroupRoleMapping>();
var roles = new List<string>();
foreach (var mapping in groupRoleMappings)
{
if (userGroupClaims.Any(x => x.Value.TrimEnd() == mapping.GroupSID))
{
roles.Add(mapping.RoleName);
}
}
foreach (var role in roles)
{
identity.AddClaim(new Claim(ClaimTypes.Role, role));
}
return principal;
}
}
This is registered as a scoped service in Program.cs:
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
// add windows auth
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
.AddNegotiate();
// database interaction logic
builder.Services.AddScoped<IDataService, SqlDataService>();
// add claimstransformation
builder.Services.AddScoped<IClaimsTransformation, WindowsAuthClaimsTransformation>();
builder.Services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the default policy.
options.FallbackPolicy = options.DefaultPolicy;
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAntiforgery();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();
}
}
SamplePage.razor:
<AuthorizeView Roles="Admin" >
<Authorized>
<p>You are an admin. Your roles: @string.Join(", ", @context.User.Claims.Where(c => c.Type == ClaimTypes.Role).Select(c => c.Value))</p>
<br />
</Authorized>
<NotAuthorized>
<p>You are not an admin. Your roles: @string.Join(", ", @context.User.Claims.Where(c => c.Type == ClaimTypes.Role).Select(c => c.Value))</p>
</NotAuthorized>
</AuthorizeView>
This gives me the following text output:
You are not an admin. Your roles: Admin, User
Which makes me think that my ClaimsTransformation is working properly, however the AuthorizeView is not properly recognizing it. How can I solve this?
It could pass the authorization if you add the role claims like below:
foreach (var role in roles)
{
identity.AddClaim(new Claim(identity.RoleClaimType, role));
//identity.AddClaim(new Claim(ClaimTypes.Role, role));
}