Description
Is there an existing issue for this?
- I have searched the existing issues
Describe the bug
I'm working through the Blazor 8 OIDC web app example and I've ran into a snag where I'm trying to select what roles have access to specific pages.
I'm able to access and view my claims (including roles) with the basic [Authorize]
attribute, but when I specify [Authorize(Roles="Admin")]
I receive an access denied redirection.
I've updated the UserInfo Class to the following to get the role claim added into what's stored clientside.
using System.Security.Claims;
namespace BlazorWebAppOidc.Client;
// Add properties to this class and update the server and client AuthenticationStateProviders
// to expose more information about the authenticated user to the client.
public sealed class UserInfo
{
public required string Email { get; init; }
public required string Name { get; init; }
public required string Roles { get; init; }
public required string UserId { get; init; }
private const string UserIdClaimType = "preferred_username";
public const string NameClaimType = "name";
private const string RoleClaimType = "roles";
private const string CustomClaimType = "userid";
public static UserInfo FromClaimsPrincipal(ClaimsPrincipal principal) =>
new()
{
Email = GetRequiredClaim(principal, UserIdClaimType),
Name = GetRequiredClaim(principal, NameClaimType),
Roles = GetRequiredClaim(principal, RoleClaimType),
UserId = (GetRequiredClaim(principal, UserIdClaimType).Split("@")[0])
};
public ClaimsPrincipal ToClaimsPrincipal() =>
new(new ClaimsIdentity(
[new Claim(UserIdClaimType, Email),
new Claim(NameClaimType, Name),
new Claim(RoleClaimType, Roles),
new Claim(CustomClaimType,UserId)
],
authenticationType: nameof(UserInfo),
nameType: NameClaimType,
roleType: null));
private static string GetRequiredClaim(ClaimsPrincipal principal, string claimType) =>
principal.FindFirst(claimType)?.Value ??
throw new InvalidOperationException($"Could not find required '{claimType}' claim.");
}
And I've modified the UserClaims PageComponent to the following
@page "/user-claims"
@using System.Security.Claims
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "Admin")]
<PageTitle>User Claims</PageTitle>
<h1>User Claims</h1>
@if (_claims.Any())
{
<ul>
@foreach (var claim in _claims)
{
<li><b>@claim.Type:</b> @claim.Value</li>
}
</ul>
}
@code {
private IEnumerable<Claim> _claims = Enumerable.Empty<Claim>();
[CascadingParameter]
private Task<AuthenticationState>? AuthState { get; set; }
protected override async Task OnInitializedAsync()
{
if (AuthState == null)
{
return;
}
var authState = await AuthState;
_claims = authState.User.Claims;
}
}
Expected Behavior
I expect to be able to be able to see the user-claims page when I specify my role in the [Authorize(Roles="")]
Attribute
Steps To Reproduce
For this I'm using an Entra App Registration + Enterprise application in order to set up an Admin role and assign my userid to the Admin role.
Those are the prerequisites for getting started, but other than that filling out your clientid/secret/authority should be the same.
I've referenced above the only deviations from the templated code.
Exceptions (if any)
No response
.NET Version
8.0.201
Anything else?
