Skip to content

Commit eb02c0a

Browse files
committed
Title: Added role management and admin role checks
Summary: This commit introduces role management to the Identity service and adds an "Admin" role check. The `Program.cs` file was updated to include role management and a new Razor page with authorization. The `GetProfileDataAsync` method in `ProfileService.cs` was refactored to retrieve the user directly from the subject and add the user's roles as claims. The `UsersSeed` class now includes a `RoleManager<IdentityRole>` dependency and checks if the "Admin" role exists, creating it if necessary. The user "alice" is also added to the "Admin" role. A new menu item was added to `UserMenu.razor` that links to the new Razor page `CascadeAuthState.razor`, which displays the user's authentication state and admin role status.
1 parent 545f334 commit eb02c0a

File tree

5 files changed

+88
-11
lines changed

5 files changed

+88
-11
lines changed

Identity.API/Program.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,16 @@
1111
// migrations instead.
1212
builder.Services.AddMigration<ApplicationDbContext, UsersSeed>();
1313

14-
builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
15-
.AddEntityFrameworkStores<ApplicationDbContext>()
16-
.AddDefaultTokenProviders();
14+
builder.Services
15+
.AddIdentity<ApplicationUser, IdentityRole>()
16+
.AddEntityFrameworkStores<ApplicationDbContext>()
17+
.AddDefaultTokenProviders()
18+
.AddRoles<IdentityRole>();
19+
20+
builder.Services.AddRazorPages(options =>
21+
{
22+
options.Conventions.AuthorizePage("/user/cascade-auth-state");
23+
});
1724

1825
builder.Services.AddIdentityServer(options =>
1926
{

Identity.API/Services/ProfileService.cs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,22 @@ public ProfileService(UserManager<ApplicationUser> userManager)
1111

1212
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
1313
{
14-
var subject = context.Subject ?? throw new ArgumentNullException(nameof(context.Subject));
14+
var user = await _userManager.GetUserAsync(context.Subject);
1515

16-
var subjectId = subject.Claims.Where(x => x.Type == "sub").FirstOrDefault()?.Value;
16+
var roles = await _userManager.GetRolesAsync(user);
1717

18-
var user = await _userManager.FindByIdAsync(subjectId);
19-
if (user == null)
20-
throw new ArgumentException("Invalid subject identifier");
18+
var claims = new List<Claim>
19+
{
20+
new Claim(JwtClaimTypes.Subject, user.Id),
21+
new Claim(JwtClaimTypes.PreferredUserName, user.UserName),
22+
};
23+
24+
foreach (var role in roles)
25+
{
26+
claims.Add(new Claim(JwtClaimTypes.Role, role));
27+
}
2128

22-
var claims = GetClaimsFromUser(user);
23-
context.IssuedClaims = claims.ToList();
29+
context.IssuedClaims = claims;
2430
}
2531

2632
public async Task IsActiveAsync(IsActiveContext context)

Identity.API/UsersSeed.cs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,32 @@
11

22
namespace eShop.Identity.API;
33

4-
public class UsersSeed(ILogger<UsersSeed> logger, UserManager<ApplicationUser> userManager) : IDbSeeder<ApplicationDbContext>
4+
using Microsoft.AspNetCore.Identity;
5+
6+
public class UsersSeed(
7+
ILogger<UsersSeed> logger,
8+
UserManager<ApplicationUser> userManager,
9+
RoleManager<IdentityRole> roleManager) : IDbSeeder<ApplicationDbContext>
510
{
611
public async Task SeedAsync(ApplicationDbContext context)
712
{
13+
// Check if the Admin role exists and create it if it doesn't
14+
if (!await roleManager.RoleExistsAsync("Admin"))
15+
{
16+
var role = new IdentityRole("Admin");
17+
var result = await roleManager.CreateAsync(role);
18+
19+
if (!result.Succeeded)
20+
{
21+
throw new Exception(result.Errors.First().Description);
22+
}
23+
24+
if (logger.IsEnabled(LogLevel.Debug))
25+
{
26+
logger.LogDebug("Admin role created");
27+
}
28+
}
29+
830
var alice = await userManager.FindByNameAsync("alice");
931

1032
if (alice == null)
@@ -41,6 +63,13 @@ public async Task SeedAsync(ApplicationDbContext context)
4163
{
4264
logger.LogDebug("alice created");
4365
}
66+
67+
var roleResult = await userManager.AddToRoleAsync(alice, "Admin");
68+
69+
if (!roleResult.Succeeded)
70+
{
71+
throw new Exception(roleResult.Errors.First().Description);
72+
}
4473
}
4574
else
4675
{

WebApp/Components/Layout/UserMenu.razor

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
<div class="dropdown-content">
1414
<a class="dropdown-item" href="user/orders">My orders</a>
1515
<a class="dropdown-item" href="user/editprofile">Edit profile</a>
16+
<a class="dropdown-item" href="user/cascade-auth-state">View authentication state</a>
1617
<form class="dropdown-item" method="post" action="user/logout" @formname="logout" @onsubmit="LogOutAsync">
1718
<AntiforgeryToken />
1819
<button type="submit">Log out</button>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
@page "/user/cascade-auth-state"
2+
@using Microsoft.AspNetCore.Components.Authorization
3+
4+
<h1>Cascade Auth State</h1>
5+
6+
<p>@authMessage</p>
7+
8+
@code {
9+
private string authMessage = "The user is NOT authenticated.";
10+
11+
[CascadingParameter]
12+
private Task<AuthenticationState>? authenticationState { get; set; }
13+
14+
protected override async Task OnInitializedAsync()
15+
{
16+
if (authenticationState is not null)
17+
{
18+
var authState = await authenticationState;
19+
var user = authState?.User;
20+
21+
if (user?.Identity is not null && user.Identity.IsAuthenticated)
22+
{
23+
if (user.IsInRole("Admin"))
24+
{
25+
authMessage = $"{user.Identity.Name} is authenticated and in the Admin role.";
26+
}
27+
else
28+
{
29+
authMessage = $"{user.Identity.Name} is authenticated but not in the Admin role.";
30+
}
31+
}
32+
}
33+
}
34+
}

0 commit comments

Comments
 (0)