using System.Net.Http.Json; using System.Text.Json; using Tiku.Api.Contracts; using Tiku.Domain.Identity; using Tiku.Domain.Tenancy; namespace Tiku.IntegrationTests.Api; internal sealed record TestAuthenticationTokens(string AccessToken, string RefreshToken); internal static class AuthenticationTestClientExtensions { public static async Task LoginAsTenantAsync( this HttpClient client, Guid tenantId, string identifier, string password = PasswordTestUserExtensions.TestPassword) { SetTenantHeader(client, tenantId); var response = await client.PostAsJsonAsync( "/api/auth/login/password", new PasswordLoginDto { Realm = AuthRealm.Tenant, TenantCode = tenantId.ToString("N"), Identifier = identifier, Password = password }); return await client.CompleteTenantAuthenticationAsync(response, tenantId, identifier); } public static async Task LoginAsPlatformAsync( this HttpClient client, string identifier, string password = PasswordTestUserExtensions.TestPassword) { client.DefaultRequestHeaders.Remove("x-tenant-code"); var response = await client.PostAsJsonAsync( "/api/auth/login/password", new PasswordLoginDto { Realm = AuthRealm.Platform, Identifier = identifier, Password = password }); return await client.CompletePlatformAuthenticationAsync(response, identifier); } public static async Task CompleteTenantAuthenticationAsync( this HttpClient client, HttpResponseMessage response, Guid tenantId, string _) { SetTenantHeader(client, tenantId); using var authentication = await ReadSuccessfulJsonAsync(response); var root = authentication.RootElement; var status = root.GetProperty("status").GetString(); return string.Equals(status, "authenticated", StringComparison.OrdinalIgnoreCase) ? ReadTokens(root.GetProperty("user").GetProperty("tokens")) : throw new InvalidOperationException($"Unsupported test authentication status '{status}'."); } private static async Task CompletePlatformAuthenticationAsync( this HttpClient client, HttpResponseMessage response, string _) { client.DefaultRequestHeaders.Remove("x-tenant-code"); using var authentication = await ReadSuccessfulJsonAsync(response); var root = authentication.RootElement; var status = root.GetProperty("status").GetString(); return string.Equals(status, "authenticated", StringComparison.OrdinalIgnoreCase) ? ReadTokens(root.GetProperty("user").GetProperty("tokens")) : throw new InvalidOperationException($"Unsupported test authentication status '{status}'."); } public static void UseAccessToken(this HttpClient client, TestAuthenticationTokens tokens) { client.DefaultRequestHeaders.Authorization = new("Bearer", tokens.AccessToken); } private static void SetTenantHeader(HttpClient client, Guid tenantId) { client.DefaultRequestHeaders.Remove("x-tenant-code"); client.DefaultRequestHeaders.Add("x-tenant-code", tenantId.ToString("N")); } private static async Task ReadSuccessfulJsonAsync(HttpResponseMessage response) { var body = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) { throw new HttpRequestException( $"Authentication request failed with {(int)response.StatusCode} ({response.StatusCode}): {body}"); } return JsonDocument.Parse(body); } private static TestAuthenticationTokens ReadTokens(JsonElement tokens) { var accessToken = tokens.GetProperty("accessToken").GetString() ?? throw new InvalidOperationException("Authentication response did not contain an access token."); var refreshToken = tokens.GetProperty("refreshToken").GetString() ?? throw new InvalidOperationException("Authentication response did not contain a refresh token."); return new TestAuthenticationTokens(accessToken, refreshToken); } }