diff --git a/src/Passwordless/IPasswordlessClient.cs b/src/Passwordless/IPasswordlessClient.cs index 57d4b9a..73b7393 100644 --- a/src/Passwordless/IPasswordlessClient.cs +++ b/src/Passwordless/IPasswordlessClient.cs @@ -75,14 +75,15 @@ public interface IPasswordlessClient 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. + /// 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. + /// If the token is not valid, an exception of type will be thrown. /// /// The token to verify. /// /// A task object representing the asynchronous operation containing the . /// An exception containing details about the reason for failure. - Task VerifyTokenAsync(string verifyToken, CancellationToken cancellationToken = default); + Task VerifyTokenAsync(string verifyToken, CancellationToken cancellationToken = default); /// /// Deletes a user. diff --git a/src/Passwordless/PasswordlessClient.cs b/src/Passwordless/PasswordlessClient.cs index 21f260f..c008e12 100644 --- a/src/Passwordless/PasswordlessClient.cs +++ b/src/Passwordless/PasswordlessClient.cs @@ -62,7 +62,8 @@ public async Task SetAliasAsync(SetAliasRequest request, CancellationToken cance using var response = await _http.PostAsJsonAsync("alias", request, PasswordlessSerializerContext.Default.SetAliasRequest, - cancellationToken); + cancellationToken + ); response.EnsureSuccessStatusCode(); } @@ -73,7 +74,8 @@ public async Task CreateRegisterTokenAsync(RegisterOption using var response = await _http.PostAsJsonAsync("register/token", registerOptions, PasswordlessSerializerContext.Default.RegisterOptions, - cancellationToken); + cancellationToken + ); response.EnsureSuccessStatusCode(); @@ -83,27 +85,20 @@ public async Task CreateRegisterTokenAsync(RegisterOption } /// - public async Task VerifyTokenAsync(string verifyToken, CancellationToken cancellationToken = default) + public async Task VerifyTokenAsync(string verifyToken, CancellationToken cancellationToken = default) { - using var request = new HttpRequestMessage(HttpMethod.Post, "signin/verify"); + using var response = await _http.PostAsJsonAsync("signin/verify", + new VerifyTokenRequest(verifyToken), + PasswordlessSerializerContext.Default.VerifyTokenRequest, + cancellationToken + ); - // TODO: No JsonTypeInfo overload yet? - request.Content = JsonContent.Create(new VerifyTokenRequest(verifyToken)); - - // We just want to return null if there is a problem. - request.SkipErrorHandling(); - using var response = await _http.SendAsync(request, cancellationToken); - - if (response.IsSuccessStatusCode) - { - var res = await response.Content.ReadFromJsonAsync( - PasswordlessSerializerContext.Default.VerifiedUser, - cancellationToken); - - return res; - } + response.EnsureSuccessStatusCode(); - return null; + return (await response.Content.ReadFromJsonAsync( + PasswordlessSerializerContext.Default.VerifiedUser, + cancellationToken + ))!; } /// @@ -112,7 +107,8 @@ public async Task DeleteUserAsync(string userId, CancellationToken cancellationT using var response = await _http.PostAsJsonAsync("users/delete", new DeleteUserRequest(userId), PasswordlessSerializerContext.Default.DeleteUserRequest, - cancellationToken); + cancellationToken + ); } /// @@ -121,7 +117,8 @@ public async Task> ListUsersAsync(Cancell var response = await _http.GetFromJsonAsync( "users/list", PasswordlessSerializerContext.Default.ListResponsePasswordlessUserSummary, - cancellationToken); + cancellationToken + ); return response!.Values; } @@ -132,7 +129,8 @@ public async Task> ListAliasesAsync(string userId, C var response = await _http.GetFromJsonAsync( $"alias/list?userid={userId}", PasswordlessSerializerContext.Default.ListResponseAliasPointer, - cancellationToken); + cancellationToken + ); return response!.Values; } @@ -143,7 +141,8 @@ public async Task> ListCredentialsAsync(string userId, var response = await _http.GetFromJsonAsync( $"credentials/list?userid={userId}", PasswordlessSerializerContext.Default.ListResponseCredential, - cancellationToken); + cancellationToken + ); return response!.Values; } @@ -154,23 +153,20 @@ public async Task DeleteCredentialAsync(string id, CancellationToken cancellatio using var response = await _http.PostAsJsonAsync("credentials/delete", new DeleteCredentialRequest(id), PasswordlessSerializerContext.Default.DeleteCredentialRequest, - cancellationToken); + cancellationToken + ); } /// - public async Task DeleteCredentialAsync(byte[] id, CancellationToken cancellationToken = default) - { + public async Task DeleteCredentialAsync(byte[] id, CancellationToken cancellationToken = default) => await DeleteCredentialAsync(Base64Url.Encode(id), cancellationToken); - } /// - public async Task GetUsersCountAsync(CancellationToken cancellationToken = default) - { - return (await _http.GetFromJsonAsync( + public async Task GetUsersCountAsync(CancellationToken cancellationToken = default) => + (await _http.GetFromJsonAsync( "users/count", PasswordlessSerializerContext.Default.UsersCount, cancellationToken))!; - } private string DebuggerToString() { diff --git a/src/Passwordless/PasswordlessHttpHandler.cs b/src/Passwordless/PasswordlessHttpHandler.cs index 52ad65b..d716038 100644 --- a/src/Passwordless/PasswordlessHttpHandler.cs +++ b/src/Passwordless/PasswordlessHttpHandler.cs @@ -30,12 +30,12 @@ protected override async Task SendAsync( cancellationToken ); - // On failed requests, check if responded with ProblemDetails and provide a nicer error if so - if (!request.ShouldSkipErrorHandling() && - !response.IsSuccessStatusCode && - string.Equals(response.Content.Headers.ContentType?.MediaType, + // Provide nice errors for problem details responses + if (string.Equals( + response.Content.Headers.ContentType?.MediaType, "application/problem+json", - StringComparison.OrdinalIgnoreCase)) + StringComparison.OrdinalIgnoreCase) + ) { var problemDetails = await response.Content.ReadFromJsonAsync( PasswordlessSerializerContext.Default.PasswordlessProblemDetails, diff --git a/src/Passwordless/PasswordlessHttpRequestExtensions.cs b/src/Passwordless/PasswordlessHttpRequestExtensions.cs deleted file mode 100644 index c4c028b..0000000 --- a/src/Passwordless/PasswordlessHttpRequestExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Passwordless; - -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 true; -#endif - } -} \ No newline at end of file diff --git a/tests/Passwordless.AspNetCore.Tests/Services/PasswordlessServiceTests.cs b/tests/Passwordless.AspNetCore.Tests/Services/PasswordlessServiceTests.cs index 8af11d5..9ecf034 100644 --- a/tests/Passwordless.AspNetCore.Tests/Services/PasswordlessServiceTests.cs +++ b/tests/Passwordless.AspNetCore.Tests/Services/PasswordlessServiceTests.cs @@ -311,7 +311,7 @@ public async Task LoginUserAsync_PasswordlessClientReturnsNull_ReturnsUnauthoriz { _mockPasswordlessClient .Setup(s => s.VerifyTokenAsync("test_token", default)) - .Returns(Task.FromResult(null)); + .Returns(Task.FromResult(null!)); var sut = CreateSut(); diff --git a/tests/Passwordless.Tests/Fixtures/TestApiFixture.cs b/tests/Passwordless.Tests/Fixtures/TestApiFixture.cs index 37d7c61..e644eba 100644 --- a/tests/Passwordless.Tests/Fixtures/TestApiFixture.cs +++ b/tests/Passwordless.Tests/Fixtures/TestApiFixture.cs @@ -4,6 +4,7 @@ using System.Text.Json; using DotNet.Testcontainers.Builders; using DotNet.Testcontainers.Containers; +using DotNet.Testcontainers.Images; using DotNet.Testcontainers.Networks; using Microsoft.Extensions.DependencyInjection; using Testcontainers.MsSql; @@ -47,6 +48,8 @@ public TestApiFixture() // https://github.com/passwordless/passwordless-server/pkgs/container/passwordless-test-api // TODO: replace with ':stable' after the next release of the server. .WithImage("ghcr.io/passwordless/passwordless-test-api:latest") + // Make sure we always have the latest version of the image + .WithImagePullPolicy(PullPolicy.Always) .WithNetwork(_network) // Run in development environment to execute migrations .WithEnvironment("ASPNETCORE_ENVIRONMENT", "Development")