forked from gongxuegit/tiku-backend.net
feat: add wechat authentication
This commit is contained in:
@@ -2,13 +2,14 @@ using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Npgsql;
|
||||
using Tiku.Application.Auth;
|
||||
using Tiku.Domain.Identity;
|
||||
using Tiku.Domain.Tenancy;
|
||||
using Tiku.Infrastructure.Persistence;
|
||||
|
||||
namespace Tiku.IntegrationTests.Api;
|
||||
|
||||
public sealed class ApiTestFactory : WebApplicationFactory<Program>
|
||||
public sealed class ApiTestFactory(IWechatOAuthClient? wechatOAuthClient = null) : WebApplicationFactory<Program>
|
||||
{
|
||||
private readonly string databaseName = Guid.NewGuid().ToString();
|
||||
|
||||
@@ -28,6 +29,11 @@ public sealed class ApiTestFactory : WebApplicationFactory<Program>
|
||||
|
||||
services.AddDbContext<TikuDbContext>(options =>
|
||||
options.UseInMemoryDatabase(databaseName));
|
||||
|
||||
if (wechatOAuthClient is not null)
|
||||
{
|
||||
services.AddSingleton(wechatOAuthClient);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Tiku.Application.Auth;
|
||||
using Tiku.Api.Controllers;
|
||||
using Tiku.Domain.Identity;
|
||||
using Tiku.Domain.Tenancy;
|
||||
@@ -91,6 +92,53 @@ public sealed class AuthEndpointTests
|
||||
Assert.Equal(HttpStatusCode.Unauthorized, refreshResponse.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Wechat_miniapp_login_can_access_current_user()
|
||||
{
|
||||
await using var factory = new ApiTestFactory(new FakeWechatOAuthClient());
|
||||
var tenantId = Guid.NewGuid();
|
||||
await factory.SeedAsync(
|
||||
new Tenant
|
||||
{
|
||||
Id = tenantId,
|
||||
Slug = tenantId.ToString("N"),
|
||||
Name = "Wechat Tenant"
|
||||
},
|
||||
new TenantAuthProvider
|
||||
{
|
||||
TenantId = tenantId,
|
||||
Provider = "wechat-miniapp",
|
||||
Status = TenantAuthProviderStatus.Testing,
|
||||
ConfigPublic = JsonSerializer.SerializeToElement(new
|
||||
{
|
||||
appId = "wx-app-id",
|
||||
appSecret = "wx-app-secret"
|
||||
})
|
||||
});
|
||||
using var client = factory.CreateClient();
|
||||
|
||||
var loginResponse = await client.PostAsJsonAsync(
|
||||
"/api/auth/oauth/wechat-miniapp",
|
||||
new WechatLoginHttpRequest(tenantId, "wx-code"));
|
||||
var loginJson = await ReadJsonAsync(loginResponse);
|
||||
var accessToken = loginJson.RootElement
|
||||
.GetProperty("tokens")
|
||||
.GetProperty("accessToken")
|
||||
.GetString();
|
||||
|
||||
client.DefaultRequestHeaders.Authorization = new("Bearer", accessToken);
|
||||
var meResponse = await client.GetAsync("/api/me");
|
||||
using var scope = factory.Services.CreateScope();
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<TikuDbContext>();
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, loginResponse.StatusCode);
|
||||
Assert.Equal(HttpStatusCode.OK, meResponse.StatusCode);
|
||||
Assert.Contains(dbContext.UserIdentities, identity =>
|
||||
identity.Provider == "wechat-miniapp" &&
|
||||
identity.OpenId == "mini-open-id" &&
|
||||
identity.UnionId == "union-id");
|
||||
}
|
||||
|
||||
private static async Task<(Guid TenantId, Guid UserId, string Phone)> SeedLoginUserAsync(
|
||||
ApiTestFactory factory)
|
||||
{
|
||||
@@ -163,4 +211,35 @@ public sealed class AuthEndpointTests
|
||||
$$"""{"passwordHash":{{JsonSerializer.Serialize(passwordHash)}}}""");
|
||||
return document.RootElement.Clone();
|
||||
}
|
||||
|
||||
private sealed class FakeWechatOAuthClient : IWechatOAuthClient
|
||||
{
|
||||
public Task<WechatIdentity> ExchangeWebCodeAsync(
|
||||
WechatProviderOptions options,
|
||||
string code,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult(new WechatIdentity(
|
||||
"web-open-id",
|
||||
"union-id",
|
||||
"Wechat User",
|
||||
"https://example.test/avatar.png",
|
||||
null,
|
||||
"""{"openid":"web-open-id","unionid":"union-id"}"""));
|
||||
}
|
||||
|
||||
public Task<WechatIdentity> ExchangeMiniAppCodeAsync(
|
||||
WechatProviderOptions options,
|
||||
string code,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return Task.FromResult(new WechatIdentity(
|
||||
"mini-open-id",
|
||||
"union-id",
|
||||
null,
|
||||
null,
|
||||
"session-key",
|
||||
"""{"openid":"mini-open-id","unionid":"union-id","session_key":"session-key"}"""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user