From da2bf87ad3b98c24e29636c45aeee5e1b1d77b4c Mon Sep 17 00:00:00 2001 From: svemagie <869694+svemagie@users.noreply.github.com> Date: Sun, 8 Mar 2026 04:49:56 +0100 Subject: [PATCH] Normalize legacy /admin redirect query targets in auth callback --- README.md | 1 + scripts/patch-indieauth-devmode-guard.mjs | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/README.md b/README.md index c12006f5..c4d35b40 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ - The IndieKit admin uses root auth/session paths (for example: `/session/login`, `/auth`, `/auth/new-password`). - Legacy `/admin` request paths are normalized to root login redirects (for example `/admin/posts` -> `/session/login?redirect=/posts`) to avoid post-login dead-end targets. - Legacy auth/session aliases are redirected directly (for example `/admin/auth/new-password` -> `/auth/new-password`, `/admin/session/login` -> `/session/login`). +- Legacy redirect query targets are normalized as well (for example `/session/login?redirect=/admin/posts` becomes post-login redirect to `/posts`). - Login page now auto-continues to the password consent screen by default. Add `?noautocontinue=1` to `/session/login` if you want to keep the manual button step. - Login uses `PASSWORD_SECRET` (bcrypt hash), not `INDIEKIT_PASSWORD`. - If no `PASSWORD_SECRET` exists yet, open `/auth/new-password` once to generate it. diff --git a/scripts/patch-indieauth-devmode-guard.mjs b/scripts/patch-indieauth-devmode-guard.mjs index a7a2572a..10910938 100644 --- a/scripts/patch-indieauth-devmode-guard.mjs +++ b/scripts/patch-indieauth-devmode-guard.mjs @@ -14,6 +14,20 @@ const newDevModeCode = `if (devMode && process.env.INDIEKIT_ALLOW_DEV_AUTH === " request.session.scope = "create update delete media"; } else if (!process.env.PASSWORD_SECRET) {`; +const newCallbackRedirectCode = ` const { redirect } = request.query; + const requestedRedirect = + typeof redirect === "string" ? redirect : ""; + const normalizedRedirect = + requestedRedirect === "/admin" + ? "/" + : requestedRedirect.replace(/^\\/admin(?=\\/)/, ""); + this.redirectUri = normalizedRedirect + ? \`\${callbackUrl}?redirect=\${normalizedRedirect}\` + : \`\${callbackUrl}\`;`; + +const oldCallbackRedirectRegex = + /const \{ redirect \} = request\.query;\n\s+this\.redirectUri = redirect\n\s+\? `\$\{callbackUrl\}\?redirect=\$\{redirect\}`\n\s+: `\$\{callbackUrl\}`;/m; + const newLoginRedirectCode = ` if (request.method === "GET") { const directAlias = request.originalUrl.replace( /^\\/admin\\/(auth|session)(?=\\/|$)/, @@ -63,6 +77,13 @@ for (const filePath of candidates) { updated = updated.replace(oldDevModeCode, newDevModeCode); } + if ( + !updated.includes("const normalizedRedirect =") && + oldCallbackRedirectRegex.test(updated) + ) { + updated = updated.replace(oldCallbackRedirectRegex, newCallbackRedirectCode); + } + if (!updated.includes("const directAlias = request.originalUrl.replace(")) { for (const regex of oldLoginRedirectRegexes) { if (regex.test(updated)) {