diff --git a/src/pages/AuthCallbackPage.vue b/src/pages/AuthCallbackPage.vue index 84cc0ac..7d015a3 100644 --- a/src/pages/AuthCallbackPage.vue +++ b/src/pages/AuthCallbackPage.vue @@ -32,8 +32,10 @@ const error = ref('') onMounted(async () => { try { const code = route.query.code + const state = route.query.state const oauthError = route.query.error const oauthErrorDesc = route.query.error_description + const expectedState = sessionStorage.getItem('oidc_expected_state') if (oauthError) { const reason = typeof oauthErrorDesc === 'string' && oauthErrorDesc @@ -50,6 +52,13 @@ onMounted(async () => { return } + if (!state || !expectedState || state !== expectedState) { + sessionStorage.removeItem('oidc_expected_state') + error.value = '登入驗證失敗,請重新登入' + setTimeout(() => router.push('/login'), 3000) + return + } + const redirectUri = `${window.location.origin}/auth/callback` const res = await exchangeOidcCode(code, redirectUri) const { access_token } = res.data @@ -65,10 +74,12 @@ onMounted(async () => { await authStore.fetchMe() // 導向原頁面或預設的 /me + sessionStorage.removeItem('oidc_expected_state') const redirect = sessionStorage.getItem('post_login_redirect') || '/me' sessionStorage.removeItem('post_login_redirect') router.push(redirect) } catch (err) { + sessionStorage.removeItem('oidc_expected_state') const detail = err.response?.data?.detail if (detail === 'invalid_authorization_code') { error.value = '授權代碼無效,請重新登入' diff --git a/src/pages/LoginPage.vue b/src/pages/LoginPage.vue index 4087fc4..6a792f9 100644 --- a/src/pages/LoginPage.vue +++ b/src/pages/LoginPage.vue @@ -51,7 +51,13 @@ async function handleOidcLogin() { sessionStorage.setItem('post_login_redirect', typeof redirect === 'string' ? redirect : '/me') const callbackUrl = `${window.location.origin}/auth/callback` const res = await getOidcAuthorizeUrl(callbackUrl) - window.location.href = res.data.authorize_url + const authorizeUrl = res.data.authorize_url + const parsed = new URL(authorizeUrl) + const state = parsed.searchParams.get('state') + if (state) { + sessionStorage.setItem('oidc_expected_state', state) + } + window.location.href = authorizeUrl } catch (err) { const detail = err.response?.data?.detail if (detail === 'authentik_login_not_configured') {