Skip to content

Commit 2e826f7

Browse files
committed
Added Self User Status Change
1 parent da72d5e commit 2e826f7

File tree

38 files changed

+376
-83
lines changed

38 files changed

+376
-83
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
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+
6+
</MudPage>
7+
8+
@code {
9+
[Parameter]
10+
public RenderFragment? ChildContent { get; set; }
11+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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+
27+
private MudForm? _form;
28+
29+
private string? _firstName;
30+
private string? _lastName;
31+
private string? _displayName;
32+
33+
private DateTime? _birthDate;
34+
private string? _gender;
35+
private string? _bio;
36+
37+
private string? _language;
38+
private string? _timeZone;
39+
private string? _culture;
40+
41+
[CascadingParameter]
42+
private IMudDialogInstance MudDialog { get; set; } = default!;
43+
44+
[Parameter]
45+
public UAuthState AuthState { get; set; } = default!;
46+
47+
private async Task SuspendAccountAsync()
48+
{
49+
var info = await DialogService.ShowMessageBoxAsync(
50+
title: "Are You Sure",
51+
markupMessage: (MarkupString)
52+
"""
53+
You are going to suspend your account.<br/><br/>
54+
You can still active your account later.
55+
""",
56+
yesText: "Suspend", noText: "Cancel");
57+
58+
if (info != true)
59+
{
60+
Snackbar.Add("Suspend process cancelled", Severity.Info);
61+
return;
62+
}
63+
64+
ChangeUserStatusSelfRequest request = new() { NewStatus = SelfUserStatus.SelfSuspended };
65+
var result = await UAuthClient.Users.ChangeStatusSelfAsync(request);
66+
if (result.IsSuccess)
67+
{
68+
Snackbar.Add("Your account suspended successfully.", Severity.Success);
69+
MudDialog.Close();
70+
}
71+
else
72+
{
73+
Snackbar.Add(result?.Problem?.Detail ?? result?.Problem?.Title ?? "Delete failed.", Severity.Error);
74+
}
75+
}
76+
77+
private async Task DeleteAccountAsync()
78+
{
79+
var info = await DialogService.ShowMessageBoxAsync(
80+
title: "Are You Sure",
81+
markupMessage: (MarkupString)
82+
"""
83+
You are going to delete your account.<br/><br/>
84+
This action can't be undone.<br/><br/>
85+
(Actually it is, admin can handle soft deleted accounts.)
86+
""",
87+
yesText: "Delete", noText: "Cancel");
88+
89+
if (info != true)
90+
{
91+
Snackbar.Add("Deletion cancelled", Severity.Info);
92+
return;
93+
}
94+
95+
var result = await UAuthClient.Users.DeleteMeAsync();
96+
if (result.IsSuccess)
97+
{
98+
Snackbar.Add("Your account deleted successfully.", Severity.Success);
99+
MudDialog.Close();
100+
}
101+
else
102+
{
103+
Snackbar.Add(result?.Problem?.Detail ?? result?.Problem?.Title ?? "Delete failed.", Severity.Error);
104+
}
105+
}
106+
}

samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Layout/MainLayout.razor

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
<MudDivider />
3535

3636
<MudMenuItem Icon="@Icons.Material.Filled.Refresh" IconColor="Color.Secondary" Label="Refresh Session" OnClick="@Refresh" />
37+
<MudMenuItem Icon="@Icons.Material.Filled.VerifiedUser" IconColor="Color.Secondary" Label="Validate Session" OnClick="@Validate" />
3738
<MudMenuItem Icon="@Icons.Material.Filled.Logout" IconColor="Color.Secondary" Label="Logout" OnClick="@Logout" />
3839

3940
@if (state.Identity?.SessionState is not null && state.Identity.SessionState != SessionState.Active)

samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Layout/MainLayout.razor.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
using CodeBeam.UltimateAuth.Client;
2+
using CodeBeam.UltimateAuth.Client.Errors;
3+
using CodeBeam.UltimateAuth.Core.Contracts;
24
using CodeBeam.UltimateAuth.Core.Domain;
5+
using CodeBeam.UltimateAuth.Core.Errors;
36
using CodeBeam.UltimateAuth.Sample.BlazorServer.Infrastructure;
47
using Microsoft.AspNetCore.Components;
58
using MudBlazor;
@@ -56,6 +59,57 @@ private void HandleSignInClick()
5659
GoToLoginWithReturn();
5760
}
5861

