using System.Net; using System.Text; using System.Text.Json; using FluentAssertions; using VRCAuthProxy.Tests.Helpers; using VRCAuthProxy.types; using WireMock.RequestBuilders; using WireMock.ResponseBuilders; using Xunit; namespace VRCAuthProxy.Tests.Integration { public class VRChatAuthenticationTests : IClassFixture { private readonly TestSetup _testSetup; public VRChatAuthenticationTests(TestSetup testSetup) { _testSetup = testSetup; } [Fact] public async Task Authentication_WithValidCredentials_ShouldReturnUserInfo() { // Arrange var mockServer = _testSetup.MockVRChatApi; // Mock the authentication endpoint mockServer.Given(Request.Create() .WithPath("/api/1/auth/user") .UsingGet() .WithHeader("Authorization", "*")) // Accept any authorization header .RespondWith(Response.Create() .WithStatusCode(200) .WithHeader("Content-Type", "application/json") .WithHeader("Set-Cookie", "auth=test-auth-token; path=/; secure; httponly") .WithBodyAsJson(new User { displayName = "TestUser" })); // Create a client with the test setup var handler = new HttpClientHandler { UseCookies = true }; var mockServerUrls = mockServer.Urls; if (mockServerUrls == null || !mockServerUrls.Any()) { throw new InvalidOperationException("Mock server URLs not available"); } var httpClient = new HttpClient(handler) { BaseAddress = new Uri(mockServerUrls.First()) }; // Act string username = "testuser"; string password = "testpassword"; string authString = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}")); var request = new HttpRequestMessage(HttpMethod.Get, "/api/1/auth/user"); request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authString); var response = await httpClient.SendAsync(request); // Assert response.StatusCode.Should().Be(HttpStatusCode.OK); // Verify the response can be parsed var responseContent = await response.Content.ReadAsStringAsync(); var user = JsonSerializer.Deserialize(responseContent); user.Should().NotBeNull(); user!.displayName.Should().Be("TestUser"); } [Fact] public async Task Authentication_WithTOTP_ShouldVerifyAndProvideUserInfo() { // Arrange var mockServer = _testSetup.MockVRChatApi; // Mock API behavior when TOTP is required mockServer.Given(Request.Create() .WithPath("/api/1/auth/user") .UsingGet() .WithHeader("Authorization", "*")) .RespondWith(Response.Create() .WithStatusCode(200) .WithHeader("Content-Type", "application/json") .WithBody(@"{""requiresTwoFactorAuth"":true,""totp"":true}")); // Mock TOTP verification endpoint mockServer.Given(Request.Create() .WithPath("/api/1/auth/twofactorauth/totp/verify") .UsingPost() .WithBody(x => x != null && x.Contains("code"))) .RespondWith(Response.Create() .WithStatusCode(200) .WithHeader("Content-Type", "application/json") .WithHeader("Set-Cookie", "auth=totp-verified-token; path=/; secure; httponly") .WithBodyAsJson(new TotpVerifyResponse { verified = true })); // Mock user info after successful TOTP verification mockServer.Given(Request.Create() .WithPath("/api/1/auth/user") .UsingGet() .WithCookie("auth", "totp-verified-token")) .RespondWith(Response.Create() .WithStatusCode(200) .WithHeader("Content-Type", "application/json") .WithBodyAsJson(new User { displayName = "TOTPVerifiedUser" })); // Create a client with the test setup var handler = new HttpClientHandler { UseCookies = true }; var mockServerUrls = mockServer.Urls; if (mockServerUrls == null || !mockServerUrls.Any()) { throw new InvalidOperationException("Mock server URLs not available"); } var httpClient = new HttpClient(handler) { BaseAddress = new Uri(mockServerUrls.First()) }; // Act - First authenticate which will indicate TOTP is required string username = "totpuser"; string password = "totppassword"; string authString = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}")); var request = new HttpRequestMessage(HttpMethod.Get, "/api/1/auth/user"); request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authString); var response = await httpClient.SendAsync(request); // Assert initial response response.StatusCode.Should().Be(HttpStatusCode.OK); var initialContent = await response.Content.ReadAsStringAsync(); initialContent.Should().Contain("requiresTwoFactorAuth"); // Now verify with TOTP var verifyReq = new HttpRequestMessage(HttpMethod.Post, "/api/1/auth/twofactorauth/totp/verify"); verifyReq.Content = new StringContent(@"{""code"":""123456""}", Encoding.UTF8, "application/json"); var verifyResp = await httpClient.SendAsync(verifyReq); // Assert TOTP verification verifyResp.StatusCode.Should().Be(HttpStatusCode.OK); var verifyContent = await verifyResp.Content.ReadAsStringAsync(); var verifyResult = JsonSerializer.Deserialize(verifyContent); verifyResult.Should().NotBeNull(); verifyResult!.verified.Should().BeTrue(); } } }