From 106cdd09e1557222303f3ed1376e1dadf0638621 Mon Sep 17 00:00:00 2001 From: Fred <323546+fguillot@users.noreply.github.com> Date: Tue, 11 Aug 2026 12:05:27 +0200 Subject: [PATCH] fix(webauthn): preserve redirect_url after passkey login The passkey login flow reloaded the login page once the assertion was validated. Since the user was authenticated at that point, the login handler redirected to the default home page and the redirect_url query parameter was dropped. The client now forwards redirect_url to the login finish endpoint, which validates it as a relative path and returns the destination, falling back to the user's default home page. Fixes: #4494 --- internal/ui/static/js/webauthn_handler.js | 19 +++++++++++++++---- internal/ui/webauthn.go | 7 ++++++- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/internal/ui/static/js/webauthn_handler.js b/internal/ui/static/js/webauthn_handler.js index 3ea6934964a..21c2af6653a 100644 --- a/internal/ui/static/js/webauthn_handler.js +++ b/internal/ui/static/js/webauthn_handler.js @@ -45,8 +45,13 @@ class WebAuthnHandler { .replace(/=+$/g, ""); } - async post(urlKey, data) { - const url = document.body.dataset[urlKey]; + async post(urlKey, data, queryParams) { + let url = document.body.dataset[urlKey]; + if (queryParams) { + const parsedURL = new URL(url, window.location.origin); + parsedURL.search = queryParams.toString(); + url = parsedURL.toString(); + } return sendPOSTRequest(url, data); } @@ -172,6 +177,11 @@ class WebAuthnHandler { let loginFinishResponse; try { + const queryParams = new URLSearchParams(); + const redirectURL = new URLSearchParams(window.location.search).get("redirect_url"); + if (redirectURL) { + queryParams.set("redirect_url", redirectURL); + } loginFinishResponse = await this.post("webauthnLoginFinishUrl", { id: assertion.id, rawId: this.encodeBuffer(assertion.rawId), @@ -182,7 +192,7 @@ class WebAuthnHandler { signature: this.encodeBuffer(assertion.response.signature), userHandle: this.encodeBuffer(assertion.response.userHandle), }, - }); + }, queryParams); } catch (err) { WebAuthnHandler.showErrorMessage(err); return; @@ -192,6 +202,7 @@ class WebAuthnHandler { throw new Error(`Login failed with HTTP status code ${loginFinishResponse.status}`); } - window.location.reload(); + const jsonData = await loginFinishResponse.json(); + window.location.href = jsonData.redirect; } } diff --git a/internal/ui/webauthn.go b/internal/ui/webauthn.go index 15baaaed2f2..fed15f64b3a 100644 --- a/internal/ui/webauthn.go +++ b/internal/ui/webauthn.go @@ -22,6 +22,7 @@ import ( "miniflux.app/v2/internal/model" "miniflux.app/v2/internal/ui/form" "miniflux.app/v2/internal/ui/view" + "miniflux.app/v2/internal/urllib" ) type WebAuthnUser struct { @@ -260,7 +261,11 @@ func (h *handler) finishLogin(w http.ResponseWriter, r *http.Request) { return } - response.NoContent(w, r) + redirectURL := request.QueryStringParam(r, "redirect_url", "") + if !urllib.IsRelativePath(redirectURL) { + redirectURL = h.basePath + "/" + user.DefaultHomePage + } + response.JSON(w, r, map[string]string{"redirect": redirectURL}) } func (h *handler) renameCredential(w http.ResponseWriter, r *http.Request) {