Skip to content

Commit 1aafa3b

Browse files
committed
Complete Credential Change & Tests
1 parent 342df09 commit 1aafa3b

File tree

17 files changed

+378
-144
lines changed

17 files changed

+378
-144
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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+
<MudStack Class="mud-width-full">
18+
<MudTextField @bind-Value="_oldPassword" Label="Old Password" Variant="Variant.Outlined" Required="true" />
19+
<MudTextField @bind-Value="_newPassword" Label="New Password" Variant="Variant.Outlined" Required="true" />
20+
<MudTextField @bind-Value="_newPasswordCheck" Label="New Password (Again)" Variant="Variant.Outlined" Required="true" Validation="@(new Func<string, string>(PasswordMatch))" />
21+
<MudButton Color="Color.Primary" Variant="Variant.Filled" OnClick="ChangePasswordAsync">Change Password</MudButton>
22+
</MudStack>
23+
</MudForm>
24+
</DialogContent>
25+
<DialogActions>
26+
<MudButton OnClick="Cancel">Cancel</MudButton>
27+
<MudButton Color="Color.Primary" OnClick="Submit">OK</MudButton>
28+
</DialogActions>
29+
</MudDialog>
30+
31+
@code {
32+
private MudForm _form = null!;
33+
private string? _oldPassword;
34+
private string? _newPassword;
35+
private string? _newPasswordCheck;
36+
37+
[CascadingParameter]
38+
private IMudDialogInstance MudDialog { get; set; } = default!;
39+
40+
[Parameter]
41+
public UAuthState AuthState { get; set; } = default!;
42+
43+
protected override async Task OnAfterRenderAsync(bool firstRender)
44+
{
45+
await base.OnAfterRenderAsync(firstRender);
46+
47+
if (firstRender)
48+
{
49+
50+
}
51+
}
52+
53+
private async Task ChangePasswordAsync()
54+
{
55+
if (_form is null)
56+
return;
57+
58+
await _form.Validate();
59+
if (!_form.IsValid)
60+
{
61+
Snackbar.Add("Form is not valid.", Severity.Error);
62+
return;
63+
}
64+
65+
66+
if (_newPassword != _newPasswordCheck)
67+
{
68+
Snackbar.Add("New password and check do not match", Severity.Error);
69+
return;
70+
}
71+
72+
ChangeCredentialRequest request = new ChangeCredentialRequest
73+
{
74+
CurrentSecret = _oldPassword!,
75+
NewSecret = _newPassword!,
76+
};
77+
78+
var result = await UAuthClient.Credentials.ChangeMyAsync(request);
79+
if (result.IsSuccess)
80+
{
81+
Snackbar.Add("Password changed successfully", Severity.Success);
82+
await UAuthClient.Flows.LogoutAsync();
83+
}
84+
else
85+
{
86+
Snackbar.Add(result?.Problem?.Detail ?? result?.Problem?.Title ?? "An error occurred while changing password", Severity.Error);
87+
}
88+
}
89+
90+
private string PasswordMatch(string arg) => _newPassword != arg ? "Passwords don't match" : null;
91+
92+
private void Submit() => MudDialog.Close(DialogResult.Ok(true));
93+
94+
private void Cancel() => MudDialog.Cancel();
95+
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,8 @@
226226
Manage Identifiers
227227
</MudButton>
228228

229-
<MudButton FullWidth Variant="Variant.Outlined" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Password">
230-
Change Password
229+
<MudButton FullWidth Variant="Variant.Outlined" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Password" OnClick="OpenCredentialDialog">
230+
Manage Credentials
231231
</MudButton>
232232

233233
<MudDivider Class="my-2" />

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,11 @@ private async Task OpenSessionDialog()
161161
await DialogService.ShowAsync<SessionDialog>("Manage Sessions", GetDialogParameters(), GetDialogOptions());
162162
}
163163

