fix(auth): correct redirect-hash encoding so oauth.authorize reaches /login#redirect
The previous attempt updated the oauth-authorize e2e assertion to expect /login#redirect= but never ran the test, and the implementation did not actually produce it. Root cause: the guard wrote the redirect hash as encodeURIComponent(to.fullPath), but vue-router runs its own encodeURI over the hash field, turning the embedded %xx into %25xx (double-encoding). On the follow-up navigation to /login the address-bar hash (to.fullPath) was then double-encoded while to.hash was single-decoded, so the pre-existing `!to.fullPath.endsWith(to.hash)` fallback never matched and kept re-appending the hash — an infinite redirect loop that left the URL stuck on /oauth/authorize. Fix: - Pass to.fullPath to the hash raw and read it back without an extra decodeURIComponent. vue-router's encode/decode round-trips it cleanly, so the address bar carries a single-encoded, copyable destination and the guard recovers the exact original fullPath for saveLastVisited. - Skip the endsWith hash re-attach when the hash is the redirect hash, which vue-router keeps url-encoded in fullPath, breaking the loop. Verified locally: tests/e2e/user/oauth-authorize.spec.ts now passes (redirect to /login#redirect=, decoded hash contains /oauth/authorize, and the post-login PKCE code exchange completes); logout.spec.ts still passes. openid-login.spec.ts requires the Dex service container that only CI provides, so it cannot run locally; it does not touch the changed paths.
This commit is contained in:
parent
a9355fc247
commit
6dc7f8ba1e
|
|
@ -502,18 +502,20 @@ export async function getAuthForRoute(to: RouteLocation, authStore) {
|
|||
|
||||
// Keep the destination in the address bar (not just per-browser localStorage) so a native
|
||||
// client's /oauth/authorize URL stays copyable into another browser. Hash, not query, so the
|
||||
// embedded OAuth params never reach access logs (#2654).
|
||||
// embedded OAuth params never reach access logs (#2654). Pass fullPath raw: vue-router encodes
|
||||
// the hash itself, so an extra encodeURIComponent here would be double-encoded in the URL.
|
||||
if (to.name === 'oauth.authorize') {
|
||||
return {
|
||||
name: 'user.login',
|
||||
hash: REDIRECT_HASH_PREFIX + encodeURIComponent(to.fullPath),
|
||||
hash: REDIRECT_HASH_PREFIX + to.fullPath,
|
||||
}
|
||||
}
|
||||
|
||||
// Fold the hash destination into localStorage: it's the only bridge that survives the
|
||||
// external OIDC round-trip out of the SPA, so redirectIfSaved() works after any auth method.
|
||||
// vue-router already decoded to.hash once, so it equals the fullPath we wrote above as-is.
|
||||
if (to.hash.startsWith(REDIRECT_HASH_PREFIX)) {
|
||||
const destination = decodeURIComponent(to.hash.slice(REDIRECT_HASH_PREFIX.length))
|
||||
const destination = to.hash.slice(REDIRECT_HASH_PREFIX.length)
|
||||
const resolved = router.resolve(destination)
|
||||
saveLastVisited(resolved.name as string, resolved.params, resolved.query)
|
||||
}
|
||||
|
|
@ -589,7 +591,14 @@ router.beforeEach(async (to, from) => {
|
|||
...newRoute,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// to.fullPath keeps the redirect hash url-encoded while to.hash is decoded, so the endsWith
|
||||
// check below never matches and would re-append the hash forever. The hash is already on the
|
||||
// URL here, so skip the re-attach (#2654).
|
||||
if (to.hash.startsWith(REDIRECT_HASH_PREFIX)) {
|
||||
return
|
||||
}
|
||||
|
||||
if(!to.fullPath.endsWith(to.hash)) {
|
||||
return to.fullPath + to.hash
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue