From cfd56a7cfffd130f02f935f544a75e90254c97f1 Mon Sep 17 00:00:00 2001 From: Roman Bukin Date: Thu, 8 Feb 2024 16:02:37 +0300 Subject: [PATCH 1/5] Upgrade .NET dependencies --- .../WebAuthn.Net.Demo.FidoConformance.csproj | 2 +- .../WebAuthn.Net.Storage.MySql.csproj | 2 +- .../WebAuthn.Net.Storage.SqlServer.csproj | 2 +- tests/WebAuthn.Net.Tests.Unit/WebAuthn.Net.Tests.Unit.csproj | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/demo/WebAuthn.Net.Demo.FidoConformance/WebAuthn.Net.Demo.FidoConformance.csproj b/demo/WebAuthn.Net.Demo.FidoConformance/WebAuthn.Net.Demo.FidoConformance.csproj index e16eeb3..b796cab 100644 --- a/demo/WebAuthn.Net.Demo.FidoConformance/WebAuthn.Net.Demo.FidoConformance.csproj +++ b/demo/WebAuthn.Net.Demo.FidoConformance/WebAuthn.Net.Demo.FidoConformance.csproj @@ -35,7 +35,7 @@ - + diff --git a/src/WebAuthn.Net.Storage.MySql/WebAuthn.Net.Storage.MySql.csproj b/src/WebAuthn.Net.Storage.MySql/WebAuthn.Net.Storage.MySql.csproj index fafb23a..0c3d6b9 100644 --- a/src/WebAuthn.Net.Storage.MySql/WebAuthn.Net.Storage.MySql.csproj +++ b/src/WebAuthn.Net.Storage.MySql/WebAuthn.Net.Storage.MySql.csproj @@ -32,6 +32,6 @@ - + diff --git a/src/WebAuthn.Net.Storage.SqlServer/WebAuthn.Net.Storage.SqlServer.csproj b/src/WebAuthn.Net.Storage.SqlServer/WebAuthn.Net.Storage.SqlServer.csproj index 434cbf0..31f46e4 100644 --- a/src/WebAuthn.Net.Storage.SqlServer/WebAuthn.Net.Storage.SqlServer.csproj +++ b/src/WebAuthn.Net.Storage.SqlServer/WebAuthn.Net.Storage.SqlServer.csproj @@ -23,7 +23,7 @@ - + diff --git a/tests/WebAuthn.Net.Tests.Unit/WebAuthn.Net.Tests.Unit.csproj b/tests/WebAuthn.Net.Tests.Unit/WebAuthn.Net.Tests.Unit.csproj index dce86aa..856c62b 100644 --- a/tests/WebAuthn.Net.Tests.Unit/WebAuthn.Net.Tests.Unit.csproj +++ b/tests/WebAuthn.Net.Tests.Unit/WebAuthn.Net.Tests.Unit.csproj @@ -9,9 +9,9 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive From ba0f15d708d4407d7f02501052a15bbe5cc5a1ed Mon Sep 17 00:00:00 2001 From: Roman Bukin Date: Fri, 9 Feb 2024 14:38:17 +0300 Subject: [PATCH 2/5] Improve MySQL queries and adopt README.md --- src/WebAuthn.Net.Storage.MySql/README.md | 12 +++++++--- .../DefaultMySqlCredentialStorage.cs | 23 ++++++++++--------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/WebAuthn.Net.Storage.MySql/README.md b/src/WebAuthn.Net.Storage.MySql/README.md index 9e63573..14c9bb7 100644 --- a/src/WebAuthn.Net.Storage.MySql/README.md +++ b/src/WebAuthn.Net.Storage.MySql/README.md @@ -40,11 +40,17 @@ CREATE TABLE `CredentialRecords` ) CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci; -CREATE UNIQUE INDEX `IX_CredentialRecords_RpId_UserHandle_CredentialId` ON `CredentialRecords` +CREATE UNIQUE INDEX `IX_CredentialRecords_UserHandle_CredentialId_RpId` ON `CredentialRecords` ( - `RpId`, `UserHandle`, - `CredentialId` + `CredentialId`, + `RpId` +); + +CREATE UNIQUE INDEX `IX_CredentialRecords_CredentialId_RpId` ON `CredentialRecords` +( + `CredentialId`, + `RpId` ); ``` diff --git a/src/WebAuthn.Net.Storage.MySql/Storage/CredentialStorage/DefaultMySqlCredentialStorage.cs b/src/WebAuthn.Net.Storage.MySql/Storage/CredentialStorage/DefaultMySqlCredentialStorage.cs index 29aee73..26947b0 100644 --- a/src/WebAuthn.Net.Storage.MySql/Storage/CredentialStorage/DefaultMySqlCredentialStorage.cs +++ b/src/WebAuthn.Net.Storage.MySql/Storage/CredentialStorage/DefaultMySqlCredentialStorage.cs @@ -46,8 +46,9 @@ public virtual async Task FindDescriptorsAsync( ArgumentNullException.ThrowIfNull(context); cancellationToken.ThrowIfCancellationRequested(); var dbPublicKeysEnumerable = await context.Connection.QueryAsync(new(@" -SELECT `Type`, `CredentialId`, `Transports`, `CreatedAtUnixTime` FROM `CredentialRecords` -WHERE `RpId` = @rpId AND `UserHandle` = @userHandle;", +SELECT `Type`, `CredentialId`, `Transports`, `CreatedAtUnixTime` +FROM `CredentialRecords` +WHERE `UserHandle` = @userHandle AND `RpId` = @rpId;", new { rpId, @@ -93,7 +94,7 @@ public virtual async Task FindDescriptorsAsync( var exisingId = await context.Connection.QuerySingleOrDefaultAsync(new(@" SELECT `Id` FROM `CredentialRecords` -WHERE `RpId` = @rpId AND `UserHandle` = @userHandle AND `CredentialId` = @credentialId;", +WHERE `UserHandle` = @userHandle AND `CredentialId` = @credentialId AND `RpId` = @rpId;", new { rpId, @@ -168,10 +169,9 @@ public virtual async Task SaveIfNotRegisteredForOtherUserAsync( cancellationToken.ThrowIfCancellationRequested(); var existingCount = await context.Connection.ExecuteScalarAsync(new( @" -SELECT COUNT(`Id`) FROM `CredentialRecords` -WHERE - `RpId` = @rpId - AND `CredentialId` = @credentialId;", +SELECT COUNT(`CredentialId`) +FROM `CredentialRecords` +WHERE `CredentialId` = @credentialId AND `RpId` = @rpId;", new { rpId = credential.RpId, @@ -290,11 +290,12 @@ public virtual async Task UpdateCredentialAsync( cancellationToken.ThrowIfCancellationRequested(); var recordIdToUpdate = await context.Connection.QuerySingleOrDefaultAsync(new( @" -SELECT `Id` FROM `CredentialRecords` +SELECT `Id` +FROM `CredentialRecords` WHERE - `RpId` = @rpId - AND `UserHandle` = @userHandle - AND `CredentialId` = @credentialId;", + `UserHandle` = @userHandle + AND `CredentialId` = @credentialId + AND `RpId` = @rpId;", new { rpId = credential.RpId, From df96af238a45a3e77f33599118de4754404eba4d Mon Sep 17 00:00:00 2001 From: Roman Bukin Date: Fri, 9 Feb 2024 14:43:50 +0300 Subject: [PATCH 3/5] Reformat SQL query --- .../CredentialStorage/DefaultMySqlCredentialStorage.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/WebAuthn.Net.Storage.MySql/Storage/CredentialStorage/DefaultMySqlCredentialStorage.cs b/src/WebAuthn.Net.Storage.MySql/Storage/CredentialStorage/DefaultMySqlCredentialStorage.cs index 26947b0..579a80e 100644 --- a/src/WebAuthn.Net.Storage.MySql/Storage/CredentialStorage/DefaultMySqlCredentialStorage.cs +++ b/src/WebAuthn.Net.Storage.MySql/Storage/CredentialStorage/DefaultMySqlCredentialStorage.cs @@ -292,10 +292,7 @@ public virtual async Task UpdateCredentialAsync( @" SELECT `Id` FROM `CredentialRecords` -WHERE - `UserHandle` = @userHandle - AND `CredentialId` = @credentialId - AND `RpId` = @rpId;", +WHERE `UserHandle` = @userHandle AND `CredentialId` = @credentialId AND `RpId` = @rpId;", new { rpId = credential.RpId, From a0c368aaea7b593b1125345593fb7be2564699dd Mon Sep 17 00:00:00 2001 From: Roman Bukin Date: Fri, 9 Feb 2024 14:49:59 +0300 Subject: [PATCH 4/5] Improve PostgreSQL queries and adopt README.md --- src/WebAuthn.Net.Storage.PostgreSql/README.md | 3 ++- .../DefaultPostgreSqlCredentialStorage.cs | 22 +++++++++---------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/WebAuthn.Net.Storage.PostgreSql/README.md b/src/WebAuthn.Net.Storage.PostgreSql/README.md index dc33760..f97a0ff 100644 --- a/src/WebAuthn.Net.Storage.PostgreSql/README.md +++ b/src/WebAuthn.Net.Storage.PostgreSql/README.md @@ -38,7 +38,8 @@ CREATE TABLE "CredentialRecords" ( CONSTRAINT "PK_CredentialRecords" PRIMARY KEY ("Id") ); -CREATE UNIQUE INDEX "IX_CredentialRecords_RpId_UserHandle_CredentialId" ON "CredentialRecords" ("RpId", "UserHandle", "CredentialId"); +CREATE UNIQUE INDEX "IX_CredentialRecords_UserHandle_CredentialId_RpId" ON "CredentialRecords" ("UserHandle", "CredentialId", "RpId"); +CREATE UNIQUE INDEX "IX_CredentialRecords_CredentialId_RpId" ON "CredentialRecords" ("CredentialId", "RpId"); ``` ## Local dev environment diff --git a/src/WebAuthn.Net.Storage.PostgreSql/Storage/DefaultPostgreSqlCredentialStorage.cs b/src/WebAuthn.Net.Storage.PostgreSql/Storage/DefaultPostgreSqlCredentialStorage.cs index f60363b..789791e 100644 --- a/src/WebAuthn.Net.Storage.PostgreSql/Storage/DefaultPostgreSqlCredentialStorage.cs +++ b/src/WebAuthn.Net.Storage.PostgreSql/Storage/DefaultPostgreSqlCredentialStorage.cs @@ -47,8 +47,9 @@ public virtual async Task FindDescriptorsAsync( ArgumentNullException.ThrowIfNull(context); cancellationToken.ThrowIfCancellationRequested(); var dbPublicKeysEnumerable = await context.Connection.QueryAsync(new(@" -SELECT ""Type"", ""CredentialId"", ""Transports"", ""CreatedAtUnixTime"" FROM ""CredentialRecords"" -WHERE ""RpId"" = @rpId AND ""UserHandle"" = @userHandle;", +SELECT ""Type"", ""CredentialId"", ""Transports"", ""CreatedAtUnixTime"" +FROM ""CredentialRecords"" +WHERE ""UserHandle"" = @userHandle AND ""RpId"" = @rpId;", new { rpId, @@ -94,7 +95,7 @@ public virtual async Task FindDescriptorsAsync( var exisingId = await context.Connection.QuerySingleOrDefaultAsync(new(@" SELECT ""Id"" FROM ""CredentialRecords"" -WHERE ""RpId"" = @rpId AND ""UserHandle"" = @userHandle AND ""CredentialId"" = @credentialId;", +WHERE ""UserHandle"" = @userHandle AND ""CredentialId"" = @credentialId AND ""RpId"" = @rpId;", new { rpId, @@ -169,10 +170,9 @@ public virtual async Task SaveIfNotRegisteredForOtherUserAsync( cancellationToken.ThrowIfCancellationRequested(); var existingCount = await context.Connection.ExecuteScalarAsync(new( @" -SELECT COUNT(""Id"") FROM ""CredentialRecords"" -WHERE - ""RpId"" = @rpId - AND ""CredentialId"" = @credentialId;", +SELECT COUNT(""CredentialId"") +FROM ""CredentialRecords"" +WHERE ""CredentialId"" = @credentialId AND ""RpId"" = @rpId;", new { rpId = credential.RpId, @@ -291,11 +291,9 @@ public virtual async Task UpdateCredentialAsync( cancellationToken.ThrowIfCancellationRequested(); var recordIdToUpdate = await context.Connection.QuerySingleOrDefaultAsync(new( @" -SELECT ""Id"" FROM ""CredentialRecords"" -WHERE - ""RpId"" = @rpId - AND ""UserHandle"" = @userHandle - AND ""CredentialId"" = @credentialId;", +SELECT ""Id"" +FROM ""CredentialRecords"" +WHERE ""UserHandle"" = @userHandle AND ""CredentialId"" = @credentialId AND ""RpId"" = @rpId;", new { rpId = credential.RpId, From 27c3fe96da9fc9adc68165a7cffd05bec8c14308 Mon Sep 17 00:00:00 2001 From: Roman Bukin Date: Fri, 9 Feb 2024 14:59:57 +0300 Subject: [PATCH 5/5] Improve MSSQL queries and adopt README.md --- src/WebAuthn.Net.Storage.SqlServer/README.md | 54 ++++++++++--------- .../DefaultSqlServerCredentialStorage.cs | 22 ++++---- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/WebAuthn.Net.Storage.SqlServer/README.md b/src/WebAuthn.Net.Storage.SqlServer/README.md index 3e89e2f..76efe2b 100644 --- a/src/WebAuthn.Net.Storage.SqlServer/README.md +++ b/src/WebAuthn.Net.Storage.SqlServer/README.md @@ -15,34 +15,36 @@ As the library is intended to be integrated into existing applications, they may ```tsql CREATE TABLE [CredentialRecords] ( - [Id] uniqueidentifier NOT NULL, - [RpId] nvarchar(256) NOT NULL, - [UserHandle] varbinary(128) NOT NULL, - [CredentialId] varbinary(1024) NOT NULL, - [Type] int NOT NULL, - [Kty] int NOT NULL, - [Alg] int NOT NULL, - [Ec2Crv] int NULL, - [Ec2X] varbinary(256) NULL, - [Ec2Y] varbinary(256) NULL, - [RsaModulusN] varbinary(1024) NULL, - [RsaExponentE] varbinary(32) NULL, - [OkpCrv] int NULL, - [OkpX] varbinary(32) NULL, - [SignCount] bigint NOT NULL, - [Transports] nvarchar(max) NOT NULL, - [UvInitialized] bit NOT NULL, - [BackupEligible] bit NOT NULL, - [BackupState] bit NOT NULL, - [AttestationObject] varbinary(max) NULL, - [AttestationClientDataJson] varbinary(max) NULL, - [Description] nvarchar(200) NULL, - [CreatedAtUnixTime] bigint NOT NULL, - [UpdatedAtUnixTime] bigint NOT NULL, + [Id] uniqueidentifier NOT NULL, + [RpId] nvarchar(256) NOT NULL, + [UserHandle] varbinary(128) NOT NULL, + [CredentialId] varbinary(1024) NOT NULL, + [Type] int NOT NULL, + [Kty] int NOT NULL, + [Alg] int NOT NULL, + [Ec2Crv] int NULL, + [Ec2X] varbinary(256) NULL, + [Ec2Y] varbinary(256) NULL, + [RsaModulusN] varbinary(1024) NULL, + [RsaExponentE] varbinary(32) NULL, + [OkpCrv] int NULL, + [OkpX] varbinary(32) NULL, + [SignCount] bigint NOT NULL, + [Transports] nvarchar(max) NOT NULL, + [UvInitialized] bit NOT NULL, + [BackupEligible] bit NOT NULL, + [BackupState] bit NOT NULL, + [AttestationObject] varbinary(max) NULL, + [AttestationClientDataJson] varbinary(max) NULL, + [Description] nvarchar(200) NULL, + [CreatedAtUnixTime] bigint NOT NULL, + [UpdatedAtUnixTime] bigint NOT NULL, CONSTRAINT [PK_CredentialRecords] PRIMARY KEY ([Id]) ); -ALTER TABLE [CredentialRecords] ADD CONSTRAINT [Transports should be formatted as JSON] CHECK (ISJSON(Transports)=1); -CREATE UNIQUE INDEX [IX_CredentialRecords_RpId_UserHandle_CredentialId] ON [CredentialRecords] ([RpId], [UserHandle], [CredentialId]); +ALTER TABLE [CredentialRecords] + ADD CONSTRAINT [Transports should be formatted as JSON] CHECK (ISJSON(Transports) = 1); +CREATE UNIQUE INDEX [IX_CredentialRecords_UserHandle_CredentialId_RpId] ON [CredentialRecords] ([UserHandle], [CredentialId], [RpId]); +CREATE UNIQUE INDEX [IX_CredentialRecords_CredentialId_RpId] ON [CredentialRecords] ([CredentialId], [RpId]); ``` ## Local dev environment diff --git a/src/WebAuthn.Net.Storage.SqlServer/Storage/DefaultSqlServerCredentialStorage.cs b/src/WebAuthn.Net.Storage.SqlServer/Storage/DefaultSqlServerCredentialStorage.cs index 45f5ea4..49ac670 100644 --- a/src/WebAuthn.Net.Storage.SqlServer/Storage/DefaultSqlServerCredentialStorage.cs +++ b/src/WebAuthn.Net.Storage.SqlServer/Storage/DefaultSqlServerCredentialStorage.cs @@ -46,8 +46,9 @@ public virtual async Task FindDescriptorsAsync( ArgumentNullException.ThrowIfNull(context); cancellationToken.ThrowIfCancellationRequested(); var dbPublicKeysEnumerable = await context.Connection.QueryAsync(new(@" -SELECT Type, CredentialId, Transports, CreatedAtUnixTime FROM CredentialRecords -WHERE RpId = @rpId AND UserHandle = @userHandle;", +SELECT Type, CredentialId, Transports, CreatedAtUnixTime +FROM CredentialRecords +WHERE UserHandle = @userHandle AND RpId = @rpId;", new { rpId, @@ -92,7 +93,7 @@ public virtual async Task FindDescriptorsAsync( var exisingId = await context.Connection.QuerySingleOrDefaultAsync(new(@" SELECT Id FROM CredentialRecords -WHERE RpId = @rpId AND UserHandle = @userHandle AND CredentialId = @credentialId;", +WHERE UserHandle = @userHandle AND CredentialId = @credentialId AND RpId = @rpId;", new { rpId, @@ -166,10 +167,9 @@ public virtual async Task SaveIfNotRegisteredForOtherUserAsync( cancellationToken.ThrowIfCancellationRequested(); var existingCount = await context.Connection.ExecuteScalarAsync(new( @" -SELECT COUNT(Id) FROM CredentialRecords -WHERE - RpId = @rpId - AND CredentialId = @credentialId;", +SELECT COUNT(CredentialId) +FROM CredentialRecords +WHERE CredentialId = @credentialId AND RpId = @rpId;", new { rpId = credential.RpId, @@ -288,11 +288,9 @@ public virtual async Task UpdateCredentialAsync( cancellationToken.ThrowIfCancellationRequested(); var recordIdToUpdate = await context.Connection.QuerySingleOrDefaultAsync(new( @" -SELECT Id FROM CredentialRecords -WHERE - RpId = @rpId - AND UserHandle = @userHandle - AND CredentialId = @credentialId;", +SELECT Id +FROM CredentialRecords +WHERE UserHandle = @userHandle AND CredentialId = @credentialId AND RpId = @rpId;", new { rpId = credential.RpId,