Add provider-neutral Google authentication

This commit is contained in:
hz
2026-07-20 21:42:56 -05:00
parent 48e19f138e
commit 72fd1e3e0f
20 changed files with 1084 additions and 36 deletions
+47
View File
@@ -86,6 +86,53 @@ public sealed class AuthTests
Assert.Null(SteamOpenIdService.ExtractSteamId("https://steamcommunity.com/openid/id/123"));
}
[Fact]
public void GoogleProviderIsDisabledUntilAllSecureConfigurationExists()
{
var incomplete = new GoogleExternalAuthProvider(new TestHttpClientFactory(), Configuration(new Dictionary<string, string?>
{
["Google:ClientId"] = "client-id",
["Google:ReturnUrl"] = "https://example.test/api/Auth/external/google/callback"
}));
var complete = new GoogleExternalAuthProvider(new TestHttpClientFactory(), Configuration(new Dictionary<string, string?>
{
["Google:ClientId"] = "client-id",
["Google:ClientSecret"] = "client-secret",
["Google:ReturnUrl"] = "https://example.test/api/Auth/external/google/callback"
}));
Assert.False(incomplete.IsConfigured);
Assert.True(complete.IsConfigured);
}
[Fact]
public void GoogleAuthorizationUsesStateNonceAndPkce()
{
var provider = new GoogleExternalAuthProvider(new TestHttpClientFactory(), Configuration(new Dictionary<string, string?>
{
["Google:ClientId"] = "client-id",
["Google:ClientSecret"] = "client-secret",
["Google:ReturnUrl"] = "https://example.test/api/Auth/external/google/callback"
}));
var url = provider.BuildAuthorizationUrl(new ExternalAuthTransaction
{
Id = "transaction-state",
Nonce = "transaction-nonce",
CodeVerifier = "a-long-test-code-verifier"
});
Assert.Contains("state=transaction-state", url);
Assert.Contains("nonce=transaction-nonce", url);
Assert.Contains("code_challenge_method=S256", url);
Assert.Contains("scope=openid%20email%20profile", url);
Assert.DoesNotContain("client-secret", url);
}
private static IConfiguration Configuration(Dictionary<string, string?> values) =>
new ConfigurationBuilder().AddInMemoryCollection(values).Build();
private sealed class TestHttpClientFactory : IHttpClientFactory
{
public HttpClient CreateClient(string name) => new();
}
}