164+
private async Task OpenCredentialDialog()
165+
{
166+
await DialogService.ShowAsync<CredentialDialog>("Session Diagnostics", GetDialogParameters(), GetDialogOptions());
167+
}
168+
164169
private DialogOptions GetDialogOptions()
165170
{
166171
return new DialogOptions

src/CodeBeam.UltimateAuth.Client/Services/ICredentialClient.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@ public interface ICredentialClient
88
{
99
Task<UAuthResult<GetCredentialsResult>> GetMyAsync();
1010
Task<UAuthResult<AddCredentialResult>> AddMyAsync(AddCredentialRequest request);
11-
Task<UAuthResult<ChangeCredentialResult>> ChangeMyAsync(CredentialType type, ChangeCredentialRequest request);
12-
Task<UAuthResult> RevokeMyAsync(CredentialType type, RevokeCredentialRequest request);
13-
Task<UAuthResult> BeginResetMyAsync(CredentialType type, BeginCredentialResetRequest request);
14-
Task<UAuthResult> CompleteResetMyAsync(CredentialType type, CompleteCredentialResetRequest request);
11+
Task<UAuthResult<ChangeCredentialResult>> ChangeMyAsync(ChangeCredentialRequest request);
12+
Task<UAuthResult> RevokeMyAsync(RevokeCredentialRequest request);
13+
Task<UAuthResult> BeginResetMyAsync(BeginCredentialResetRequest request);
14+
Task<UAuthResult> CompleteResetMyAsync(CompleteCredentialResetRequest request);
1515

1616
Task<UAuthResult<GetCredentialsResult>> GetUserAsync(UserKey userKey);
1717
Task<UAuthResult<AddCredentialResult>> AddUserAsync(UserKey userKey, AddCredentialRequest request);
18-
Task<UAuthResult> RevokeUserAsync(UserKey userKey, CredentialType type, RevokeCredentialRequest request);
19-
Task<UAuthResult> ActivateUserAsync(UserKey userKey, CredentialType type);
20-
Task<UAuthResult> BeginResetUserAsync(UserKey userKey, CredentialType type, BeginCredentialResetRequest request);
21-
Task<UAuthResult> CompleteResetUserAsync(UserKey userKey, CredentialType type, CompleteCredentialResetRequest request);
22-
Task<UAuthResult> DeleteUserAsync(UserKey userKey, CredentialType type);
18+
Task<UAuthResult> RevokeUserAsync(UserKey userKey, RevokeCredentialRequest request);
19+
Task<UAuthResult> ActivateUserAsync(UserKey userKey);
20+
Task<UAuthResult> BeginResetUserAsync(UserKey userKey, BeginCredentialResetRequest request);
21+
Task<UAuthResult> CompleteResetUserAsync(UserKey userKey, CompleteCredentialResetRequest request);
22+
Task<UAuthResult> DeleteUserAsync(UserKey userKey);
2323
}

src/CodeBeam.UltimateAuth.Client/Services/UAuthCredentialClient.cs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,27 +32,27 @@ public async Task<UAuthResult<AddCredentialResult>> AddMyAsync(AddCredentialRequ
3232
return UAuthResultMapper.FromJson<AddCredentialResult>(raw);
3333
}
3434

35-
public async Task<UAuthResult<ChangeCredentialResult>> ChangeMyAsync(CredentialType type, ChangeCredentialRequest request)
35+
public async Task<UAuthResult<ChangeCredentialResult>> ChangeMyAsync(ChangeCredentialRequest request)
3636
{
37-
var raw = await _request.SendJsonAsync(Url($"/credentials/{type}/change"), request);
37+
var raw = await _request.SendJsonAsync(Url($"/credentials/change"), request);
3838
return UAuthResultMapper.FromJson<ChangeCredentialResult>(raw);
3939
}
4040

41-
public async Task<UAuthResult> RevokeMyAsync(CredentialType type, RevokeCredentialRequest request)
41+
public async Task<UAuthResult> RevokeMyAsync(RevokeCredentialRequest request)
4242
{
43-
var raw = await _request.SendJsonAsync(Url($"/credentials/{type}/revoke"), request);
43+
var raw = await _request.SendJsonAsync(Url($"/credentials/revoke"), request);
4444
return UAuthResultMapper.From(raw);
4545
}
4646

47-
public async Task<UAuthResult> BeginResetMyAsync(CredentialType type, BeginCredentialResetRequest request)
47+
public async Task<UAuthResult> BeginResetMyAsync(BeginCredentialResetRequest request)
4848
{
49-
var raw = await _request.SendJsonAsync(Url($"/credentials/{type}/reset/begin"), request);
49+
var raw = await _request.SendJsonAsync(Url($"/credentials/reset/begin"), request);
5050
return UAuthResultMapper.From(raw);
5151
}
5252

53-
public async Task<UAuthResult> CompleteResetMyAsync(CredentialType type, CompleteCredentialResetRequest request)
53+
public async Task<UAuthResult> CompleteResetMyAsync(CompleteCredentialResetRequest request)
5454
{
55-
var raw = await _request.SendJsonAsync(Url($"/credentials/{type}/reset/complete"), request);
55+
var raw = await _request.SendJsonAsync(Url($"/credentials/reset/complete"), request);
5656
return UAuthResultMapper.From(raw);
5757
}
5858

@@ -69,33 +69,33 @@ public async Task<UAuthResult<AddCredentialResult>> AddUserAsync(UserKey userKey
6969
return UAuthResultMapper.FromJson<AddCredentialResult>(raw);
7070
}
7171

72-
public async Task<UAuthResult> RevokeUserAsync(UserKey userKey, CredentialType type, RevokeCredentialRequest request)
72+
public async Task<UAuthResult> RevokeUserAsync(UserKey userKey, RevokeCredentialRequest request)
7373
{
74-
var raw = await _request.SendJsonAsync(Url($"/admin/users/{userKey}/credentials/{type}/revoke"), request);
74+
var raw = await _request.SendJsonAsync(Url($"/admin/users/{userKey}/credentials/revoke"), request);
7575
return UAuthResultMapper.From(raw);
7676
}
7777

78-
public async Task<UAuthResult> ActivateUserAsync(UserKey userKey, CredentialType type)
78+
public async Task<UAuthResult> ActivateUserAsync(UserKey userKey)
7979
{
80-
var raw = await _request.SendFormAsync(Url($"/admin/users/{userKey}/credentials/{type}/activate"));
80+
var raw = await _request.SendFormAsync(Url($"/admin/users/{userKey}/credentials/activate"));
8181
return UAuthResultMapper.From(raw);
8282
}
8383

84-
public async Task<UAuthResult> BeginResetUserAsync(UserKey userKey, CredentialType type, BeginCredentialResetRequest request)
84+
public async Task<UAuthResult> BeginResetUserAsync(UserKey userKey, BeginCredentialResetRequest request)
8585
{
86-
var raw = await _request.SendJsonAsync(Url($"/admin/users/{userKey}/credentials/{type}/reset/begin"), request);
86+
var raw = await _request.SendJsonAsync(Url($"/admin/users/{userKey}/credentials/reset/begin"), request);
8787
return UAuthResultMapper.From(raw);
8888
}
8989

90-
public async Task<UAuthResult> CompleteResetUserAsync(UserKey userKey, CredentialType type, CompleteCredentialResetRequest request)
90+
public async Task<UAuthResult> CompleteResetUserAsync(UserKey userKey, CompleteCredentialResetRequest request)
9191
{
92-
var raw = await _request.SendJsonAsync(Url($"/admin/users/{userKey}/credentials/{type}/reset/complete"), request);
92+
var raw = await _request.SendJsonAsync(Url($"/admin/users/{userKey}/credentials/reset/complete"), request);
9393
return UAuthResultMapper.From(raw);
9494
}
9595

96-
public async Task<UAuthResult> DeleteUserAsync(UserKey userKey, CredentialType type)
96+
public async Task<UAuthResult> DeleteUserAsync(UserKey userKey)
9797
{
98-
var raw = await _request.SendFormAsync(Url($"/admin/users/{userKey}/credentials/{type}/delete"));
98+
var raw = await _request.SendFormAsync(Url($"/admin/users/{userKey}/credentials/delete"));
9999
return UAuthResultMapper.From(raw);
100100
}
101101

src/credentials/CodeBeam.UltimateAuth.Credentials.Contracts/Dtos/CredentialDto.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,6 @@ public sealed record CredentialDto
2121
public DateTimeOffset? ResetRequestedAt { get; init; }
2222

2323
public string? Source { get; init; }
24+
25+
public long Version { get; init; }
2426
}

