From 53ee04c16370ea966309b2a29607440c2bf008f3 Mon Sep 17 00:00:00 2001
From: Oleksii Holub <1935960+Tyrrrz@users.noreply.github.com>
Date: Wed, 3 Apr 2024 18:55:18 +0300
Subject: [PATCH] PAS-392 | Add rate limit bypass (#509)
---
src/Api/Endpoints/Magic.cs | 14 ++-
src/Api/Overrides/ApplicationOverrides.cs | 12 +++
.../ApplicationOverridesExtensions.cs | 17 ++++
.../AuthorizationTests.cs | 30 +++++-
.../Endpoints/App/AppTests.cs | 99 +++++++++----------
.../Authenticators/AuthenticatorsTests.cs | 42 ++++----
.../Endpoints/Credentials/CredentialsTests.cs | 26 +++--
.../Endpoints/Events/EventsTests.cs | 89 ++++++++++-------
.../Endpoints/Magic/MagicTests.cs | 73 ++++++++------
.../Register/RegisterAttestationTests.cs | 18 +++-
.../Endpoints/Register/RegisterTests.cs | 18 +++-
.../Endpoints/Register/RegisterTokenTests.cs | 30 +++++-
.../Endpoints/SignIn/SignInTests.cs | 42 ++++++--
.../AuthorizationIntegrationTests.cs | 24 ++++-
.../Middleware/RoutingIntegrationTests.cs | 12 ++-
tests/Api.IntegrationTests/PasswordlessApi.cs | 66 +++++--------
.../PasswordlessApiFixture.cs | 15 ++-
.../PasswordlessApiOptions.cs | 10 ++
18 files changed, 411 insertions(+), 226 deletions(-)
create mode 100644 src/Api/Overrides/ApplicationOverrides.cs
create mode 100644 src/Api/Overrides/ApplicationOverridesExtensions.cs
create mode 100644 tests/Api.IntegrationTests/PasswordlessApiOptions.cs
diff --git a/src/Api/Endpoints/Magic.cs b/src/Api/Endpoints/Magic.cs
index 894e1be9a..f7ce17af8 100644
--- a/src/Api/Endpoints/Magic.cs
+++ b/src/Api/Endpoints/Magic.cs
@@ -6,6 +6,7 @@
using Microsoft.AspNetCore.RateLimiting;
using Passwordless.Api.Authorization;
using Passwordless.Api.OpenApi;
+using Passwordless.Api.Overrides;
using Passwordless.Common.MagicLinks.Models;
using Passwordless.Service.Features;
using Passwordless.Service.Helpers;
@@ -26,12 +27,20 @@ public static class MagicEndpoints
///
/// Adds a rate limiter policy for the MagicEndpoints. Each tenant will have its own partition.
///
- /// The RateLimiterOptions builder.
public static void AddMagicRateLimiterPolicy(this RateLimiterOptions builder) =>
builder.AddPolicy(RateLimiterPolicy, context =>
{
var tenant = context.User.FindFirstValue(CustomClaimTypes.AccountName) ?? "";
+ var isRateLimitBypassed = context.RequestServices
+ .GetRequiredService()
+ .GetSection("ApplicationOverrides")
+ .GetApplicationOverrides(tenant)
+ .IsRateLimitBypassEnabled;
+
+ if (isRateLimitBypassed)
+ return RateLimitPartition.GetNoLimiter(tenant);
+
return RateLimitPartition.GetFixedWindowLimiter(tenant, _ =>
new FixedWindowRateLimiterOptions { Window = TimeSpan.FromMinutes(5), PermitLimit = 10, QueueLimit = 0, AutoReplenishment = true }
);
@@ -40,7 +49,6 @@ public static void AddMagicRateLimiterPolicy(this RateLimiterOptions builder) =>
///
/// Maps the magic link endpoints.
///
- /// The object.
public static void MapMagicEndpoints(this IEndpointRouteBuilder app)
{
var group = app.MapGroup("/magic-links")
@@ -55,8 +63,6 @@ public static void MapMagicEndpoints(this IEndpointRouteBuilder app)
///
/// Sends an e-mail containing a magic link template allowing users to login.
- ///
- /// Warning: Verify the e-mail address matches the user identifier in your backend.
///
[ProducesResponseType((int)HttpStatusCode.NoContent)]
[ProducesResponseType(typeof(ValidationProblemDetails), (int)HttpStatusCode.BadRequest, MediaTypeNames.Application.ProblemJson)]
diff --git a/src/Api/Overrides/ApplicationOverrides.cs b/src/Api/Overrides/ApplicationOverrides.cs
new file mode 100644
index 000000000..bfc229011
--- /dev/null
+++ b/src/Api/Overrides/ApplicationOverrides.cs
@@ -0,0 +1,12 @@
+namespace Passwordless.Api.Overrides;
+
+///
+/// Configures behavior overrides for an application.
+///
+public class ApplicationOverrides
+{
+ ///
+ /// Whether actions on behalf of this application bypass rate limiting.
+ ///
+ public bool IsRateLimitBypassEnabled { get; init; }
+}
\ No newline at end of file
diff --git a/src/Api/Overrides/ApplicationOverridesExtensions.cs b/src/Api/Overrides/ApplicationOverridesExtensions.cs
new file mode 100644
index 000000000..0f1f403e2
--- /dev/null
+++ b/src/Api/Overrides/ApplicationOverridesExtensions.cs
@@ -0,0 +1,17 @@
+namespace Passwordless.Api.Overrides;
+
+///
+/// Extensions for .
+///
+public static class ApplicationOverridesExtensions
+{
+ ///
+ /// Gets overrides for the specified application from the configuration.
+ ///
+ public static ApplicationOverrides GetApplicationOverrides(
+ this IConfiguration configuration,
+ string applicationId) =>
+ configuration
+ .GetSection(applicationId)
+ .Get() ?? new ApplicationOverrides();
+}
\ No newline at end of file
diff --git a/tests/Api.IntegrationTests/AuthorizationTests.cs b/tests/Api.IntegrationTests/AuthorizationTests.cs
index 21e26166f..4c79a97f3 100644
--- a/tests/Api.IntegrationTests/AuthorizationTests.cs
+++ b/tests/Api.IntegrationTests/AuthorizationTests.cs
@@ -30,7 +30,11 @@ public class AuthorizationTests(ITestOutputHelper testOutput, PasswordlessApiFix
public async Task ValidateThatEndpointsHaveProtectionAsync()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient().AddAcceptApplicationJson();
// Act
@@ -64,7 +68,11 @@ public async Task ValidateThatEndpointsHaveProtectionAsync()
public async Task ValidateThatMissingApiSecretThrowsAsync()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient().AddAcceptApplicationJson();
// Act
@@ -88,7 +96,11 @@ public async Task ValidateThatMissingApiSecretThrowsAsync()
public async Task ValidateThatInvalidApiSecretThrowsAsync()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient().AddAcceptApplicationJson();
// Act
@@ -121,7 +133,11 @@ public async Task ValidateThatInvalidApiSecretThrowsAsync()
public async Task ApiSecretGivesHelpfulAdviceAsync(string input, string details)
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient().AddAcceptApplicationJson();
using var request = new HttpRequestMessage(HttpMethod.Get, "/credentials/list?userId=1");
@@ -163,7 +179,11 @@ public async Task ApiSecretGivesHelpfulAdviceAsync(string input, string details)
public async Task ApiPublicGivesHelpfulAdviceAsync(string input, string details)
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient().AddAcceptApplicationJson();
using var request = new HttpRequestMessage(HttpMethod.Post, "/signin/begin");
diff --git a/tests/Api.IntegrationTests/Endpoints/App/AppTests.cs b/tests/Api.IntegrationTests/Endpoints/App/AppTests.cs
index bbb75dec9..942121748 100644
--- a/tests/Api.IntegrationTests/Endpoints/App/AppTests.cs
+++ b/tests/Api.IntegrationTests/Endpoints/App/AppTests.cs
@@ -26,7 +26,7 @@ public class AppTests(ITestOutputHelper testOutput, PasswordlessApiFixture apiFi
public async Task I_cannot_create_an_account_with_an_invalid_name(string name)
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient().AddManagementKey();
// Act
@@ -45,7 +45,7 @@ public async Task I_cannot_create_an_account_with_an_invalid_name(string name)
public async Task I_can_create_an_account_with_a_valid_name()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient().AddManagementKey();
const string accountName = "anders";
@@ -68,7 +68,7 @@ public async Task I_can_create_an_account_with_a_valid_name()
public async Task I_can_create_an_app_and_its_features_will_be_set_correctly()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient().AddManagementKey();
var name = GetApplicationName();
@@ -80,7 +80,8 @@ public async Task I_can_create_an_app_and_its_features_will_be_set_correctly()
using var scope = api.Services.CreateScope();
- var appFeature = await scope.ServiceProvider.GetRequiredService().Create(name).GetAppFeaturesAsync();
+ var appFeature = await scope.ServiceProvider.GetRequiredService().Create(name)
+ .GetAppFeaturesAsync();
appFeature.Should().NotBeNull();
appFeature!.Tenant.Should().Be(name);
@@ -93,7 +94,7 @@ public async Task I_can_create_an_app_and_its_features_will_be_set_correctly()
public async Task I_can_set_event_logging_retention_period()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient().AddManagementKey();
const int expectedEventLoggingRetentionPeriod = 30;
@@ -114,7 +115,8 @@ public async Task I_can_set_event_logging_retention_period()
setFeatureResponse.StatusCode.Should().Be(HttpStatusCode.NoContent);
using var scope = api.Services.CreateScope();
- var appFeature = await scope.ServiceProvider.GetRequiredService().Create(name).GetAppFeaturesAsync();
+ var appFeature = await scope.ServiceProvider.GetRequiredService().Create(name)
+ .GetAppFeaturesAsync();
appFeature.Should().NotBeNull();
appFeature!.EventLoggingRetentionPeriod.Should().Be(expectedEventLoggingRetentionPeriod);
}
@@ -125,7 +127,7 @@ public async Task I_can_set_event_logging_retention_period()
public async Task I_can_not_set_the_event_logging_retention_period_to_an_invalid_value(int invalidRetentionPeriod)
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient().AddManagementKey();
var name = GetApplicationName();
@@ -134,11 +136,8 @@ public async Task I_can_not_set_the_event_logging_retention_period_to_an_invalid
using var appHttpClient = api.CreateClient().AddSecretKey(appCreateDto!.ApiSecret1);
// Act
- using var setFeatureResponse = await appHttpClient.PostAsJsonAsync("/apps/features", new SetFeaturesRequest
- {
- PerformedBy = "a_user",
- EventLoggingRetentionPeriod = invalidRetentionPeriod
- });
+ using var setFeatureResponse = await appHttpClient.PostAsJsonAsync("/apps/features",
+ new SetFeaturesRequest { PerformedBy = "a_user", EventLoggingRetentionPeriod = invalidRetentionPeriod });
// Assert
setFeatureResponse.StatusCode.Should().Be(HttpStatusCode.BadRequest);
@@ -151,13 +150,17 @@ public async Task I_can_not_set_the_event_logging_retention_period_to_an_invalid
public async Task I_can_manage_an_apps_features()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient().AddManagementKey();
const int expectedEventLoggingRetentionPeriod = 30;
var name = GetApplicationName();
_ = await client.CreateApplicationAsync(name);
- var manageFeatureRequest = new ManageFeaturesRequest { EventLoggingRetentionPeriod = expectedEventLoggingRetentionPeriod, EventLoggingIsEnabled = true };
+ var manageFeatureRequest = new ManageFeaturesRequest
+ {
+ EventLoggingRetentionPeriod = expectedEventLoggingRetentionPeriod,
+ EventLoggingIsEnabled = true
+ };
// Act
var manageFeatureResponse = await client.PostAsJsonAsync($"/admin/apps/{name}/features", manageFeatureRequest);
@@ -166,7 +169,8 @@ public async Task I_can_manage_an_apps_features()
manageFeatureResponse.StatusCode.Should().Be(HttpStatusCode.NoContent);
using var scope = api.Services.CreateScope();
- var appFeature = await scope.ServiceProvider.GetRequiredService().Create(name).GetAppFeaturesAsync();
+ var appFeature = await scope.ServiceProvider.GetRequiredService().Create(name)
+ .GetAppFeaturesAsync();
appFeature.Should().NotBeNull();
appFeature!.EventLoggingRetentionPeriod.Should().Be(expectedEventLoggingRetentionPeriod);
appFeature.EventLoggingIsEnabled.Should().BeTrue();
@@ -177,13 +181,17 @@ public async Task I_can_manage_an_apps_features()
public async Task I_can_get_an_apps_features()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient().AddManagementKey();
const int expectedEventLoggingRetentionPeriod = 30;
var name = GetApplicationName();
_ = await client.CreateApplicationAsync(name);
- var manageAppFeatureRequest = new ManageFeaturesRequest { EventLoggingRetentionPeriod = expectedEventLoggingRetentionPeriod, EventLoggingIsEnabled = true };
+ var manageAppFeatureRequest = new ManageFeaturesRequest
+ {
+ EventLoggingRetentionPeriod = expectedEventLoggingRetentionPeriod,
+ EventLoggingIsEnabled = true
+ };
_ = await client.PostAsJsonAsync($"/admin/apps/{name}/features", manageAppFeatureRequest);
// Act
@@ -203,7 +211,7 @@ public async Task I_can_get_an_apps_features()
public async Task I_can_get_all_api_keys_for_my_application()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
var applicationName = GetApplicationName();
using var client = api.CreateClient().AddManagementKey();
@@ -224,7 +232,7 @@ public async Task I_can_get_all_api_keys_for_my_application()
public async Task I_can_create_a_new_public_key()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient().AddManagementKey();
var applicationName = GetApplicationName();
@@ -250,7 +258,7 @@ public async Task I_can_create_a_new_public_key()
public async Task I_can_create_a_new_secret_key()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient().AddManagementKey();
var applicationName = GetApplicationName();
@@ -276,7 +284,7 @@ public async Task I_can_create_a_new_secret_key()
public async Task I_can_lock_an_api_key()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient().AddManagementKey();
var applicationName = GetApplicationName();
@@ -286,7 +294,8 @@ public async Task I_can_lock_an_api_key()
var keyToLock = apiKeys!.First(x => x.Type == ApiKeyTypes.Public);
// Act
- using var response = await client.PostAsync($"/admin/apps/{applicationName}/api-keys/{keyToLock.Id}/lock", null);
+ using var response =
+ await client.PostAsync($"/admin/apps/{applicationName}/api-keys/{keyToLock.Id}/lock", null);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.NoContent);
@@ -307,7 +316,7 @@ public async Task I_can_lock_an_api_key()
public async Task I_can_unlock_a_locked_api_key()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient().AddManagementKey();
var applicationName = GetApplicationName();
@@ -339,7 +348,7 @@ public async Task I_can_unlock_a_locked_api_key()
public async Task I_can_delete_an_api_key()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient().AddManagementKey();
var applicationName = GetApplicationName();
@@ -349,12 +358,14 @@ public async Task I_can_delete_an_api_key()
var keyToDelete = apiKeys!.First();
// Act
- using var responseMessage = await client.DeleteAsync($"/admin/apps/{applicationName}/api-keys/{keyToDelete.Id}");
+ using var responseMessage =
+ await client.DeleteAsync($"/admin/apps/{applicationName}/api-keys/{keyToDelete.Id}");
// Assert
responseMessage.StatusCode.Should().Be(HttpStatusCode.NoContent);
using var assertKeyIsDeletedResponse = await client.GetAsync($"/admin/apps/{applicationName}/api-keys");
- var assertKeyIsDeleted = await assertKeyIsDeletedResponse.Content.ReadFromJsonAsync>();
+ var assertKeyIsDeleted =
+ await assertKeyIsDeletedResponse.Content.ReadFromJsonAsync>();
assertKeyIsDeleted.Should().NotContain(x => x.Id == keyToDelete.Id);
}
@@ -363,7 +374,7 @@ public async Task I_can_delete_an_api_key()
public async Task I_can_enable_the_generate_sign_in_token_endpoint()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient().AddManagementKey();
var applicationName = GetApplicationName();
@@ -373,11 +384,7 @@ public async Task I_can_enable_the_generate_sign_in_token_endpoint()
// Act
using var enableResponse = await client.PostAsJsonAsync("apps/features",
- new SetFeaturesRequest
- {
- PerformedBy = "a_user",
- EnableManuallyGeneratedAuthenticationTokens = true
- });
+ new SetFeaturesRequest { PerformedBy = "a_user", EnableManuallyGeneratedAuthenticationTokens = true });
// Assert
enableResponse.StatusCode.Should().Be(HttpStatusCode.NoContent);
@@ -396,7 +403,7 @@ public async Task I_can_enable_the_generate_sign_in_token_endpoint()
public async Task I_can_disable_the_generate_sign_in_token_endpoint()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient().AddManagementKey();
var applicationName = GetApplicationName();
@@ -406,11 +413,7 @@ public async Task I_can_disable_the_generate_sign_in_token_endpoint()
// Act
using var enableResponse = await client.PostAsJsonAsync("apps/features",
- new SetFeaturesRequest
- {
- PerformedBy = "a_user",
- EnableManuallyGeneratedAuthenticationTokens = false
- });
+ new SetFeaturesRequest { PerformedBy = "a_user", EnableManuallyGeneratedAuthenticationTokens = false });
// Assert
enableResponse.StatusCode.Should().Be(HttpStatusCode.NoContent);
@@ -429,7 +432,7 @@ public async Task I_can_disable_the_generate_sign_in_token_endpoint()
public async Task I_can_enable_magic_links()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient().AddManagementKey();
var applicationName = GetApplicationName();
@@ -439,11 +442,7 @@ public async Task I_can_enable_magic_links()
// Act
using var enableResponse = await client.PostAsJsonAsync("apps/features",
- new SetFeaturesRequest
- {
- PerformedBy = "a_user",
- EnableMagicLinks = true
- });
+ new SetFeaturesRequest { PerformedBy = "a_user", EnableMagicLinks = true });
// Assert
enableResponse.StatusCode.Should().Be(HttpStatusCode.NoContent);
@@ -460,7 +459,7 @@ public async Task I_can_enable_magic_links()
public async Task I_can_disable_magic_links()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient().AddManagementKey();
var applicationName = GetApplicationName();
@@ -470,11 +469,7 @@ public async Task I_can_disable_magic_links()
// Act
using var enableResponse = await client.PostAsJsonAsync("apps/features",
- new SetFeaturesRequest
- {
- PerformedBy = "a_user",
- EnableMagicLinks = false
- });
+ new SetFeaturesRequest { PerformedBy = "a_user", EnableMagicLinks = false });
// Assert
enableResponse.StatusCode.Should().Be(HttpStatusCode.NoContent);
@@ -489,7 +484,7 @@ public async Task I_can_disable_magic_links()
public async Task I_can_check_whether_an_app_id_is_available()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient().AddManagementKey();
var applicationName = GetApplicationName();
@@ -507,7 +502,7 @@ public async Task I_can_check_whether_an_app_id_is_available()
public async Task I_can_check_whether_an_app_id_is_unavailable()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient().AddManagementKey();
var applicationName = GetApplicationName();
diff --git a/tests/Api.IntegrationTests/Endpoints/Authenticators/AuthenticatorsTests.cs b/tests/Api.IntegrationTests/Endpoints/Authenticators/AuthenticatorsTests.cs
index 27903ebc2..9608a34d1 100644
--- a/tests/Api.IntegrationTests/Endpoints/Authenticators/AuthenticatorsTests.cs
+++ b/tests/Api.IntegrationTests/Endpoints/Authenticators/AuthenticatorsTests.cs
@@ -31,7 +31,7 @@ public class AuthenticatorsTests(ITestOutputHelper testOutput, PasswordlessApiFi
public async Task I_can_retrieve_configured_authenticators_when_attestation_is_allowed()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -52,7 +52,7 @@ public async Task I_can_retrieve_configured_authenticators_when_attestation_is_a
public async Task I_can_retrieve_configured_authenticators_with_expected_result()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -61,14 +61,14 @@ public async Task I_can_retrieve_configured_authenticators_with_expected_result(
client.AddSecretKey(accountKeysCreation!.ApiSecret1);
client.AddPublicKey(accountKeysCreation!.ApiKey1);
await client.EnableAttestation(applicationName);
- var request = new AddAuthenticatorsRequest(new List
- {
- Guid.Parse("973446CA-E21C-9A9B-99F5-9B985A67AF0F")
- }, true);
+ var request =
+ new AddAuthenticatorsRequest(new List { Guid.Parse("973446CA-E21C-9A9B-99F5-9B985A67AF0F") }, true);
_ = await client.PostAsJsonAsync("authenticators/add", request);
// Act
- var actual = await client.GetFromJsonAsync>("authenticators/list?isAllowed=true");
+ var actual =
+ await client.GetFromJsonAsync>(
+ "authenticators/list?isAllowed=true");
// Assert
actual.Should().NotBeNull();
@@ -80,7 +80,7 @@ public async Task I_can_retrieve_configured_authenticators_with_expected_result(
public async Task I_receive_forbidden_when_retrieving_configured_authenticators_when_attestation_is_not_allowed()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -100,7 +100,7 @@ public async Task I_receive_forbidden_when_retrieving_configured_authenticators_
public async Task I_can_whitelist_authenticators_when_attestation_is_allowed()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -110,10 +110,8 @@ public async Task I_can_whitelist_authenticators_when_attestation_is_allowed()
client.AddPublicKey(accountKeysCreation!.ApiKey1);
await client.EnableAttestation(applicationName);
- var request = new AddAuthenticatorsRequest(new List
- {
- Guid.Parse("973446CA-E21C-9A9B-99F5-9B985A67AF0F")
- }, true);
+ var request =
+ new AddAuthenticatorsRequest(new List { Guid.Parse("973446CA-E21C-9A9B-99F5-9B985A67AF0F") }, true);
// Act
var actual = await client.PostAsJsonAsync("authenticators/add", request);
@@ -126,7 +124,7 @@ public async Task I_can_whitelist_authenticators_when_attestation_is_allowed()
public async Task I_receive_forbidden_when_whitelisting_authenticators_when_attestation_is_not_allowed()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -135,10 +133,8 @@ public async Task I_receive_forbidden_when_whitelisting_authenticators_when_atte
client.AddSecretKey(accountKeysCreation!.ApiSecret1);
client.AddPublicKey(accountKeysCreation!.ApiKey1);
- var request = new AddAuthenticatorsRequest(new List
- {
- Guid.Parse("973446CA-E21C-9A9B-99F5-9B985A67AF0F")
- }, true);
+ var request =
+ new AddAuthenticatorsRequest(new List { Guid.Parse("973446CA-E21C-9A9B-99F5-9B985A67AF0F") }, true);
// Act
var actual = await client.PostAsJsonAsync("authenticators/add", request);
@@ -151,7 +147,7 @@ public async Task I_receive_forbidden_when_whitelisting_authenticators_when_atte
public async Task I_can_delist_authenticators_when_attestation_is_allowed()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -161,10 +157,8 @@ public async Task I_can_delist_authenticators_when_attestation_is_allowed()
client.AddPublicKey(accountKeysCreation!.ApiKey1);
await client.EnableAttestation(applicationName);
- var whitelistRequest = new AddAuthenticatorsRequest(new List
- {
- Guid.Parse("973446CA-E21C-9A9B-99F5-9B985A67AF0F")
- }, true);
+ var whitelistRequest =
+ new AddAuthenticatorsRequest(new List { Guid.Parse("973446CA-E21C-9A9B-99F5-9B985A67AF0F") }, true);
_ = await client.PostAsJsonAsync("authenticators/add", whitelistRequest);
var request = new RemoveAuthenticatorsRequest(
new List { Guid.Parse("973446CA-E21C-9A9B-99F5-9B985A67AF0F") });
@@ -180,7 +174,7 @@ public async Task I_can_delist_authenticators_when_attestation_is_allowed()
public async Task I_receive_forbidden_when_delisting_authenticators_when_attestation_is_not_allowed()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
diff --git a/tests/Api.IntegrationTests/Endpoints/Credentials/CredentialsTests.cs b/tests/Api.IntegrationTests/Endpoints/Credentials/CredentialsTests.cs
index 7f2694fc8..9275f5689 100644
--- a/tests/Api.IntegrationTests/Endpoints/Credentials/CredentialsTests.cs
+++ b/tests/Api.IntegrationTests/Endpoints/Credentials/CredentialsTests.cs
@@ -24,22 +24,32 @@ public class CredentialsTests(ITestOutputHelper testOutput, PasswordlessApiFixtu
public async Task I_can_view_a_list_of_registered_users_credentials()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient().AddPublicKey().AddSecretKey().AddUserAgent();
const string originUrl = PasswordlessApi.OriginUrl;
const string rpId = PasswordlessApi.RpId;
var tokenRequest = _tokenGenerator.Generate();
using var tokenResponse = await client.PostAsJsonAsync("register/token", tokenRequest);
- var registerTokenResponse = await tokenResponse.Content.ReadFromJsonAsync();
- var registrationBeginRequest = new FidoRegistrationBeginDTO { Token = registerTokenResponse!.Token, Origin = originUrl, RPID = rpId };
+ var registerTokenResponse =
+ await tokenResponse.Content.ReadFromJsonAsync();
+ var registrationBeginRequest =
+ new FidoRegistrationBeginDTO { Token = registerTokenResponse!.Token, Origin = originUrl, RPID = rpId };
using var registrationBeginResponse = await client.PostAsJsonAsync("register/begin", registrationBeginRequest);
- var sessionResponse = await registrationBeginResponse.Content.ReadFromJsonAsync>();
+ var sessionResponse = await registrationBeginResponse.Content
+ .ReadFromJsonAsync>();
- var authenticatorAttestationRawResponse = await BrowserCredentialsHelper.CreateCredentialsAsync(sessionResponse!.Data, originUrl);
+ var authenticatorAttestationRawResponse =
+ await BrowserCredentialsHelper.CreateCredentialsAsync(sessionResponse!.Data, originUrl);
_ = await client.PostAsJsonAsync("register/complete",
- new RegistrationCompleteDTO { Origin = originUrl, RPID = rpId, Session = sessionResponse.Session, Response = authenticatorAttestationRawResponse });
+ new RegistrationCompleteDTO
+ {
+ Origin = originUrl,
+ RPID = rpId,
+ Session = sessionResponse.Session,
+ Response = authenticatorAttestationRawResponse
+ });
// Act
using var credentialsResponse = await client.GetAsync($"credentials/list?userId={tokenRequest.UserId}");
@@ -57,7 +67,7 @@ public async Task I_can_view_a_list_of_registered_users_credentials()
public async Task I_am_told_to_pass_the_user_id_when_getting_credential_list_using_get_with_secret_key()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient().AddPublicKey().AddSecretKey().AddUserAgent();
// Act
@@ -76,7 +86,7 @@ public async Task I_am_told_to_pass_the_user_id_when_getting_credential_list_usi
public async Task I_am_told_to_pass_the_user_id_when_getting_credential_list_using_post_with_secret_key()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient().AddPublicKey().AddSecretKey().AddUserAgent();
var request = new GetCredentialsRequest(null!);
diff --git a/tests/Api.IntegrationTests/Endpoints/Events/EventsTests.cs b/tests/Api.IntegrationTests/Endpoints/Events/EventsTests.cs
index e449ceec1..061464eb4 100644
--- a/tests/Api.IntegrationTests/Endpoints/Events/EventsTests.cs
+++ b/tests/Api.IntegrationTests/Endpoints/Events/EventsTests.cs
@@ -21,7 +21,7 @@ public class EventsTests(ITestOutputHelper testOutput, PasswordlessApiFixture ap
public async Task I_can_view_the_event_for_a_user_retrieving_the_api_keys()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -36,7 +36,8 @@ public async Task I_can_view_the_event_for_a_user_retrieving_the_api_keys()
// Assert
getApplicationEventsResponse.StatusCode.Should().Be(HttpStatusCode.OK);
- var applicationEvents = await getApplicationEventsResponse.Content.ReadFromJsonAsync();
+ var applicationEvents =
+ await getApplicationEventsResponse.Content.ReadFromJsonAsync();
applicationEvents.Should().NotBeNull();
applicationEvents!.Events.Should().NotBeEmpty();
applicationEvents.Events.Should().Contain(x => x.EventType == EventType.AdminApiKeysEnumerated.ToString());
@@ -46,7 +47,7 @@ public async Task I_can_view_the_event_for_a_user_retrieving_the_api_keys()
public async Task I_can_view_the_event_for_a_user_creating_an_api_key()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -62,7 +63,8 @@ public async Task I_can_view_the_event_for_a_user_creating_an_api_key()
// Assert
getApplicationEventsResponse.StatusCode.Should().Be(HttpStatusCode.OK);
- var applicationEvents = await getApplicationEventsResponse.Content.ReadFromJsonAsync();
+ var applicationEvents =
+ await getApplicationEventsResponse.Content.ReadFromJsonAsync();
applicationEvents.Should().NotBeNull();
applicationEvents!.Events.Should().NotBeEmpty();
applicationEvents.Events.Should().Contain(x => x.EventType == EventType.AdminApiKeyCreated.ToString());
@@ -72,7 +74,7 @@ public async Task I_can_view_the_event_for_a_user_creating_an_api_key()
public async Task I_can_view_the_event_for_locking_an_api_key()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -90,7 +92,8 @@ public async Task I_can_view_the_event_for_locking_an_api_key()
// Assert
getApplicationEventsResponse.StatusCode.Should().Be(HttpStatusCode.OK);
- var applicationEvents = await getApplicationEventsResponse.Content.ReadFromJsonAsync();
+ var applicationEvents =
+ await getApplicationEventsResponse.Content.ReadFromJsonAsync();
applicationEvents.Should().NotBeNull();
applicationEvents!.Events.Should().NotBeEmpty();
applicationEvents.Events.Should().Contain(x => x.EventType == EventType.AdminApiKeyLocked.ToString());
@@ -100,7 +103,7 @@ public async Task I_can_view_the_event_for_locking_an_api_key()
public async Task I_can_view_the_event_for_unlocking_an_api_key()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -119,7 +122,8 @@ public async Task I_can_view_the_event_for_unlocking_an_api_key()
// Assert
getApplicationEventsResponse.StatusCode.Should().Be(HttpStatusCode.OK);
- var applicationEvents = await getApplicationEventsResponse.Content.ReadFromJsonAsync();
+ var applicationEvents =
+ await getApplicationEventsResponse.Content.ReadFromJsonAsync();
applicationEvents.Should().NotBeNull();
applicationEvents!.Events.Should().NotBeEmpty();
applicationEvents.Events.Should().Contain(x => x.EventType == EventType.AdminApiKeyUnlocked.ToString());
@@ -129,7 +133,7 @@ public async Task I_can_view_the_event_for_unlocking_an_api_key()
public async Task I_can_view_the_event_for_deleting_an_api_key()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -147,7 +151,8 @@ public async Task I_can_view_the_event_for_deleting_an_api_key()
// Assert
getApplicationEventsResponse.StatusCode.Should().Be(HttpStatusCode.OK);
- var applicationEvents = await getApplicationEventsResponse.Content.ReadFromJsonAsync();
+ var applicationEvents =
+ await getApplicationEventsResponse.Content.ReadFromJsonAsync();
applicationEvents.Should().NotBeNull();
applicationEvents!.Events.Should().NotBeEmpty();
applicationEvents.Events.Should().Contain(x => x.EventType == EventType.AdminApiKeyDeleted.ToString());
@@ -157,7 +162,7 @@ public async Task I_can_view_the_event_for_deleting_an_api_key()
public async Task I_can_view_the_event_for_enabling_the_generate_sign_in_token_endpoint()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -173,10 +178,12 @@ public async Task I_can_view_the_event_for_enabling_the_generate_sign_in_token_e
// Assert
getApplicationEventsResponse.StatusCode.Should().Be(HttpStatusCode.OK);
- var applicationEvents = await getApplicationEventsResponse.Content.ReadFromJsonAsync();
+ var applicationEvents =
+ await getApplicationEventsResponse.Content.ReadFromJsonAsync();
applicationEvents.Should().NotBeNull();
applicationEvents!.Events.Should().NotBeEmpty();
- var enabledEvent = applicationEvents.Events.FirstOrDefault(x => x.EventType == EventType.AdminGenerateSignInTokenEndpointEnabled.ToString());
+ var enabledEvent = applicationEvents.Events.FirstOrDefault(x =>
+ x.EventType == EventType.AdminGenerateSignInTokenEndpointEnabled.ToString());
enabledEvent.Should().NotBeNull();
enabledEvent!.PerformedBy.Should().Be(user);
}
@@ -185,7 +192,7 @@ public async Task I_can_view_the_event_for_enabling_the_generate_sign_in_token_e
public async Task I_can_view_the_event_for_disabling_the_generate_sign_in_token_endpoint()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -201,10 +208,12 @@ public async Task I_can_view_the_event_for_disabling_the_generate_sign_in_token_
// Assert
getApplicationEventsResponse.StatusCode.Should().Be(HttpStatusCode.OK);
- var applicationEvents = await getApplicationEventsResponse.Content.ReadFromJsonAsync();
+ var applicationEvents =
+ await getApplicationEventsResponse.Content.ReadFromJsonAsync();
applicationEvents.Should().NotBeNull();
applicationEvents!.Events.Should().NotBeEmpty();
- var enabledEvent = applicationEvents.Events.FirstOrDefault(x => x.EventType == EventType.AdminGenerateSignInTokenEndpointDisabled.ToString());
+ var enabledEvent = applicationEvents.Events.FirstOrDefault(x =>
+ x.EventType == EventType.AdminGenerateSignInTokenEndpointDisabled.ToString());
enabledEvent.Should().NotBeNull();
enabledEvent!.PerformedBy.Should().Be(user);
}
@@ -213,7 +222,7 @@ public async Task I_can_view_the_event_for_disabling_the_generate_sign_in_token_
public async Task I_can_view_the_event_for_enabling_magic_links()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -229,10 +238,12 @@ public async Task I_can_view_the_event_for_enabling_magic_links()
// Assert
getApplicationEventsResponse.StatusCode.Should().Be(HttpStatusCode.OK);
- var applicationEvents = await getApplicationEventsResponse.Content.ReadFromJsonAsync();
+ var applicationEvents =
+ await getApplicationEventsResponse.Content.ReadFromJsonAsync();
applicationEvents.Should().NotBeNull();
applicationEvents!.Events.Should().NotBeEmpty();
- var enabledEvent = applicationEvents.Events.FirstOrDefault(x => x.EventType == EventType.AdminMagicLinksEnabled.ToString());
+ var enabledEvent =
+ applicationEvents.Events.FirstOrDefault(x => x.EventType == EventType.AdminMagicLinksEnabled.ToString());
enabledEvent.Should().NotBeNull();
enabledEvent!.PerformedBy.Should().Be(user);
}
@@ -241,7 +252,7 @@ public async Task I_can_view_the_event_for_enabling_magic_links()
public async Task I_can_view_the_event_for_disabling_magic_links()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -257,10 +268,12 @@ public async Task I_can_view_the_event_for_disabling_magic_links()
// Assert
getApplicationEventsResponse.StatusCode.Should().Be(HttpStatusCode.OK);
- var applicationEvents = await getApplicationEventsResponse.Content.ReadFromJsonAsync();
+ var applicationEvents =
+ await getApplicationEventsResponse.Content.ReadFromJsonAsync();
applicationEvents.Should().NotBeNull();
applicationEvents!.Events.Should().NotBeEmpty();
- var enabledEvent = applicationEvents.Events.FirstOrDefault(x => x.EventType == EventType.AdminMagicLinksDisabled.ToString());
+ var enabledEvent =
+ applicationEvents.Events.FirstOrDefault(x => x.EventType == EventType.AdminMagicLinksDisabled.ToString());
enabledEvent.Should().NotBeNull();
enabledEvent!.PerformedBy.Should().Be(user);
}
@@ -269,7 +282,7 @@ public async Task I_can_view_the_event_for_disabling_magic_links()
public async Task I_can_view_the_event_for_using_a_disabled_api_secret()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -288,17 +301,19 @@ public async Task I_can_view_the_event_for_using_a_disabled_api_secret()
using var getApplicationEventsResponse = await client.GetAsync("events?pageNumber=1");
// Assert
getApplicationEventsResponse.StatusCode.Should().Be(HttpStatusCode.OK);
- var applicationEvents = await getApplicationEventsResponse.Content.ReadFromJsonAsync();
+ var applicationEvents =
+ await getApplicationEventsResponse.Content.ReadFromJsonAsync();
applicationEvents.Should().NotBeNull();
applicationEvents!.Events.Should().NotBeEmpty();
- applicationEvents.Events.Should().Contain(x => x.EventType == EventType.ApiAuthDisabledSecretKeyUsed.ToString());
+ applicationEvents.Events.Should()
+ .Contain(x => x.EventType == EventType.ApiAuthDisabledSecretKeyUsed.ToString());
}
[Fact]
public async Task I_can_view_the_event_for_using_a_disabled_public_key()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -311,7 +326,8 @@ public async Task I_can_view_the_event_for_using_a_disabled_public_key()
var apiKeys = await getApiKeysResponse.Content.ReadFromJsonAsync>();
var keyToLock = apiKeys!.First(x => x.ApiKey.EndsWith(accountKeysCreation.ApiKey1.GetLast(4)));
_ = await client.PostAsync($"/admin/apps/{applicationName}/api-keys/{keyToLock.Id}/lock", null);
- _ = await client.PostAsJsonAsync("/signin/begin", new SignInBeginDTO { Origin = PasswordlessApi.OriginUrl, RPID = PasswordlessApi.RpId });
+ _ = await client.PostAsJsonAsync("/signin/begin",
+ new SignInBeginDTO { Origin = PasswordlessApi.OriginUrl, RPID = PasswordlessApi.RpId });
_ = await client.PostAsync($"/admin/apps/{applicationName}/api-keys/{keyToLock.Id}/unlock", null);
// Act
@@ -319,17 +335,19 @@ public async Task I_can_view_the_event_for_using_a_disabled_public_key()
// Assert
getApplicationEventsResponse.StatusCode.Should().Be(HttpStatusCode.OK);
- var applicationEvents = await getApplicationEventsResponse.Content.ReadFromJsonAsync();
+ var applicationEvents =
+ await getApplicationEventsResponse.Content.ReadFromJsonAsync();
applicationEvents.Should().NotBeNull();
applicationEvents!.Events.Should().NotBeEmpty();
- applicationEvents.Events.Should().Contain(x => x.EventType == EventType.ApiAuthDisabledPublicKeyUsed.ToString());
+ applicationEvents.Events.Should()
+ .Contain(x => x.EventType == EventType.ApiAuthDisabledPublicKeyUsed.ToString());
}
[Fact]
public async Task I_can_view_the_event_for_using_a_non_existent_api_key()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -338,14 +356,16 @@ public async Task I_can_view_the_event_for_using_a_non_existent_api_key()
client.AddSecretKey(accountKeysCreation!.ApiSecret1);
client.AddPublicKey($"{applicationName}:public:invalid-public-key");
await client.EnableEventLogging(applicationName);
- _ = await client.PostAsJsonAsync("/signin/begin", new SignInBeginDTO { Origin = PasswordlessApi.OriginUrl, RPID = PasswordlessApi.RpId });
+ _ = await client.PostAsJsonAsync("/signin/begin",
+ new SignInBeginDTO { Origin = PasswordlessApi.OriginUrl, RPID = PasswordlessApi.RpId });
// Act
using var getApplicationEventsResponse = await client.GetAsync("events?pageNumber=1");
// Assert
getApplicationEventsResponse.StatusCode.Should().Be(HttpStatusCode.OK);
- var applicationEvents = await getApplicationEventsResponse.Content.ReadFromJsonAsync();
+ var applicationEvents =
+ await getApplicationEventsResponse.Content.ReadFromJsonAsync();
applicationEvents.Should().NotBeNull();
applicationEvents!.Events.Should().NotBeEmpty();
applicationEvents.Events.Should().Contain(x => x.EventType == EventType.ApiAuthInvalidPublicKeyUsed.ToString());
@@ -355,7 +375,7 @@ public async Task I_can_view_the_event_for_using_a_non_existent_api_key()
public async Task I_can_view_the_event_for_using_a_non_existent_api_secret()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -372,7 +392,8 @@ public async Task I_can_view_the_event_for_using_a_non_existent_api_secret()
// Assert
getApplicationEventsResponse.StatusCode.Should().Be(HttpStatusCode.OK);
- var applicationEvents = await getApplicationEventsResponse.Content.ReadFromJsonAsync();
+ var applicationEvents =
+ await getApplicationEventsResponse.Content.ReadFromJsonAsync();
applicationEvents.Should().NotBeNull();
applicationEvents!.Events.Should().NotBeEmpty();
applicationEvents.Events.Should().Contain(x => x.EventType == EventType.ApiAuthInvalidSecretKeyUsed.ToString());
diff --git a/tests/Api.IntegrationTests/Endpoints/Magic/MagicTests.cs b/tests/Api.IntegrationTests/Endpoints/Magic/MagicTests.cs
index c88273a9f..530d6ddca 100644
--- a/tests/Api.IntegrationTests/Endpoints/Magic/MagicTests.cs
+++ b/tests/Api.IntegrationTests/Endpoints/Magic/MagicTests.cs
@@ -21,7 +21,7 @@ public class MagicTests(ITestOutputHelper testOutput, PasswordlessApiFixture api
public async Task I_can_send_a_magic_link_email()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -45,7 +45,7 @@ public async Task I_can_send_a_magic_link_email()
public async Task I_cannot_send_a_magic_link_email_if_the_feature_is_disabled()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -69,7 +69,7 @@ public async Task I_cannot_send_a_magic_link_email_if_the_feature_is_disabled()
public async Task I_receive_a_validation_error_when_the_url_does_not_contain_the_token_template()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -91,16 +91,18 @@ public async Task I_receive_a_validation_error_when_the_url_does_not_contain_the
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
var responseDetails = await response.Content.ReadFromJsonAsync();
responseDetails.Should().NotBeNull();
- var magicLinkUrlError = responseDetails!.Errors.FirstOrDefault(x => x.Key.Equals(nameof(request.UrlTemplate), StringComparison.CurrentCultureIgnoreCase));
+ var magicLinkUrlError = responseDetails!.Errors.FirstOrDefault(x =>
+ x.Key.Equals(nameof(request.UrlTemplate), StringComparison.CurrentCultureIgnoreCase));
magicLinkUrlError.Should().NotBeNull();
- magicLinkUrlError.Value.Should().Contain($"You have provided a {nameof(request.UrlTemplate)} without a {SendMagicLinkRequest.TokenTemplate} template. Please include it like so: https://www.example.com?token={SendMagicLinkRequest.TokenTemplate}");
+ magicLinkUrlError.Value.Should().Contain(
+ $"You have provided a {nameof(request.UrlTemplate)} without a {SendMagicLinkRequest.TokenTemplate} template. Please include it like so: https://www.example.com?token={SendMagicLinkRequest.TokenTemplate}");
}
[Fact]
public async Task I_receive_a_validation_error_when_an_invalid_url_is_sent()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -122,23 +124,23 @@ public async Task I_receive_a_validation_error_when_an_invalid_url_is_sent()
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
var responseDetails = await response.Content.ReadFromJsonAsync();
responseDetails.Should().NotBeNull();
- var magicLinkUrlError = responseDetails!.Errors.FirstOrDefault(x => x.Key.Equals(nameof(request.UrlTemplate), StringComparison.CurrentCultureIgnoreCase));
+ var magicLinkUrlError = responseDetails!.Errors.FirstOrDefault(x =>
+ x.Key.Equals(nameof(request.UrlTemplate), StringComparison.CurrentCultureIgnoreCase));
magicLinkUrlError.Should().NotBeNull();
- magicLinkUrlError.Value.Should().Contain($"You have provided a {nameof(request.UrlTemplate)} that cannot be converted to a URL.");
+ magicLinkUrlError.Value.Should()
+ .Contain($"You have provided a {nameof(request.UrlTemplate)} that cannot be converted to a URL.");
}
[Fact]
public async Task I_cannot_send_a_magic_link_email_to_a_non_admin_address_if_the_application_is_too_new()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
- using var appCreateResponse = await client.CreateApplicationAsync(applicationName, new CreateAppDto
- {
- AdminEmail = "admin@email.com"
- });
+ using var appCreateResponse =
+ await client.CreateApplicationAsync(applicationName, new CreateAppDto { AdminEmail = "admin@email.com" });
var appCreated = await appCreateResponse.Content.ReadFromJsonAsync();
client.AddSecretKey(appCreated!.ApiSecret1);
await client.EnableMagicLinks("a_user");
@@ -159,15 +161,13 @@ public async Task I_cannot_send_a_magic_link_email_to_a_non_admin_address_if_the
public async Task I_can_send_a_magic_link_email_to_an_admin_address_even_if_the_application_is_too_new()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
const string emailAddress = "admin@email.com";
var applicationName = CreateAppHelpers.GetApplicationName();
- using var appCreateResponse = await client.CreateApplicationAsync(applicationName, new CreateAppDto
- {
- AdminEmail = emailAddress
- });
+ using var appCreateResponse =
+ await client.CreateApplicationAsync(applicationName, new CreateAppDto { AdminEmail = emailAddress });
var appCreated = await appCreateResponse.Content.ReadFromJsonAsync();
client.AddSecretKey(appCreated!.ApiSecret1);
await client.EnableMagicLinks("a_user");
@@ -188,7 +188,7 @@ public async Task I_can_send_a_magic_link_email_to_an_admin_address_even_if_the_
public async Task I_cannot_send_too_many_magic_link_emails_in_a_short_time()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput, false);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions { TestOutput = testOutput });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -219,7 +219,7 @@ public async Task I_cannot_send_too_many_magic_link_emails_in_a_short_time()
// Assert
unsuccessfulResponse.Should().NotBeNull();
- unsuccessfulResponse!.StatusCode.Should().Be(HttpStatusCode.TooManyRequests);
+ unsuccessfulResponse.StatusCode.Should().Be(HttpStatusCode.TooManyRequests);
unsuccessfulResponse.Dispose();
}
@@ -230,14 +230,22 @@ public async Task I_cannot_send_too_many_magic_link_emails_in_a_short_time()
public async Task I_cannot_send_too_many_magic_link_emails_in_a_month()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
- using var client = api.CreateClient();
-
var applicationName = CreateAppHelpers.GetApplicationName();
- using var appCreateResponse = await client.CreateApplicationAsync(applicationName, new CreateAppDto
+
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
{
- MagicLinkEmailMonthlyQuota = 1000
+ Settings = new Dictionary
+ {
+ [$"ApplicationOverrides:{applicationName}:IsRateLimitBypassEnabled"] = "true"
+ },
+ TestOutput = testOutput
});
+
+ using var client = api.CreateClient();
+
+ using var appCreateResponse =
+ await client.CreateApplicationAsync(applicationName,
+ new CreateAppDto { MagicLinkEmailMonthlyQuota = 1000 });
var appCreated = await appCreateResponse.Content.ReadFromJsonAsync();
client.AddSecretKey(appCreated!.ApiSecret1);
await client.EnableMagicLinks("a_user");
@@ -269,7 +277,7 @@ public async Task I_cannot_send_too_many_magic_link_emails_in_a_month()
// Assert
successfulResponseCount.Should().Be(1000);
unsuccessfulResponse.Should().NotBeNull();
- unsuccessfulResponse!.StatusCode.Should().Be(HttpStatusCode.TooManyRequests);
+ unsuccessfulResponse.StatusCode.Should().Be(HttpStatusCode.TooManyRequests);
var details = await unsuccessfulResponse.Content.ReadFromJsonAsync();
details.Should().NotBeNull();
@@ -282,10 +290,19 @@ public async Task I_cannot_send_too_many_magic_link_emails_in_a_month()
public async Task I_can_send_a_magic_link_email_after_enough_time_passed_since_the_monthly_quota_was_exceeded()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ var applicationName = CreateAppHelpers.GetApplicationName();
+
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+ Settings = new Dictionary
+ {
+ [$"ApplicationOverrides:{applicationName}:IsRateLimitBypassEnabled"] = "true"
+ },
+ TestOutput = testOutput
+ });
+
using var client = api.CreateClient();
- var applicationName = CreateAppHelpers.GetApplicationName();
using var appCreateResponse = await client.CreateApplicationAsync(applicationName);
var appCreated = await appCreateResponse.Content.ReadFromJsonAsync();
client.AddSecretKey(appCreated!.ApiSecret1);
diff --git a/tests/Api.IntegrationTests/Endpoints/Register/RegisterAttestationTests.cs b/tests/Api.IntegrationTests/Endpoints/Register/RegisterAttestationTests.cs
index 051e707e0..e65b893bf 100644
--- a/tests/Api.IntegrationTests/Endpoints/Register/RegisterAttestationTests.cs
+++ b/tests/Api.IntegrationTests/Endpoints/Register/RegisterAttestationTests.cs
@@ -39,7 +39,11 @@ public class RegisterAttestationTests(ITestOutputHelper testOutput, Passwordless
public async Task I_can_use_supported_attestation_methods_to_register_a_new_user_when_attestation_is_allowed(string attestation, AttestationConveyancePreference expectedAttestation)
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient();
var tokenRequest = TokenGenerator
@@ -87,7 +91,11 @@ public async Task I_can_use_supported_attestation_methods_to_register_a_new_user
public async Task I_can_use_supported_none_attestation_method_to_register_a_new_user_when_attestation_is_disallowed(string attestation, AttestationConveyancePreference expectedAttestation)
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient();
var tokenRequest = TokenGenerator
@@ -135,7 +143,11 @@ public async Task I_can_use_supported_none_attestation_method_to_register_a_new_
public async Task I_cannot_use_other_than_none_attestation_method_to_register_a_new_user_when_attestation_is_disallowed(string attestation)
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient();
var tokenRequest = TokenGenerator
diff --git a/tests/Api.IntegrationTests/Endpoints/Register/RegisterTests.cs b/tests/Api.IntegrationTests/Endpoints/Register/RegisterTests.cs
index 5a4caee3e..dc6eabbfe 100644
--- a/tests/Api.IntegrationTests/Endpoints/Register/RegisterTests.cs
+++ b/tests/Api.IntegrationTests/Endpoints/Register/RegisterTests.cs
@@ -22,7 +22,11 @@ public class RegisterTests(ITestOutputHelper testOutput, PasswordlessApiFixture
public async Task I_can_retrieve_token_to_start_registration()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient().AddPublicKey().AddSecretKey().AddUserAgent();
var request = _tokenGenerator.Generate();
@@ -41,7 +45,11 @@ public async Task I_can_retrieve_token_to_start_registration()
public async Task I_can_retrieve_the_credential_create_options_and_session_token_for_creating_a_new_user()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient().AddPublicKey().AddSecretKey().AddUserAgent();
var tokenRequest = _tokenGenerator.Generate();
@@ -70,7 +78,11 @@ public async Task I_can_retrieve_the_credential_create_options_and_session_token
public async Task I_can_use_a_passkey_to_register_a_new_user()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient().AddPublicKey().AddSecretKey().AddUserAgent();
using var driver = WebDriverFactory.GetDriver(PasswordlessApi.OriginUrl);
diff --git a/tests/Api.IntegrationTests/Endpoints/Register/RegisterTokenTests.cs b/tests/Api.IntegrationTests/Endpoints/Register/RegisterTokenTests.cs
index 012aea6ba..69e912fb7 100644
--- a/tests/Api.IntegrationTests/Endpoints/Register/RegisterTokenTests.cs
+++ b/tests/Api.IntegrationTests/Endpoints/Register/RegisterTokenTests.cs
@@ -19,7 +19,11 @@ public async Task UserIdAndDisplayNameIsTheOnlyRequiredProperties()
// Arrange
var payload = new { UserId = "1", Username = "test" };
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient().AddSecretKey();
// Act
@@ -40,7 +44,11 @@ public async Task InvalidUserIdReturnsError(string userid)
registerTokenGenerator.RuleFor(x => x.UserId, userid);
var registerToken = registerTokenGenerator.Generate();
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient().AddSecretKey();
// Act
@@ -69,7 +77,11 @@ public async Task InvalidUsernameReturnsError(string input)
registerTokenGenerator.RuleFor(x => x.Username, input);
var registerToken = registerTokenGenerator.Generate();
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient().AddSecretKey();
// Act
@@ -93,7 +105,11 @@ public async Task OtherAssertionIsNotAccepted(string attestation)
var payload = new { UserId = "1", Username = "test", attestation };
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient().AddSecretKey();
// Act
@@ -130,7 +146,11 @@ public async Task NoneAssertionIsAccepted(string attestation)
Attestation = attestation
};
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient().AddSecretKey();
// Act
diff --git a/tests/Api.IntegrationTests/Endpoints/SignIn/SignInTests.cs b/tests/Api.IntegrationTests/Endpoints/SignIn/SignInTests.cs
index 14a44541d..42fee3977 100644
--- a/tests/Api.IntegrationTests/Endpoints/SignIn/SignInTests.cs
+++ b/tests/Api.IntegrationTests/Endpoints/SignIn/SignInTests.cs
@@ -22,7 +22,11 @@ public class SignInTests(ITestOutputHelper testOutput, PasswordlessApiFixture ap
public async Task I_can_retrieve_assertion_options_to_begin_sign_in()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient().AddPublicKey().AddSecretKey().AddUserAgent();
var request = new SignInBeginDTO { Origin = PasswordlessApi.OriginUrl, RPID = PasswordlessApi.RpId };
@@ -44,7 +48,11 @@ public async Task I_can_retrieve_assertion_options_to_begin_sign_in()
public async Task I_can_retrieve_my_passkey_after_registering_and_receive_a_sign_in_token()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var httpClient = api.CreateClient().AddPublicKey().AddSecretKey().AddUserAgent();
using var driver = WebDriverFactory.GetDriver(PasswordlessApi.OriginUrl);
@@ -74,7 +82,11 @@ public async Task I_can_retrieve_my_passkey_after_registering_and_receive_a_sign
public async Task I_can_retrieve_my_passkey_after_registering_and_receive_a_valid_sign_in_token()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var httpClient = api.CreateClient().AddPublicKey().AddSecretKey().AddUserAgent();
using var driver = WebDriverFactory.GetDriver(PasswordlessApi.OriginUrl);
@@ -108,7 +120,11 @@ public async Task I_can_retrieve_my_passkey_after_registering_and_receive_a_vali
public async Task I_receive_an_error_message_when_sending_an_unrecognized_passkey()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var _httpClient = api.CreateClient().AddPublicKey().AddSecretKey().AddUserAgent();
using var options = await _httpClient.PostAsJsonAsync("/signin/begin", new { Origin = PasswordlessApi.OriginUrl, RPID = PasswordlessApi.RpId });
@@ -157,7 +173,11 @@ public async Task I_receive_an_error_message_when_sending_an_unrecognized_passke
public async Task An_expired_apps_token_keys_should_be_removed_when_a_request_is_made()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var httpClient = api.CreateClient().AddPublicKey().AddSecretKey().AddUserAgent();
var applicationName = $"test{Guid.NewGuid():N}";
@@ -185,7 +205,11 @@ public async Task An_expired_apps_token_keys_should_be_removed_when_a_request_is
public async Task I_receive_a_sign_in_token_for_a_valid_user_id()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var httpClient = api.CreateClient().AddPublicKey().AddSecretKey().AddUserAgent();
using var client = api.CreateClient().AddManagementKey();
@@ -219,7 +243,11 @@ public async Task I_receive_an_api_exception_when_using_an_expired_token()
{
// Arrange
const int timeToLive = 120;
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var httpClient = api.CreateClient().AddPublicKey().AddSecretKey().AddUserAgent();
using var client = api.CreateClient().AddManagementKey();
diff --git a/tests/Api.IntegrationTests/Middleware/AuthorizationIntegrationTests.cs b/tests/Api.IntegrationTests/Middleware/AuthorizationIntegrationTests.cs
index 76b94125d..5acee30e6 100644
--- a/tests/Api.IntegrationTests/Middleware/AuthorizationIntegrationTests.cs
+++ b/tests/Api.IntegrationTests/Middleware/AuthorizationIntegrationTests.cs
@@ -16,7 +16,11 @@ public class AuthorizationIntegrationTests(ITestOutputHelper testOutput, Passwor
public async Task I_receive_a_403_when_i_use_a_invalid_api_key_with_an_existing_endpoint()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -35,7 +39,11 @@ public async Task I_receive_a_403_when_i_use_a_invalid_api_key_with_an_existing_
public async Task I_receive_a_403_when_i_use_a_badly_formatted_api_key_with_an_existing_endpoint()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -54,7 +62,11 @@ public async Task I_receive_a_403_when_i_use_a_badly_formatted_api_key_with_an_e
public async Task I_receive_a_403_when_i_use_a_invalid_api_secret_with_an_existing_endpoint()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -73,7 +85,11 @@ public async Task I_receive_a_403_when_i_use_a_invalid_api_secret_with_an_existi
public async Task I_receive_a_403_when_i_use_a_badly_formatted_api_secret_with_an_existing_endpoint()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
diff --git a/tests/Api.IntegrationTests/Middleware/RoutingIntegrationTests.cs b/tests/Api.IntegrationTests/Middleware/RoutingIntegrationTests.cs
index 0b9afc05a..9a9168cc6 100644
--- a/tests/Api.IntegrationTests/Middleware/RoutingIntegrationTests.cs
+++ b/tests/Api.IntegrationTests/Middleware/RoutingIntegrationTests.cs
@@ -15,7 +15,11 @@ public class RoutingIntegrationTests(ITestOutputHelper testOutput, PasswordlessA
public async Task I_receive_a_404_when_i_use_a_badly_formatted_api_secret_with_a_non_existing_endpoint()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
@@ -34,7 +38,11 @@ public async Task I_receive_a_404_when_i_use_a_badly_formatted_api_secret_with_a
public async Task I_receive_a_404_when_i_use_a_badly_formatted_api_key_with_a_non_existing_endpoint()
{
// Arrange
- await using var api = await apiFixture.CreateApiAsync(testOutput);
+ await using var api = await apiFixture.CreateApiAsync(new PasswordlessApiOptions
+ {
+
+ TestOutput = testOutput
+ });
using var client = api.CreateClient();
var applicationName = CreateAppHelpers.GetApplicationName();
diff --git a/tests/Api.IntegrationTests/PasswordlessApi.cs b/tests/Api.IntegrationTests/PasswordlessApi.cs
index 8ddf228b0..b7470863a 100644
--- a/tests/Api.IntegrationTests/PasswordlessApi.cs
+++ b/tests/Api.IntegrationTests/PasswordlessApi.cs
@@ -1,16 +1,13 @@
using MartinCostello.Logging.XUnit;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
-using Microsoft.AspNetCore.RateLimiting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
using Microsoft.Extensions.Time.Testing;
-using Passwordless.Api.Endpoints;
using Passwordless.Api.IntegrationTests.Helpers;
using Passwordless.Common.Services.Mail;
using Xunit.Abstractions;
@@ -24,49 +21,32 @@ public class PasswordlessApi : ITestOutputHelperAccessor, IDisposable, IAsyncDis
private readonly WebApplicationFactory _factory;
- public PasswordlessApi(
- ITestOutputHelper? testOutput,
- string databaseConnectionString,
- bool disableRateLimiting)
+ public PasswordlessApi(PasswordlessApiOptions options)
{
_factory = new WebApplicationFactory()
- .WithWebHostBuilder(host => host
- .UseSetting("ConnectionStrings:sqlite:api", string.Empty)
- .UseSetting("ConnectionStrings:mssql:api", databaseConnectionString)
- .ConfigureLogging(logging => logging.ClearProviders().AddXUnit(this))
- .ConfigureTestServices(services =>
- {
- // Disable background services
- services.RemoveAll();
-
- // Disable rate limiting
- if (disableRateLimiting)
+ .WithWebHostBuilder(host =>
+ {
+ foreach (var (key, value) in options.Settings)
+ host.UseSetting(key, value);
+
+ host
+ .ConfigureLogging(logging => logging.ClearProviders().AddXUnit(this))
+ .ConfigureTestServices(services =>
{
- services.RemoveAll>();
- services.AddSingleton>(_ =>
- Options.Create(
- // Have to re-add all the rate limiter policies, because they are referenced
- // by the endpoints.
- new RateLimiterOptions()
- .AddFixedWindowLimiter(MagicEndpoints.RateLimiterPolicy, limiter =>
- {
- limiter.PermitLimit = int.MaxValue;
- limiter.Window = TimeSpan.FromSeconds(1);
- })
- )
- );
- }
-
- // Replace time
- services.RemoveAll();
- services.AddSingleton(Time);
-
- // Replace mail provider
- services.RemoveAll();
- services.AddSingleton();
- }));
-
- TestOutput = testOutput;
+ // Disable background services
+ services.RemoveAll();
+
+ // Replace time
+ services.RemoveAll();
+ services.AddSingleton(Time);
+
+ // Replace mail provider
+ services.RemoveAll();
+ services.AddSingleton();
+ });
+ });
+
+ TestOutput = options.TestOutput;
Services = _factory.Services;
}
diff --git a/tests/Api.IntegrationTests/PasswordlessApiFixture.cs b/tests/Api.IntegrationTests/PasswordlessApiFixture.cs
index e6eb7ce2b..75459af70 100644
--- a/tests/Api.IntegrationTests/PasswordlessApiFixture.cs
+++ b/tests/Api.IntegrationTests/PasswordlessApiFixture.cs
@@ -10,11 +10,18 @@ public class PasswordlessApiFixture : IAsyncDisposable, IAsyncLifetime
public async Task InitializeAsync() => await _dbContainer.StartAsync();
- public async Task CreateApiAsync(
- ITestOutputHelper? testOutput = null,
- bool disableRateLimiting = true)
+ public async Task CreateApiAsync(PasswordlessApiOptions options)
{
- var api = new PasswordlessApi(testOutput, _dbContainer.GetConnectionString(), disableRateLimiting);
+ var api = new PasswordlessApi(new PasswordlessApiOptions
+ {
+ TestOutput = options.TestOutput,
+ // Add the connection string to the settings
+ Settings = new Dictionary(options.Settings)
+ {
+ ["ConnectionStrings:sqlite:api"] = null,
+ ["ConnectionStrings:mssql:api"] = _dbContainer.GetConnectionString()
+ }
+ });
// Perform migrations and make sure the API is ready to receive requests
using var httpClient = api.CreateClient();
diff --git a/tests/Api.IntegrationTests/PasswordlessApiOptions.cs b/tests/Api.IntegrationTests/PasswordlessApiOptions.cs
new file mode 100644
index 000000000..7c7578f83
--- /dev/null
+++ b/tests/Api.IntegrationTests/PasswordlessApiOptions.cs
@@ -0,0 +1,10 @@
+using Xunit.Abstractions;
+
+namespace Passwordless.Api.IntegrationTests;
+
+public class PasswordlessApiOptions
+{
+ public IReadOnlyDictionary Settings { get; init; } = new Dictionary();
+
+ public ITestOutputHelper? TestOutput { get; init; }
+}
\ No newline at end of file