From 6b6ac4eb7653a1001ffe641a3407d06fb33fa1c7 Mon Sep 17 00:00:00 2001 From: Oleksii Holub <1935960+Tyrrrz@users.noreply.github.com> Date: Wed, 14 Aug 2024 20:30:36 +0300 Subject: [PATCH 1/3] Add support for credential hints to the register flow --- src/AdminConsole/AdminConsole.csproj | 2 +- .../Pages/App/Playground/NewAccount.cshtml | 10 ++++-- .../Pages/App/Playground/NewAccount.cshtml.cs | 34 +++++++------------ src/Service/Fido2Service.cs | 4 ++- src/Service/Models/RegisterToken.cs | 14 ++++++-- src/Service/TokenService.cs | 22 +++++------- 6 files changed, 44 insertions(+), 42 deletions(-) diff --git a/src/AdminConsole/AdminConsole.csproj b/src/AdminConsole/AdminConsole.csproj index 2cc1a2ebc..fcb1cd589 100644 --- a/src/AdminConsole/AdminConsole.csproj +++ b/src/AdminConsole/AdminConsole.csproj @@ -23,7 +23,7 @@ - + diff --git a/src/AdminConsole/Pages/App/Playground/NewAccount.cshtml b/src/AdminConsole/Pages/App/Playground/NewAccount.cshtml index 8fd1bd045..08bac95d1 100644 --- a/src/AdminConsole/Pages/App/Playground/NewAccount.cshtml +++ b/src/AdminConsole/Pages/App/Playground/NewAccount.cshtml @@ -12,7 +12,6 @@ var requestToken = Antiforgery.GetAndStoreTokens(Model.HttpContext).RequestToken; } -
@@ -85,6 +84,13 @@ }
+
+ +
+ + +
+
@@ -127,7 +133,7 @@ const createNewAccount = async (e) => { if (req.ok) { const { token } = await req.json(); const nicknameForDevice = data.get("nickname"); - const { error } = await p.register(token , nicknameForDevice); + const { error } = await p.register(token, nicknameForDevice); if (error) { console.error(error); diff --git a/src/AdminConsole/Pages/App/Playground/NewAccount.cshtml.cs b/src/AdminConsole/Pages/App/Playground/NewAccount.cshtml.cs index ac2ed0e32..aec51b24e 100644 --- a/src/AdminConsole/Pages/App/Playground/NewAccount.cshtml.cs +++ b/src/AdminConsole/Pages/App/Playground/NewAccount.cshtml.cs @@ -5,33 +5,28 @@ namespace Passwordless.AdminConsole.Pages.App.Playground; -public class NewAccountModel : PageModel +public class NewAccountModel(IScopedPasswordlessClient passwordlessClient) + : PageModel { - private readonly ILogger _logger; - private readonly IScopedPasswordlessClient _passwordlessClient; + [MaxLength(64)] + public string Nickname { get; set; } = ""; - public NewAccountModel(ILogger logger, IScopedPasswordlessClient passwordlessClient) - { - _logger = logger; - this._passwordlessClient = passwordlessClient; - } + public string Attestation { get; set; } = "none"; - public void OnGet() - { + public string Hints { get; set; } = ""; - } - - public async Task OnPostToken(string name, string email, string attestation) + public async Task OnPostToken(string name, string email, string attestation, string hints) { try { var userId = Guid.NewGuid().ToString(); - var token = await _passwordlessClient.CreateRegisterTokenAsync(new RegisterOptions(userId, $"Playground: {email}") + var token = await passwordlessClient.CreateRegisterTokenAsync(new RegisterOptions(userId, $"Playground: {email}") { DisplayName = name, - Aliases = new HashSet(1) { email }, + Aliases = [email], AliasHashing = false, - Attestation = attestation + Attestation = attestation, + Hints = hints.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries) }); return new JsonResult(token); @@ -44,12 +39,7 @@ public async Task OnPostToken(string name, string email, string a public async Task OnPost(string token) { - var res = await _passwordlessClient.VerifyAuthenticationTokenAsync(token); + var res = await passwordlessClient.VerifyAuthenticationTokenAsync(token); return new JsonResult(res); } - - [MaxLength(64)] - public string Nickname { get; set; } - - public string Attestation { get; set; } = "none"; } \ No newline at end of file diff --git a/src/Service/Fido2Service.cs b/src/Service/Fido2Service.cs index dbcf717df..6ea62733f 100644 --- a/src/Service/Fido2Service.cs +++ b/src/Service/Fido2Service.cs @@ -74,7 +74,7 @@ public async Task CreateRegisterTokenAsync(RegisterToken tokenProps) if (string.IsNullOrEmpty(tokenProps.Attestation)) tokenProps.Attestation = "none"; TokenValidator.ValidateAttestation(tokenProps, features); - // check if aliases is available + // Check if aliases is available if (tokenProps.Aliases != null) { var hashedAliases = tokenProps.Aliases.Select(alias => HashAlias(alias, _tenantProvider.Tenant)); @@ -152,6 +152,8 @@ public async Task> RegisterBeginAsync(F CredProps = true }); + options.Hints = token.Hints; + var session = await _tokenService.EncodeTokenAsync( new RegisterSession { diff --git a/src/Service/Models/RegisterToken.cs b/src/Service/Models/RegisterToken.cs index 7704561c9..582423042 100644 --- a/src/Service/Models/RegisterToken.cs +++ b/src/Service/Models/RegisterToken.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using Fido2NetLib.Objects; using MessagePack; using Passwordless.Common.Validation; @@ -20,17 +21,26 @@ public class RegisterToken : Token [MessagePack.Key(13)] public string Attestation { get; set; } = "None"; + [MessagePack.Key(14)] public string AuthenticatorType { get; set; } + [MessagePack.Key(15)] public bool Discoverable { get; set; } = true; + [MessagePack.Key(16)] public string UserVerification { get; set; } = "Preferred"; + [MessagePack.Key(17)] [MaxLength(10), MaxLengthCollection(250), RequiredCollection(AllowEmptyStrings = false)] - public HashSet Aliases { get; set; } + public HashSet Aliases { get; set; } = []; - [MessagePack.Key(18)] public bool AliasHashing { get; set; } = true; + [MessagePack.Key(18)] + public bool AliasHashing { get; set; } = true; + + [MessagePack.Key(19)] + [MaxLength(3)] + public IReadOnlyList Hints { get; set; } = []; } [MessagePackObject] diff --git a/src/Service/TokenService.cs b/src/Service/TokenService.cs index 1d110662b..26302ac4e 100644 --- a/src/Service/TokenService.cs +++ b/src/Service/TokenService.cs @@ -149,31 +149,25 @@ private async Task> GetRandomKeyAsync() public async Task EncodeTokenAsync(T token, string prefix, bool contractless = false) { - byte[] msgpack; - if (contractless) - { - msgpack = MessagePackSerializer.Serialize(token, ContractlessStandardResolver.Options); - } - else - { - msgpack = MessagePackSerializer.Serialize(token); - } + var msgpack = contractless + ? MessagePackSerializer.Serialize(token, ContractlessStandardResolver.Options) + : MessagePackSerializer.Serialize(token); - (Key key, int keyId) = await GetRandomKeyAsync(); + (Key key, var keyId) = await GetRandomKeyAsync(); _log.LogInformation("Encoding using keyId={keyId}", keyId); var mac = CreateMac(key, msgpack); var envelope = new MacEnvelope { Mac = mac, Token = msgpack, KeyId = keyId }; - var envelop_binary = MessagePackSerializer.Serialize(envelope); - var envelop_binary_b64 = Base64Url.Encode(envelop_binary); + var envelopeBinary = MessagePackSerializer.Serialize(envelope); + var envelopeBinaryB64 = Base64Url.Encode(envelopeBinary); if (!string.IsNullOrEmpty(prefix)) { - return prefix + envelop_binary_b64; + return prefix + envelopeBinaryB64; } - return envelop_binary_b64; + return envelopeBinaryB64; } /// From 474cf418b5350adce762bea350645e01c408291d Mon Sep 17 00:00:00 2001 From: Oleksii Holub <1935960+Tyrrrz@users.noreply.github.com> Date: Thu, 15 Aug 2024 23:52:58 +0300 Subject: [PATCH 2/3] asd --- src/Service/Models/RegisterToken.cs | 2 +- .../Implementations/Fido2ServiceTests.cs | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Service/Models/RegisterToken.cs b/src/Service/Models/RegisterToken.cs index 582423042..5db89aa67 100644 --- a/src/Service/Models/RegisterToken.cs +++ b/src/Service/Models/RegisterToken.cs @@ -33,7 +33,7 @@ public class RegisterToken : Token [MessagePack.Key(17)] [MaxLength(10), MaxLengthCollection(250), RequiredCollection(AllowEmptyStrings = false)] - public HashSet Aliases { get; set; } = []; + public HashSet? Aliases { get; set; } = null; [MessagePack.Key(18)] public bool AliasHashing { get; set; } = true; diff --git a/tests/Service.Tests/Implementations/Fido2ServiceTests.cs b/tests/Service.Tests/Implementations/Fido2ServiceTests.cs index f0bc8faa2..51cf41955 100644 --- a/tests/Service.Tests/Implementations/Fido2ServiceTests.cs +++ b/tests/Service.Tests/Implementations/Fido2ServiceTests.cs @@ -74,7 +74,7 @@ public async Task CreateRegisterToken_Throws_ApiException_WhenMaxUsersExceededFo .ReturnsAsync("test_token"); _mockFeatureContextProvider.Setup(x => x.UseContext()).ReturnsAsync(new FeaturesContext(false, 0, null, 10000, false, true, true)); _mockTenantStorage.Setup(x => x.GetUsersCount()).ReturnsAsync(10000); - _mockTenantStorage.Setup(x => x.GetCredentialsByUserIdAsync(It.Is(p => p == "test"))).ReturnsAsync(new List(0)); + _mockTenantStorage.Setup(x => x.GetCredentialsByUserIdAsync(It.Is(p => p == "test"))).ReturnsAsync([]); // act var actual = await Assert.ThrowsAsync(async () => @@ -104,7 +104,19 @@ public async Task CreateRegisterToken_Works_WhenMaxUsersExceededForExistingUser( _mockFeatureContextProvider.Setup(x => x.UseContext()).ReturnsAsync(new FeaturesContext(false, 0, null, 10000, false, true, true)); _mockTenantStorage.Setup(x => x.GetUsersCount()).ReturnsAsync(10000); _mockTenantStorage.Setup(x => x.GetCredentialsByUserIdAsync(It.Is(p => p == "test"))).ReturnsAsync( - new List(1) { new() { UserHandle = "test"u8.ToArray(), Descriptor = null!, Origin = null!, AttestationFmt = null!, CreatedAt = DateTime.UtcNow, PublicKey = null!, SignatureCounter = 123, RPID = null! } }); + [ + new StoredCredential + { + UserHandle = "test"u8.ToArray(), + Descriptor = null!, + Origin = null!, + AttestationFmt = null!, + CreatedAt = DateTime.UtcNow, + PublicKey = null!, + SignatureCounter = 123, + RPID = null! + } + ]); // act var actual = await _sut.CreateRegisterTokenAsync(new RegisterToken From e64329d2e30eafd65b3925b34242666c03f3b284 Mon Sep 17 00:00:00 2001 From: Oleksii Holub <1935960+Tyrrrz@users.noreply.github.com> Date: Fri, 16 Aug 2024 17:54:09 +0300 Subject: [PATCH 3/3] Update src/Service/Models/RegisterToken.cs --- src/Service/Models/RegisterToken.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service/Models/RegisterToken.cs b/src/Service/Models/RegisterToken.cs index 5db89aa67..a527b7487 100644 --- a/src/Service/Models/RegisterToken.cs +++ b/src/Service/Models/RegisterToken.cs @@ -33,7 +33,7 @@ public class RegisterToken : Token [MessagePack.Key(17)] [MaxLength(10), MaxLengthCollection(250), RequiredCollection(AllowEmptyStrings = false)] - public HashSet? Aliases { get; set; } = null; + public HashSet? Aliases { get; set; } [MessagePack.Key(18)] public bool AliasHashing { get; set; } = true;