Skip to content

Commit

Permalink
Make VerifyTokenAsync(...) never return null (#72)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tyrrrz authored Nov 10, 2023
1 parent 57767cd commit 185a2e3
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 71 deletions.
7 changes: 4 additions & 3 deletions src/Passwordless/IPasswordlessClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,15 @@ public interface IPasswordlessClient
Task<IReadOnlyList<PasswordlessUserSummary>> ListUsersAsync(CancellationToken cancellationToken = default);

/// <summary>
/// Verifies that the given token is valid and returns information packed into it. The token should have been generated
/// via calling a <c>signInWith*</c> 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 <c>signInWith*</c> method from your frontend code.
/// If the token is not valid, an exception of type <see cref="PasswordlessApiException" /> will be thrown.
/// </summary>
/// <param name="verifyToken">The token to verify.</param>
/// <param name="cancellationToken"></param>
/// <returns>A task object representing the asynchronous operation containing the <see cref="VerifiedUser" />.</returns>
/// <exception cref="PasswordlessApiException">An exception containing details about the reason for failure.</exception>
Task<VerifiedUser?> VerifyTokenAsync(string verifyToken, CancellationToken cancellationToken = default);
Task<VerifiedUser> VerifyTokenAsync(string verifyToken, CancellationToken cancellationToken = default);

/// <summary>
/// Deletes a user.
Expand Down
60 changes: 28 additions & 32 deletions src/Passwordless/PasswordlessClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
Expand All @@ -73,7 +74,8 @@ public async Task<RegisterTokenResponse> CreateRegisterTokenAsync(RegisterOption
using var response = await _http.PostAsJsonAsync("register/token",
registerOptions,
PasswordlessSerializerContext.Default.RegisterOptions,
cancellationToken);
cancellationToken
);

response.EnsureSuccessStatusCode();

Expand All @@ -83,27 +85,20 @@ public async Task<RegisterTokenResponse> CreateRegisterTokenAsync(RegisterOption
}

/// <inheritdoc />
public async Task<VerifiedUser?> VerifyTokenAsync(string verifyToken, CancellationToken cancellationToken = default)
public async Task<VerifiedUser> 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
))!;
}

/// <inheritdoc />
Expand All @@ -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
);
}

/// <inheritdoc />
Expand All @@ -121,7 +117,8 @@ public async Task<IReadOnlyList<PasswordlessUserSummary>> ListUsersAsync(Cancell
var response = await _http.GetFromJsonAsync(
"users/list",
PasswordlessSerializerContext.Default.ListResponsePasswordlessUserSummary,
cancellationToken);
cancellationToken
);

return response!.Values;
}
Expand All @@ -132,7 +129,8 @@ public async Task<IReadOnlyList<AliasPointer>> ListAliasesAsync(string userId, C
var response = await _http.GetFromJsonAsync(
$"alias/list?userid={userId}",
PasswordlessSerializerContext.Default.ListResponseAliasPointer,
cancellationToken);
cancellationToken
);

return response!.Values;
}
Expand All @@ -143,7 +141,8 @@ public async Task<IReadOnlyList<Credential>> ListCredentialsAsync(string userId,
var response = await _http.GetFromJsonAsync(
$"credentials/list?userid={userId}",
PasswordlessSerializerContext.Default.ListResponseCredential,
cancellationToken);
cancellationToken
);

return response!.Values;
}
Expand All @@ -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
);
}

/// <inheritdoc />
public async Task DeleteCredentialAsync(byte[] id, CancellationToken cancellationToken = default)
{
public async Task DeleteCredentialAsync(byte[] id, CancellationToken cancellationToken = default) =>
await DeleteCredentialAsync(Base64Url.Encode(id), cancellationToken);
}

/// <inheritdoc />
public async Task<UsersCount> GetUsersCountAsync(CancellationToken cancellationToken = default)
{
return (await _http.GetFromJsonAsync(
public async Task<UsersCount> GetUsersCountAsync(CancellationToken cancellationToken = default) =>
(await _http.GetFromJsonAsync(
"users/count",
PasswordlessSerializerContext.Default.UsersCount,
cancellationToken))!;
}

private string DebuggerToString()
{
Expand Down
10 changes: 5 additions & 5 deletions src/Passwordless/PasswordlessHttpHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ protected override async Task<HttpResponseMessage> 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,
Expand Down
30 changes: 0 additions & 30 deletions src/Passwordless/PasswordlessHttpRequestExtensions.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ public async Task LoginUserAsync_PasswordlessClientReturnsNull_ReturnsUnauthoriz
{
_mockPasswordlessClient
.Setup(s => s.VerifyTokenAsync("test_token", default))
.Returns(Task.FromResult<VerifiedUser?>(null));
.Returns(Task.FromResult<VerifiedUser>(null!));

var sut = CreateSut();

Expand Down
3 changes: 3 additions & 0 deletions tests/Passwordless.Tests/Fixtures/TestApiFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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")
Expand Down

0 comments on commit 185a2e3

Please sign in to comment.