diff --git a/src/App.vue b/src/App.vue index f38f8d2..77311e0 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,32 +1,52 @@ diff --git a/src/modules/auth/store/auth.ts b/src/modules/auth/store/auth.ts index 574c1f4..76e63fb 100644 --- a/src/modules/auth/store/auth.ts +++ b/src/modules/auth/store/auth.ts @@ -223,15 +223,6 @@ export const useAuthStore = defineStore('client-auth', () => { } window.addEventListener('cc-client-session-expired', clearSession) - window.addEventListener('message', (event: MessageEvent) => { - const configuredAdminOrigin = String(import.meta.env.VITE_ADMIN_WEB_ORIGIN || '').trim() - const expectedAdminOrigin = configuredAdminOrigin || window.location.origin - const message = event.data as { source?: string; type?: string; code?: string } | null - if (event.source !== window.opener || event.origin !== expectedAdminOrigin || message?.source !== 'chongchong-admin' || message.type !== 'impersonation-ticket' || !message.code) return - void acceptImpersonationCode(message.code) - .then(() => window.location.replace('/')) - .catch(() => window.location.replace('/login?impersonation=failed')) - }) window.addEventListener('storage', (event) => { if (event.key === 'cc-client-access-token' || event.key === 'cc-client-refresh-token') applyStoredSession() }) diff --git a/src/modules/auth/views/AuthViews.test.mjs b/src/modules/auth/views/AuthViews.test.mjs index 6adc8fb..e9d911c 100644 --- a/src/modules/auth/views/AuthViews.test.mjs +++ b/src/modules/auth/views/AuthViews.test.mjs @@ -13,6 +13,8 @@ const httpSource = readFileSync(new URL('../../../api/http.ts', import.meta.url) const sessionSource = readFileSync(new URL('../../../api/session.ts', import.meta.url), 'utf8') const authApiSource = readFileSync(new URL('../api/auth.ts', import.meta.url), 'utf8') const mainSource = readFileSync(new URL('../../../main.ts', import.meta.url), 'utf8') +const appSource = readFileSync(new URL('../../../App.vue', import.meta.url), 'utf8') +const routerSource = readFileSync(new URL('../../../router/index.ts', import.meta.url), 'utf8') const stylesSource = readFileSync(new URL('../../../styles/index.css', import.meta.url), 'utf8') test('uses one prototype-inspired responsive shell for login and registration', () => { @@ -105,7 +107,12 @@ test('uses real auth endpoints and single-flight refresh handling', () => { test('handles impersonation exchange as public authentication and exposes failures', () => { assert.match(httpSource, /publicAuthPaths = \[[^\]]*'\/auth\/impersonations\/exchange'/) - assert.match(authStoreSource, /\/login\?impersonation=failed/) + assert.match(appSource, /impersonationOrigin/) + assert.match(appSource, /await auth\.acceptImpersonationCode\(message\.code\)/) + assert.match(appSource, /type: 'impersonation-complete'/) + assert.match(appSource, /type: 'impersonation-failed'/) + assert.doesNotMatch(authStoreSource, /message\.type !== 'impersonation-ticket'/) + assert.match(routerSource, /isImpersonationHandoff[\s\S]*!isImpersonationHandoff/) assert.match(loginSource, /route\.query\.impersonation === 'failed'/) assert.match(loginSource, /代登录失败或授权已失效,请返回管理后台重新发起/) }) diff --git a/src/router/index.ts b/src/router/index.ts index 0280d8c..679f504 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -32,7 +32,8 @@ router.beforeEach(async (to) => { if (to.meta.requiresAuth && !auth.isAuthenticated) { return { name: 'login', query: { redirect: to.fullPath } } } - if ((to.name === 'login' || to.name === 'register') && auth.isAuthenticated) { + const isImpersonationHandoff = to.name === 'login' && typeof to.query.impersonationOrigin === 'string' + if ((to.name === 'login' || to.name === 'register') && auth.isAuthenticated && !isImpersonationHandoff) { return getFirstAccessiblePath(auth.permissionCodes) } const permission = typeof to.meta.permission === 'string' ? to.meta.permission : undefined