156 lines
4.1 KiB
JavaScript
156 lines
4.1 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import { buildApp } from '../dist/app.js';
|
|
import { signAccessToken, verifyAccessToken } from '../dist/auth/jwt.js';
|
|
import { WechatApiError, parseWechatAppSecrets } from '../dist/auth/wechat-client.js';
|
|
|
|
const secret = 'test-only-jwt-secret-with-at-least-32-characters';
|
|
const token = signAccessToken({
|
|
sub: '21',
|
|
sid: '5c4d3af8-c63c-4edb-bf95-b84127bb3f6e',
|
|
tid: '7',
|
|
aid: '9',
|
|
rv: 3
|
|
}, secret, 900, 1000);
|
|
assert.deepEqual(verifyAccessToken(token, secret, 1001), {
|
|
iss: 'qipai-api',
|
|
aud: 'qipai-miniapp',
|
|
sub: '21',
|
|
sid: '5c4d3af8-c63c-4edb-bf95-b84127bb3f6e',
|
|
tid: '7',
|
|
aid: '9',
|
|
rv: 3,
|
|
iat: 1000,
|
|
exp: 1900
|
|
});
|
|
assert.throws(() => verifyAccessToken(token, `${secret}-wrong`, 1001), /signature/);
|
|
assert.throws(() => verifyAccessToken(token, secret, 1900), /expired/);
|
|
assert.deepEqual(parseWechatAppSecrets('{"wx-app":"secret-value"}'), {
|
|
'wx-app': 'secret-value'
|
|
});
|
|
|
|
let sessionValid = true;
|
|
let revokedSessionId = null;
|
|
const auth = {
|
|
repository: {
|
|
async resolveLoginContext(appId, tenantId) {
|
|
assert.equal(appId, 'wx-test-app');
|
|
assert.equal(tenantId, '7');
|
|
return { appId, tenantId, platformAppId: '9' };
|
|
},
|
|
async loginWithWechat(input) {
|
|
assert.equal(input.openid, 'openid-test');
|
|
return {
|
|
id: input.sessionId,
|
|
tenantId: '7',
|
|
platformAppId: '9',
|
|
expiresAt: input.expiresAt,
|
|
user: {
|
|
id: '21',
|
|
tenantId: '7',
|
|
userType: 'CUSTOMER',
|
|
status: 'ACTIVE',
|
|
roleVersion: 1,
|
|
nickname: '',
|
|
avatarUrl: '',
|
|
phone: ''
|
|
}
|
|
};
|
|
},
|
|
async validateSession(sessionId, tenantId, userId) {
|
|
if (!sessionValid) return null;
|
|
return {
|
|
id: sessionId,
|
|
tenantId,
|
|
platformAppId: '9',
|
|
expiresAt: new Date(Date.now() + 60_000),
|
|
user: {
|
|
id: userId,
|
|
tenantId,
|
|
userType: 'CUSTOMER',
|
|
status: 'ACTIVE',
|
|
roleVersion: 1,
|
|
nickname: '',
|
|
avatarUrl: '',
|
|
phone: ''
|
|
}
|
|
};
|
|
},
|
|
async revokeSession(sessionId) {
|
|
revokedSessionId = sessionId;
|
|
sessionValid = false;
|
|
return true;
|
|
}
|
|
},
|
|
wechat: {
|
|
async exchange(appId, code) {
|
|
assert.equal(appId, 'wx-test-app');
|
|
assert.equal(code, 'valid-code');
|
|
return { openid: 'openid-test', unionid: 'unionid-test' };
|
|
}
|
|
},
|
|
jwtSecret: secret,
|
|
accessTokenTtlSeconds: 900,
|
|
sessionTtlSeconds: 604800
|
|
};
|
|
|
|
const app = await buildApp({ auth });
|
|
const login = await app.inject({
|
|
method: 'POST',
|
|
url: '/app-api/auth/wechat-login',
|
|
headers: {
|
|
'x-wechat-appid': 'wx-test-app',
|
|
'tenant-id': '7'
|
|
},
|
|
payload: { code: 'valid-code' }
|
|
});
|
|
assert.equal(login.statusCode, 200);
|
|
const accessToken = login.json().data.accessToken;
|
|
assert.equal(login.json().data.user.tenantId, '7');
|
|
|
|
const me = await app.inject({
|
|
method: 'GET',
|
|
url: '/app-api/auth/me',
|
|
headers: { authorization: `Bearer ${accessToken}` }
|
|
});
|
|
assert.equal(me.statusCode, 200);
|
|
assert.equal(me.json().data.user.id, '21');
|
|
|
|
const logout = await app.inject({
|
|
method: 'POST',
|
|
url: '/app-api/auth/logout',
|
|
headers: { authorization: `Bearer ${accessToken}` }
|
|
});
|
|
assert.equal(logout.statusCode, 200);
|
|
assert.ok(revokedSessionId);
|
|
|
|
const afterLogout = await app.inject({
|
|
method: 'GET',
|
|
url: '/app-api/auth/me',
|
|
headers: { authorization: `Bearer ${accessToken}` }
|
|
});
|
|
assert.equal(afterLogout.statusCode, 401);
|
|
assert.equal(afterLogout.json().code, 'AUTH_SESSION_INVALID');
|
|
await app.close();
|
|
|
|
const failedApp = await buildApp({
|
|
auth: {
|
|
...auth,
|
|
wechat: {
|
|
async exchange() {
|
|
throw new WechatApiError('invalid code');
|
|
}
|
|
}
|
|
}
|
|
});
|
|
const failedLogin = await failedApp.inject({
|
|
method: 'POST',
|
|
url: '/app-api/auth/wechat-login',
|
|
headers: { 'x-wechat-appid': 'wx-test-app', 'tenant-id': '7' },
|
|
payload: { code: 'bad-code' }
|
|
});
|
|
assert.equal(failedLogin.statusCode, 401);
|
|
assert.equal(failedLogin.json().code, 'WECHAT_LOGIN_FAILED');
|
|
await failedApp.close();
|
|
|
|
console.log('PASS: M02-B JWT, WeChat login and revocable session flow is present.');
|