| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- import { test } from 'node:test';
- import assert from 'node:assert/strict';
- import { createPlatformAuthClient, PlatformSession, AUTH_STORAGE_KEY } from '../src/services/platform-auth.js';
- const config = { parseUrl: 'https://platform.example/parse', appId: 'platform-app', companyId: 'company-one', companyName: '测试企业' };
- const profile = { objectId: 'user-one', username: 'member', nickname: '测试成员', company: { __type: 'Pointer', className: 'Company', objectId: 'company-one' }, hash: 'private-field', ACL: {}, sessionToken: 'fixture-session-only' };
- const publicUser = { id: 'user-one', username: 'member', displayName: '测试成员', companyId: 'company-one', companyName: '测试企业' };
- const reply = (data, status = 200) => ({ ok: status >= 200 && status < 300, status, json: async () => data });
- const memory = () => {
- const map = new Map();
- return { map, getItem: key => map.get(key) || null, setItem: (key, value) => map.set(key, value), removeItem: key => map.delete(key) };
- };
- const deferred = () => { let resolve, reject;const promise = new Promise((a, b) => { resolve = a;reject = b; });return { promise, resolve, reject }; };
- test('平台登录用POST密码,随后核会话和企业;公开状态仅保留必要身份字段', async () => {
- const calls = [];
- const client = createPlatformAuthClient({ ...config, fetchImpl: async (url, options) => {
- calls.push({ url, options });
- return reply(url.endsWith('/login') ? { ...profile, company: null } : url.endsWith('/logout') ? {} : profile);
- } });
- const result = await client.login(' member ', 'fixture-password-only');
- assert.deepEqual(result.user, publicUser);
- assert.equal(result.token, profile.sessionToken);
- assert.equal(calls[0].options.method, 'POST');
- assert.deepEqual(JSON.parse(calls[0].options.body), { username: 'member', password: 'fixture-password-only' });
- assert.equal(calls[1].options.headers['X-Parse-Session-Token'], profile.sessionToken);
- assert.equal(calls[1].url, config.parseUrl + '/users/me');
- await client.logout(result.token);
- assert.equal(calls[2].options.method, 'POST');
- assert.ok(calls.every(c => !c.url.includes('fixture-') && c.options.credentials === 'omit' && c.options.redirect === 'error'));
- assert.ok(calls.every(c => !Object.keys(c.options.headers).some(k => /master|service/i.test(k))));
- });
- test('账号密码错误不重试,不把平台原始错误内容交给界面', async () => {
- let calls = 0;
- const client = createPlatformAuthClient({ ...config, fetchImpl: async () => { calls++;return reply({ code: 101, error: 'sensitive internal diagnostic' }, 404); } });
- await assert.rejects(client.login('member', 'fixture-wrong-only'), error => error.code === 'INVALID_CREDENTIALS' && !error.message.includes('sensitive'));
- assert.equal(calls, 1);
- });
- test('登录返回企业正确也必须以users/me为准,跨企业拒绝并关闭新会话', async () => {
- const calls = [];
- const client = createPlatformAuthClient({ ...config, fetchImpl: async (url) => {
- calls.push(url);
- return reply(url.endsWith('/login') ? profile : url.endsWith('/logout') ? {} : { ...profile, company: { ...profile.company, objectId: 'another-company' } });
- } });
- await assert.rejects(client.login('member', 'fixture-password-only'), { code: 'COMPANY_FORBIDDEN' });
- assert.deepEqual(calls.map(url => url.slice(config.parseUrl.length)), ['/login', '/users/me', '/logout']);
- });
- test('网络、限流、非JSON响应提示可恢复;不会泄露原始响应', async () => {
- for (const [fetchImpl, code] of [
- [async () => { throw Error('raw connection secret'); }, 'AUTH_UNAVAILABLE'],
- [async () => reply({ error: 'private limit details' }, 429), 'AUTH_RATE_LIMITED'],
- [async () => ({ ok: false, status: 502, json: async () => { throw Error('raw proxy body'); } }), 'AUTH_UNAVAILABLE']
- ]) {
- const client = createPlatformAuthClient({ ...config, fetchImpl });
- await assert.rejects(client.login('member', 'fixture-password-only'), error => error.code === code && !/private|raw|secret/.test(error.message));
- }
- });
- test('刷新从服务端重新核验,不信任浏览器伪造的用户或姓名', async () => {
- const storage = memory();
- storage.setItem(AUTH_STORAGE_KEY, JSON.stringify({ token: 'fixture-session-only', user: { displayName: '伪造管理员' } }));
- let calls = 0;
- const session = new PlatformSession({ storage, client: { current: async token => { calls++;assert.equal(token, 'fixture-session-only');return publicUser; } } });
- assert.equal(session.state.user, null);
- await session.restore();
- assert.equal(calls, 1);
- assert.deepEqual(session.state.user, publicUser);
- assert.equal(session.state.status, 'authenticated');
- });
- test('登录只持久化会话,不持久化密码/平台档案;退出先清本机再关闭服务端会话', async () => {
- const storage = memory(), logout = deferred();
- const session = new PlatformSession({ storage, client: { login: async () => ({ token: 'fixture-session-only', user: publicUser }), logout: () => logout.promise } });
- await session.login('member', 'fixture-password-only');
- assert.deepEqual(JSON.parse(storage.getItem(AUTH_STORAGE_KEY)), { token: 'fixture-session-only' });
- assert.ok(!JSON.stringify(session.state).includes('fixture-'));
- const pending = session.logout();
- assert.equal(session.state.user, null);
- assert.equal(storage.getItem(AUTH_STORAGE_KEY), null);
- logout.resolve({});
- assert.deepEqual(await pending, { remoteClosed: true });
- });
- test('失效会话清除身份,临时网络故障保留可重试凭据但不冒称已登录', async () => {
- for (const code of ['AUTH_REQUIRED', 'COMPANY_FORBIDDEN', 'AUTH_UNAVAILABLE']) {
- const storage = memory();storage.setItem(AUTH_STORAGE_KEY, JSON.stringify({ token: 'fixture-session-only' }));
- let online = false;
- const session = new PlatformSession({ storage, client: { current: async () => {
- if (online) return publicUser;
- throw Object.assign(Error('会话检查失败'), { code });
- } } });
- await session.restore();
- assert.equal(session.state.user, null);
- assert.equal(session.state.status, code === 'AUTH_UNAVAILABLE' ? 'unavailable' : 'guest');
- assert.equal(!!storage.getItem(AUTH_STORAGE_KEY), code === 'AUTH_UNAVAILABLE');
- if (code === 'AUTH_UNAVAILABLE') { online = true;await session.restore();assert.deepEqual(session.state.user, publicUser); }
- }
- });
- test('浏览器禁用存储仍可登录,公开状态不暴露会话', async () => {
- const storage = { getItem() { throw Error('denied'); }, setItem() { throw Error('denied'); }, removeItem() { throw Error('denied'); } };
- const session = new PlatformSession({ storage, client: { login: async () => ({ token: 'fixture-session-only', user: publicUser }), logout: async () => ({}) } });
- await session.restore();
- await session.login('member', 'fixture-password-only');
- assert.equal(session.state.status, 'authenticated');
- assert.equal(session.state.retained, false);
- assert.ok(!JSON.stringify(session.state).includes('fixture-session-only'));
- await session.logout();
- assert.equal(session.state.user, null);
- });
- test('退出后才返回的登录请求不恢复身份,并撤销刚创建的会话', async () => {
- const response = deferred(), closed = [];
- const session = new PlatformSession({ storage: memory(), client: { login: () => response.promise, logout: async token => closed.push(token) } });
- const pending = session.login('member', 'fixture-password-only');
- await session.logout();
- response.resolve({ token: 'fixture-late-session', user: publicUser });
- assert.equal(await pending, null);
- assert.equal(session.state.status, 'guest');
- assert.deepEqual(closed, ['fixture-late-session']);
- });
- test('并发登录仅保留最后一次明确提交的账号;迟到响应不覆盖', async () => {
- const first = deferred(), second = deferred(), closed = [], storage = memory();
- let count = 0;
- const session = new PlatformSession({ storage, client: { login: () => (++count === 1 ? first : second).promise, logout: async token => closed.push(token) } });
- const a = session.login('member', 'fixture-one'), b = session.login('member-two', 'fixture-two');
- second.resolve({ token: 'fixture-second-session', user: { ...publicUser, id: 'user-two', username: 'member-two' } });
- await b;
- first.resolve({ token: 'fixture-first-session', user: publicUser });
- await a;
- assert.equal(session.state.user.id, 'user-two');
- assert.equal(JSON.parse(storage.getItem(AUTH_STORAGE_KEY)).token, 'fixture-second-session');
- assert.deepEqual(closed, ['fixture-first-session']);
- });
- test('退出中断刷新核验;恢复请求迟到也不能再次变为登录', async () => {
- const storage = memory(), response = deferred();storage.setItem(AUTH_STORAGE_KEY, JSON.stringify({ token: 'fixture-session-only' }));
- const session = new PlatformSession({ storage, client: { current: () => response.promise, logout: async () => ({}) } });
- const pending = session.restore();
- await session.logout();response.resolve(publicUser);await pending;
- assert.equal(session.state.user, null);
- assert.equal(storage.getItem(AUTH_STORAGE_KEY), null);
- });
|