Skip to content

Commit 7ed56fd

Browse files
authored
Sample Improvement (Part 2/2) (#21)
* Sample Improvement (Part 2/2) * Identifier Normalization & Improved Exists Logic * Complete Identifier and Add New Tests * Complete Needed Identifier Logics & Improved PagedResult * Added Concurrency Support & Identifier Implementation * Add & Fix Test * Added Session Concurrency * Improved Session Domains & Basic Device Id Binding * Improved Device Context Properties * Added State Clear on Current Chain Revoke * Credential Enhancement * Support Credential Level Lockout * Complete Credential Change & Tests * Complete Credential Reset & Added Tests * Added Generic InMemory Store and User Lifecycle Store Implementation * Completed User Create * Enhanced Authorization (RBAC) * Authorization Completion & Tests * Fixed Identifier Concurrency Test * Profile Dialog & UAuthClientEvents * User Self Deletion * Added Self User Status Change * Complete Logout & Revoke Semantics & Added Tests * Home Page Design and ReauthRequired Raiese Event Test Fix * Admin Role Crud Dialog & State Handling Mode Enhancement :) * Permission Set Logic * UAuthStateView Enhancement * Completed User Role Management * Finalized Endpoints & Admin User Management Dialog * Finalize Blazor Server Sample
1 parent edfcb21 commit 7ed56fd

File tree

442 files changed

+14038
-4334
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

442 files changed

+14038
-4334
lines changed

UltimateAuth.slnx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<Folder Name="/Tests/">
1313
<Project Path="tests/CodeBeam.UltimateAuth.Tests.Unit/CodeBeam.UltimateAuth.Tests.Unit.csproj" Id="6f4b22da-849a-4a79-b5c5-aee7cb1429a6" />
1414
</Folder>
15+
<Project Path="src/authentication/CodeBeam.UltimateAuth.Authentication.InMemory/CodeBeam.UltimateAuth.Authentication.InMemory.csproj" Id="bd87e254-0565-4fc5-950d-ee5bbb416079" />
1516
<Project Path="src/authorization/CodeBeam.UltimateAuth.Authorization.Contracts/CodeBeam.UltimateAuth.Authorization.Contracts.csproj" Id="40a23002-f885-42a8-bdd9-fd962ab28742" />
1617
<Project Path="src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/CodeBeam.UltimateAuth.Authorization.InMemory.csproj" Id="a1e6d007-bdc0-4574-b549-ec863757edd3" />
1718
<Project Path="src/authorization/CodeBeam.UltimateAuth.Authorization.Reference/CodeBeam.UltimateAuth.Authorization.Reference.csproj" Id="84b784d0-bb48-406a-a0d1-c600da667597" />

samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
</ItemGroup>
1515

1616
<ItemGroup>
17+
<ProjectReference Include="..\..\..\src\authentication\CodeBeam.UltimateAuth.Authentication.InMemory\CodeBeam.UltimateAuth.Authentication.InMemory.csproj" />
1718
<ProjectReference Include="..\..\..\src\authorization\CodeBeam.UltimateAuth.Authorization.InMemory\CodeBeam.UltimateAuth.Authorization.InMemory.csproj" />
1819
<ProjectReference Include="..\..\..\src\authorization\CodeBeam.UltimateAuth.Authorization.Reference\CodeBeam.UltimateAuth.Authorization.Reference.csproj" />
1920
<ProjectReference Include="..\..\..\src\authorization\CodeBeam.UltimateAuth.Authorization\CodeBeam.UltimateAuth.Authorization.csproj" />

samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub/Program.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using CodeBeam.UltimateAuth.Authentication.InMemory;
12
using CodeBeam.UltimateAuth.Authorization.InMemory;
23
using CodeBeam.UltimateAuth.Authorization.InMemory.Extensions;
34
using CodeBeam.UltimateAuth.Authorization.Reference.Extensions;
@@ -8,8 +9,6 @@
89
using CodeBeam.UltimateAuth.Credentials.Reference;
910
using CodeBeam.UltimateAuth.Sample.UAuthHub.Components;
1011
using CodeBeam.UltimateAuth.Security.Argon2;
11-
using CodeBeam.UltimateAuth.Server.Authentication;
12-
using CodeBeam.UltimateAuth.Server.Defaults;
1312
using CodeBeam.UltimateAuth.Server.Extensions;
1413
using CodeBeam.UltimateAuth.Sessions.InMemory;
1514
using CodeBeam.UltimateAuth.Tokens.InMemory;
@@ -57,6 +56,7 @@
5756
.AddUltimateAuthAuthorizationReference()
5857
.AddUltimateAuthInMemorySessions()
5958
.AddUltimateAuthInMemoryTokens()
59+
.AddUltimateAuthInMemoryAuthenticationSecurity()
6060
.AddUltimateAuthArgon2();
6161