src/credentials/CodeBeam.UltimateAuth.Credentials.Contracts/Responses/ChangeCredentialResult.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace CodeBeam.UltimateAuth.Credentials.Contracts;
44

55
public sealed record ChangeCredentialResult
66
{
7-
public bool Succeeded { get; init; }
7+
public bool IsSuccess { get; init; }
88

99
public string? Error { get; init; }
1010

@@ -13,14 +13,14 @@ public sealed record ChangeCredentialResult
1313
public static ChangeCredentialResult Success(CredentialType type)
1414
=> new()
1515
{
16-
Succeeded = true,
16+
IsSuccess = true,
1717
Type = type
1818
};
1919

2020
public static ChangeCredentialResult Fail(string error)
2121
=> new()
2222
{
23-
Succeeded = false,
23+
IsSuccess = false,
2424
Error = error
2525
};
2626
}

src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/InMemoryCredentialSeedContributor.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,14 @@ private async Task SeedCredentialAsync(UserKey userKey, Guid credentialId, strin
3838
{
3939
await _credentials.AddAsync(
4040
tenant,
41-
new PasswordCredential(
41+
PasswordCredential.Create(
4242
credentialId,
4343
tenant,
4444
userKey,
4545
_hasher.Hash(secretHash),
4646
CredentialSecurityState.Active(),
4747
new CredentialMetadata(),
48-
DateTimeOffset.UtcNow,
49-
null),
48+
DateTimeOffset.UtcNow),
5049
ct);
5150
}
5251
catch (UAuthConflictException)

src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/InMemoryCredentialStore.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using CodeBeam.UltimateAuth.Core.MultiTenancy;
66
using CodeBeam.UltimateAuth.Credentials.Reference;
77
using System.Collections.Concurrent;
8+
using System.Text;
89

910
namespace CodeBeam.UltimateAuth.Credentials.InMemory;
1011

@@ -57,10 +58,10 @@ public Task UpdateAsync(TenantKey tenant, ICredential credential, long expectedV
5758

5859
var key = (tenant, pwd.Id);
5960

60-
if (!_store.ContainsKey(key))
61+
if (!_store.TryGetValue(key, out var current))
6162
throw new UAuthNotFoundException("credential_not_found");
6263

63-
if (pwd.Version != expectedVersion)
64+
if (current.Version != expectedVersion)
6465
throw new UAuthConflictException("credential_version_conflict");
6566

6667
_store[key] = pwd;

0 commit comments

Comments
 (0)