forked from wangziqi/gongxue-base
feat: add china auth providers
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { spawn } from 'node:child_process';
|
||||
import http from 'node:http';
|
||||
import net from 'node:net';
|
||||
|
||||
const DEFAULT_DATABASE_URL = 'postgresql://postgres:postgres@127.0.0.1:54322/postgres';
|
||||
@@ -36,6 +37,7 @@ let serverProcess = null;
|
||||
let serverLogs = '';
|
||||
let legacyDisabledServer = null;
|
||||
let legacyDisabledServerLogs = '';
|
||||
let fakeWechatServer = null;
|
||||
|
||||
function buildUrl(path, query = {}) {
|
||||
return buildUrlAt(apiBase, path, query);
|
||||
@@ -200,6 +202,56 @@ async function startLegacyDisabledServer() {
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
async function startFakeWechatServer() {
|
||||
const port = await getFreePort();
|
||||
const baseUrl = `http://127.0.0.1:${port}`;
|
||||
const requests = [];
|
||||
fakeWechatServer = http.createServer((req, res) => {
|
||||
const url = new URL(req.url || '/', baseUrl);
|
||||
requests.push({
|
||||
method: req.method,
|
||||
pathname: url.pathname,
|
||||
query: Object.fromEntries(url.searchParams.entries()),
|
||||
});
|
||||
|
||||
if (url.pathname !== '/sns/jscode2session') {
|
||||
res.writeHead(404, { 'content-type': 'application/json' });
|
||||
res.end(JSON.stringify({ errcode: 404, errmsg: 'not found' }));
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
url.searchParams.get('appid') !== 'wx-smoke-appid' ||
|
||||
url.searchParams.get('secret') !== 'wechat-app-secret-smoke' ||
|
||||
url.searchParams.get('grant_type') !== 'authorization_code'
|
||||
) {
|
||||
res.writeHead(200, { 'content-type': 'application/json' });
|
||||
res.end(JSON.stringify({ errcode: 40013, errmsg: 'invalid appid or secret' }));
|
||||
return;
|
||||
}
|
||||
|
||||
const jsCode = url.searchParams.get('js_code') || 'unknown';
|
||||
res.writeHead(200, { 'content-type': 'application/json' });
|
||||
res.end(
|
||||
JSON.stringify({
|
||||
openid: `openid-${jsCode}`,
|
||||
session_key: `session-key-${jsCode}`,
|
||||
unionid: 'unionid-smoke-user',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
fakeWechatServer.once('error', reject);
|
||||
fakeWechatServer.listen(port, '127.0.0.1', resolve);
|
||||
});
|
||||
|
||||
return {
|
||||
endpoint: `${baseUrl}/sns/jscode2session`,
|
||||
requests,
|
||||
};
|
||||
}
|
||||
|
||||
async function waitForProcessExit(child, timeoutMs = 5000) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const timer = setTimeout(() => {
|
||||
@@ -366,6 +418,10 @@ function stopServer() {
|
||||
if (legacyDisabledServer && !legacyDisabledServer.killed) {
|
||||
legacyDisabledServer.kill();
|
||||
}
|
||||
if (fakeWechatServer) {
|
||||
fakeWechatServer.close();
|
||||
fakeWechatServer = null;
|
||||
}
|
||||
}
|
||||
|
||||
async function testCatalogAndLearning() {
|
||||
@@ -1426,6 +1482,7 @@ async function testTenantContentAssetsAndImports() {
|
||||
}
|
||||
|
||||
async function testTenantAdminOps() {
|
||||
const fakeWechat = await startFakeWechatServer();
|
||||
const denied = await request('/api/tenant-admin/branding', {
|
||||
method: 'PUT',
|
||||
body: { brandName: '学生不能改品牌' },
|
||||
@@ -1477,6 +1534,7 @@ async function testTenantAdminOps() {
|
||||
configPublic: {
|
||||
appId: 'wx-smoke-appid',
|
||||
envVersion: 'trial',
|
||||
endpoint: fakeWechat.endpoint,
|
||||
},
|
||||
secret: {
|
||||
secretValue: 'wechat-app-secret-smoke',
|
||||
@@ -1488,6 +1546,32 @@ async function testTenantAdminOps() {
|
||||
assert.equal(authProvider.item?.secret?.hasSecretValue, true, 'auth provider should report masked secret status');
|
||||
assert.ok(!JSON.stringify(authProvider).includes('wechat-app-secret-smoke'), 'auth provider response must not include secret plaintext');
|
||||
|
||||
const miniappLogin = await request('/api/auth/oauth/wechat-miniapp', {
|
||||
userId: false,
|
||||
method: 'POST',
|
||||
body: {
|
||||
code: 'integration-code-001',
|
||||
profile: {
|
||||
nickName: '微信烟测学生',
|
||||
avatarUrl: 'https://example.test/avatar.png',
|
||||
},
|
||||
},
|
||||
});
|
||||
assert.equal(miniappLogin.provider, 'wechat-miniapp', 'wechat miniapp login should return provider');
|
||||
assert.ok(miniappLogin.user?.id, 'wechat miniapp login should create or resolve user');
|
||||
assert.ok(miniappLogin.session?.token?.startsWith('tk_'), 'wechat miniapp login should issue API session token');
|
||||
assert.equal(miniappLogin.identity?.openId, 'openid-integration-code-001', 'wechat miniapp login should expose openId');
|
||||
assert.equal(miniappLogin.identity?.unionId, 'unionid-smoke-user', 'wechat miniapp login should expose unionId');
|
||||
assert.equal(fakeWechat.requests.at(-1)?.query?.js_code, 'integration-code-001', 'wechat code should be exchanged server-side');
|
||||
assert.ok(!JSON.stringify(miniappLogin).includes('session-key-integration-code-001'), 'login response must not leak WeChat session_key');
|
||||
assert.ok(!JSON.stringify(miniappLogin).includes('wechat-app-secret-smoke'), 'login response must not leak app secret');
|
||||
|
||||
const miniappMe = await request('/api/auth/me', {
|
||||
userId: false,
|
||||
headers: { authorization: `Bearer ${miniappLogin.session.token}` },
|
||||
});
|
||||
assert.equal(miniappMe.user?.id, miniappLogin.user.id, 'wechat session should work with auth/me');
|
||||
|
||||
const paymentAccount = await request('/api/tenant-admin/payment-accounts', {
|
||||
userId: TENANT_ADMIN_USER_ID,
|
||||
method: 'PUT',
|
||||
|
||||
Reference in New Issue
Block a user