6262
builder.Services.AddUltimateAuthClient(o =>

samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/CodeBeam.UltimateAuth.Sample.BlazorServer.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
</ItemGroup>
1616

1717
<ItemGroup>
18+
<ProjectReference Include="..\..\..\src\authentication\CodeBeam.UltimateAuth.Authentication.InMemory\CodeBeam.UltimateAuth.Authentication.InMemory.csproj" />
1819
<ProjectReference Include="..\..\..\src\authorization\CodeBeam.UltimateAuth.Authorization.InMemory\CodeBeam.UltimateAuth.Authorization.InMemory.csproj" />
1920
<ProjectReference Include="..\..\..\src\authorization\CodeBeam.UltimateAuth.Authorization.Reference\CodeBeam.UltimateAuth.Authorization.Reference.csproj" />
2021
<ProjectReference Include="..\..\..\src\authorization\CodeBeam.UltimateAuth.Authorization\CodeBeam.UltimateAuth.Authorization.csproj" />
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using CodeBeam.UltimateAuth.Client;
2+
using CodeBeam.UltimateAuth.Core.Domain;
3+
using MudBlazor;
4+
5+
namespace CodeBeam.UltimateAuth.Sample.BlazorServer.Common;
6+
7+
public static class UAuthDialog
8+
{
9+
public static DialogParameters GetDialogParameters(UAuthState state, UserKey? userKey = null)
10+
{
11+
DialogParameters parameters = new DialogParameters();
12+
parameters.Add("AuthState", state);
13+
if (userKey != null )
14+
{
15+
parameters.Add("UserKey", userKey);
16+
}
17+
return parameters;
18+
}
19+
20+
public static DialogOptions GetDialogOptions(MaxWidth maxWidth = MaxWidth.Medium)
21+
{
22+
return new DialogOptions
23+
{
24+
MaxWidth = maxWidth,
25+
FullWidth = true,
26+
CloseButton = true
27+
};
28+
}
29+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<MudPage Class="mud-width-full" FullScreen="FullScreen.FullWithoutAppbar" Column="1" Row="1">
2+
<MudContainer Class="mud-height-full" MaxWidth="MaxWidth.Small">
3+
@ChildContent
4+
</MudContainer>
5+
</MudPage>
6+
7+
@code {
8+
[Parameter]
9+
public RenderFragment? ChildContent { get; set; }
10+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
@using CodeBeam.UltimateAuth.Core.Contracts
2+
@using CodeBeam.UltimateAuth.Users.Contracts
3+
@inject IUAuthClient UAuthClient
4+
@inject ISnackbar Snackbar
5+
@inject IDialogService DialogService
6+
7+
<MudDialog Class="mud-width-full" ContentClass="uauth-dialog">
8+
<TitleContent>
9+
<MudText>Identifier Management</MudText>
10+
<MudText Typo="Typo.subtitle2" Color="Color.Primary">User: @AuthState?.Identity?.DisplayName</MudText>
11+
</TitleContent>
12+
<DialogContent>
13+
<MudStack Class="mud-width-full">
14+
<MudButton Variant="Variant.Outlined" Color="Color.Primary" StartIcon="@Icons.Material.Filled.NearbyOff" OnClick="SuspendAccountAsync">
15+
Suspend Account
16+
</MudButton>
17+
18+
<MudButton Variant="Variant.Outlined" Color="Color.Error" StartIcon="@Icons.Material.Filled.Delete" OnClick="DeleteAccountAsync">
19+
Delete Account
20+
</MudButton>
21+
</MudStack>
22+
</DialogContent>
23+
</MudDialog>
24+
25+
@code {
26+
[CascadingParameter]
27+
private IMudDialogInstance MudDialog { get; set; } = default!;
28+
29+
[Parameter]
30+
public UAuthState AuthState { get; set; } = default!;
31+
32+
private async Task SuspendAccountAsync()
33+
{
34+
var info = await DialogService.ShowMessageBoxAsync(
35+
title: "Are You Sure",
36+
markupMessage: (MarkupString)
37+
"""
38+
You are going to suspend your account.<br/><br/>
39+
You can still active your account later.
40+
""",
41+
yesText: "Suspend", noText: "Cancel",
42+
options: new DialogOptions() { MaxWidth = MaxWidth.Medium, FullWidth = true, BackgroundClass = "uauth-blur-slight" });
43+
44+
if (info != true)
45+
{
46+
Snackbar.Add("Suspend process cancelled", Severity.Info);
47+
return;
48+
}
49+
50+
ChangeUserStatusSelfRequest request = new() { NewStatus = SelfUserStatus.SelfSuspended };
51+
var result = await UAuthClient.Users.ChangeStatusSelfAsync(request);
52+
if (result.IsSuccess)
53+
{
54+
Snackbar.Add("Your account suspended successfully.", Severity.Success);
55+
MudDialog.Close();
56+
}
57+
else
58+
{
59+
Snackbar.Add(result?.Problem?.Detail ?? result?.Problem?.Title ?? "Delete failed.", Severity.Error);
60+
}
61+
}
62+
63+
private async Task DeleteAccountAsync()
64+
{
65+
var info = await DialogService.ShowMessageBoxAsync(
66+
title: "Are You Sure",
67+
markupMessage: (MarkupString)
68+
"""
69+
You are going to delete your account.<br/><br/>
70+
This action can't be undone.<br/><br/>
71+
(Actually it is, admin can handle soft deleted accounts.)
72+
""",
73+
yesText: "Delete", noText: "Cancel",
74+
options: new DialogOptions() { MaxWidth = MaxWidth.Medium, FullWidth = true, BackgroundClass = "uauth-blur-slight" });
75+
76+
if (info != true)
77+
{
78+
Snackbar.Add("Deletion cancelled", Severity.Info);
79+
return;
80+
}
81+
82+
var result = await UAuthClient.Users.DeleteMeAsync();
83+
if (result.IsSuccess)
84+
{
85+
Snackbar.Add("Your account deleted successfully.", Severity.Success);
86+
MudDialog.Close();
87+
}
88+
else
89+
{
90+
Snackbar.Add(result?.Problem?.Detail ?? result?.Problem?.Title ?? "Delete failed.", Severity.Error);
91+
}
92+
}
93+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
@using CodeBeam.UltimateAuth.Credentials.Contracts
2+
@using CodeBeam.UltimateAuth.Users.Contracts
3+
@inject IUAuthClient UAuthClient
4+
@inject ISnackbar Snackbar
5+
6+
<MudDialog>
7+
<TitleContent>
8+
<MudText Typo="Typo.h6">Create User</MudText>
9+
</TitleContent>
10+
11+
<DialogContent>
12+
<MudForm @ref="_form">
13+
<MudStack Spacing="2">
14+
<MudTextField @bind-Value="_username" Label="Username" Variant="Variant.Outlined" Required="true" />
15+
<MudTextField @bind-Value="_email" Label="Email" Variant="Variant.Outlined" Required="true" />
16+
<MudPasswordField @bind-Value="_password" Label="Password" Variant="Variant.Outlined" Required="true" />
17+
<MudPasswordField @bind-Value="_passwordCheck" Label="Password (Again)" Variant="Variant.Outlined" Required="true" Validation="@(() => PasswordMatch(_password))" />
18+
<MudTextField @bind-Value="_displayName" Label="Display Name" Variant="Variant.Outlined" />
19+
</MudStack>
20+
</MudForm>
21+
</DialogContent>
22+
23+
<DialogActions>
24+
<MudButton OnClick="Cancel">Cancel</MudButton>
25+
<MudButton Color="Color.Primary" Variant="Variant.Filled" OnClick="CreateUserAsync">Create</MudButton>
26+
</DialogActions>
27+
</MudDialog>
28+
29+
@code {
30+
private MudForm _form = null!;
31+
private string? _username;
32+
private string? _email;
33+
private string? _password;
34+
private string? _passwordCheck;
35+
private string? _displayName;
36+
37+
[CascadingParameter]
38+
private IMudDialogInstance MudDialog { get; set; } = default!;
39+
40+
private async Task CreateUserAsync()
41+
{
42+
await _form.Validate();
43+
44+
if (!_form.IsValid)
45+
return;
46+
47+
if (_password != _passwordCheck)
48+
{
49+
Snackbar.Add("Passwords do not match", Severity.Error);
50+
return;
51+
}
52+
53+
var request = new CreateUserRequest
54+
{
55+
UserName = _username,
56+
Email = _email,
57+
DisplayName = _displayName,
58+
Password = _password
59+
};
60+
61+
var result = await UAuthClient.Users.CreateAdminAsync(request);
62+
63+
if (!result.IsSuccess)
64+
{
65+
Snackbar.Add(result.GetErrorText ?? "User creation failed", Severity.Error);
66+
return;
67+
}
68+
69+
Snackbar.Add("User created successfully", Severity.Success);
70+
MudDialog.Close(DialogResult.Ok(true));
71+
}
72+
73+
private string PasswordMatch(string? arg) => _password != arg ? "Passwords don't match" : string.Empty;
74+
75+
private void Cancel() => MudDialog.Cancel();
76+
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
@using CodeBeam.UltimateAuth.Core.Contracts
2+
@using CodeBeam.UltimateAuth.Credentials.Contracts
3+
@using CodeBeam.UltimateAuth.Users.Contracts
4+
@inject IUAuthClient UAuthClient
5+
@inject ISnackbar Snackbar
6+
@inject IDialogService DialogService
7+
@inject IUAuthStateManager StateManager
8+
@inject NavigationManager Nav
9+
10+
<MudDialog Class="mud-width-full" ContentClass="uauth-dialog">
11+
<TitleContent>
12+
<MudText>Credential Management</MudText>
13+
<MudText Typo="Typo.subtitle2" Color="Color.Primary">User: @AuthState?.Identity?.DisplayName</MudText>
14+
</TitleContent>
15+
<DialogContent>
16+
<MudForm @ref="@_form" OnEnterPressed="@ChangePasswordAsync">
17+
<MudGrid Spacing="2">
18+
@if (UserKey == null)
19+
{
20+
<MudItem xs="12" sm="6">
21+
<MudPasswordField @bind-Value="_oldPassword" @bind-PasswordMode="@_passwordMode1" Label="Old Password" Variant="Variant.Outlined" Immediate="true" Required="true" />
22+
</MudItem>
23+
}
24+
else
25+
{
26+
<MudItem xs="12">
27+
<MudAlert Severity="Severity.Warning">
28+
Administrators can directly assign passwords to users.
29+
However, using the credential reset flow is generally recommended for better security and auditability.
30+
</MudAlert>
31+
</MudItem>
32+
}
33+
34+
<MudItem xs="12" sm="6">
35+
<MudPasswordField @bind-Value="_newPassword" @bind-PasswordMode="@_passwordMode2" Label="New Password" Variant="Variant.Outlined" Immediate="true" Required="true" />
36+
</MudItem>
37+
38+
<MudItem xs="12" sm="6">
39+
<MudPasswordField @bind-Value="_newPasswordCheck" @bind-PasswordMode="@_passwordMode3" Label="New Password (Again)" Variant="Variant.Outlined" Immediate="true" Required="true" Validation="@(new Func<string, string>(PasswordMatch))" />
40+
</MudItem>
41+
42+
<MudItem xs="12">
43+
<MudButton Color="Color.Primary" Variant="Variant.Filled" OnClick="ChangePasswordAsync">@(UserKey is null ? "Change Password" : "Set Password")</MudButton>
44+
</MudItem>
45+
</MudGrid>
46+
</MudForm>
47+
</DialogContent>
48+
<DialogActions>
49+
<MudButton OnClick="Cancel">Cancel</MudButton>
50+
</DialogActions>
51+
</MudDialog>
52+
53+
@code {
54+
private MudForm _form = null!;
55+
private string? _oldPassword;
56+
private string? _newPassword;
57+
private string? _newPasswordCheck;
58+
private bool _passwordMode1 = false;
59+
private bool _passwordMode2 = false;
60+
private bool _passwordMode3 = true;
61+
62+
[CascadingParameter]
63+
private IMudDialogInstance MudDialog { get; set; } = default!;
64+
65+
[Parameter]
66+
public UAuthState AuthState { get; set; } = default!;
67+
68+
[Parameter]
69+
public UserKey? UserKey { get; set; }
70+
71+
private async Task ChangePasswordAsync()
72+
{
73+
if (_form is null)
74+
return;
75+
76+
await _form.Validate();
77+
if (!_form.IsValid)
78+
{
79+
Snackbar.Add("Form is not valid.", Severity.Error);
80+
return;
81+
}
82+
83+
if (_newPassword != _newPasswordCheck)
84+
{
85+
Snackbar.Add("New password and check do not match", Severity.Error);
86+
return;
87+
}
88+
89+
ChangeCredentialRequest request;
90+
91+
if (UserKey is null)
92+
{
93+
request = new ChangeCredentialRequest
94+
{
95+
CurrentSecret = _oldPassword!,
96+
NewSecret = _newPassword!
97+
};
98+
}
99+
else
100+
{
101+
request = new ChangeCredentialRequest
102+
{
103+
NewSecret = _newPassword!
104+
};
105+
}
106+
107+
UAuthResult<ChangeCredentialResult> result;
108+
if (UserKey is null)
109+
{
110+
result = await UAuthClient.Credentials.ChangeMyAsync(request);
111+
}
112+
else
113+
{
114+
result = await UAuthClient.Credentials.ChangeCredentialAsync(UserKey.Value, request);
115+
}
116+
117+
if (result.IsSuccess)
118+
{
119+
Snackbar.Add("Password changed successfully", Severity.Success);
120+
_oldPassword = null;
121+
_newPassword = null;
122+
_newPasswordCheck = null;
123+
MudDialog.Close(DialogResult.Ok(true));
124+
}
125+
else
126+
{
127+
Snackbar.Add(result.GetErrorText ?? "An error occurred while changing password", Severity.Error);
128+
}
129+
}
130+
131+
private string PasswordMatch(string arg) => _newPassword != arg ? "Passwords don't match" : string.Empty;
132+
133+
private void Cancel() => MudDialog.Cancel();
134+
}

0 commit comments

Comments
 (0)