Deep links (invites, shared motifs) are lost through the login redirect #91
Labels
No labels
bug
duplicate
enhancement
future
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
rbrooks/TeaLeaves#91
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Severity: High · Confidence: High · Effort: M · Category: ui
Evidence
app/src/App.tsx:91-93- unauthenticated → renders<LoginPage/>with no memory oflocation.pathname.app/src/pages/LoginPage.tsx- hardcodeswindow.location.href = '/auth/login'.api/src/routes/auth.ts:131- callback hardcodesres.redirect('/')- no returnTo/state round-trip.Problem
A logged-out user who clicks an
/invites/:tokenlink is bounced to login, and after authenticating lands on/entries- the invite token is gone, with no message. Same for any shared deep link (e.g. a private/motifs/:id).Impact
The editor-invite flow silently fails for its most common audience: a first-time, not-yet-logged-in collaborator.
Fix
Persist the target path (OIDC
stateor a signed short-lived cookie set before redirect) and have the callback redirect there. Validate the return path is same-origin/relative to avoid an open redirect.Acceptance criteria
Filed from the 2026-07-15 codebase audit. Full report:
docs/.internal/report-2026-07-15.md(gitignored).Fixed in
4f8e0fe(v7.1.0 wave 4).The target path is now round-tripped through the OIDC flow in the session:
LoginPagesends the path the user actually hit:/auth/login?returnTo=${encodeURIComponent(pathname + search + hash)}(omitted for/).GET /auth/loginvalidates it and stores it in the session afterregenerate()(which wipes the session — ordering matters).GET /auth/callbackreads it before its ownregenerate(), re-validates, andres.redirect(returnTo ?? '/').Open-redirect guard —
safeReturnTo()accepts only same-origin relative paths, and is applied on the way in and on the way out (never trust a stored value blindly). Rejects absolute URLs,javascript:/data:, protocol-relative//evil.com,/\evil.com(which some browsers normalise to a host), non-strings (a duplicated query param arrives as an array), and empty values.Acceptance criteria:
api/src/test/unit/safeReturnTo.test.tscovers the accept/reject matrix.Verified live on the dev server:
/auth/login?returnTo=//evil.com→Location: https://auth.rhoving.com/application/o/authorize/…(hostile value dropped, not a redirect to evil.com)/auth/login?returnTo=%2Finvites%2Fabc→ same normal Authentik redirect, with the path preserved in-session for the callback.CI green (238/238 api, 63/63 app).