62+
private async Task Validate()
63+
{
64+
try
65+
{
66+
var result = await UAuthClient.Flows.ValidateAsync();
67+
68+
if (result.IsValid)
69+
{
70+
if (result.Snapshot?.Identity.UserStatus == UserStatus.SelfSuspended)
71+
{
72+
Snackbar.Add("Your account is suspended by you.", Severity.Warning);
73+
return;
74+
}
75+
Snackbar.Add($"Session active • Tenant: {result.Snapshot?.Identity?.Tenant.Value} • User: {result.Snapshot?.Identity?.PrimaryUserName}", Severity.Success);
76+
}
77+
else
78+
{
79+
switch (result.State)
80+
{
81+
case SessionState.Expired:
82+
Snackbar.Add("Session expired. Please sign in again.", Severity.Warning);
83+
break;
84+
85+
case SessionState.DeviceMismatch:
86+
Snackbar.Add("Session invalid for this device.", Severity.Error);
87+
break;
88+
89+
default:
90+
Snackbar.Add($"Session state: {result.State}", Severity.Error);
91+
break;
92+
}
93+
}
94+
}
95+
catch (UAuthTransportException)
96+
{
97+
Snackbar.Add("Network error.", Severity.Error);
98+
}
99+
catch (UAuthProtocolException)
100+
{
101+
Snackbar.Add("Invalid response.", Severity.Error);
102+
}
103+
catch (UAuthException ex)
104+
{
105+
Snackbar.Add($"UAuth error: {ex.Message}", Severity.Error);
106+
}
107+
catch (Exception ex)
108+
{
109+
Snackbar.Add($"Unexpected error: {ex.Message}", Severity.Error);
110+
}
111+
}
112+
59113
private void GoToLoginWithReturn()
60114
{
61115
var uri = Nav.ToAbsoluteUri(Nav.Uri);

samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Pages/Home.razor

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,25 @@
88
@inject ISnackbar Snackbar
99
@inject IDialogService DialogService
1010
@using System.Security.Claims
11+
@using CodeBeam.UltimateAuth.Core.Contracts
12+
@using CodeBeam.UltimateAuth.Sample.BlazorServer.Components.Custom
13+
14+
@if (AuthState?.Identity?.UserStatus == UserStatus.SelfSuspended)
15+
{
16+
<UAuthPageComponent>
17+
<MudStack>
18+
<MudAlert Severity="Severity.Warning">
19+
Your account is suspended. Please active it before continue.
20+
</MudAlert>
21+
22+
<MudStack Class="mud-width-full" Row="true" Justify="Justify.Center">
23+
<MudButton Color="Color.Primary" Variant="Variant.Filled" OnClick="SetAccountActiveAsync">Set Active</MudButton>
24+
<MudButton Color="Color.Error" Variant="Variant.Outlined" OnClick="Logout">Logout</MudButton>
25+
</MudStack>
26+
</MudStack>
27+
</UAuthPageComponent>
28+
return;
29+
}
1130

1231
<MudPage Class="mud-width-full" FullScreen="FullScreen.FullWithoutAppbar" Column="1" Row="1">
1332
<MudContainer Class="pb-4" MaxWidth="MaxWidth.Medium">
@@ -23,7 +42,7 @@
2342
<MudDivider Class="my-4" />
2443
<MudText Style="font-weight: 300; letter-spacing: 0.5rem" Typo="Typo.h5">@AuthState?.Identity?.DisplayName</MudText>
2544
<MudChipSet T="string" Class="mt-2" Size="Size.Small" Variant="Variant.Outlined">
26-
@foreach (var role in AuthState.Claims.Roles)
45+
@foreach (var role in AuthState?.Claims?.Roles ?? Enumerable.Empty<string>())
2746
{
2847
<MudChip Color="Color.Primary">
2948
@role
@@ -68,7 +87,7 @@
6887
<MudIcon Icon="@Icons.Material.Filled.Security" Color="Color.Primary" Size="Size.Small" />
6988
<MudText Typo="Typo.subtitle2" Color="Color.Primary">Authenticated</MudText>
7089
</MudStack>
71-
<MudText Align="Align.Center">@(AuthState.IsAuthenticated ? "Yes" : "No")</MudText>
90+
<MudText Align="Align.Center">@(AuthState?.IsAuthenticated == true ? "Yes" : "No")</MudText>
7291
</div>
7392
</MudItem>
7493

@@ -237,8 +256,8 @@
237256
Manage Credentials
238257
</MudButton>
239258

240-
<MudButton FullWidth Variant="Variant.Outlined" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Delete" OnClick="DeleteAccountAsync">
241-
Delete Account
259+
<MudButton FullWidth Variant="Variant.Outlined" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Delete" OnClick="OpenAccountStatusDialog">
260+
Suspend | Delete Account
242261
</MudButton>
243262

244263
<MudDivider Class="my-2" />
@@ -252,7 +271,7 @@
252271
</UAuthStateView>
253272
</MudStack>
254273

255-
@if (AuthState.IsInRole("Admin") || _showAdminPreview)
274+
@if (AuthState?.IsInRole("Admin") == true || _showAdminPreview)
256275
{
257276
<MudGrid Spacing="2">
258277
<MudItem xs="12" sm="6">

samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/Components/Pages/Home.razor.cs

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
using CodeBeam.UltimateAuth.Client;
22
using CodeBeam.UltimateAuth.Client.Errors;
3+
using CodeBeam.UltimateAuth.Core.Contracts;
34
using CodeBeam.UltimateAuth.Core.Domain;
45
using CodeBeam.UltimateAuth.Core.Errors;
56
using CodeBeam.UltimateAuth.Sample.BlazorServer.Components.Dialogs;
67
using CodeBeam.UltimateAuth.Users.Contracts;
7-
using Microsoft.AspNetCore.Components;
88
using Microsoft.AspNetCore.Components.Authorization;
99
using MudBlazor;
1010
using System.Security.Claims;
@@ -62,6 +62,11 @@ private async Task Validate()
6262

6363
if (result.IsValid)
6464
{
65+
if (result.Snapshot?.Identity.UserStatus == UserStatus.SelfSuspended)
66+
{
67+
Snackbar.Add("Your account is suspended by you.", Severity.Warning);
68+
return;
69+
}
6570
Snackbar.Add($"Session active • Tenant: {result.Snapshot?.Identity?.Tenant.Value} • User: {result.Snapshot?.Identity?.PrimaryUserName}", Severity.Success);
6671
}
6772
else
@@ -100,31 +105,6 @@ private async Task Validate()
100105
}
101106
}
102107

103-
private async Task DeleteAccountAsync()
104-
{
105-
var info = await DialogService.ShowMessageBoxAsync(
106-
title: "Are You Sure",
107-
markupMessage: (MarkupString)
108-
"""
109-
You are going to delete your account.<br/><br/>
110-
This action can't be undone.<br/><br/>
111-
(Actually it is, admin can handle soft deleted accounts.)
112-
""",
113-
yesText: "Delete");
114-
115-
if (info != true)
116-
{
117-
Snackbar.Add("Deletion cancelled", Severity.Info);
118-
return;
119-
}
120-
121-
var result = await UAuthClient.Users.DeleteMeAsync();
122-
if (result.IsSuccess)
123-
{
124-
Snackbar.Add("Your account deleted successfully.", Severity.Success);
125-
}
126-
}
127-
128108
private Color GetHealthColor()
129109
{
130110
if (Diagnostics.RefreshReauthRequiredCount > 0)
@@ -194,6 +174,11 @@ private async Task OpenCredentialDialog()
194174
await DialogService.ShowAsync<CredentialDialog>("Session Diagnostics", GetDialogParameters(), GetDialogOptions());
195175
}
196176

177+
private async Task OpenAccountStatusDialog()
178+
{
179+
await DialogService.ShowAsync<AccountStatusDialog>("Manage Account", GetDialogParameters(), GetDialogOptions());
180+
}
181+
197182
private DialogOptions GetDialogOptions()
198183
{
199184
return new DialogOptions
@@ -205,13 +190,28 @@ private DialogOptions GetDialogOptions()
205190
}
206191

207192
private DialogParameters GetDialogParameters()
208-
{
193+
{
209194
return new DialogParameters
210195
{
211196
["AuthState"] = AuthState
212197
};
213198
}
214199

200+
private async Task SetAccountActiveAsync()
201+
{
202+
ChangeUserStatusSelfRequest request = new() { NewStatus = SelfUserStatus.Active };
203+
var result = await UAuthClient.Users.ChangeStatusSelfAsync(request);
204+
205+
if (result.IsSuccess)
206+
{
207+
Snackbar.Add("Account activated successfully.", Severity.Success);
208+
}
209+
else
210+
{
211+
Snackbar.Add(result?.Problem?.Detail ?? result?.Problem?.Title ?? "Activation failed.", Severity.Error);
212+
}
213+
}
214+
215215
public override void Dispose()
216216
{
217217
base.Dispose();

src/CodeBeam.UltimateAuth.Client/AuthState/UAuthState.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,19 @@ internal void UpdateProfile(UpdateProfileRequest req)
5858
Changed?.Invoke(UAuthStateChangeReason.Patched);
5959
}
6060

61+
internal void UpdateUserStatus(ChangeUserStatusSelfRequest req)
62+
{
63+
if (Identity is null)
64+
return;
65+
66+
Identity = Identity with
67+
{
68+
UserStatus = UserStatusMapper.ToUserStatus(req.NewStatus)
69+
};
70+
71+
Changed?.Invoke(UAuthStateChangeReason.Patched);
72+
}
73+
6174
internal void MarkValidated(DateTimeOffset now)
6275
{
6376
if (!IsAuthenticated)

src/CodeBeam.UltimateAuth.Client/AuthState/UAuthStateEvent.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ public enum UAuthStateEvent
44
{
55
ValidationCalled,
66
IdentifiersChanged,
7+
UserStatusChanged,
78
ProfileChanged,
89
CredentialsChanged,
910
CredentialsChangedSelf,

0 commit comments

Comments
 (0)