- **TestSetup.cs** - Updated `CreateTestConfig` method to initialize `Config` with required properties using object initializer syntax. - **ProxyIntegrationTests.cs** - Added null checks for `mockServer.Urls` before accessing it to prevent potential null reference exceptions. - Improved error handling for mock server URL access. - **VRChatAuthenticationTests.cs** - Added null checks for `mockServer.Urls` before accessing it to prevent potential null reference exceptions. - Enhanced the mock server setup to include null checks for request body content. - **Config.cs** - Added the `required` modifier to non-nullable properties in `ConfigAccount` and `iConfig` classes. - Updated the `Load` method to initialize the `Config` instance with required properties using object initializer syntax. - **Program.cs** - Added a null check for `result.CloseStatus` in WebSocket handling to prevent potential null reference exceptions.
146 lines
No EOL
6.5 KiB
C#
146 lines
No EOL
6.5 KiB
C#
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<TestSetup>
|
|
{
|
|
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<User>(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<TotpVerifyResponse>(verifyContent);
|
|
verifyResult.Should().NotBeNull();
|
|
verifyResult!.verified.Should().BeTrue();
|
|
}
|
|
}
|
|
} |