From 10de4967235bd62f24111c59c598209b409058e2 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Tue, 19 Sep 2023 16:55:26 +0300 Subject: [PATCH 1/4] Standardize projects --- Passwordless-dotnet.sln | 4 +- src/{Sdk => Passwordless.Net}/Base64Url.cs | 270 ++++++------- .../Helpers/Base64UrlConverter.cs | 40 +- .../Helpers/PasswordlessSerializerContext.cs | 52 +-- .../IPasswordlessClient.cs | 178 ++++---- .../Models/AddAliasRequest.cs | 70 ++-- .../Models/AliasPointer.cs | 28 +- .../Models/Credential.cs | 76 ++-- .../Models/CredentialDescriptor.cs | 26 +- .../Models/DeleteCredentialRequest.cs | 20 +- .../Models/DeleteUserRequest.cs | 20 +- .../Models/ListResponse.cs | 20 +- .../Models/PasswordlessUserSummary.cs | 38 +- .../Models/RegisterOptions.cs | 158 ++++---- .../Models/RegisterTokenResponse.cs | 24 +- .../Models/UsersCount.cs | 20 +- .../Models/VerifiedUser.cs | 68 ++-- .../Models/VerifyTokenRequest.cs | 20 +- .../Passwordless.Net.csproj} | 21 +- .../PasswordlessApiException.cs | 76 ++-- .../PasswordlessClient.cs | 382 +++++++++--------- .../PasswordlessDelegatingHandler.cs | 62 +-- .../PasswordlessExtensions.cs | 16 +- .../PasswordlessHttpRequestExtensions.cs | 64 +-- .../PasswordlessOptions.cs | 64 +-- src/{Sdk => Passwordless.Net}/Pollyfill.cs | 284 ++++++------- .../ServiceCollectionExtensions.cs | 102 ++--- .../ApiFactAttribute.cs | 26 +- .../Passwordless.Net.Tests.csproj} | 18 +- .../PasswordlessClientTests.cs | 248 ++++++------ .../Usings.cs | 0 31 files changed, 1244 insertions(+), 1251 deletions(-) rename src/{Sdk => Passwordless.Net}/Base64Url.cs (96%) rename src/{Sdk => Passwordless.Net}/Helpers/Base64UrlConverter.cs (96%) rename src/{Sdk => Passwordless.Net}/Helpers/PasswordlessSerializerContext.cs (97%) rename src/{Sdk => Passwordless.Net}/IPasswordlessClient.cs (98%) rename src/{Sdk => Passwordless.Net}/Models/AddAliasRequest.cs (97%) rename src/{Sdk => Passwordless.Net}/Models/AliasPointer.cs (95%) rename src/{Sdk => Passwordless.Net}/Models/Credential.cs (97%) rename src/{Sdk => Passwordless.Net}/Models/CredentialDescriptor.cs (95%) rename src/{Sdk => Passwordless.Net}/Models/DeleteCredentialRequest.cs (95%) rename src/{Sdk => Passwordless.Net}/Models/DeleteUserRequest.cs (94%) rename src/{Sdk => Passwordless.Net}/Models/ListResponse.cs (95%) rename src/{Sdk => Passwordless.Net}/Models/PasswordlessUserSummary.cs (96%) rename src/{Sdk => Passwordless.Net}/Models/RegisterOptions.cs (97%) rename src/{Sdk => Passwordless.Net}/Models/RegisterTokenResponse.cs (95%) rename src/{Sdk => Passwordless.Net}/Models/UsersCount.cs (93%) rename src/{Sdk => Passwordless.Net}/Models/VerifiedUser.cs (96%) rename src/{Sdk => Passwordless.Net}/Models/VerifyTokenRequest.cs (94%) rename src/{Sdk/Sdk.csproj => Passwordless.Net/Passwordless.Net.csproj} (65%) rename src/{Sdk => Passwordless.Net}/PasswordlessApiException.cs (96%) rename src/{Sdk => Passwordless.Net}/PasswordlessClient.cs (97%) rename src/{Sdk => Passwordless.Net}/PasswordlessDelegatingHandler.cs (97%) rename src/{Sdk => Passwordless.Net}/PasswordlessExtensions.cs (95%) rename src/{Sdk => Passwordless.Net}/PasswordlessHttpRequestExtensions.cs (97%) rename src/{Sdk => Passwordless.Net}/PasswordlessOptions.cs (97%) rename src/{Sdk => Passwordless.Net}/Pollyfill.cs (97%) rename src/{Sdk => Passwordless.Net}/ServiceCollectionExtensions.cs (97%) rename tests/{Sdk.Tests => Passwordless.Net.Tests}/ApiFactAttribute.cs (96%) rename tests/{Sdk.Tests/Sdk.Tests.csproj => Passwordless.Net.Tests/Passwordless.Net.Tests.csproj} (62%) rename tests/{Sdk.Tests => Passwordless.Net.Tests}/PasswordlessClientTests.cs (96%) rename tests/{Sdk.Tests => Passwordless.Net.Tests}/Usings.cs (100%) diff --git a/Passwordless-dotnet.sln b/Passwordless-dotnet.sln index 61913ec..bde9b97 100644 --- a/Passwordless-dotnet.sln +++ b/Passwordless-dotnet.sln @@ -3,9 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sdk.Tests", "tests\Sdk.Tests\Sdk.Tests.csproj", "{F64C850E-9923-43F1-BC84-432AFBBA4425}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Passwordless.Net.Tests", "tests\Passwordless.Net.Tests\Passwordless.Net.Tests.csproj", "{F64C850E-9923-43F1-BC84-432AFBBA4425}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sdk", "src\Sdk\Sdk.csproj", "{A01503A8-6AB9-43A7-AC5A-4EAE091B07B6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Passwordless.Net", "src\Passwordless.Net\Passwordless.Net.csproj", "{A01503A8-6AB9-43A7-AC5A-4EAE091B07B6}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F239A76C-408E-4919-AB70-4499425E6F29}" ProjectSection(SolutionItems) = preProject diff --git a/src/Sdk/Base64Url.cs b/src/Passwordless.Net/Base64Url.cs similarity index 96% rename from src/Sdk/Base64Url.cs rename to src/Passwordless.Net/Base64Url.cs index dfc7b9a..3b25a8a 100644 --- a/src/Sdk/Base64Url.cs +++ b/src/Passwordless.Net/Base64Url.cs @@ -1,136 +1,136 @@ -using System.Buffers; -using System.Buffers.Text; - -namespace Passwordless.Net; - -internal static class Base64Url -{ - /// - /// Converts arg data to a Base64Url encoded string. - /// - public static string Encode(ReadOnlySpan arg) - { - int minimumLength = (int)(((long)arg.Length + 2L) / 3 * 4); - char[] array = ArrayPool.Shared.Rent(minimumLength); - -#if NET5_0_OR_GREATER - Convert.TryToBase64Chars(arg, array, out var charsWritten); -#elif NET462 || NETSTANDARD2_0 - var charsWritten = Convert.ToBase64CharArray(arg.ToArray(), 0, minimumLength, array, 0); -#endif - Span span = array.AsSpan(0, charsWritten); - - - for (int i = 0; i < span.Length; i++) - { - ref char reference = ref span[i]; - switch (reference) - { - case '+': - reference = '-'; - break; - case '/': - reference = '_'; - break; - } - } - int num = span.IndexOf('='); - if (num > -1) - { - span = span.Slice(0, num); - } - -#if NET5_0_OR_GREATER - string result = new string(span); -#elif NET462 || NETSTANDARD2_0 - string result = new string(span.ToArray()); -#endif - ArrayPool.Shared.Return(array, clearArray: true); - return result; - } - - /// - /// Decodes a Base64Url encoded string to its raw bytes. - /// - public static byte[] Decode(ReadOnlySpan text) - { - int num = (text.Length % 4) switch - { - 2 => 2, - 3 => 1, - _ => 0, - }; - int num2 = text.Length + num; - char[] array = ArrayPool.Shared.Rent(num2); - text.CopyTo(array); - for (int i = 0; i < text.Length; i++) - { - ref char reference = ref array[i]; - switch (reference) - { - case '-': - reference = '+'; - break; - case '_': - reference = '/'; - break; - } - } - switch (num) - { - case 1: - array[num2 - 1] = '='; - break; - case 2: - array[num2 - 1] = '='; - array[num2 - 2] = '='; - break; - } - byte[] result = Convert.FromBase64CharArray(array, 0, num2); - ArrayPool.Shared.Return(array, clearArray: true); - return result; - } - - /// - /// Decodes a Base64Url encoded string to its raw bytes. - /// - public static byte[] DecodeUtf8(ReadOnlySpan text) - { - int num = (text.Length % 4) switch - { - 2 => 2, - 3 => 1, - _ => 0, - }; - int num2 = text.Length + num; - byte[] array = ArrayPool.Shared.Rent(num2); - text.CopyTo(array); - for (int i = 0; i < text.Length; i++) - { - ref byte reference = ref array[i]; - switch (reference) - { - case 45: - reference = 43; - break; - case 95: - reference = 47; - break; - } - } - switch (num) - { - case 1: - array[num2 - 1] = 61; - break; - case 2: - array[num2 - 1] = 61; - array[num2 - 2] = 61; - break; - } - Base64.DecodeFromUtf8InPlace(array.AsSpan(0, num2), out var bytesWritten); - byte[] result = array.AsSpan(0, bytesWritten).ToArray(); - ArrayPool.Shared.Return(array, clearArray: true); - return result; - } +using System.Buffers; +using System.Buffers.Text; + +namespace Passwordless.Net; + +internal static class Base64Url +{ + /// + /// Converts arg data to a Base64Url encoded string. + /// + public static string Encode(ReadOnlySpan arg) + { + int minimumLength = (int)(((long)arg.Length + 2L) / 3 * 4); + char[] array = ArrayPool.Shared.Rent(minimumLength); + +#if NET5_0_OR_GREATER + Convert.TryToBase64Chars(arg, array, out var charsWritten); +#elif NET462 || NETSTANDARD2_0 + var charsWritten = Convert.ToBase64CharArray(arg.ToArray(), 0, minimumLength, array, 0); +#endif + Span span = array.AsSpan(0, charsWritten); + + + for (int i = 0; i < span.Length; i++) + { + ref char reference = ref span[i]; + switch (reference) + { + case '+': + reference = '-'; + break; + case '/': + reference = '_'; + break; + } + } + int num = span.IndexOf('='); + if (num > -1) + { + span = span.Slice(0, num); + } + +#if NET5_0_OR_GREATER + string result = new string(span); +#elif NET462 || NETSTANDARD2_0 + string result = new string(span.ToArray()); +#endif + ArrayPool.Shared.Return(array, clearArray: true); + return result; + } + + /// + /// Decodes a Base64Url encoded string to its raw bytes. + /// + public static byte[] Decode(ReadOnlySpan text) + { + int num = (text.Length % 4) switch + { + 2 => 2, + 3 => 1, + _ => 0, + }; + int num2 = text.Length + num; + char[] array = ArrayPool.Shared.Rent(num2); + text.CopyTo(array); + for (int i = 0; i < text.Length; i++) + { + ref char reference = ref array[i]; + switch (reference) + { + case '-': + reference = '+'; + break; + case '_': + reference = '/'; + break; + } + } + switch (num) + { + case 1: + array[num2 - 1] = '='; + break; + case 2: + array[num2 - 1] = '='; + array[num2 - 2] = '='; + break; + } + byte[] result = Convert.FromBase64CharArray(array, 0, num2); + ArrayPool.Shared.Return(array, clearArray: true); + return result; + } + + /// + /// Decodes a Base64Url encoded string to its raw bytes. + /// + public static byte[] DecodeUtf8(ReadOnlySpan text) + { + int num = (text.Length % 4) switch + { + 2 => 2, + 3 => 1, + _ => 0, + }; + int num2 = text.Length + num; + byte[] array = ArrayPool.Shared.Rent(num2); + text.CopyTo(array); + for (int i = 0; i < text.Length; i++) + { + ref byte reference = ref array[i]; + switch (reference) + { + case 45: + reference = 43; + break; + case 95: + reference = 47; + break; + } + } + switch (num) + { + case 1: + array[num2 - 1] = 61; + break; + case 2: + array[num2 - 1] = 61; + array[num2 - 2] = 61; + break; + } + Base64.DecodeFromUtf8InPlace(array.AsSpan(0, num2), out var bytesWritten); + byte[] result = array.AsSpan(0, bytesWritten).ToArray(); + ArrayPool.Shared.Return(array, clearArray: true); + return result; + } } \ No newline at end of file diff --git a/src/Sdk/Helpers/Base64UrlConverter.cs b/src/Passwordless.Net/Helpers/Base64UrlConverter.cs similarity index 96% rename from src/Sdk/Helpers/Base64UrlConverter.cs rename to src/Passwordless.Net/Helpers/Base64UrlConverter.cs index 6c08a7f..ce632c3 100644 --- a/src/Sdk/Helpers/Base64UrlConverter.cs +++ b/src/Passwordless.Net/Helpers/Base64UrlConverter.cs @@ -1,21 +1,21 @@ -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Passwordless.Net; - -public sealed class Base64UrlConverter : JsonConverter -{ - public override byte[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (!reader.HasValueSequence) - { - return Base64Url.DecodeUtf8(reader.ValueSpan); - } - return Base64Url.Decode(reader.GetString().AsSpan()); - } - - public override void Write(Utf8JsonWriter writer, byte[] value, JsonSerializerOptions options) - { - writer.WriteStringValue(Base64Url.Encode(value)); - } +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Passwordless.Net; + +public sealed class Base64UrlConverter : JsonConverter +{ + public override byte[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (!reader.HasValueSequence) + { + return Base64Url.DecodeUtf8(reader.ValueSpan); + } + return Base64Url.Decode(reader.GetString().AsSpan()); + } + + public override void Write(Utf8JsonWriter writer, byte[] value, JsonSerializerOptions options) + { + writer.WriteStringValue(Base64Url.Encode(value)); + } } \ No newline at end of file diff --git a/src/Sdk/Helpers/PasswordlessSerializerContext.cs b/src/Passwordless.Net/Helpers/PasswordlessSerializerContext.cs similarity index 97% rename from src/Sdk/Helpers/PasswordlessSerializerContext.cs rename to src/Passwordless.Net/Helpers/PasswordlessSerializerContext.cs index f75a51f..18ed5d0 100644 --- a/src/Sdk/Helpers/PasswordlessSerializerContext.cs +++ b/src/Passwordless.Net/Helpers/PasswordlessSerializerContext.cs @@ -1,27 +1,27 @@ -using System.Text.Json; -using System.Text.Json.Serialization; -using Passwordless.Net.Models; - -namespace Passwordless.Net.Helpers; - -[JsonSourceGenerationOptions( - PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] -[JsonSerializable(typeof(AddAliasRequest))] -[JsonSerializable(typeof(RegisterTokenResponse))] -[JsonSerializable(typeof(RegisterOptions))] -[JsonSerializable(typeof(VerifyTokenRequest))] // TODO: Use this with JsonContent.Create -[JsonSerializable(typeof(VerifiedUser))] -[JsonSerializable(typeof(DeleteUserRequest))] -[JsonSerializable(typeof(ListResponse))] -[JsonSerializable(typeof(ListResponse))] -[JsonSerializable(typeof(ListResponse))] -[JsonSerializable(typeof(DeleteCredentialRequest))] -[JsonSerializable(typeof(UsersCount))] -[JsonSerializable(typeof(PasswordlessProblemDetails))] -[JsonSerializable(typeof(Dictionary))] -[JsonSerializable(typeof(JsonElement))] -internal partial class PasswordlessSerializerContext : JsonSerializerContext -{ - +using System.Text.Json; +using System.Text.Json.Serialization; +using Passwordless.Net.Models; + +namespace Passwordless.Net.Helpers; + +[JsonSourceGenerationOptions( + PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] +[JsonSerializable(typeof(AddAliasRequest))] +[JsonSerializable(typeof(RegisterTokenResponse))] +[JsonSerializable(typeof(RegisterOptions))] +[JsonSerializable(typeof(VerifyTokenRequest))] // TODO: Use this with JsonContent.Create +[JsonSerializable(typeof(VerifiedUser))] +[JsonSerializable(typeof(DeleteUserRequest))] +[JsonSerializable(typeof(ListResponse))] +[JsonSerializable(typeof(ListResponse))] +[JsonSerializable(typeof(ListResponse))] +[JsonSerializable(typeof(DeleteCredentialRequest))] +[JsonSerializable(typeof(UsersCount))] +[JsonSerializable(typeof(PasswordlessProblemDetails))] +[JsonSerializable(typeof(Dictionary))] +[JsonSerializable(typeof(JsonElement))] +internal partial class PasswordlessSerializerContext : JsonSerializerContext +{ + } \ No newline at end of file diff --git a/src/Sdk/IPasswordlessClient.cs b/src/Passwordless.Net/IPasswordlessClient.cs similarity index 98% rename from src/Sdk/IPasswordlessClient.cs rename to src/Passwordless.Net/IPasswordlessClient.cs index ef1d6e8..756e2dc 100644 --- a/src/Sdk/IPasswordlessClient.cs +++ b/src/Passwordless.Net/IPasswordlessClient.cs @@ -1,90 +1,90 @@ -using Passwordless.Net.Models; - -namespace Passwordless.Net; - -/// -/// Provides APIs that help you interact with Passwordless.dev. -/// -public interface IPasswordlessClient -{ - /// - /// Adds one or more aliases to an existing user. - /// - /// - /// - /// - Task AddAliasAsync(AddAliasRequest request, CancellationToken cancellationToken = default); - - /// - /// Creates a which will be used by your frontend to negotiate - /// the creation of a WebAuth credential. - /// - /// The that will be used to configure your token. - /// - /// A task object representing the asynchronous operation containing the . - /// An exception containing details abaout the reason for failure. - Task CreateRegisterTokenAsync(RegisterOptions registerOptions, CancellationToken cancellationToken = default); - - /// - /// Attempts to delete a credential via the supplied id. - /// - /// The id of a credential representing as a Base64 URL encoded . - /// - /// A task object representing the asynchronous operation. - /// An exception containing details abaout the reason for failure. - Task DeleteCredentialAsync(string id, CancellationToken cancellationToken = default); - - /// - /// Attempts to delete a credential via the supplied id. - /// - /// The id of a credential representing as a Base64 URL encoded . - /// - /// A task object representing the asynchronous operation. - /// An exception containing details abaout the reason for failure. - Task DeleteCredentialAsync(byte[] id, CancellationToken cancellationToken = default); - - /// - /// List all the for a given user. - /// - /// The userId of the user for which the aliases will be returned. - /// - /// A task object representing the asynchronous operation containing the . - /// An exception containing details abaout the reason for failure. - Task> ListAliasesAsync(string userId, CancellationToken cancellationToken = default); - - /// - /// List all the for a given user. - /// - /// The userId of the user for which the credentials will be returned. - /// - /// A task object representing the asynchronous operation containing the . - /// An exception containing details abaout the reason for failure. - Task> ListCredentialsAsync(string userId, CancellationToken cancellationToken = default); - - /// - /// List all the for the account associated with your ApiSecret. - /// - /// - /// A task object representing the asynchronous operation containing the . - /// An exception containing details abaout the reason for failure. - Task> ListUsersAsync(CancellationToken cancellationToken = default); - - /// - /// Verifies that the given token is valid and returns information packed into it. The token should have been generated - /// via calling a signInWith* method from your frontend code. - /// - /// The token to verify. - /// - /// A task object representing the asynchronous operation containing the . - /// An exception containing details abaout the reason for failure. - Task VerifyTokenAsync(string verifyToken, CancellationToken cancellationToken = default); - - /// - /// Deletes a user. - /// - /// The id of the user that should be deleted. - /// - /// A task object representing the asynchronous operation. - /// An exception containing details abaout the reason for failure. - Task DeleteUserAsync(string userId, CancellationToken cancellationToken = default); +using Passwordless.Net.Models; + +namespace Passwordless.Net; + +/// +/// Provides APIs that help you interact with Passwordless.dev. +/// +public interface IPasswordlessClient +{ + /// + /// Adds one or more aliases to an existing user. + /// + /// + /// + /// + Task AddAliasAsync(AddAliasRequest request, CancellationToken cancellationToken = default); + + /// + /// Creates a which will be used by your frontend to negotiate + /// the creation of a WebAuth credential. + /// + /// The that will be used to configure your token. + /// + /// A task object representing the asynchronous operation containing the . + /// An exception containing details abaout the reason for failure. + Task CreateRegisterTokenAsync(RegisterOptions registerOptions, CancellationToken cancellationToken = default); + + /// + /// Attempts to delete a credential via the supplied id. + /// + /// The id of a credential representing as a Base64 URL encoded . + /// + /// A task object representing the asynchronous operation. + /// An exception containing details abaout the reason for failure. + Task DeleteCredentialAsync(string id, CancellationToken cancellationToken = default); + + /// + /// Attempts to delete a credential via the supplied id. + /// + /// The id of a credential representing as a Base64 URL encoded . + /// + /// A task object representing the asynchronous operation. + /// An exception containing details abaout the reason for failure. + Task DeleteCredentialAsync(byte[] id, CancellationToken cancellationToken = default); + + /// + /// List all the for a given user. + /// + /// The userId of the user for which the aliases will be returned. + /// + /// A task object representing the asynchronous operation containing the . + /// An exception containing details abaout the reason for failure. + Task> ListAliasesAsync(string userId, CancellationToken cancellationToken = default); + + /// + /// List all the for a given user. + /// + /// The userId of the user for which the credentials will be returned. + /// + /// A task object representing the asynchronous operation containing the . + /// An exception containing details abaout the reason for failure. + Task> ListCredentialsAsync(string userId, CancellationToken cancellationToken = default); + + /// + /// List all the for the account associated with your ApiSecret. + /// + /// + /// A task object representing the asynchronous operation containing the . + /// An exception containing details abaout the reason for failure. + Task> ListUsersAsync(CancellationToken cancellationToken = default); + + /// + /// Verifies that the given token is valid and returns information packed into it. The token should have been generated + /// via calling a signInWith* method from your frontend code. + /// + /// The token to verify. + /// + /// A task object representing the asynchronous operation containing the . + /// An exception containing details abaout the reason for failure. + Task VerifyTokenAsync(string verifyToken, CancellationToken cancellationToken = default); + + /// + /// Deletes a user. + /// + /// The id of the user that should be deleted. + /// + /// A task object representing the asynchronous operation. + /// An exception containing details abaout the reason for failure. + Task DeleteUserAsync(string userId, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/Sdk/Models/AddAliasRequest.cs b/src/Passwordless.Net/Models/AddAliasRequest.cs similarity index 97% rename from src/Sdk/Models/AddAliasRequest.cs rename to src/Passwordless.Net/Models/AddAliasRequest.cs index 5230f6e..62319c2 100644 --- a/src/Sdk/Models/AddAliasRequest.cs +++ b/src/Passwordless.Net/Models/AddAliasRequest.cs @@ -1,36 +1,36 @@ -namespace Passwordless.Net.Models; - -public class AddAliasRequest -{ - public AddAliasRequest(string userId, string alias, bool hashing = true) - : this(userId, hashing) - { - if (string.IsNullOrWhiteSpace(alias)) throw new ArgumentException($"'{nameof(alias)}' cannot be null, empty or whitespace."); - Aliases = new HashSet - { - alias ?? throw new ArgumentNullException(nameof(alias)) - }; - } - - public AddAliasRequest(string userId, HashSet aliases, bool hashing = true) - : this(userId, hashing) - { - if (aliases == null || !aliases.Any()) throw new ArgumentException($"'{nameof(aliases)}' cannot be null or empty."); - if (aliases.Any(string.IsNullOrWhiteSpace)) throw new ArgumentException("One of the aliases is null, empty or whitespace"); - Aliases = aliases; - } - - private AddAliasRequest(string userId, bool hashing = true) - { - UserId = userId ?? throw new ArgumentNullException(nameof(userId)); - Hashing = hashing; - } - - public string UserId { get; } - public HashSet Aliases { get; } - - /// - /// If you want your aliases to be available in plain text, set the false. - /// - public bool Hashing { get; } = true; +namespace Passwordless.Net.Models; + +public class AddAliasRequest +{ + public AddAliasRequest(string userId, string alias, bool hashing = true) + : this(userId, hashing) + { + if (string.IsNullOrWhiteSpace(alias)) throw new ArgumentException($"'{nameof(alias)}' cannot be null, empty or whitespace."); + Aliases = new HashSet + { + alias ?? throw new ArgumentNullException(nameof(alias)) + }; + } + + public AddAliasRequest(string userId, HashSet aliases, bool hashing = true) + : this(userId, hashing) + { + if (aliases == null || !aliases.Any()) throw new ArgumentException($"'{nameof(aliases)}' cannot be null or empty."); + if (aliases.Any(string.IsNullOrWhiteSpace)) throw new ArgumentException("One of the aliases is null, empty or whitespace"); + Aliases = aliases; + } + + private AddAliasRequest(string userId, bool hashing = true) + { + UserId = userId ?? throw new ArgumentNullException(nameof(userId)); + Hashing = hashing; + } + + public string UserId { get; } + public HashSet Aliases { get; } + + /// + /// If you want your aliases to be available in plain text, set the false. + /// + public bool Hashing { get; } = true; } \ No newline at end of file diff --git a/src/Sdk/Models/AliasPointer.cs b/src/Passwordless.Net/Models/AliasPointer.cs similarity index 95% rename from src/Sdk/Models/AliasPointer.cs rename to src/Passwordless.Net/Models/AliasPointer.cs index 44a26bb..7477d4b 100644 --- a/src/Sdk/Models/AliasPointer.cs +++ b/src/Passwordless.Net/Models/AliasPointer.cs @@ -1,15 +1,15 @@ -namespace Passwordless.Net; - -public class AliasPointer -{ - public AliasPointer(string userId, string alias, string plaintext) - { - UserId = userId; - Alias = alias; - Plaintext = plaintext; - } - - public string UserId { get; } - public string Alias { get; } - public string Plaintext { get; } +namespace Passwordless.Net; + +public class AliasPointer +{ + public AliasPointer(string userId, string alias, string plaintext) + { + UserId = userId; + Alias = alias; + Plaintext = plaintext; + } + + public string UserId { get; } + public string Alias { get; } + public string Plaintext { get; } } \ No newline at end of file diff --git a/src/Sdk/Models/Credential.cs b/src/Passwordless.Net/Models/Credential.cs similarity index 97% rename from src/Sdk/Models/Credential.cs rename to src/Passwordless.Net/Models/Credential.cs index 5e81ab5..dac918c 100644 --- a/src/Sdk/Models/Credential.cs +++ b/src/Passwordless.Net/Models/Credential.cs @@ -1,39 +1,39 @@ -namespace Passwordless.Net; - -public class Credential -{ - public Credential(CredentialDescriptor descriptor, byte[] publicKey, byte[] userHandle, uint signatureCounter, - string attestationFmt, DateTime createdAt, Guid aaGuid, DateTime lastUsedAt, string rpid, - string origin, string country, string device, string nickname, string userId) - { - Descriptor = descriptor; - PublicKey = publicKey; - UserHandle = userHandle; - SignatureCounter = signatureCounter; - AttestationFmt = attestationFmt; - CreatedAt = createdAt; - AaGuid = aaGuid; - LastUsedAt = lastUsedAt; - RPID = rpid; - Origin = origin; - Country = country; - Device = device; - Nickname = nickname; - UserId = userId; - } - - public CredentialDescriptor Descriptor { get; } - public byte[] PublicKey { get; } - public byte[] UserHandle { get; } - public uint SignatureCounter { get; } - public string AttestationFmt { get; } - public DateTime CreatedAt { get; } - public Guid AaGuid { get; } - public DateTime LastUsedAt { get; } - public string RPID { get; } - public string Origin { get; } - public string Country { get; } - public string Device { get; } - public string Nickname { get; } - public string UserId { get; } +namespace Passwordless.Net; + +public class Credential +{ + public Credential(CredentialDescriptor descriptor, byte[] publicKey, byte[] userHandle, uint signatureCounter, + string attestationFmt, DateTime createdAt, Guid aaGuid, DateTime lastUsedAt, string rpid, + string origin, string country, string device, string nickname, string userId) + { + Descriptor = descriptor; + PublicKey = publicKey; + UserHandle = userHandle; + SignatureCounter = signatureCounter; + AttestationFmt = attestationFmt; + CreatedAt = createdAt; + AaGuid = aaGuid; + LastUsedAt = lastUsedAt; + RPID = rpid; + Origin = origin; + Country = country; + Device = device; + Nickname = nickname; + UserId = userId; + } + + public CredentialDescriptor Descriptor { get; } + public byte[] PublicKey { get; } + public byte[] UserHandle { get; } + public uint SignatureCounter { get; } + public string AttestationFmt { get; } + public DateTime CreatedAt { get; } + public Guid AaGuid { get; } + public DateTime LastUsedAt { get; } + public string RPID { get; } + public string Origin { get; } + public string Country { get; } + public string Device { get; } + public string Nickname { get; } + public string UserId { get; } } \ No newline at end of file diff --git a/src/Sdk/Models/CredentialDescriptor.cs b/src/Passwordless.Net/Models/CredentialDescriptor.cs similarity index 95% rename from src/Sdk/Models/CredentialDescriptor.cs rename to src/Passwordless.Net/Models/CredentialDescriptor.cs index 1aee987..3f412e4 100644 --- a/src/Sdk/Models/CredentialDescriptor.cs +++ b/src/Passwordless.Net/Models/CredentialDescriptor.cs @@ -1,14 +1,14 @@ -using System.Text.Json.Serialization; - -namespace Passwordless.Net; - -public class CredentialDescriptor -{ - public CredentialDescriptor(byte[] id) - { - Id = id; - } - - [JsonConverter(typeof(Base64UrlConverter))] - public byte[] Id { get; set; } +using System.Text.Json.Serialization; + +namespace Passwordless.Net; + +public class CredentialDescriptor +{ + public CredentialDescriptor(byte[] id) + { + Id = id; + } + + [JsonConverter(typeof(Base64UrlConverter))] + public byte[] Id { get; set; } } \ No newline at end of file diff --git a/src/Sdk/Models/DeleteCredentialRequest.cs b/src/Passwordless.Net/Models/DeleteCredentialRequest.cs similarity index 95% rename from src/Sdk/Models/DeleteCredentialRequest.cs rename to src/Passwordless.Net/Models/DeleteCredentialRequest.cs index ce60135..a86fc83 100644 --- a/src/Sdk/Models/DeleteCredentialRequest.cs +++ b/src/Passwordless.Net/Models/DeleteCredentialRequest.cs @@ -1,11 +1,11 @@ -namespace Passwordless.Net.Models; - -internal class DeleteCredentialRequest -{ - public DeleteCredentialRequest(string credentialId) - { - CredentialId = credentialId; - } - - public string CredentialId { get; } +namespace Passwordless.Net.Models; + +internal class DeleteCredentialRequest +{ + public DeleteCredentialRequest(string credentialId) + { + CredentialId = credentialId; + } + + public string CredentialId { get; } } \ No newline at end of file diff --git a/src/Sdk/Models/DeleteUserRequest.cs b/src/Passwordless.Net/Models/DeleteUserRequest.cs similarity index 94% rename from src/Sdk/Models/DeleteUserRequest.cs rename to src/Passwordless.Net/Models/DeleteUserRequest.cs index 8ff250a..2d6fac5 100644 --- a/src/Sdk/Models/DeleteUserRequest.cs +++ b/src/Passwordless.Net/Models/DeleteUserRequest.cs @@ -1,11 +1,11 @@ -namespace Passwordless.Net.Models; - -internal class DeleteUserRequest -{ - public DeleteUserRequest(string userId) - { - UserId = userId; - } - - public string UserId { get; } +namespace Passwordless.Net.Models; + +internal class DeleteUserRequest +{ + public DeleteUserRequest(string userId) + { + UserId = userId; + } + + public string UserId { get; } } \ No newline at end of file diff --git a/src/Sdk/Models/ListResponse.cs b/src/Passwordless.Net/Models/ListResponse.cs similarity index 95% rename from src/Sdk/Models/ListResponse.cs rename to src/Passwordless.Net/Models/ListResponse.cs index 0a91c42..f95b0ea 100644 --- a/src/Sdk/Models/ListResponse.cs +++ b/src/Passwordless.Net/Models/ListResponse.cs @@ -1,11 +1,11 @@ -namespace Passwordless.Net.Models; - -internal class ListResponse -{ - public ListResponse(IReadOnlyList values) - { - Values = values; - } - - public IReadOnlyList Values { get; } +namespace Passwordless.Net.Models; + +internal class ListResponse +{ + public ListResponse(IReadOnlyList values) + { + Values = values; + } + + public IReadOnlyList Values { get; } } \ No newline at end of file diff --git a/src/Sdk/Models/PasswordlessUserSummary.cs b/src/Passwordless.Net/Models/PasswordlessUserSummary.cs similarity index 96% rename from src/Sdk/Models/PasswordlessUserSummary.cs rename to src/Passwordless.Net/Models/PasswordlessUserSummary.cs index a285adc..609ee86 100644 --- a/src/Sdk/Models/PasswordlessUserSummary.cs +++ b/src/Passwordless.Net/Models/PasswordlessUserSummary.cs @@ -1,20 +1,20 @@ -namespace Passwordless.Net; - -public class PasswordlessUserSummary -{ - public PasswordlessUserSummary(string userId, IReadOnlyList aliases, int credentialsCount, - int aliasCount, DateTime? lastUsedAt) - { - UserId = userId; - Aliases = aliases; - CredentialsCount = credentialsCount; - AliasCount = aliasCount; - LastUsedAt = lastUsedAt; - } - - public string UserId { get; } - public IReadOnlyList Aliases { get; } - public int CredentialsCount { get; } - public int AliasCount { get; } - public DateTime? LastUsedAt { get; } +namespace Passwordless.Net; + +public class PasswordlessUserSummary +{ + public PasswordlessUserSummary(string userId, IReadOnlyList aliases, int credentialsCount, + int aliasCount, DateTime? lastUsedAt) + { + UserId = userId; + Aliases = aliases; + CredentialsCount = credentialsCount; + AliasCount = aliasCount; + LastUsedAt = lastUsedAt; + } + + public string UserId { get; } + public IReadOnlyList Aliases { get; } + public int CredentialsCount { get; } + public int AliasCount { get; } + public DateTime? LastUsedAt { get; } } \ No newline at end of file diff --git a/src/Sdk/Models/RegisterOptions.cs b/src/Passwordless.Net/Models/RegisterOptions.cs similarity index 97% rename from src/Sdk/Models/RegisterOptions.cs rename to src/Passwordless.Net/Models/RegisterOptions.cs index 7a67df9..57a5972 100644 --- a/src/Sdk/Models/RegisterOptions.cs +++ b/src/Passwordless.Net/Models/RegisterOptions.cs @@ -1,80 +1,80 @@ -using System.Text.Json.Serialization; - -namespace Passwordless.Net; - -/// -/// -/// -public class RegisterOptions -{ - /// - /// - /// - /// - /// - public RegisterOptions(string userId, string username) - { - UserId = userId; - Username = username; - } - - /// - /// A WebAuthn User Handle, which should be generated by your application. - /// This is used to identify your user (could be a database primary key ID or a guid). - /// Max. 64 bytes. Should not contain PII about the user. - /// - public string UserId { get; } - - /// - /// A human-palatable identifier for a user account. It is intended only for display, - /// i.e., aiding the user in determining the difference between user accounts with - /// similar displayNames. Used in Browser UI's and never stored on the server. - /// - public string Username { get; } - - /// - /// A human-palatable name for the account, which should be chosen by the user. - /// Used in Browser UI's and never stored on the server. - /// - public string? DisplayName { get; set; } - - /// - /// WebAuthn attestation conveyance preference. Only "none" (default) is supported. - /// - public string? Attestation { get; set; } - - /// - /// WebAuthn authenticator attachment modality. Can be "any" (default), "platform", - /// which triggers client device-specific options Windows Hello, FaceID, or TouchID, - /// or "cross-platform", which triggers roaming options like security keys. - /// - public string? AuthenticatorType { get; set; } - - /// - /// If true, creates a client-side Discoverable Credential that allows sign in without needing a username. - /// - public bool? Discoverable { get; set; } - - /// - /// Allows choosing preference for requiring User Verification - /// (biometrics, pin code etc) when authenticating Can be "preferred" (default), "required" or "discouraged". - /// - public string? UserVerification { get; set; } - - /// - /// Timestamp (UTC) when the registration token should expire. By default, current time + 120 seconds. - /// - public DateTime? ExpiresAt { get; set; } - - /// - /// A array of aliases for the userId, such as an email or username. Used to initiate a - /// signin on the client side with the signinWithAlias() method. An alias must be unique to the userId. - /// Defaults to an empty array []. - /// - public HashSet Aliases { get; set; } = new HashSet(); - - /// - /// Whether aliases should be hashed before being stored. Defaults to true. - /// - public bool? AliasHashing { get; set; } +using System.Text.Json.Serialization; + +namespace Passwordless.Net; + +/// +/// +/// +public class RegisterOptions +{ + /// + /// + /// + /// + /// + public RegisterOptions(string userId, string username) + { + UserId = userId; + Username = username; + } + + /// + /// A WebAuthn User Handle, which should be generated by your application. + /// This is used to identify your user (could be a database primary key ID or a guid). + /// Max. 64 bytes. Should not contain PII about the user. + /// + public string UserId { get; } + + /// + /// A human-palatable identifier for a user account. It is intended only for display, + /// i.e., aiding the user in determining the difference between user accounts with + /// similar displayNames. Used in Browser UI's and never stored on the server. + /// + public string Username { get; } + + /// + /// A human-palatable name for the account, which should be chosen by the user. + /// Used in Browser UI's and never stored on the server. + /// + public string? DisplayName { get; set; } + + /// + /// WebAuthn attestation conveyance preference. Only "none" (default) is supported. + /// + public string? Attestation { get; set; } + + /// + /// WebAuthn authenticator attachment modality. Can be "any" (default), "platform", + /// which triggers client device-specific options Windows Hello, FaceID, or TouchID, + /// or "cross-platform", which triggers roaming options like security keys. + /// + public string? AuthenticatorType { get; set; } + + /// + /// If true, creates a client-side Discoverable Credential that allows sign in without needing a username. + /// + public bool? Discoverable { get; set; } + + /// + /// Allows choosing preference for requiring User Verification + /// (biometrics, pin code etc) when authenticating Can be "preferred" (default), "required" or "discouraged". + /// + public string? UserVerification { get; set; } + + /// + /// Timestamp (UTC) when the registration token should expire. By default, current time + 120 seconds. + /// + public DateTime? ExpiresAt { get; set; } + + /// + /// A array of aliases for the userId, such as an email or username. Used to initiate a + /// signin on the client side with the signinWithAlias() method. An alias must be unique to the userId. + /// Defaults to an empty array []. + /// + public HashSet Aliases { get; set; } = new HashSet(); + + /// + /// Whether aliases should be hashed before being stored. Defaults to true. + /// + public bool? AliasHashing { get; set; } } \ No newline at end of file diff --git a/src/Sdk/Models/RegisterTokenResponse.cs b/src/Passwordless.Net/Models/RegisterTokenResponse.cs similarity index 95% rename from src/Sdk/Models/RegisterTokenResponse.cs rename to src/Passwordless.Net/Models/RegisterTokenResponse.cs index ebdf77d..74b220c 100644 --- a/src/Sdk/Models/RegisterTokenResponse.cs +++ b/src/Passwordless.Net/Models/RegisterTokenResponse.cs @@ -1,13 +1,13 @@ -using System.Text.Json.Serialization; - -namespace Passwordless.Net.Models; - -public class RegisterTokenResponse -{ - public RegisterTokenResponse(string token) - { - Token = token; - } - - public string Token { get; } +using System.Text.Json.Serialization; + +namespace Passwordless.Net.Models; + +public class RegisterTokenResponse +{ + public RegisterTokenResponse(string token) + { + Token = token; + } + + public string Token { get; } } \ No newline at end of file diff --git a/src/Sdk/Models/UsersCount.cs b/src/Passwordless.Net/Models/UsersCount.cs similarity index 93% rename from src/Sdk/Models/UsersCount.cs rename to src/Passwordless.Net/Models/UsersCount.cs index 756983c..fd8aa21 100644 --- a/src/Sdk/Models/UsersCount.cs +++ b/src/Passwordless.Net/Models/UsersCount.cs @@ -1,11 +1,11 @@ -namespace Passwordless.Net; - -public class UsersCount -{ - public UsersCount(int count) - { - Count = count; - } - - public int Count { get; } +namespace Passwordless.Net; + +public class UsersCount +{ + public UsersCount(int count) + { + Count = count; + } + + public int Count { get; } } \ No newline at end of file diff --git a/src/Sdk/Models/VerifiedUser.cs b/src/Passwordless.Net/Models/VerifiedUser.cs similarity index 96% rename from src/Sdk/Models/VerifiedUser.cs rename to src/Passwordless.Net/Models/VerifiedUser.cs index 84f7146..b0184e9 100644 --- a/src/Sdk/Models/VerifiedUser.cs +++ b/src/Passwordless.Net/Models/VerifiedUser.cs @@ -1,35 +1,35 @@ -namespace Passwordless.Net; - -public class VerifiedUser -{ - public VerifiedUser(string userId, byte[] credentialId, bool success, - DateTime timestamp, string rpId, string origin, string device, - string country, string nickname, DateTime expiresAt, Guid tokenId, - string type) - { - UserId = userId; - CredentialId = credentialId; - Success = success; - Timestamp = timestamp; - RpId = rpId; - Origin = origin; - Device = device; - Country = country; - Nickname = nickname; - ExpiresAt = expiresAt; - TokenId = tokenId; - Type = type; - } - public string UserId { get; } - public byte[] CredentialId { get; } - public bool Success { get; } - public DateTime Timestamp { get; } - public string RpId { get; } - public string Origin { get; } - public string Device { get; } - public string Country { get; } - public string Nickname { get; } - public DateTime ExpiresAt { get; } - public Guid TokenId { get; } - public string Type { get; } +namespace Passwordless.Net; + +public class VerifiedUser +{ + public VerifiedUser(string userId, byte[] credentialId, bool success, + DateTime timestamp, string rpId, string origin, string device, + string country, string nickname, DateTime expiresAt, Guid tokenId, + string type) + { + UserId = userId; + CredentialId = credentialId; + Success = success; + Timestamp = timestamp; + RpId = rpId; + Origin = origin; + Device = device; + Country = country; + Nickname = nickname; + ExpiresAt = expiresAt; + TokenId = tokenId; + Type = type; + } + public string UserId { get; } + public byte[] CredentialId { get; } + public bool Success { get; } + public DateTime Timestamp { get; } + public string RpId { get; } + public string Origin { get; } + public string Device { get; } + public string Country { get; } + public string Nickname { get; } + public DateTime ExpiresAt { get; } + public Guid TokenId { get; } + public string Type { get; } } \ No newline at end of file diff --git a/src/Sdk/Models/VerifyTokenRequest.cs b/src/Passwordless.Net/Models/VerifyTokenRequest.cs similarity index 94% rename from src/Sdk/Models/VerifyTokenRequest.cs rename to src/Passwordless.Net/Models/VerifyTokenRequest.cs index 89cc8b4..3b6eb8d 100644 --- a/src/Sdk/Models/VerifyTokenRequest.cs +++ b/src/Passwordless.Net/Models/VerifyTokenRequest.cs @@ -1,11 +1,11 @@ -namespace Passwordless.Net.Models; - -internal class VerifyTokenRequest -{ - public VerifyTokenRequest(string token) - { - Token = token; - } - - public string Token { get; } +namespace Passwordless.Net.Models; + +internal class VerifyTokenRequest +{ + public VerifyTokenRequest(string token) + { + Token = token; + } + + public string Token { get; } } \ No newline at end of file diff --git a/src/Sdk/Sdk.csproj b/src/Passwordless.Net/Passwordless.Net.csproj similarity index 65% rename from src/Sdk/Sdk.csproj rename to src/Passwordless.Net/Passwordless.Net.csproj index b0e9f00..4ce715f 100644 --- a/src/Sdk/Sdk.csproj +++ b/src/Passwordless.Net/Passwordless.Net.csproj @@ -1,20 +1,22 @@ - Passwordless.Net - Passwordless.Net - Passwordless net462;net6.0;net7.0;netstandard2.0 - $(TargetFrameworks);$(CurrentPreviewTfm) + $(TargetFrameworks);$(CurrentPreviewTfm) + true + + + + + Passwordless Bitwarden https://github.com/passwordless/passwordless-dotnet git README.md - true - + $(DefineConstants);INCLUDE_DYNAMICALLY_ACCESSED_MEMBERS @@ -23,20 +25,19 @@ - + - + - - + diff --git a/src/Sdk/PasswordlessApiException.cs b/src/Passwordless.Net/PasswordlessApiException.cs similarity index 96% rename from src/Sdk/PasswordlessApiException.cs rename to src/Passwordless.Net/PasswordlessApiException.cs index 0e6a409..bc01f59 100644 --- a/src/Sdk/PasswordlessApiException.cs +++ b/src/Passwordless.Net/PasswordlessApiException.cs @@ -1,39 +1,39 @@ -using System.Diagnostics; -using System.Net.Http; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Passwordless.Net; - -public sealed class PasswordlessApiException : HttpRequestException -{ - public PasswordlessProblemDetails Details { get; } - - public PasswordlessApiException(PasswordlessProblemDetails problemDetails) : base(problemDetails.Title) - { - Details = problemDetails; - } -} - -public class PasswordlessProblemDetails -{ - public PasswordlessProblemDetails(string type, - string title, int status, string? detail, string? instance) - { - Type = type; - Title = title; - Status = status; - Detail = detail; - Instance = instance; - } - - // TODO: Include errorCode as a property once it's more common - public string Type { get; } - public string Title { get; } - public int Status { get; } - public string? Detail { get; } - public string? Instance { get; } - - [JsonExtensionData] - public Dictionary Extensions { get; set; } = new Dictionary(); +using System.Diagnostics; +using System.Net.Http; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Passwordless.Net; + +public sealed class PasswordlessApiException : HttpRequestException +{ + public PasswordlessProblemDetails Details { get; } + + public PasswordlessApiException(PasswordlessProblemDetails problemDetails) : base(problemDetails.Title) + { + Details = problemDetails; + } +} + +public class PasswordlessProblemDetails +{ + public PasswordlessProblemDetails(string type, + string title, int status, string? detail, string? instance) + { + Type = type; + Title = title; + Status = status; + Detail = detail; + Instance = instance; + } + + // TODO: Include errorCode as a property once it's more common + public string Type { get; } + public string Title { get; } + public int Status { get; } + public string? Detail { get; } + public string? Instance { get; } + + [JsonExtensionData] + public Dictionary Extensions { get; set; } = new Dictionary(); } \ No newline at end of file diff --git a/src/Sdk/PasswordlessClient.cs b/src/Passwordless.Net/PasswordlessClient.cs similarity index 97% rename from src/Sdk/PasswordlessClient.cs rename to src/Passwordless.Net/PasswordlessClient.cs index e9b354e..89a8a6d 100644 --- a/src/Sdk/PasswordlessClient.cs +++ b/src/Passwordless.Net/PasswordlessClient.cs @@ -1,192 +1,192 @@ -using System.Diagnostics; -using System.Net.Http.Json; -using System.Text; -using Passwordless.Net.Models; -using JsonContext = Passwordless.Net.Helpers.PasswordlessSerializerContext; - -namespace Passwordless.Net; - -/// -/// TODO: FILL IN -/// -[DebuggerDisplay("{DebuggerToString(),nq}")] -public class PasswordlessClient : IPasswordlessClient, IDisposable -{ - private readonly bool _needsDisposing; - private readonly HttpClient _client; - - public static PasswordlessClient Create(PasswordlessOptions options, IHttpClientFactory factory) - { - var client = factory.CreateClient(); - client.BaseAddress = new Uri(options.ApiUrl); - client.DefaultRequestHeaders.Add("ApiSecret", options.ApiSecret); - return new PasswordlessClient(client); - } - - public PasswordlessClient(PasswordlessOptions passwordlessOptions) - { - _needsDisposing = true; - _client = new HttpClient - { - BaseAddress = new Uri(passwordlessOptions.ApiUrl), - }; - _client.DefaultRequestHeaders.Add("ApiSecret", passwordlessOptions.ApiSecret); - } - - public PasswordlessClient(HttpClient client) - { - _needsDisposing = false; - _client = client; - } - - /// - public async Task AddAliasAsync(AddAliasRequest request, CancellationToken cancellationToken) - { - var res = await _client.PostAsJsonAsync("alias", - request, - JsonContext.Default.AddAliasRequest, - cancellationToken); - res.EnsureSuccessStatusCode(); - } - - /// - public async Task CreateRegisterTokenAsync(RegisterOptions registerOptions, CancellationToken cancellationToken = default) - { - var res = await _client.PostAsJsonAsync("register/token", - registerOptions, - JsonContext.Default.RegisterOptions, - cancellationToken); - res.EnsureSuccessStatusCode(); - return (await res.Content.ReadFromJsonAsync( - JsonContext.Default.RegisterTokenResponse, - cancellationToken))!; - } - - /// - public async Task VerifyTokenAsync(string verifyToken, CancellationToken cancellationToken = default) - { - var request = new HttpRequestMessage(HttpMethod.Post, "signin/verify") - { - // TODO: No JsonTypeInfo overload yet? - Content = JsonContent.Create(new VerifyTokenRequest(verifyToken)), - }; - - // We just want to return null if there is a problem. - request.SkipErrorHandling(); - var response = await _client.SendAsync(request, cancellationToken); - - if (response.IsSuccessStatusCode) - { - var res = await response.Content.ReadFromJsonAsync( - JsonContext.Default.VerifiedUser, - cancellationToken); - return res; - } - - return null; - } - - /// - public async Task DeleteUserAsync(string userId, CancellationToken cancellationToken = default) - { - await _client.PostAsJsonAsync("users/delete", - new DeleteUserRequest(userId), - JsonContext.Default.DeleteUserRequest, - cancellationToken); - } - - /// - public async Task> ListUsersAsync(CancellationToken cancellationToken = default) - { - var response = await _client.GetFromJsonAsync( - "users/list", - JsonContext.Default.ListResponsePasswordlessUserSummary, - cancellationToken); - return response!.Values; - } - - /// - public async Task> ListAliasesAsync(string userId, CancellationToken cancellationToken = default) - { - var response = await _client.GetFromJsonAsync( - $"alias/list?userid={userId}", - JsonContext.Default.ListResponseAliasPointer, - cancellationToken); - return response!.Values; - } - - /// - public async Task> ListCredentialsAsync(string userId, CancellationToken cancellationToken = default) - { - var response = await _client.GetFromJsonAsync( - $"credentials/list?userid={userId}", - JsonContext.Default.ListResponseCredential, - cancellationToken); - return response!.Values; - } - - /// - public async Task DeleteCredentialAsync(string id, CancellationToken cancellationToken = default) - { - await _client.PostAsJsonAsync("credentials/delete", - new DeleteCredentialRequest(id), - JsonContext.Default.DeleteCredentialRequest, - cancellationToken); - } - - /// - public async Task DeleteCredentialAsync(byte[] id, CancellationToken cancellationToken = default) - { - await DeleteCredentialAsync(Base64Url.Encode(id), cancellationToken); - } - - public async Task GetUsersCountAsync(CancellationToken cancellationToken = default) - { - return (await _client.GetFromJsonAsync( - "users/count", - JsonContext.Default.UsersCount, - cancellationToken))!; - } - - private string DebuggerToString() - { - var sb = new StringBuilder(); - sb.Append("ApiUrl = "); - sb.Append(_client.BaseAddress); - if (_client.DefaultRequestHeaders.TryGetValues("ApiSecret", out var values)) - { - var apiSecret = values.First(); - if (apiSecret.Length > 5) - { - sb.Append(' '); - sb.Append("ApiSecret = "); - sb.Append("***"); - sb.Append(apiSecret.Substring(apiSecret.Length - 4)); - } - } - else - { - sb.Append(' '); - sb.Append("ApiSecret = (null)"); - } - - return sb.ToString(); - } - - public void Dispose() - { - if (_needsDisposing) - { - Dispose(true); - GC.SuppressFinalize(this); - } - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _client.Dispose(); - } - } +using System.Diagnostics; +using System.Net.Http.Json; +using System.Text; +using Passwordless.Net.Models; +using JsonContext = Passwordless.Net.Helpers.PasswordlessSerializerContext; + +namespace Passwordless.Net; + +/// +/// TODO: FILL IN +/// +[DebuggerDisplay("{DebuggerToString(),nq}")] +public class PasswordlessClient : IPasswordlessClient, IDisposable +{ + private readonly bool _needsDisposing; + private readonly HttpClient _client; + + public static PasswordlessClient Create(PasswordlessOptions options, IHttpClientFactory factory) + { + var client = factory.CreateClient(); + client.BaseAddress = new Uri(options.ApiUrl); + client.DefaultRequestHeaders.Add("ApiSecret", options.ApiSecret); + return new PasswordlessClient(client); + } + + public PasswordlessClient(PasswordlessOptions passwordlessOptions) + { + _needsDisposing = true; + _client = new HttpClient + { + BaseAddress = new Uri(passwordlessOptions.ApiUrl), + }; + _client.DefaultRequestHeaders.Add("ApiSecret", passwordlessOptions.ApiSecret); + } + + public PasswordlessClient(HttpClient client) + { + _needsDisposing = false; + _client = client; + } + + /// + public async Task AddAliasAsync(AddAliasRequest request, CancellationToken cancellationToken) + { + var res = await _client.PostAsJsonAsync("alias", + request, + JsonContext.Default.AddAliasRequest, + cancellationToken); + res.EnsureSuccessStatusCode(); + } + + /// + public async Task CreateRegisterTokenAsync(RegisterOptions registerOptions, CancellationToken cancellationToken = default) + { + var res = await _client.PostAsJsonAsync("register/token", + registerOptions, + JsonContext.Default.RegisterOptions, + cancellationToken); + res.EnsureSuccessStatusCode(); + return (await res.Content.ReadFromJsonAsync( + JsonContext.Default.RegisterTokenResponse, + cancellationToken))!; + } + + /// + public async Task VerifyTokenAsync(string verifyToken, CancellationToken cancellationToken = default) + { + var request = new HttpRequestMessage(HttpMethod.Post, "signin/verify") + { + // TODO: No JsonTypeInfo overload yet? + Content = JsonContent.Create(new VerifyTokenRequest(verifyToken)), + }; + + // We just want to return null if there is a problem. + request.SkipErrorHandling(); + var response = await _client.SendAsync(request, cancellationToken); + + if (response.IsSuccessStatusCode) + { + var res = await response.Content.ReadFromJsonAsync( + JsonContext.Default.VerifiedUser, + cancellationToken); + return res; + } + + return null; + } + + /// + public async Task DeleteUserAsync(string userId, CancellationToken cancellationToken = default) + { + await _client.PostAsJsonAsync("users/delete", + new DeleteUserRequest(userId), + JsonContext.Default.DeleteUserRequest, + cancellationToken); + } + + /// + public async Task> ListUsersAsync(CancellationToken cancellationToken = default) + { + var response = await _client.GetFromJsonAsync( + "users/list", + JsonContext.Default.ListResponsePasswordlessUserSummary, + cancellationToken); + return response!.Values; + } + + /// + public async Task> ListAliasesAsync(string userId, CancellationToken cancellationToken = default) + { + var response = await _client.GetFromJsonAsync( + $"alias/list?userid={userId}", + JsonContext.Default.ListResponseAliasPointer, + cancellationToken); + return response!.Values; + } + + /// + public async Task> ListCredentialsAsync(string userId, CancellationToken cancellationToken = default) + { + var response = await _client.GetFromJsonAsync( + $"credentials/list?userid={userId}", + JsonContext.Default.ListResponseCredential, + cancellationToken); + return response!.Values; + } + + /// + public async Task DeleteCredentialAsync(string id, CancellationToken cancellationToken = default) + { + await _client.PostAsJsonAsync("credentials/delete", + new DeleteCredentialRequest(id), + JsonContext.Default.DeleteCredentialRequest, + cancellationToken); + } + + /// + public async Task DeleteCredentialAsync(byte[] id, CancellationToken cancellationToken = default) + { + await DeleteCredentialAsync(Base64Url.Encode(id), cancellationToken); + } + + public async Task GetUsersCountAsync(CancellationToken cancellationToken = default) + { + return (await _client.GetFromJsonAsync( + "users/count", + JsonContext.Default.UsersCount, + cancellationToken))!; + } + + private string DebuggerToString() + { + var sb = new StringBuilder(); + sb.Append("ApiUrl = "); + sb.Append(_client.BaseAddress); + if (_client.DefaultRequestHeaders.TryGetValues("ApiSecret", out var values)) + { + var apiSecret = values.First(); + if (apiSecret.Length > 5) + { + sb.Append(' '); + sb.Append("ApiSecret = "); + sb.Append("***"); + sb.Append(apiSecret.Substring(apiSecret.Length - 4)); + } + } + else + { + sb.Append(' '); + sb.Append("ApiSecret = (null)"); + } + + return sb.ToString(); + } + + public void Dispose() + { + if (_needsDisposing) + { + Dispose(true); + GC.SuppressFinalize(this); + } + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _client.Dispose(); + } + } } \ No newline at end of file diff --git a/src/Sdk/PasswordlessDelegatingHandler.cs b/src/Passwordless.Net/PasswordlessDelegatingHandler.cs similarity index 97% rename from src/Sdk/PasswordlessDelegatingHandler.cs rename to src/Passwordless.Net/PasswordlessDelegatingHandler.cs index 1e145e5..a29ff95 100644 --- a/src/Sdk/PasswordlessDelegatingHandler.cs +++ b/src/Passwordless.Net/PasswordlessDelegatingHandler.cs @@ -1,32 +1,32 @@ -using System.Net.Http; -using System.Net.Http.Json; -using Passwordless.Net.Helpers; - -namespace Passwordless.Net; - -internal class PasswordlessDelegatingHandler : DelegatingHandler -{ - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - var response = await base.SendAsync(request, cancellationToken); - - if (request.ShouldSkipErrorHandling()) - { - return response; - } - - if (!response.IsSuccessStatusCode - && string.Equals(response.Content.Headers.ContentType?.MediaType, "application/problem+json", StringComparison.OrdinalIgnoreCase)) - { - // Attempt to read problem details - var problemDetails = await response.Content.ReadFromJsonAsync( - PasswordlessSerializerContext.Default.PasswordlessProblemDetails, - cancellationToken); - - // Throw exception - throw new PasswordlessApiException(problemDetails!); - } - - return response; - } +using System.Net.Http; +using System.Net.Http.Json; +using Passwordless.Net.Helpers; + +namespace Passwordless.Net; + +internal class PasswordlessDelegatingHandler : DelegatingHandler +{ + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var response = await base.SendAsync(request, cancellationToken); + + if (request.ShouldSkipErrorHandling()) + { + return response; + } + + if (!response.IsSuccessStatusCode + && string.Equals(response.Content.Headers.ContentType?.MediaType, "application/problem+json", StringComparison.OrdinalIgnoreCase)) + { + // Attempt to read problem details + var problemDetails = await response.Content.ReadFromJsonAsync( + PasswordlessSerializerContext.Default.PasswordlessProblemDetails, + cancellationToken); + + // Throw exception + throw new PasswordlessApiException(problemDetails!); + } + + return response; + } } \ No newline at end of file diff --git a/src/Sdk/PasswordlessExtensions.cs b/src/Passwordless.Net/PasswordlessExtensions.cs similarity index 95% rename from src/Sdk/PasswordlessExtensions.cs rename to src/Passwordless.Net/PasswordlessExtensions.cs index 1b7a855..283dc22 100644 --- a/src/Sdk/PasswordlessExtensions.cs +++ b/src/Passwordless.Net/PasswordlessExtensions.cs @@ -1,9 +1,9 @@ -namespace Passwordless.Net; - -public static class PasswordlessExtensions -{ - public static string ToBase64Url(this byte[] bytes) - { - return Base64Url.Encode(bytes); - } +namespace Passwordless.Net; + +public static class PasswordlessExtensions +{ + public static string ToBase64Url(this byte[] bytes) + { + return Base64Url.Encode(bytes); + } } \ No newline at end of file diff --git a/src/Sdk/PasswordlessHttpRequestExtensions.cs b/src/Passwordless.Net/PasswordlessHttpRequestExtensions.cs similarity index 97% rename from src/Sdk/PasswordlessHttpRequestExtensions.cs rename to src/Passwordless.Net/PasswordlessHttpRequestExtensions.cs index 75d4fb9..bb1dd49 100644 --- a/src/Sdk/PasswordlessHttpRequestExtensions.cs +++ b/src/Passwordless.Net/PasswordlessHttpRequestExtensions.cs @@ -1,33 +1,33 @@ -using System.Net.Http; - -namespace Passwordless.Net; - -internal static class PasswordlessHttpRequestExtensions -{ -#if NET5_0_OR_GREATER - internal static HttpRequestOptionsKey SkipErrorHandlingOption = new(nameof(SkipErrorHandling)); -#elif NET462 || NETSTANDARD2_0 - internal const string SkipErrorHandlingOption = nameof(SkipErrorHandling); -#endif - - internal static HttpRequestMessage SkipErrorHandling(this HttpRequestMessage request, bool skip = true) - { -#if NET5_0_OR_GREATER - request.Options.Set(SkipErrorHandlingOption, skip); -#elif NET462 || NETSTANDARD2_0 - request.Properties[SkipErrorHandlingOption] = skip; -#endif - return request; - } - - internal static bool ShouldSkipErrorHandling(this HttpRequestMessage request) - { -#if NET5_0_OR_GREATER - return request.Options.TryGetValue(SkipErrorHandlingOption, out var doNotErrorHandle) && doNotErrorHandle; -#elif NET462 || NETSTANDARD2_0 - return request.Properties.TryGetValue(SkipErrorHandlingOption, out var shouldSkipOptionObject) - && shouldSkipOptionObject is bool shouldSkipOption - && shouldSkipOption; -#endif - } +using System.Net.Http; + +namespace Passwordless.Net; + +internal static class PasswordlessHttpRequestExtensions +{ +#if NET5_0_OR_GREATER + internal static HttpRequestOptionsKey SkipErrorHandlingOption = new(nameof(SkipErrorHandling)); +#elif NET462 || NETSTANDARD2_0 + internal const string SkipErrorHandlingOption = nameof(SkipErrorHandling); +#endif + + internal static HttpRequestMessage SkipErrorHandling(this HttpRequestMessage request, bool skip = true) + { +#if NET5_0_OR_GREATER + request.Options.Set(SkipErrorHandlingOption, skip); +#elif NET462 || NETSTANDARD2_0 + request.Properties[SkipErrorHandlingOption] = skip; +#endif + return request; + } + + internal static bool ShouldSkipErrorHandling(this HttpRequestMessage request) + { +#if NET5_0_OR_GREATER + return request.Options.TryGetValue(SkipErrorHandlingOption, out var doNotErrorHandle) && doNotErrorHandle; +#elif NET462 || NETSTANDARD2_0 + return request.Properties.TryGetValue(SkipErrorHandlingOption, out var shouldSkipOptionObject) + && shouldSkipOptionObject is bool shouldSkipOption + && shouldSkipOption; +#endif + } } \ No newline at end of file diff --git a/src/Sdk/PasswordlessOptions.cs b/src/Passwordless.Net/PasswordlessOptions.cs similarity index 97% rename from src/Sdk/PasswordlessOptions.cs rename to src/Passwordless.Net/PasswordlessOptions.cs index 2ccecf9..38f10e6 100644 --- a/src/Sdk/PasswordlessOptions.cs +++ b/src/Passwordless.Net/PasswordlessOptions.cs @@ -1,33 +1,33 @@ -namespace Passwordless.Net; - -/// -/// Represents all the options you can use to configure a backend Passwordless system. -/// -public class PasswordlessOptions -{ - /// - /// Passwordless Cloud Url - /// - public const string CloudApiUrl = "https://v4.passwordless.dev"; - - /// - /// Gets or sets the url to use for Passwordless operations. - /// - /// - /// Defaults to . - /// - public string ApiUrl { get; set; } = CloudApiUrl; - - /// - /// Gets or sets the secret API key used to authenticate with the Passwordless API. - /// - public string ApiSecret { get; set; } = default!; - - /// - /// Gets or sets the public API key used to interact with the Passwordless API. - /// - /// - /// Optional: Only used for frontend operations by the JS Client. E.g: Useful if you're using MVC/Razor pages - /// - public string? ApiKey { get; set; } +namespace Passwordless.Net; + +/// +/// Represents all the options you can use to configure a backend Passwordless system. +/// +public class PasswordlessOptions +{ + /// + /// Passwordless Cloud Url + /// + public const string CloudApiUrl = "https://v4.passwordless.dev"; + + /// + /// Gets or sets the url to use for Passwordless operations. + /// + /// + /// Defaults to . + /// + public string ApiUrl { get; set; } = CloudApiUrl; + + /// + /// Gets or sets the secret API key used to authenticate with the Passwordless API. + /// + public string ApiSecret { get; set; } = default!; + + /// + /// Gets or sets the public API key used to interact with the Passwordless API. + /// + /// + /// Optional: Only used for frontend operations by the JS Client. E.g: Useful if you're using MVC/Razor pages + /// + public string? ApiKey { get; set; } } \ No newline at end of file diff --git a/src/Sdk/Pollyfill.cs b/src/Passwordless.Net/Pollyfill.cs similarity index 97% rename from src/Sdk/Pollyfill.cs rename to src/Passwordless.Net/Pollyfill.cs index 63234d9..3b9f508 100644 --- a/src/Sdk/Pollyfill.cs +++ b/src/Passwordless.Net/Pollyfill.cs @@ -1,143 +1,143 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Diagnostics.CodeAnalysis -{ -#if INCLUDE_DYNAMICALLY_ACCESSED_MEMBERS - /// - /// Indicates that certain members on a specified are accessed dynamically, - /// for example through . - /// - /// - /// This allows tools to understand which members are being accessed during the execution - /// of a program. - /// - /// This attribute is valid on members whose type is or . - /// - /// When this attribute is applied to a location of type , the assumption is - /// that the string represents a fully qualified type name. - /// - /// When this attribute is applied to a class, interface, or struct, the members specified - /// can be accessed dynamically on instances returned from calling - /// on instances of that class, interface, or struct. - /// - /// If the attribute is applied to a method it's treated as a special case and it implies - /// the attribute should be applied to the "this" parameter of the method. As such the attribute - /// should only be used on instance methods of types assignable to System.Type (or string, but no methods - /// will use it there). - /// - [AttributeUsage( - AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter | - AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Method | - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, - Inherited = false)] - internal sealed class DynamicallyAccessedMembersAttribute : Attribute - { - /// - /// Initializes a new instance of the class - /// with the specified member types. - /// - /// The types of members dynamically accessed. - public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes) - { - MemberTypes = memberTypes; - } - - /// - /// Gets the which specifies the type - /// of members dynamically accessed. - /// - public DynamicallyAccessedMemberTypes MemberTypes { get; } - } - - /// - /// Specifies the types of members that are dynamically accessed. - /// - /// This enumeration has a attribute that allows a - /// bitwise combination of its member values. - /// - [Flags] - internal enum DynamicallyAccessedMemberTypes - { - /// - /// Specifies no members. - /// - None = 0, - - /// - /// Specifies the default, parameterless public constructor. - /// - PublicParameterlessConstructor = 0x0001, - - /// - /// Specifies all public constructors. - /// - PublicConstructors = 0x0002 | PublicParameterlessConstructor, - - /// - /// Specifies all non-public constructors. - /// - NonPublicConstructors = 0x0004, - - /// - /// Specifies all public methods. - /// - PublicMethods = 0x0008, - - /// - /// Specifies all non-public methods. - /// - NonPublicMethods = 0x0010, - - /// - /// Specifies all public fields. - /// - PublicFields = 0x0020, - - /// - /// Specifies all non-public fields. - /// - NonPublicFields = 0x0040, - - /// - /// Specifies all public nested types. - /// - PublicNestedTypes = 0x0080, - - /// - /// Specifies all non-public nested types. - /// - NonPublicNestedTypes = 0x0100, - - /// - /// Specifies all public properties. - /// - PublicProperties = 0x0200, - - /// - /// Specifies all non-public properties. - /// - NonPublicProperties = 0x0400, - - /// - /// Specifies all public events. - /// - PublicEvents = 0x0800, - - /// - /// Specifies all non-public events. - /// - NonPublicEvents = 0x1000, - - /// - /// Specifies all interfaces implemented by the type. - /// - Interfaces = 0x2000, - - /// - /// Specifies all members. - /// - All = ~None - } -#endif +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Diagnostics.CodeAnalysis +{ +#if INCLUDE_DYNAMICALLY_ACCESSED_MEMBERS + /// + /// Indicates that certain members on a specified are accessed dynamically, + /// for example through . + /// + /// + /// This allows tools to understand which members are being accessed during the execution + /// of a program. + /// + /// This attribute is valid on members whose type is or . + /// + /// When this attribute is applied to a location of type , the assumption is + /// that the string represents a fully qualified type name. + /// + /// When this attribute is applied to a class, interface, or struct, the members specified + /// can be accessed dynamically on instances returned from calling + /// on instances of that class, interface, or struct. + /// + /// If the attribute is applied to a method it's treated as a special case and it implies + /// the attribute should be applied to the "this" parameter of the method. As such the attribute + /// should only be used on instance methods of types assignable to System.Type (or string, but no methods + /// will use it there). + /// + [AttributeUsage( + AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter | + AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Method | + AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct, + Inherited = false)] + internal sealed class DynamicallyAccessedMembersAttribute : Attribute + { + /// + /// Initializes a new instance of the class + /// with the specified member types. + /// + /// The types of members dynamically accessed. + public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes) + { + MemberTypes = memberTypes; + } + + /// + /// Gets the which specifies the type + /// of members dynamically accessed. + /// + public DynamicallyAccessedMemberTypes MemberTypes { get; } + } + + /// + /// Specifies the types of members that are dynamically accessed. + /// + /// This enumeration has a attribute that allows a + /// bitwise combination of its member values. + /// + [Flags] + internal enum DynamicallyAccessedMemberTypes + { + /// + /// Specifies no members. + /// + None = 0, + + /// + /// Specifies the default, parameterless public constructor. + /// + PublicParameterlessConstructor = 0x0001, + + /// + /// Specifies all public constructors. + /// + PublicConstructors = 0x0002 | PublicParameterlessConstructor, + + /// + /// Specifies all non-public constructors. + /// + NonPublicConstructors = 0x0004, + + /// + /// Specifies all public methods. + /// + PublicMethods = 0x0008, + + /// + /// Specifies all non-public methods. + /// + NonPublicMethods = 0x0010, + + /// + /// Specifies all public fields. + /// + PublicFields = 0x0020, + + /// + /// Specifies all non-public fields. + /// + NonPublicFields = 0x0040, + + /// + /// Specifies all public nested types. + /// + PublicNestedTypes = 0x0080, + + /// + /// Specifies all non-public nested types. + /// + NonPublicNestedTypes = 0x0100, + + /// + /// Specifies all public properties. + /// + PublicProperties = 0x0200, + + /// + /// Specifies all non-public properties. + /// + NonPublicProperties = 0x0400, + + /// + /// Specifies all public events. + /// + PublicEvents = 0x0800, + + /// + /// Specifies all non-public events. + /// + NonPublicEvents = 0x1000, + + /// + /// Specifies all interfaces implemented by the type. + /// + Interfaces = 0x2000, + + /// + /// Specifies all members. + /// + All = ~None + } +#endif } \ No newline at end of file diff --git a/src/Sdk/ServiceCollectionExtensions.cs b/src/Passwordless.Net/ServiceCollectionExtensions.cs similarity index 97% rename from src/Sdk/ServiceCollectionExtensions.cs rename to src/Passwordless.Net/ServiceCollectionExtensions.cs index fc64789..4dbc46e 100644 --- a/src/Sdk/ServiceCollectionExtensions.cs +++ b/src/Passwordless.Net/ServiceCollectionExtensions.cs @@ -1,52 +1,52 @@ -using System.Diagnostics.CodeAnalysis; -using System.Net.Http; -using Microsoft.Extensions.Options; -using Passwordless.Net; - -// This is a trick to always show up in a class when people are registering services -namespace Microsoft.Extensions.DependencyInjection; - -public static class ServiceCollectionExtensions -{ - public static IServiceCollection AddPasswordlessSdk(this IServiceCollection services, Action configureOptions) - { - services.AddOptions() - .Configure(configureOptions) - .PostConfigure(options => options.ApiUrl ??= PasswordlessOptions.CloudApiUrl) - .Validate(options => !string.IsNullOrEmpty(options.ApiSecret), "Passwordless: Missing ApiSecret"); - - services.AddPasswordlessClientCore((sp, client) => - { - var options = sp.GetRequiredService>().Value; - - client.BaseAddress = new Uri(options.ApiUrl); - client.DefaultRequestHeaders.Add("ApiSecret", options.ApiSecret); - }); - - // TODO: Get rid of this service, all consumers should use the interface - services.AddTransient(sp => (PasswordlessClient)sp.GetRequiredService()); - - return services; - } - - /// - /// Helper method for making custom typed HttpClient implementations that also have - /// the inner handler for throwing fancy exceptions. Not intended for public use, - /// hence the hiding of it in IDE's. - /// - /// - /// This method signature is subject to change without major version bump/announcement. - /// - internal static IServiceCollection AddPasswordlessClientCore<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(this IServiceCollection services, Action configureClient) - where TClient : class - where TImplementation : class, TClient - { - services.AddTransient(); - - services - .AddHttpClient(configureClient) - .AddHttpMessageHandler(); - - return services; - } +using System.Diagnostics.CodeAnalysis; +using System.Net.Http; +using Microsoft.Extensions.Options; +using Passwordless.Net; + +// This is a trick to always show up in a class when people are registering services +namespace Microsoft.Extensions.DependencyInjection; + +public static class ServiceCollectionExtensions +{ + public static IServiceCollection AddPasswordlessSdk(this IServiceCollection services, Action configureOptions) + { + services.AddOptions() + .Configure(configureOptions) + .PostConfigure(options => options.ApiUrl ??= PasswordlessOptions.CloudApiUrl) + .Validate(options => !string.IsNullOrEmpty(options.ApiSecret), "Passwordless: Missing ApiSecret"); + + services.AddPasswordlessClientCore((sp, client) => + { + var options = sp.GetRequiredService>().Value; + + client.BaseAddress = new Uri(options.ApiUrl); + client.DefaultRequestHeaders.Add("ApiSecret", options.ApiSecret); + }); + + // TODO: Get rid of this service, all consumers should use the interface + services.AddTransient(sp => (PasswordlessClient)sp.GetRequiredService()); + + return services; + } + + /// + /// Helper method for making custom typed HttpClient implementations that also have + /// the inner handler for throwing fancy exceptions. Not intended for public use, + /// hence the hiding of it in IDE's. + /// + /// + /// This method signature is subject to change without major version bump/announcement. + /// + internal static IServiceCollection AddPasswordlessClientCore<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TClient, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TImplementation>(this IServiceCollection services, Action configureClient) + where TClient : class + where TImplementation : class, TClient + { + services.AddTransient(); + + services + .AddHttpClient(configureClient) + .AddHttpMessageHandler(); + + return services; + } } \ No newline at end of file diff --git a/tests/Sdk.Tests/ApiFactAttribute.cs b/tests/Passwordless.Net.Tests/ApiFactAttribute.cs similarity index 96% rename from tests/Sdk.Tests/ApiFactAttribute.cs rename to tests/Passwordless.Net.Tests/ApiFactAttribute.cs index 8286407..68ba415 100644 --- a/tests/Sdk.Tests/ApiFactAttribute.cs +++ b/tests/Passwordless.Net.Tests/ApiFactAttribute.cs @@ -1,14 +1,14 @@ -// Uncomment line while running API in mock mode to run tests -// #define RUNNING_API - -namespace Passwordless.Net.Tests; - -public class ApiFactAttribute : FactAttribute -{ - public ApiFactAttribute() - { -#if !RUNNING_API - Skip = "These tests are skipped unless you are running the API locally."; -#endif - } +// Uncomment line while running API in mock mode to run tests +// #define RUNNING_API + +namespace Passwordless.Net.Tests; + +public class ApiFactAttribute : FactAttribute +{ + public ApiFactAttribute() + { +#if !RUNNING_API + Skip = "These tests are skipped unless you are running the API locally."; +#endif + } } \ No newline at end of file diff --git a/tests/Sdk.Tests/Sdk.Tests.csproj b/tests/Passwordless.Net.Tests/Passwordless.Net.Tests.csproj similarity index 62% rename from tests/Sdk.Tests/Sdk.Tests.csproj rename to tests/Passwordless.Net.Tests/Passwordless.Net.Tests.csproj index 6ae067c..831bdb7 100644 --- a/tests/Sdk.Tests/Sdk.Tests.csproj +++ b/tests/Passwordless.Net.Tests/Passwordless.Net.Tests.csproj @@ -1,31 +1,23 @@ - false - true - Passwordless.Net.Tests - Passwordless.Net.Tests net6.0;net7.0 $(TargetFrameworks);net462 $(TargetFrameworks);$(CurrentPreviewTfm) + false + true - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - + + - + diff --git a/tests/Sdk.Tests/PasswordlessClientTests.cs b/tests/Passwordless.Net.Tests/PasswordlessClientTests.cs similarity index 96% rename from tests/Sdk.Tests/PasswordlessClientTests.cs rename to tests/Passwordless.Net.Tests/PasswordlessClientTests.cs index c5fe3e6..4fa95e9 100644 --- a/tests/Sdk.Tests/PasswordlessClientTests.cs +++ b/tests/Passwordless.Net.Tests/PasswordlessClientTests.cs @@ -1,125 +1,125 @@ -using System.Net.Http; -using System.Text.Json; -using Microsoft.Extensions.DependencyInjection; - - -namespace Passwordless.Net.Tests; - -public class PasswordlessClientTests -{ - private readonly PasswordlessClient _sut; - - public PasswordlessClientTests() - { - var services = new ServiceCollection(); - - services.AddPasswordlessSdk(options => - { - options.ApiUrl = "https://localhost:7002"; - options.ApiSecret = "test:secret:a679563b331846c79c20b114a4f56d02"; - }); - - var provider = services.BuildServiceProvider(); - - _sut = (PasswordlessClient)provider.GetRequiredService(); - } - - [ApiFact] - public async Task CreateRegisterTokenAsync_ThrowsExceptionWhenBad() - { - var exception = await Assert.ThrowsAnyAsync( - async () => await _sut.CreateRegisterTokenAsync(new RegisterOptions(null!, null!))); - } - - [ApiFact] - public async Task VerifyTokenAsync_DoesNotThrowOnBadToken() - { - var verifiedUser = await _sut.VerifyTokenAsync("bad_token"); - - Assert.Null(verifiedUser); - } - - [ApiFact] - public async Task DeleteUserAsync_BadUserId_ThrowsException() - { - var exception = await Assert.ThrowsAnyAsync( - async () => await _sut.DeleteUserAsync(null!)); - } - - [ApiFact] - public async Task ListAsiasesAsync_BadUserId_ThrowsException() - { - var exception = await Assert.ThrowsAnyAsync( - async () => await _sut.ListAliasesAsync(null!)); - } - - [ApiFact] - public async Task ListCredentialsAsync_BadUserId_ThrowsException() - { - var exception = await Assert.ThrowsAnyAsync( - async () => await _sut.ListCredentialsAsync(null!)); - - var errorCode = Assert.Contains("errorCode", (IDictionary)exception.Details.Extensions); - Assert.Equal(JsonValueKind.String, errorCode.ValueKind); - Assert.Equal("missing_userid", errorCode.GetString()); - } - - [ApiFact] - public async Task CreateRegisterTokenAsync_Works() - { - var userId = Guid.NewGuid().ToString(); - - var response = await _sut.CreateRegisterTokenAsync(new RegisterOptions(userId, "test_username")); - - Assert.NotNull(response.Token); - Assert.StartsWith("register_", response.Token); - } - - [ApiFact] - public async Task VerifyTokenAsync_Works() - { - var user = await _sut.VerifyTokenAsync("verify_valid"); - - Assert.NotNull(user); - Assert.True(user.Success); - } - - [ApiFact] - public async Task ListUsersAsync_Works() - { - var users = await _sut.ListUsersAsync(); - - Assert.NotEmpty(users); - } - - [ApiFact] - public async Task ListAliasesAsync_Works() - { - // Act - var aliases = await _sut.ListAliasesAsync("has_aliases"); - - // Assert - Assert.NotEmpty(aliases); - } - - [ApiFact] - public async Task ListCredentialsAsync_Works() - { - var credentials = await _sut.ListCredentialsAsync("has_credentials"); - - Assert.NotEmpty(credentials); - } - - [ApiFact] - public async Task DeleteCredentialAsync_Works() - { - await _sut.DeleteCredentialAsync("can_delete"); - } - - [ApiFact] - public async Task GetUsersCountAsync_Works() - { - var usersCount = await _sut.GetUsersCountAsync(); - Assert.NotEqual(0, usersCount.Count); - } +using System.Net.Http; +using System.Text.Json; +using Microsoft.Extensions.DependencyInjection; + + +namespace Passwordless.Net.Tests; + +public class PasswordlessClientTests +{ + private readonly PasswordlessClient _sut; + + public PasswordlessClientTests() + { + var services = new ServiceCollection(); + + services.AddPasswordlessSdk(options => + { + options.ApiUrl = "https://localhost:7002"; + options.ApiSecret = "test:secret:a679563b331846c79c20b114a4f56d02"; + }); + + var provider = services.BuildServiceProvider(); + + _sut = (PasswordlessClient)provider.GetRequiredService(); + } + + [ApiFact] + public async Task CreateRegisterTokenAsync_ThrowsExceptionWhenBad() + { + var exception = await Assert.ThrowsAnyAsync( + async () => await _sut.CreateRegisterTokenAsync(new RegisterOptions(null!, null!))); + } + + [ApiFact] + public async Task VerifyTokenAsync_DoesNotThrowOnBadToken() + { + var verifiedUser = await _sut.VerifyTokenAsync("bad_token"); + + Assert.Null(verifiedUser); + } + + [ApiFact] + public async Task DeleteUserAsync_BadUserId_ThrowsException() + { + var exception = await Assert.ThrowsAnyAsync( + async () => await _sut.DeleteUserAsync(null!)); + } + + [ApiFact] + public async Task ListAsiasesAsync_BadUserId_ThrowsException() + { + var exception = await Assert.ThrowsAnyAsync( + async () => await _sut.ListAliasesAsync(null!)); + } + + [ApiFact] + public async Task ListCredentialsAsync_BadUserId_ThrowsException() + { + var exception = await Assert.ThrowsAnyAsync( + async () => await _sut.ListCredentialsAsync(null!)); + + var errorCode = Assert.Contains("errorCode", (IDictionary)exception.Details.Extensions); + Assert.Equal(JsonValueKind.String, errorCode.ValueKind); + Assert.Equal("missing_userid", errorCode.GetString()); + } + + [ApiFact] + public async Task CreateRegisterTokenAsync_Works() + { + var userId = Guid.NewGuid().ToString(); + + var response = await _sut.CreateRegisterTokenAsync(new RegisterOptions(userId, "test_username")); + + Assert.NotNull(response.Token); + Assert.StartsWith("register_", response.Token); + } + + [ApiFact] + public async Task VerifyTokenAsync_Works() + { + var user = await _sut.VerifyTokenAsync("verify_valid"); + + Assert.NotNull(user); + Assert.True(user.Success); + } + + [ApiFact] + public async Task ListUsersAsync_Works() + { + var users = await _sut.ListUsersAsync(); + + Assert.NotEmpty(users); + } + + [ApiFact] + public async Task ListAliasesAsync_Works() + { + // Act + var aliases = await _sut.ListAliasesAsync("has_aliases"); + + // Assert + Assert.NotEmpty(aliases); + } + + [ApiFact] + public async Task ListCredentialsAsync_Works() + { + var credentials = await _sut.ListCredentialsAsync("has_credentials"); + + Assert.NotEmpty(credentials); + } + + [ApiFact] + public async Task DeleteCredentialAsync_Works() + { + await _sut.DeleteCredentialAsync("can_delete"); + } + + [ApiFact] + public async Task GetUsersCountAsync_Works() + { + var usersCount = await _sut.GetUsersCountAsync(); + Assert.NotEqual(0, usersCount.Count); + } } \ No newline at end of file diff --git a/tests/Sdk.Tests/Usings.cs b/tests/Passwordless.Net.Tests/Usings.cs similarity index 100% rename from tests/Sdk.Tests/Usings.cs rename to tests/Passwordless.Net.Tests/Usings.cs From ae8d5318d9d84f0e69b2ececfd1f23f5da842de0 Mon Sep 17 00:00:00 2001 From: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com> Date: Tue, 19 Sep 2023 18:34:39 +0300 Subject: [PATCH 2/4] Change eol to `lf` --- .editorconfig | 56 +++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/.editorconfig b/.editorconfig index f6e8274..b783571 100644 --- a/.editorconfig +++ b/.editorconfig @@ -18,7 +18,7 @@ indent_size = 4 tab_width = 4 # New line preferences -end_of_line = crlf +end_of_line = lf insert_final_newline = false #### .NET Coding Conventions #### @@ -256,31 +256,31 @@ dotnet_naming_rule.non_field_members_should_be_pascalcase.style = pascalcase dotnet_naming_symbols.interfaces.applicable_kinds = interface dotnet_naming_symbols.interfaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.interfaces.required_modifiers = +dotnet_naming_symbols.interfaces.required_modifiers = dotnet_naming_symbols.enums.applicable_kinds = enum dotnet_naming_symbols.enums.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.enums.required_modifiers = +dotnet_naming_symbols.enums.required_modifiers = dotnet_naming_symbols.events.applicable_kinds = event dotnet_naming_symbols.events.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.events.required_modifiers = +dotnet_naming_symbols.events.required_modifiers = dotnet_naming_symbols.methods.applicable_kinds = method dotnet_naming_symbols.methods.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.methods.required_modifiers = +dotnet_naming_symbols.methods.required_modifiers = dotnet_naming_symbols.properties.applicable_kinds = property dotnet_naming_symbols.properties.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.properties.required_modifiers = +dotnet_naming_symbols.properties.required_modifiers = dotnet_naming_symbols.public_fields.applicable_kinds = field dotnet_naming_symbols.public_fields.applicable_accessibilities = public, internal -dotnet_naming_symbols.public_fields.required_modifiers = +dotnet_naming_symbols.public_fields.required_modifiers = dotnet_naming_symbols.private_fields.applicable_kinds = field dotnet_naming_symbols.private_fields.applicable_accessibilities = private, protected, protected_internal, private_protected -dotnet_naming_symbols.private_fields.required_modifiers = +dotnet_naming_symbols.private_fields.required_modifiers = dotnet_naming_symbols.private_static_fields.applicable_kinds = field dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private, protected, protected_internal, private_protected @@ -288,15 +288,15 @@ dotnet_naming_symbols.private_static_fields.required_modifiers = static dotnet_naming_symbols.types_and_namespaces.applicable_kinds = namespace, class, struct, interface, enum dotnet_naming_symbols.types_and_namespaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.types_and_namespaces.required_modifiers = +dotnet_naming_symbols.types_and_namespaces.required_modifiers = dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.non_field_members.required_modifiers = +dotnet_naming_symbols.non_field_members.required_modifiers = dotnet_naming_symbols.type_parameters.applicable_kinds = namespace dotnet_naming_symbols.type_parameters.applicable_accessibilities = * -dotnet_naming_symbols.type_parameters.required_modifiers = +dotnet_naming_symbols.type_parameters.required_modifiers = dotnet_naming_symbols.private_constant_fields.applicable_kinds = field dotnet_naming_symbols.private_constant_fields.applicable_accessibilities = private, protected, protected_internal, private_protected @@ -304,7 +304,7 @@ dotnet_naming_symbols.private_constant_fields.required_modifiers = const dotnet_naming_symbols.local_variables.applicable_kinds = local dotnet_naming_symbols.local_variables.applicable_accessibilities = local -dotnet_naming_symbols.local_variables.required_modifiers = +dotnet_naming_symbols.local_variables.required_modifiers = dotnet_naming_symbols.local_constants.applicable_kinds = local dotnet_naming_symbols.local_constants.applicable_accessibilities = local @@ -312,7 +312,7 @@ dotnet_naming_symbols.local_constants.required_modifiers = const dotnet_naming_symbols.parameters.applicable_kinds = parameter dotnet_naming_symbols.parameters.applicable_accessibilities = * -dotnet_naming_symbols.parameters.required_modifiers = +dotnet_naming_symbols.parameters.required_modifiers = dotnet_naming_symbols.public_constant_fields.applicable_kinds = field dotnet_naming_symbols.public_constant_fields.applicable_accessibilities = public, internal @@ -328,36 +328,36 @@ dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = readon dotnet_naming_symbols.local_functions.applicable_kinds = local_function dotnet_naming_symbols.local_functions.applicable_accessibilities = * -dotnet_naming_symbols.local_functions.required_modifiers = +dotnet_naming_symbols.local_functions.required_modifiers = # Naming styles -dotnet_naming_style.pascalcase.required_prefix = -dotnet_naming_style.pascalcase.required_suffix = -dotnet_naming_style.pascalcase.word_separator = +dotnet_naming_style.pascalcase.required_prefix = +dotnet_naming_style.pascalcase.required_suffix = +dotnet_naming_style.pascalcase.word_separator = dotnet_naming_style.pascalcase.capitalization = pascal_case dotnet_naming_style.ipascalcase.required_prefix = I -dotnet_naming_style.ipascalcase.required_suffix = -dotnet_naming_style.ipascalcase.word_separator = +dotnet_naming_style.ipascalcase.required_suffix = +dotnet_naming_style.ipascalcase.word_separator = dotnet_naming_style.ipascalcase.capitalization = pascal_case dotnet_naming_style.tpascalcase.required_prefix = T -dotnet_naming_style.tpascalcase.required_suffix = -dotnet_naming_style.tpascalcase.word_separator = +dotnet_naming_style.tpascalcase.required_suffix = +dotnet_naming_style.tpascalcase.word_separator = dotnet_naming_style.tpascalcase.capitalization = pascal_case dotnet_naming_style._camelcase.required_prefix = _ -dotnet_naming_style._camelcase.required_suffix = -dotnet_naming_style._camelcase.word_separator = +dotnet_naming_style._camelcase.required_suffix = +dotnet_naming_style._camelcase.word_separator = dotnet_naming_style._camelcase.capitalization = camel_case -dotnet_naming_style.camelcase.required_prefix = -dotnet_naming_style.camelcase.required_suffix = -dotnet_naming_style.camelcase.word_separator = +dotnet_naming_style.camelcase.required_prefix = +dotnet_naming_style.camelcase.required_suffix = +dotnet_naming_style.camelcase.word_separator = dotnet_naming_style.camelcase.capitalization = camel_case dotnet_naming_style.s_camelcase.required_prefix = s_ -dotnet_naming_style.s_camelcase.required_suffix = -dotnet_naming_style.s_camelcase.word_separator = +dotnet_naming_style.s_camelcase.required_suffix = +dotnet_naming_style.s_camelcase.word_separator = dotnet_naming_style.s_camelcase.capitalization = camel_case From e1eb4170e450b534cb4a62dcd8c6cffaffa68b4b Mon Sep 17 00:00:00 2001 From: Oleksii Holub <1935960+Tyrrrz@users.noreply.github.com> Date: Wed, 20 Sep 2023 17:39:03 +0300 Subject: [PATCH 3/4] Merge main --- .github/workflows/relase.yml | 23 --- .github/workflows/release.yml | 42 +++++ .github/workflows/workflow.yml | 74 +++++---- Directory.Build.props | 5 +- README.md | 149 ++++++++---------- .../Models/AddAliasRequest.cs | 1 + .../Passwordless.Net.Tests.csproj | 1 + 7 files changed, 159 insertions(+), 136 deletions(-) delete mode 100644 .github/workflows/relase.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/relase.yml b/.github/workflows/relase.yml deleted file mode 100644 index 7686f1a..0000000 --- a/.github/workflows/relase.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Release to NuGet - -on: - release: - types: [published] - -jobs: - build: - runs-on: ubuntu-latest - timeout-minutes: 5 - steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - - - name: Setup dotnet - uses: actions/setup-dotnet@607fce577a46308457984d59e4954e075820f10a #v3.0.3 - - name: Build - run: dotnet build -c Release - - name: Test - run: dotnet test -c Release --no-build - - name: Pack nugets - run: dotnet pack -c Release --no-build --output . - - name: Push to NuGet - run: dotnet nuget push "*.nupkg" --api-key ${{secrets.nuget_api_key}} --source https://api.nuget.org/v3/index.json \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..b985424 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,42 @@ +name: Release to NuGet + +on: + release: + types: [published] + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Checkout + uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + + - name: Setup dotnet + uses: actions/setup-dotnet@607fce577a46308457984d59e4954e075820f10a # v3.0.3 + + - name: Build + run: dotnet build --configuration Release + + - name: Test + run: > + dotnet test + --no-build + --framework net462 + --configuration Release + --logger "GitHubActions;summary.includePassedTests=true;summary.includeSkippedTests=true" + -- + RunConfiguration.CollectSourceInformation=true + + - name: Pack nugets + run: > + dotnet pack + --configuration Release + --no-build + --output . + + - name: Push to NuGet + run: > + dotnet nuget push *.nupkg + --source https://api.nuget.org/v3/index.json + --api-key ${{secrets.nuget_api_key}} diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 7d60b99..3614527 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -22,11 +22,14 @@ jobs: clean_name: ${{steps.clean_branch_name.outputs.CLEAN_BRANCH_NAME}} steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + - name: Checkout + uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + - name: Setup dotnet - uses: actions/setup-dotnet@607fce577a46308457984d59e4954e075820f10a #v3.0.3 + uses: actions/setup-dotnet@607fce577a46308457984d59e4954e075820f10a # v3.0.3 # with: # dotnet-version: ${{ matrix.dotnet-version }} + # You can test your matrix by printing the current dotnet version - name: Display dotnet version run: dotnet --version @@ -35,13 +38,26 @@ jobs: run: dotnet restore --locked-mode - name: Build - run: dotnet build -f net6.0 --verbosity minimal --no-restore + run: > + dotnet build + --no-restore + --framework net6.0 + --configuration Release + --verbosity minimal - name: Check Format run: dotnet format --verify-no-changes --no-restore - name: Test with the dotnet CLI - run: dotnet test --no-build --no-restore --framework net6.0 --logger "trx;LogFileName=pw-test-results.trx" + run: > + dotnet test + --no-build + --no-restore + --framework net6.0 + --configuration Release + --logger "GitHubActions;summary.includePassedTests=true;summary.includeSkippedTests=true" + -- + RunConfiguration.CollectSourceInformation=true - id: clean_branch_name name: Clean Branch Name @@ -49,22 +65,16 @@ jobs: env: BRANCH_NAME: ${{ github.head_ref || github.ref_name }} - - name: Report test results - uses: dorny/test-reporter@c9b3d0e2bd2a4e96aaf424dbaa31c46b42318226 # v1.6.0 - if: always() - with: - name: Test Results - path: "**/*-test-results.trx" - reporter: dotnet-trx - fail-on-error: true - build-framework: runs-on: windows-latest needs: build-dotnet steps: - - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + - name: Checkout + uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + # - name: Setup dotnet # uses: setup msbuild? + - name: Display dotnet version run: | dotnet --version @@ -75,29 +85,39 @@ jobs: - name: Build # Don't specify a framework here so we build both .NET and .NET Framework so we can pack both - run: dotnet build --verbosity minimal --no-restore + run: > + dotnet build + --no-restore + --configuration Release + --verbosity minimal # Don't bother running formatting for this build - name: Test with the dotnet CLI # We will have already ran the tests on ubuntu, so only do .NET Framework ones here - run: dotnet test --no-build --no-restore --framework net462 --logger "trx;LogFileName=pw-framework-test-results.trx" - - - name: Report test results - uses: dorny/test-reporter@c9b3d0e2bd2a4e96aaf424dbaa31c46b42318226 # v1.6.0 - if: always() - with: - name: Test Results - path: "**/*-test-results.trx" - reporter: dotnet-trx - fail-on-error: true + run: > + dotnet test + --no-build + --no-restore + --framework net462 + --configuration Release + --logger "GitHubActions;summary.includePassedTests=true;summary.includeSkippedTests=true" + -- + RunConfiguration.CollectSourceInformation=true - name: Pack NuGet Packages - run: dotnet pack --no-build --version-suffix "ci-${{ env.CLEAN_BRANCH_NAME }}-${{ github.run_id }}" + run: > + dotnet pack + --no-build + --configuration Release + --version-suffix "ci-${{ env.CLEAN_BRANCH_NAME }}-${{ github.run_id }}" env: CLEAN_BRANCH_NAME: ${{needs.build-dotnet.outputs.clean_name}} - name: Publish NuGet Packages - run: dotnet nuget push **/*.nupkg --source https://nuget.pkg.github.com/passwordless/index.json --api-key ${{env.GITHUB_TOKEN}} + run: > + dotnet nuget push **/*.nupkg + --source https://nuget.pkg.github.com/passwordless/index.json + --api-key ${{env.GITHUB_TOKEN}} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/Directory.Build.props b/Directory.Build.props index 6b9ec43..9edb06f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -3,10 +3,11 @@ net8.0 - false + false latest enable enable + nullable @@ -19,7 +20,7 @@ diff --git a/README.md b/README.md index 29953d0..463033f 100644 --- a/README.md +++ b/README.md @@ -1,124 +1,105 @@ # Passwordless .NET SDK -The official [Bitwarden Passwordless.dev](https://passwordless.dev/) .NET library, supporting .NET Standard 2.0+, .NET Core 2.0+ (and soon .NET Framework 4.6.1+). +The official [Bitwarden Passwordless.dev](https://passwordless.dev) .NET library, supporting .NET Standard 2.0+, .NET Core 2.0+, and .NET Framework 4.6.2+. ## Installation -[Nuget Package][nuget-package] +Install the [NuGet Package](https://nuget.org/packages/Passwordless): -Using the [.NET Core command-line interface (CLI) tools][dotnet-core-cli-tools]: +- Using the [.NET CLI](https://docs.microsoft.com/en-us/dotnet/core/tools): -```sh -dotnet add package Passwordless -``` - -Using the [NuGet Command Line Interface (CLI)][nuget-cli]: + ```sh + dotnet add package Passwordless + ``` -```sh -nuget install Passwordless -``` +- Using the [NuGet CLI](https://docs.microsoft.com/en-us/nuget/tools/nuget-exe-cli-reference): -Using the [Package Manager Console][package-manager-console]: + ```sh + nuget install Passwordless + ``` -```powershell -Install-Package Passwordless -``` +- Using the [Package Manager Console](https://docs.microsoft.com/en-us/nuget/tools/package-manager-console): -From within Visual Studio: + ```powershell + Install-Package Passwordless + ``` -1. Open the Solution Explorer. -2. Right-click on a project within your solution. -3. Click on *Manage NuGet Packages...* -4. Click on the *Browse* tab and search for "Passwordless". -5. Click on the Passwordless package, select the appropriate version in the - right-tab and click *Install*. +- From within Visual Studio: + 1. Open the Solution Explorer. + 2. Right-click on a project within your solution. + 3. Click on *Manage NuGet Packages...* + 4. Click on the *Browse* tab and search for "Passwordless". + 5. Click on the Passwordless package, select the appropriate version in the + right-tab and click *Install*. ## Getting started -Follow the [Get started guide](https://docs.passwordless.dev/guide/get-started.html) +💡 See the full [Getting started guide](https://docs.passwordless.dev/guide/get-started.html) in the official documentation. -#### Register to Dependency Injection +#### Register using Dependency Injection ```csharp - -// in Program.cs or Startup.cs +// In Program.cs or Startup.cs services.AddPasswordlessSdk(options => { - options.ApiSecret = "your_api_secret"; + options.ApiKey = "your_api_key"; + options.ApiSecret = "your_api_secret"; }); ``` ### Register a passkey ```csharp - [HttpGet("/create-token")] public async Task GetRegisterToken(string alias) { - - // Get existing userid from session or create a new user in your database - var userId = Guid.NewGuid().ToString(); - - // Options to give the Api - var payload = new RegisterOptions - { - UserId = userId, // your user id - Username = alias, // e.g. user email, is shown in browser ui - Aliases = new HashSet { alias } // Optional: Link this userid to an alias (e.g. email) - }; - - try - { - var token = await _passwordlessClient.CreateRegisterTokenAsync(payload); - - // return this token to the frontend - return Ok(token); - } - catch (PasswordlessApiException e) - { - return new JsonResult(e.Details) - { - StatusCode = (int)e.StatusCode, - }; - } + // Get existing userid from session or create a new user in your database + var userId = Guid.NewGuid().ToString(); + + // Provide the userid and an alias to link to this user + var payload = new RegisterOptions(userId, alias) + { + // Optional: Link this userid to an alias (e.g. email) + Aliases = new HashSet { alias } + }; + + try + { + var tokenRegistration = await _passwordlessClient.CreateRegisterTokenAsync(payload); + + // Return this token to the frontend + return Ok(tokenRegistration); + } + catch (PasswordlessApiException e) + { + return new JsonResult(e.Details) + { + StatusCode = (int?)e.StatusCode, + }; + } } - ``` ### Verify user ```csharp -[HttpGet] -[Route("/verify-signin")] +[HttpGet("/verify-signin")] public async Task VerifySignInToken(string token) { - try - { - var verifiedUser = await _passwordlessClient.VerifyTokenAsync(token); - // Sign the user in, set a cookie, etc, - return Ok(verifiedUser); - } - catch (PasswordlessApiException e) - { - return new JsonResult(e.Details) - { - StatusCode = (int)e.StatusCode - }; - } + try + { + var verifiedUser = await _passwordlessClient.VerifyTokenAsync(token); + + // Sign the user in, set a cookie, etc + return Ok(verifiedUser); + } + catch (PasswordlessApiException e) + { + return new JsonResult(e.Details) + { + StatusCode = (int?)e.StatusCode + }; + } } ``` - - - -## Documentation - -For a comprehensive list of examples, check out the [API -documentation][api-docs]. - - -[nuget-package]:https://www.nuget.org/packages/Passwordless/ -[api-docs]:https://docs.passwordless.dev/guide/get-started.html -[dotnet-core-cli-tools]: https://docs.microsoft.com/en-us/dotnet/core/tools/ -[nuget-cli]: https://docs.microsoft.com/en-us/nuget/tools/nuget-exe-cli-reference -[package-manager-console]: https://docs.microsoft.com/en-us/nuget/tools/package-manager-console diff --git a/src/Passwordless.Net/Models/AddAliasRequest.cs b/src/Passwordless.Net/Models/AddAliasRequest.cs index 62319c2..6eacc90 100644 --- a/src/Passwordless.Net/Models/AddAliasRequest.cs +++ b/src/Passwordless.Net/Models/AddAliasRequest.cs @@ -24,6 +24,7 @@ private AddAliasRequest(string userId, bool hashing = true) { UserId = userId ?? throw new ArgumentNullException(nameof(userId)); Hashing = hashing; + Aliases = new HashSet(); } public string UserId { get; } diff --git a/tests/Passwordless.Net.Tests/Passwordless.Net.Tests.csproj b/tests/Passwordless.Net.Tests/Passwordless.Net.Tests.csproj index 831bdb7..ab8df35 100644 --- a/tests/Passwordless.Net.Tests/Passwordless.Net.Tests.csproj +++ b/tests/Passwordless.Net.Tests/Passwordless.Net.Tests.csproj @@ -9,6 +9,7 @@ + From 9b3ee02e2001381334df5bb93b23c1ed85cf17b9 Mon Sep 17 00:00:00 2001 From: Oleksii Holub <1935960+Tyrrrz@users.noreply.github.com> Date: Wed, 20 Sep 2023 18:59:36 +0300 Subject: [PATCH 4/4] Full rename --- Passwordless-dotnet.sln | 6 +++-- .../Base64Url.cs | 2 +- .../Helpers/Base64UrlConverter.cs | 2 +- .../Helpers/PasswordlessSerializerContext.cs | 4 +-- .../IPasswordlessClient.cs | 4 +-- .../Models/AddAliasRequest.cs | 2 +- .../Models/AliasPointer.cs | 2 +- .../Models/Credential.cs | 2 +- .../Models/CredentialDescriptor.cs | 2 +- .../Models/DeleteCredentialRequest.cs | 2 +- .../Models/DeleteUserRequest.cs | 2 +- .../Models/ListResponse.cs | 2 +- .../Models/PasswordlessUserSummary.cs | 2 +- .../Models/RegisterOptions.cs | 2 +- .../Models/RegisterTokenResponse.cs | 2 +- .../Models/UsersCount.cs | 2 +- .../Models/VerifiedUser.cs | 2 +- .../Models/VerifyTokenRequest.cs | 2 +- .../Passwordless.csproj} | 1 - .../PasswordlessApiException.cs | 2 +- .../PasswordlessClient.cs | 27 ++++++++++--------- .../PasswordlessDelegatingHandler.cs | 4 +-- .../PasswordlessExtensions.cs | 2 +- .../PasswordlessHttpRequestExtensions.cs | 2 +- .../PasswordlessOptions.cs | 2 +- .../Pollyfill.cs | 0 .../ServiceCollectionExtensions.cs | 2 +- .../ApiFactAttribute.cs | 2 +- .../Passwordless.Tests.csproj} | 2 +- .../PasswordlessClientTests.cs | 2 +- .../Usings.cs | 0 31 files changed, 47 insertions(+), 45 deletions(-) rename src/{Passwordless.Net => Passwordless}/Base64Url.cs (99%) rename src/{Passwordless.Net => Passwordless}/Helpers/Base64UrlConverter.cs (95%) rename src/{Passwordless.Net => Passwordless}/Helpers/PasswordlessSerializerContext.cs (93%) rename src/{Passwordless.Net => Passwordless}/IPasswordlessClient.cs (98%) rename src/{Passwordless.Net => Passwordless}/Models/AddAliasRequest.cs (97%) rename src/{Passwordless.Net => Passwordless}/Models/AliasPointer.cs (90%) rename src/{Passwordless.Net => Passwordless}/Models/Credential.cs (97%) rename src/{Passwordless.Net => Passwordless}/Models/CredentialDescriptor.cs (89%) rename src/{Passwordless.Net => Passwordless}/Models/DeleteCredentialRequest.cs (84%) rename src/{Passwordless.Net => Passwordless}/Models/DeleteUserRequest.cs (81%) rename src/{Passwordless.Net => Passwordless}/Models/ListResponse.cs (82%) rename src/{Passwordless.Net => Passwordless}/Models/PasswordlessUserSummary.cs (95%) rename src/{Passwordless.Net => Passwordless}/Models/RegisterOptions.cs (99%) rename src/{Passwordless.Net => Passwordless}/Models/RegisterTokenResponse.cs (84%) rename src/{Passwordless.Net => Passwordless}/Models/UsersCount.cs (81%) rename src/{Passwordless.Net => Passwordless}/Models/VerifiedUser.cs (97%) rename src/{Passwordless.Net => Passwordless}/Models/VerifyTokenRequest.cs (81%) rename src/{Passwordless.Net/Passwordless.Net.csproj => Passwordless/Passwordless.csproj} (98%) rename src/{Passwordless.Net => Passwordless}/PasswordlessApiException.cs (97%) rename src/{Passwordless.Net => Passwordless}/PasswordlessClient.cs (86%) rename src/{Passwordless.Net => Passwordless}/PasswordlessDelegatingHandler.cs (94%) rename src/{Passwordless.Net => Passwordless}/PasswordlessExtensions.cs (83%) rename src/{Passwordless.Net => Passwordless}/PasswordlessHttpRequestExtensions.cs (97%) rename src/{Passwordless.Net => Passwordless}/PasswordlessOptions.cs (97%) rename src/{Passwordless.Net => Passwordless}/Pollyfill.cs (100%) rename src/{Passwordless.Net => Passwordless}/ServiceCollectionExtensions.cs (98%) rename tests/{Passwordless.Net.Tests => Passwordless.Tests}/ApiFactAttribute.cs (89%) rename tests/{Passwordless.Net.Tests/Passwordless.Net.Tests.csproj => Passwordless.Tests/Passwordless.Tests.csproj} (92%) rename tests/{Passwordless.Net.Tests => Passwordless.Tests}/PasswordlessClientTests.cs (99%) rename tests/{Passwordless.Net.Tests => Passwordless.Tests}/Usings.cs (100%) diff --git a/Passwordless-dotnet.sln b/Passwordless-dotnet.sln index bde9b97..1ddbe38 100644 --- a/Passwordless-dotnet.sln +++ b/Passwordless-dotnet.sln @@ -3,15 +3,17 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Passwordless.Net.Tests", "tests\Passwordless.Net.Tests\Passwordless.Net.Tests.csproj", "{F64C850E-9923-43F1-BC84-432AFBBA4425}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Passwordless.Tests", "tests\Passwordless.Tests\Passwordless.Tests.csproj", "{F64C850E-9923-43F1-BC84-432AFBBA4425}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Passwordless.Net", "src\Passwordless.Net\Passwordless.Net.csproj", "{A01503A8-6AB9-43A7-AC5A-4EAE091B07B6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Passwordless", "src\Passwordless\Passwordless.csproj", "{A01503A8-6AB9-43A7-AC5A-4EAE091B07B6}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F239A76C-408E-4919-AB70-4499425E6F29}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig .gitignore = .gitignore Directory.Build.props = Directory.Build.props + README.md = README.md + LICENSE = LICENSE EndProjectSection EndProject Global diff --git a/src/Passwordless.Net/Base64Url.cs b/src/Passwordless/Base64Url.cs similarity index 99% rename from src/Passwordless.Net/Base64Url.cs rename to src/Passwordless/Base64Url.cs index 3b25a8a..32f92ef 100644 --- a/src/Passwordless.Net/Base64Url.cs +++ b/src/Passwordless/Base64Url.cs @@ -1,7 +1,7 @@ using System.Buffers; using System.Buffers.Text; -namespace Passwordless.Net; +namespace Passwordless; internal static class Base64Url { diff --git a/src/Passwordless.Net/Helpers/Base64UrlConverter.cs b/src/Passwordless/Helpers/Base64UrlConverter.cs similarity index 95% rename from src/Passwordless.Net/Helpers/Base64UrlConverter.cs rename to src/Passwordless/Helpers/Base64UrlConverter.cs index ce632c3..630429f 100644 --- a/src/Passwordless.Net/Helpers/Base64UrlConverter.cs +++ b/src/Passwordless/Helpers/Base64UrlConverter.cs @@ -1,7 +1,7 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace Passwordless.Net; +namespace Passwordless; public sealed class Base64UrlConverter : JsonConverter { diff --git a/src/Passwordless.Net/Helpers/PasswordlessSerializerContext.cs b/src/Passwordless/Helpers/PasswordlessSerializerContext.cs similarity index 93% rename from src/Passwordless.Net/Helpers/PasswordlessSerializerContext.cs rename to src/Passwordless/Helpers/PasswordlessSerializerContext.cs index 18ed5d0..61d0526 100644 --- a/src/Passwordless.Net/Helpers/PasswordlessSerializerContext.cs +++ b/src/Passwordless/Helpers/PasswordlessSerializerContext.cs @@ -1,8 +1,8 @@ using System.Text.Json; using System.Text.Json.Serialization; -using Passwordless.Net.Models; +using Passwordless.Models; -namespace Passwordless.Net.Helpers; +namespace Passwordless.Helpers; [JsonSourceGenerationOptions( PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, diff --git a/src/Passwordless.Net/IPasswordlessClient.cs b/src/Passwordless/IPasswordlessClient.cs similarity index 98% rename from src/Passwordless.Net/IPasswordlessClient.cs rename to src/Passwordless/IPasswordlessClient.cs index 756e2dc..de9f49c 100644 --- a/src/Passwordless.Net/IPasswordlessClient.cs +++ b/src/Passwordless/IPasswordlessClient.cs @@ -1,6 +1,6 @@ -using Passwordless.Net.Models; +using Passwordless.Models; -namespace Passwordless.Net; +namespace Passwordless; /// /// Provides APIs that help you interact with Passwordless.dev. diff --git a/src/Passwordless.Net/Models/AddAliasRequest.cs b/src/Passwordless/Models/AddAliasRequest.cs similarity index 97% rename from src/Passwordless.Net/Models/AddAliasRequest.cs rename to src/Passwordless/Models/AddAliasRequest.cs index 6eacc90..0c084ee 100644 --- a/src/Passwordless.Net/Models/AddAliasRequest.cs +++ b/src/Passwordless/Models/AddAliasRequest.cs @@ -1,4 +1,4 @@ -namespace Passwordless.Net.Models; +namespace Passwordless.Models; public class AddAliasRequest { diff --git a/src/Passwordless.Net/Models/AliasPointer.cs b/src/Passwordless/Models/AliasPointer.cs similarity index 90% rename from src/Passwordless.Net/Models/AliasPointer.cs rename to src/Passwordless/Models/AliasPointer.cs index 7477d4b..f17bad8 100644 --- a/src/Passwordless.Net/Models/AliasPointer.cs +++ b/src/Passwordless/Models/AliasPointer.cs @@ -1,4 +1,4 @@ -namespace Passwordless.Net; +namespace Passwordless; public class AliasPointer { diff --git a/src/Passwordless.Net/Models/Credential.cs b/src/Passwordless/Models/Credential.cs similarity index 97% rename from src/Passwordless.Net/Models/Credential.cs rename to src/Passwordless/Models/Credential.cs index dac918c..2e3d806 100644 --- a/src/Passwordless.Net/Models/Credential.cs +++ b/src/Passwordless/Models/Credential.cs @@ -1,4 +1,4 @@ -namespace Passwordless.Net; +namespace Passwordless; public class Credential { diff --git a/src/Passwordless.Net/Models/CredentialDescriptor.cs b/src/Passwordless/Models/CredentialDescriptor.cs similarity index 89% rename from src/Passwordless.Net/Models/CredentialDescriptor.cs rename to src/Passwordless/Models/CredentialDescriptor.cs index 3f412e4..a3584b2 100644 --- a/src/Passwordless.Net/Models/CredentialDescriptor.cs +++ b/src/Passwordless/Models/CredentialDescriptor.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Passwordless.Net; +namespace Passwordless; public class CredentialDescriptor { diff --git a/src/Passwordless.Net/Models/DeleteCredentialRequest.cs b/src/Passwordless/Models/DeleteCredentialRequest.cs similarity index 84% rename from src/Passwordless.Net/Models/DeleteCredentialRequest.cs rename to src/Passwordless/Models/DeleteCredentialRequest.cs index a86fc83..e2e1ea9 100644 --- a/src/Passwordless.Net/Models/DeleteCredentialRequest.cs +++ b/src/Passwordless/Models/DeleteCredentialRequest.cs @@ -1,4 +1,4 @@ -namespace Passwordless.Net.Models; +namespace Passwordless.Models; internal class DeleteCredentialRequest { diff --git a/src/Passwordless.Net/Models/DeleteUserRequest.cs b/src/Passwordless/Models/DeleteUserRequest.cs similarity index 81% rename from src/Passwordless.Net/Models/DeleteUserRequest.cs rename to src/Passwordless/Models/DeleteUserRequest.cs index 2d6fac5..2b98a97 100644 --- a/src/Passwordless.Net/Models/DeleteUserRequest.cs +++ b/src/Passwordless/Models/DeleteUserRequest.cs @@ -1,4 +1,4 @@ -namespace Passwordless.Net.Models; +namespace Passwordless.Models; internal class DeleteUserRequest { diff --git a/src/Passwordless.Net/Models/ListResponse.cs b/src/Passwordless/Models/ListResponse.cs similarity index 82% rename from src/Passwordless.Net/Models/ListResponse.cs rename to src/Passwordless/Models/ListResponse.cs index f95b0ea..248f527 100644 --- a/src/Passwordless.Net/Models/ListResponse.cs +++ b/src/Passwordless/Models/ListResponse.cs @@ -1,4 +1,4 @@ -namespace Passwordless.Net.Models; +namespace Passwordless.Models; internal class ListResponse { diff --git a/src/Passwordless.Net/Models/PasswordlessUserSummary.cs b/src/Passwordless/Models/PasswordlessUserSummary.cs similarity index 95% rename from src/Passwordless.Net/Models/PasswordlessUserSummary.cs rename to src/Passwordless/Models/PasswordlessUserSummary.cs index 609ee86..1542211 100644 --- a/src/Passwordless.Net/Models/PasswordlessUserSummary.cs +++ b/src/Passwordless/Models/PasswordlessUserSummary.cs @@ -1,4 +1,4 @@ -namespace Passwordless.Net; +namespace Passwordless; public class PasswordlessUserSummary { diff --git a/src/Passwordless.Net/Models/RegisterOptions.cs b/src/Passwordless/Models/RegisterOptions.cs similarity index 99% rename from src/Passwordless.Net/Models/RegisterOptions.cs rename to src/Passwordless/Models/RegisterOptions.cs index 57a5972..f7aecb1 100644 --- a/src/Passwordless.Net/Models/RegisterOptions.cs +++ b/src/Passwordless/Models/RegisterOptions.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Passwordless.Net; +namespace Passwordless; /// /// diff --git a/src/Passwordless.Net/Models/RegisterTokenResponse.cs b/src/Passwordless/Models/RegisterTokenResponse.cs similarity index 84% rename from src/Passwordless.Net/Models/RegisterTokenResponse.cs rename to src/Passwordless/Models/RegisterTokenResponse.cs index 74b220c..41c2e9a 100644 --- a/src/Passwordless.Net/Models/RegisterTokenResponse.cs +++ b/src/Passwordless/Models/RegisterTokenResponse.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace Passwordless.Net.Models; +namespace Passwordless.Models; public class RegisterTokenResponse { diff --git a/src/Passwordless.Net/Models/UsersCount.cs b/src/Passwordless/Models/UsersCount.cs similarity index 81% rename from src/Passwordless.Net/Models/UsersCount.cs rename to src/Passwordless/Models/UsersCount.cs index fd8aa21..881e648 100644 --- a/src/Passwordless.Net/Models/UsersCount.cs +++ b/src/Passwordless/Models/UsersCount.cs @@ -1,4 +1,4 @@ -namespace Passwordless.Net; +namespace Passwordless; public class UsersCount { diff --git a/src/Passwordless.Net/Models/VerifiedUser.cs b/src/Passwordless/Models/VerifiedUser.cs similarity index 97% rename from src/Passwordless.Net/Models/VerifiedUser.cs rename to src/Passwordless/Models/VerifiedUser.cs index b0184e9..7a784d1 100644 --- a/src/Passwordless.Net/Models/VerifiedUser.cs +++ b/src/Passwordless/Models/VerifiedUser.cs @@ -1,4 +1,4 @@ -namespace Passwordless.Net; +namespace Passwordless; public class VerifiedUser { diff --git a/src/Passwordless.Net/Models/VerifyTokenRequest.cs b/src/Passwordless/Models/VerifyTokenRequest.cs similarity index 81% rename from src/Passwordless.Net/Models/VerifyTokenRequest.cs rename to src/Passwordless/Models/VerifyTokenRequest.cs index 3b6eb8d..323b36b 100644 --- a/src/Passwordless.Net/Models/VerifyTokenRequest.cs +++ b/src/Passwordless/Models/VerifyTokenRequest.cs @@ -1,4 +1,4 @@ -namespace Passwordless.Net.Models; +namespace Passwordless.Models; internal class VerifyTokenRequest { diff --git a/src/Passwordless.Net/Passwordless.Net.csproj b/src/Passwordless/Passwordless.csproj similarity index 98% rename from src/Passwordless.Net/Passwordless.Net.csproj rename to src/Passwordless/Passwordless.csproj index 4ce715f..530ab82 100644 --- a/src/Passwordless.Net/Passwordless.Net.csproj +++ b/src/Passwordless/Passwordless.csproj @@ -8,7 +8,6 @@ - Passwordless Bitwarden https://github.com/passwordless/passwordless-dotnet git diff --git a/src/Passwordless.Net/PasswordlessApiException.cs b/src/Passwordless/PasswordlessApiException.cs similarity index 97% rename from src/Passwordless.Net/PasswordlessApiException.cs rename to src/Passwordless/PasswordlessApiException.cs index bc01f59..2fb6858 100644 --- a/src/Passwordless.Net/PasswordlessApiException.cs +++ b/src/Passwordless/PasswordlessApiException.cs @@ -3,7 +3,7 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace Passwordless.Net; +namespace Passwordless; public sealed class PasswordlessApiException : HttpRequestException { diff --git a/src/Passwordless.Net/PasswordlessClient.cs b/src/Passwordless/PasswordlessClient.cs similarity index 86% rename from src/Passwordless.Net/PasswordlessClient.cs rename to src/Passwordless/PasswordlessClient.cs index 89a8a6d..d5c9b01 100644 --- a/src/Passwordless.Net/PasswordlessClient.cs +++ b/src/Passwordless/PasswordlessClient.cs @@ -1,10 +1,11 @@ using System.Diagnostics; using System.Net.Http.Json; using System.Text; -using Passwordless.Net.Models; -using JsonContext = Passwordless.Net.Helpers.PasswordlessSerializerContext; +using Passwordless.Helpers; +using Passwordless.Models; +using JsonContext = Passwordless.Helpers.PasswordlessSerializerContext; -namespace Passwordless.Net; +namespace Passwordless; /// /// TODO: FILL IN @@ -44,7 +45,7 @@ public async Task AddAliasAsync(AddAliasRequest request, CancellationToken cance { var res = await _client.PostAsJsonAsync("alias", request, - JsonContext.Default.AddAliasRequest, + PasswordlessSerializerContext.Default.AddAliasRequest, cancellationToken); res.EnsureSuccessStatusCode(); } @@ -54,11 +55,11 @@ public async Task CreateRegisterTokenAsync(RegisterOption { var res = await _client.PostAsJsonAsync("register/token", registerOptions, - JsonContext.Default.RegisterOptions, + PasswordlessSerializerContext.Default.RegisterOptions, cancellationToken); res.EnsureSuccessStatusCode(); return (await res.Content.ReadFromJsonAsync( - JsonContext.Default.RegisterTokenResponse, + PasswordlessSerializerContext.Default.RegisterTokenResponse, cancellationToken))!; } @@ -78,7 +79,7 @@ public async Task CreateRegisterTokenAsync(RegisterOption if (response.IsSuccessStatusCode) { var res = await response.Content.ReadFromJsonAsync( - JsonContext.Default.VerifiedUser, + PasswordlessSerializerContext.Default.VerifiedUser, cancellationToken); return res; } @@ -91,7 +92,7 @@ public async Task DeleteUserAsync(string userId, CancellationToken cancellationT { await _client.PostAsJsonAsync("users/delete", new DeleteUserRequest(userId), - JsonContext.Default.DeleteUserRequest, + PasswordlessSerializerContext.Default.DeleteUserRequest, cancellationToken); } @@ -100,7 +101,7 @@ public async Task> ListUsersAsync(Cancell { var response = await _client.GetFromJsonAsync( "users/list", - JsonContext.Default.ListResponsePasswordlessUserSummary, + PasswordlessSerializerContext.Default.ListResponsePasswordlessUserSummary, cancellationToken); return response!.Values; } @@ -110,7 +111,7 @@ public async Task> ListAliasesAsync(string userId, C { var response = await _client.GetFromJsonAsync( $"alias/list?userid={userId}", - JsonContext.Default.ListResponseAliasPointer, + PasswordlessSerializerContext.Default.ListResponseAliasPointer, cancellationToken); return response!.Values; } @@ -120,7 +121,7 @@ public async Task> ListCredentialsAsync(string userId, { var response = await _client.GetFromJsonAsync( $"credentials/list?userid={userId}", - JsonContext.Default.ListResponseCredential, + PasswordlessSerializerContext.Default.ListResponseCredential, cancellationToken); return response!.Values; } @@ -130,7 +131,7 @@ public async Task DeleteCredentialAsync(string id, CancellationToken cancellatio { await _client.PostAsJsonAsync("credentials/delete", new DeleteCredentialRequest(id), - JsonContext.Default.DeleteCredentialRequest, + PasswordlessSerializerContext.Default.DeleteCredentialRequest, cancellationToken); } @@ -144,7 +145,7 @@ public async Task GetUsersCountAsync(CancellationToken cancellationT { return (await _client.GetFromJsonAsync( "users/count", - JsonContext.Default.UsersCount, + PasswordlessSerializerContext.Default.UsersCount, cancellationToken))!; } diff --git a/src/Passwordless.Net/PasswordlessDelegatingHandler.cs b/src/Passwordless/PasswordlessDelegatingHandler.cs similarity index 94% rename from src/Passwordless.Net/PasswordlessDelegatingHandler.cs rename to src/Passwordless/PasswordlessDelegatingHandler.cs index a29ff95..f467ed1 100644 --- a/src/Passwordless.Net/PasswordlessDelegatingHandler.cs +++ b/src/Passwordless/PasswordlessDelegatingHandler.cs @@ -1,8 +1,8 @@ using System.Net.Http; using System.Net.Http.Json; -using Passwordless.Net.Helpers; +using Passwordless.Helpers; -namespace Passwordless.Net; +namespace Passwordless; internal class PasswordlessDelegatingHandler : DelegatingHandler { diff --git a/src/Passwordless.Net/PasswordlessExtensions.cs b/src/Passwordless/PasswordlessExtensions.cs similarity index 83% rename from src/Passwordless.Net/PasswordlessExtensions.cs rename to src/Passwordless/PasswordlessExtensions.cs index 283dc22..cfa479f 100644 --- a/src/Passwordless.Net/PasswordlessExtensions.cs +++ b/src/Passwordless/PasswordlessExtensions.cs @@ -1,4 +1,4 @@ -namespace Passwordless.Net; +namespace Passwordless; public static class PasswordlessExtensions { diff --git a/src/Passwordless.Net/PasswordlessHttpRequestExtensions.cs b/src/Passwordless/PasswordlessHttpRequestExtensions.cs similarity index 97% rename from src/Passwordless.Net/PasswordlessHttpRequestExtensions.cs rename to src/Passwordless/PasswordlessHttpRequestExtensions.cs index bb1dd49..0347c6a 100644 --- a/src/Passwordless.Net/PasswordlessHttpRequestExtensions.cs +++ b/src/Passwordless/PasswordlessHttpRequestExtensions.cs @@ -1,6 +1,6 @@ using System.Net.Http; -namespace Passwordless.Net; +namespace Passwordless; internal static class PasswordlessHttpRequestExtensions { diff --git a/src/Passwordless.Net/PasswordlessOptions.cs b/src/Passwordless/PasswordlessOptions.cs similarity index 97% rename from src/Passwordless.Net/PasswordlessOptions.cs rename to src/Passwordless/PasswordlessOptions.cs index 38f10e6..1748ae5 100644 --- a/src/Passwordless.Net/PasswordlessOptions.cs +++ b/src/Passwordless/PasswordlessOptions.cs @@ -1,4 +1,4 @@ -namespace Passwordless.Net; +namespace Passwordless; /// /// Represents all the options you can use to configure a backend Passwordless system. diff --git a/src/Passwordless.Net/Pollyfill.cs b/src/Passwordless/Pollyfill.cs similarity index 100% rename from src/Passwordless.Net/Pollyfill.cs rename to src/Passwordless/Pollyfill.cs diff --git a/src/Passwordless.Net/ServiceCollectionExtensions.cs b/src/Passwordless/ServiceCollectionExtensions.cs similarity index 98% rename from src/Passwordless.Net/ServiceCollectionExtensions.cs rename to src/Passwordless/ServiceCollectionExtensions.cs index 4dbc46e..8febcb3 100644 --- a/src/Passwordless.Net/ServiceCollectionExtensions.cs +++ b/src/Passwordless/ServiceCollectionExtensions.cs @@ -1,7 +1,7 @@ using System.Diagnostics.CodeAnalysis; using System.Net.Http; using Microsoft.Extensions.Options; -using Passwordless.Net; +using Passwordless; // This is a trick to always show up in a class when people are registering services namespace Microsoft.Extensions.DependencyInjection; diff --git a/tests/Passwordless.Net.Tests/ApiFactAttribute.cs b/tests/Passwordless.Tests/ApiFactAttribute.cs similarity index 89% rename from tests/Passwordless.Net.Tests/ApiFactAttribute.cs rename to tests/Passwordless.Tests/ApiFactAttribute.cs index 68ba415..116486b 100644 --- a/tests/Passwordless.Net.Tests/ApiFactAttribute.cs +++ b/tests/Passwordless.Tests/ApiFactAttribute.cs @@ -1,7 +1,7 @@ // Uncomment line while running API in mock mode to run tests // #define RUNNING_API -namespace Passwordless.Net.Tests; +namespace Passwordless.Tests; public class ApiFactAttribute : FactAttribute { diff --git a/tests/Passwordless.Net.Tests/Passwordless.Net.Tests.csproj b/tests/Passwordless.Tests/Passwordless.Tests.csproj similarity index 92% rename from tests/Passwordless.Net.Tests/Passwordless.Net.Tests.csproj rename to tests/Passwordless.Tests/Passwordless.Tests.csproj index ab8df35..5d042a6 100644 --- a/tests/Passwordless.Net.Tests/Passwordless.Net.Tests.csproj +++ b/tests/Passwordless.Tests/Passwordless.Tests.csproj @@ -18,7 +18,7 @@ - + diff --git a/tests/Passwordless.Net.Tests/PasswordlessClientTests.cs b/tests/Passwordless.Tests/PasswordlessClientTests.cs similarity index 99% rename from tests/Passwordless.Net.Tests/PasswordlessClientTests.cs rename to tests/Passwordless.Tests/PasswordlessClientTests.cs index 4fa95e9..032f20f 100644 --- a/tests/Passwordless.Net.Tests/PasswordlessClientTests.cs +++ b/tests/Passwordless.Tests/PasswordlessClientTests.cs @@ -3,7 +3,7 @@ using Microsoft.Extensions.DependencyInjection; -namespace Passwordless.Net.Tests; +namespace Passwordless.Tests; public class PasswordlessClientTests { diff --git a/tests/Passwordless.Net.Tests/Usings.cs b/tests/Passwordless.Tests/Usings.cs similarity index 100% rename from tests/Passwordless.Net.Tests/Usings.cs rename to tests/Passwordless.Tests/Usings.cs