Skip to content

Commit 4ccf331

Browse files
authored
Two users + two API role endpoints (dotnet#241)
1 parent 2f9d23a commit 4ccf331

File tree

4 files changed

+114
-32
lines changed

4 files changed

+114
-32
lines changed

8.0/BlazorWebAssemblyStandaloneWithIdentity/Backend/Program.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,13 @@
2929
builder.Services.AddAuthorizationBuilder();
3030

3131
// Add the database (in memory for the sample)
32-
builder.Services.AddDbContext<AppDbContext>(options => options.UseInMemoryDatabase("AppDb"));
32+
builder.Services.AddDbContext<AppDbContext>(
33+
options =>
34+
{
35+
options.UseInMemoryDatabase("AppDb");
36+
//For debugging only: options.EnableDetailedErrors(true);
37+
//For debugging only: options.EnableSensitiveDataLogging(true);
38+
});
3339

3440
// Add identity and opt-in to endpoints
3541
builder.Services.AddIdentityCore<AppUser>()
@@ -119,10 +125,14 @@
119125
return Results.Unauthorized();
120126
}).RequireAuthorization();
121127

122-
app.MapPost("/data-processing", ([FromBody] FormModel model) =>
128+
app.MapPost("/data-processing-1", ([FromBody] FormModel model) =>
123129
Results.Text($"{model.Message.Length} characters"))
124130
.RequireAuthorization();
125131

132+
app.MapPost("/data-processing-2", ([FromBody] FormModel model) =>
133+
Results.Text($"{model.Message.Length} characters"))
134+
.RequireAuthorization(policy => policy.RequireRole("Manager"));
135+
126136
app.Run();
127137

128138
// Identity user
Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,31 @@
1-
using Microsoft.AspNetCore.Identity;
1+
using Microsoft.AspNetCore.Identity;
22
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
33
using Microsoft.EntityFrameworkCore;
44

55
namespace Backend;
66

77
public class SeedData
88
{
9+
private static readonly IEnumerable<SeedUser> seedUsers =
10+
[
11+
new SeedUser()
12+
{
13+
Email = "leela@contoso.com",
14+
NormalizedEmail = "LEELA@CONTOSO.COM",
15+
NormalizedUserName = "LEELA@CONTOSO.COM",
16+
RoleList = [ "Administrator", "Manager" ],
17+
UserName = "leela@contoso.com"
18+
},
19+
new SeedUser()
20+
{
21+
Email = "harry@contoso.com",
22+
NormalizedEmail = "HARRY@CONTOSO.COM",
23+
NormalizedUserName = "HARRY@CONTOSO.COM",
24+
RoleList = [ "User" ],
25+
UserName = "harry@contoso.com"
26+
},
27+
];
28+
929
public static async Task InitializeAsync(IServiceProvider serviceProvider)
1030
{
1131
using var context = new AppDbContext(serviceProvider.GetRequiredService<DbContextOptions<AppDbContext>>());
@@ -15,9 +35,13 @@ public static async Task InitializeAsync(IServiceProvider serviceProvider)
1535
return;
1636
}
1737

18-
string[] roles = [ "Administrator", "Manager" ];
38+
var userStore = new UserStore<AppUser>(context);
39+
var password = new PasswordHasher<AppUser>();
40+
1941
using var roleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
2042

43+
string[] roles = [ "Administrator", "Manager", "User" ];
44+
2145
foreach (var role in roles)
2246
{
2347
if (!await roleManager.RoleExistsAsync(role))
@@ -28,25 +52,28 @@ public static async Task InitializeAsync(IServiceProvider serviceProvider)
2852

2953
using var userManager = serviceProvider.GetRequiredService<UserManager<AppUser>>();
3054

31-
var user = new AppUser
55+
foreach (var user in seedUsers)
3256
{
33-
Email = "leela@contoso.com",
34-
NormalizedEmail = "LEELA@CONTOSO.COM",
35-
UserName = "leela@contoso.com",
36-
NormalizedUserName = "LEELA@CONTOSO.COM",
37-
EmailConfirmed = true,
38-
SecurityStamp = Guid.NewGuid().ToString("D")
39-
};
40-
41-
var password = new PasswordHasher<AppUser>();
42-
var hashed = password.HashPassword(user, "Passw0rd!");
43-
user.PasswordHash = hashed;
57+
var hashed = password.HashPassword(user, "Passw0rd!");
58+
user.PasswordHash = hashed;
59+
await userStore.CreateAsync(user);
4460

45-
await userManager.AddToRolesAsync(user, roles);
61+
if (user.Email is not null)
62+
{
63+
var appUser = await userManager.FindByEmailAsync(user.Email);
4664

47-
var userStore = new UserStore<AppUser>(context);
48-
var result = userStore.CreateAsync(user);
65+
if (appUser is not null && user.RoleList is not null)
66+
{
67+
await userManager.AddToRolesAsync(appUser, user.RoleList);
68+
}
69+
}
70+
}
4971

5072
await context.SaveChangesAsync();
5173
}
74+
75+
private class SeedUser : AppUser
76+
{
77+
public string[]? RoleList { get; set; }
78+
}
5279
}

8.0/BlazorWebAssemblyStandaloneWithIdentity/BlazorWasmAuth/Components/Pages/DataProcessing.razor

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,22 @@
77

