@autional/shared 0.1.0-rc.35 → 0.1.0-rc.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,6 +4,7 @@
4
4
  // ① 缺省回程 = 捕获的当前 URL(EntryRouter 据此解析租户 → /<slug>/login)
5
5
  // ② returnUrl 覆盖:裸 /logout 无 URL 租户段时由调用方补回上下文
6
6
  // ③ 顺序契约:先终结会话(AuthService.logout)再导航
7
+ // TASK-LS-08:client_id 可得 → 同源标准端点(post_logout_redirect_uri=门户根;state 携回程)
7
8
  // ============================================================
8
9
  import { describe, it, expect, vi, beforeEach } from 'vitest';
9
10
  import { renderHook } from '@testing-library/react';
@@ -23,6 +24,7 @@ import { useLogout } from '../useLogout';
23
24
 
24
25
  beforeEach(() => {
25
26
  vi.clearAllMocks();
27
+ localStorage.clear();
26
28
  window.history.replaceState(null, '', '/demo/dashboard');
27
29
  });
28
30
 
@@ -58,4 +60,27 @@ describe('useLogout 落点口径(AUTH-40)', () => {
58
60
  '&logout=1',
59
61
  );
60
62
  });
63
+
64
+ // TASK-LS-08:client_id 可得(会话持久化)→ 同源标准端点(不再 legacy logout=1);
65
+ // 顺序契约不变:先终结会话(AuthService.logout)再导航。
66
+ it('client_id 可得:同源标准端点 + 门户根回跳 + state 携当前 URL,顺序仍先清再跳', async () => {
67
+ localStorage.setItem('oauth_client_id', 'cid-logout');
68
+ const { result } = renderHook(() => useLogout());
69
+ result.current();
70
+
71
+ await vi.waitFor(() => expect(mockTraceRedirect).toHaveBeenCalledTimes(1));
72
+ expect(mockLogout).toHaveBeenCalledTimes(1);
73
+
74
+ const [url, opts] = mockTraceRedirect.mock.calls[0];
75
+ expect(url).toBe(
76
+ 'http://localhost:3000/oauth/api/v1/oauth/logout?client_id=cid-logout&post_logout_redirect_uri=' +
77
+ encodeURIComponent('http://localhost:3000/') +
78
+ '&state=' +
79
+ encodeURIComponent('http://localhost:3000/demo/dashboard'),
80
+ );
81
+ expect(opts).toMatchObject({ reason: 'logout', assign: true });
82
+ expect(mockLogout.mock.invocationCallOrder[0]).toBeLessThan(
83
+ mockTraceRedirect.mock.invocationCallOrder[0] as number,
84
+ );
85
+ });
61
86
  });
@@ -31,9 +31,10 @@ export function useLogout(opts?: { returnUrl?: string }) {
31
31
  const { AuthService } = await import('../auth/service');
32
32
  await AuthService.logout();
33
33
 
34
- // buildLogoutUrl = auth 域裸根 + `logout=1` 登出意图标记:入口路由据此先
35
- // 终结会话再定落点(能解析出租户 → 裸 /<slug>/login;否则 brand 裸根),
36
- // 而不是把带会话的回程当普通深链直送登录页(会静默重登)。
34
+ // buildLogoutUrl(LS-08):client_id 可得 → 同源标准端点整页导航(服务端无会话
35
+ // 浏览器支 302 确认页承接,确认后回跳门户根);不可得 → legacy auth 裸根 + `logout=1`
36
+ // 意图标记(入口路由据此先终结会话再定落点:能解析出租户 → 裸 /<slug>/login;
37
+ // 否则 brand 裸根)。两者都不会把带会话的回程当普通深链直送登录页(会静默重登)。
37
38
  // funnel:用户主动行为,静默直跳(assign 保持原 href 历史语义);仅记 trace。
38
39
  traceRedirect(buildLogoutUrl(currentUrl), { reason: 'logout', assign: true });
39
40
  };