using System.Net; using System.Net.Http.Json; using ItWorx.KnowledgeHub.IntegrationTests.Infrastructure; namespace ItWorx.KnowledgeHub.IntegrationTests.Auth; [Collection(PostgresTestGroup.Name)] public sealed class CsrfTests(PostgresContainerFixture postgres) : OwnerSetupTestBase(postgres) { [Fact] public async Task Cookie_authenticated_mutation_without_a_csrf_header_is_rejected() { using var client = Factory.CreateClient(); await SetupHelper.LoginAsync(client, OwnerEmail, OwnerPassword); // Deliberately bypasses SetupHelper.PostWithCsrfAsync to send the mutation // without ever having fetched/attached a CSRF token. using var response = await client.PostAsJsonAsync("/api/v1/auth/logout", new { }); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); } [Fact] public async Task Cookie_authenticated_mutation_with_a_valid_csrf_header_succeeds() { using var client = Factory.CreateClient(); await SetupHelper.LoginAsync(client, OwnerEmail, OwnerPassword); var response = await SetupHelper.PostWithCsrfAsync(client, "/api/v1/auth/logout", null); Assert.Equal(HttpStatusCode.OK, response.StatusCode); } [Fact] public async Task A_csrf_token_obtained_by_one_session_does_not_validate_for_another() { using var attackerClient = Factory.CreateClient(); var stolenToken = await SetupHelper.GetCsrfTokenAsync(attackerClient); using var victimClient = Factory.CreateClient(); await SetupHelper.LoginAsync(victimClient, OwnerEmail, OwnerPassword); using var request = new HttpRequestMessage(HttpMethod.Post, "/api/v1/auth/logout"); request.Headers.Add(stolenToken.HeaderName, stolenToken.Value); using var response = await victimClient.SendAsync(request); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); } }