88
<AuthorizeView>
99
<Authorized>
10-
<p>Hello, @context.User.Identity?.Name! You're authenticated, so processing data will succeed.</p>
10+
<p>Hello, @context.User.Identity?.Name! You're authenticated.</p>
1111
</Authorized>
1212
<NotAuthorized>
1313
<p>Hello! You're <em>NOT</em> authenticated, so processing data will fail.</p>
1414
</NotAuthorized>
1515
</AuthorizeView>
1616

17-
<EditForm Model="Model" OnSubmit="Submit" FormName="ProcessData">
17+
<h2>Data processing for any authenticated user</h2>
18+
19+
<p>If you're in any role, you can process data with the following form.</p>
20+
21+
<EditForm Model="Model1" OnSubmit="Submit1" FormName="ProcessData1">
1822
<div>
1923
<label>
2024
Message:
21-
<InputText @bind-Value="Model!.Message" />
25+
<InputText @bind-Value="Model1!.Message" />
2226
</label>
2327
</div>
2428
<div>
@@ -27,30 +31,68 @@
2731
</EditForm>
2832

2933
<div>
30-
@result
34+
@result1
35+
</div>
36+
37+
<h2>Data processing for Managers</h2>
38+
39+
<p>You must be in the Manager role to use the following form to process data.</p>
40+
41+
<EditForm Model="Model2" OnSubmit="Submit2" FormName="ProcessData2">
42+
<div>
43+
<label>
44+
Message:
45+
<InputText @bind-Value="Model2!.Message" />
46+
</label>
47+
</div>
48+
<div>
49+
<button type="submit">Submit</button>
50+
</div>
51+
</EditForm>
52+
53+
<div>
54+
@result2
3155
</div>
3256

3357
@code {
34-
private string? result;
58+
private string? result1;
59+
private string? result2;
3560

3661
[SupplyParameterFromForm]
37-
public FormModel? Model { get; set; }
62+
public FormModel? Model1 { get; set; }
3863

39-
protected override void OnInitialized() => Model ??= new();
64+
[SupplyParameterFromForm]
65+
public FormModel? Model2 { get; set; }
66+
67+
protected override void OnInitialized()
68+
{
69+
Model1 ??= new();
70+
Model2 ??= new();
71+
}
72+
73+
private async Task Submit1()
74+
{
75+
result1 = await ProcessData("data-processing-1", Model1!);
76+
}
77+
78+
private async Task Submit2()
79+
{
80+
result2 = await ProcessData("data-processing-2", Model2!);
81+
}
4082

41-
private async Task Submit()
83+
private async Task<string> ProcessData(string endpoint, FormModel model)
4284
{
4385
var client = ClientFactory.CreateClient("Auth");
4486

45-
var response = await client.PostAsJsonAsync<FormModel>("data-processing", Model!);
87+
var response = await client.PostAsJsonAsync<FormModel>(endpoint, model);
4688

4789
if (response.IsSuccessStatusCode)
4890
{
49-
result = $"The data was processed by the server! The server indicates that the message is {await response.Content.ReadAsStringAsync()} long.";
91+
return $"The data was processed by the server! The server indicates that the message is {await response.Content.ReadAsStringAsync()} long.";
5092
}
5193
else
5294
{
53-
result = $"The server responded with an unsuccessful status code ({response.StatusCode}).";
95+
return $"The server responded with an unsuccessful status code ({response.StatusCode}).";
5496
}
5597
}
5698

8.0/BlazorWebAssemblyStandaloneWithIdentity/README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@ For more information, see [Secure ASP.NET Core Blazor WebAssembly with ASP.NET C
2929

3030
1. Navigate to the `BlazorWasmAuth` app at the `FrontendUrl`.
3131

32-
1. Register a new user using the **Register** link in the upper-right corner of the app's UI or use the preregistered test user, `leela@contoso.com`, with the password `Passw0rd!`. Leela has `Administrator` and `Manager` roles and can access the private manager page but not the private editor page of the app.
32+
1. Register a new user using the **Register** link in the upper-right corner of the app's UI or use one of the preregistered test users:
33+
34+
* `leela@contoso.com` (Password: `Passw0rd!`). Leela has `Administrator`, `Manager`, and `User` roles and can access the private manager page but not the private editor page of the app. She can process data with both forms on the data processing page.
35+
* `harry@contoso.com` (Password: `Passw0rd!`). Harry only has the `User` role and can't access the manager and editor pages. He can only process data with the first form on the data processing page.
3336

3437
1. Log in with the user.
3538

36-
1. Navigate to the private page (`Components/Pages/PrivatePage.razor` at `/private-page`) that only authenticated users can reach. A link to the page appears in the navigation sidebar after the user is authenticated. Navigate to the private manager and editor pages to explore how the user's roles influence the pages that they can visit.
39+
1. Navigate to the private page (`Components/Pages/PrivatePage.razor` at `/private-page`) that only authenticated users can reach. A link to the page appears in the navigation sidebar after the user is authenticated. Navigate to the private manager and editor pages to explore how the user's roles influence the pages that they can visit. Navigate to the data processing page (`Components/Pages/DataProcessing.razor` at `/data-processing`) to experience authenticated and authorized data processing web API calls.
3740

3841
1. Log out of the app.

0 commit comments

Comments
 (0)