mirror of
https://github.com/OpenSquawk/OpenSquawk
synced 2026-08-10 19:36:04 +08:00
fix(sso): hand the issuer /auth/callback so the code gets redeemed
The auth guard asked the issuer to come back to the page the visitor wanted. The issuer appends ?code= to whatever URL it is given, but only /auth/callback redeems a code — so it arrived on the target page, sat there unread, the guard found no session and bounced back for a fresh code. The browser ping-ponged between the two hosts until the user gave up. The guard now hands over /auth/callback and carries the wanted page in its redirect parameter, which is exactly what that page already expected. A spent code in the URL is dropped rather than carried along, so a stale link cannot turn into a redemption error one hop later. URL building moved into shared/utils/ssoHandoff.ts because the same mistake existed twice — the retry button on the callback page had it too — and because a redirect loop deserves a regression test that does not need a browser. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { defineNuxtRouteMiddleware, navigateTo, useRuntimeConfig } from '#app'
|
||||
import { useAuthStore } from '~/stores/auth'
|
||||
import { buildIssuerLoginUrl } from '~~/shared/utils/ssoHandoff'
|
||||
|
||||
export default defineNuxtRouteMiddleware(async (to) => {
|
||||
const config = useRuntimeConfig()
|
||||
@@ -31,10 +32,7 @@ export default defineNuxtRouteMiddleware(async (to) => {
|
||||
return navigateTo(`/login?redirect=${encodeURIComponent(target)}`)
|
||||
}
|
||||
|
||||
// The issuer needs an absolute URL to come back to, and it will only accept
|
||||
// one whose origin is on its allowlist.
|
||||
const redirect = new URL(target, window.location.origin).toString()
|
||||
return navigateTo(`${issuer}/login?redirect=${encodeURIComponent(redirect)}`, {
|
||||
return navigateTo(buildIssuerLoginUrl(issuer, window.location.origin, target), {
|
||||
external: true,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useRoute, useRouter, useRuntimeConfig, navigateTo } from '#app'
|
||||
import { useAuthStore } from '~/stores/auth'
|
||||
import { buildIssuerLoginUrl } from '~~/shared/utils/ssoHandoff'
|
||||
|
||||
// Consumer end of the SSO handoff: the issuer sent the browser here with a
|
||||
// one-time code. Redeeming it happens server-side (the code alone is useless
|
||||
@@ -41,11 +42,12 @@ function safeRedirectTarget(): string {
|
||||
|
||||
function retry() {
|
||||
const issuer = String(config.public.authIssuer || '').replace(/\/+$/, '')
|
||||
const target = new URL(safeRedirectTarget(), window.location.origin).toString()
|
||||
if (!issuer) {
|
||||
return router.replace(`/login?redirect=${encodeURIComponent(safeRedirectTarget())}`)
|
||||
}
|
||||
return navigateTo(`${issuer}/login?redirect=${encodeURIComponent(target)}`, { external: true })
|
||||
return navigateTo(buildIssuerLoginUrl(issuer, window.location.origin, safeRedirectTarget()), {
|
||||
external: true,
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
|
||||
Reference in New Issue
Block a user