fix(guest): move guest token from URL query to sessionStorage (#21) #28
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/guest-token-session-storage"
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?
Why
Issue #21: The guest token (the customer's only credential) was passed as
?token=...in the URL query string on the payment page. Query strings land in browser history, access logs, and can leak via Referer headers.Changes
sessionStoragebefore navigating to payment; token removed from query stringRouterLinkwith click handler that stores token insessionStoragebefore navigatingsessionStoragewith fallback toquery.tokenfor backward compatibilitysessionStoragepersists across page refreshes within the same tab. The magic-link/gast-order/:tokenremains the canonical re-entry point (accepted pattern).Test plan
npx vitest run— 318/318 passnpm run lint— cleanCloses #21
The guest token (the customer's only credential) was passed as ?token=... in the URL query string on the payment page. Query strings land in browser history, nginx/reverse-proxy access logs, and can leak via Referer headers. The magic-link landing /gast-order/:token is inherently URL-based (accepted Notion/Stripe pattern), but the payment page does not need to expose the token in the URL. Changes: - GuestCheckoutPage.vue: store token in sessionStorage before navigating to payment page; remove token from query string - GuestOrderPage.vue: replace RouterLink with click handler that stores token in sessionStorage before navigating to payment; add useRouter import - GuestPaymentRedirect.vue: read token from sessionStorage with fallback to query.token for backward compatibility with existing magic links - GuestCheckoutPage.spec.ts: assert token is NOT in query, IS in sessionStorage after navigation - GuestPaymentRedirect.spec.ts: set sessionStorage in mount helper; clear sessionStorage in beforeEach sessionStorage persists across page refreshes within the same tab, so the payment page still survives refresh. If the user opens the payment URL in a new tab (from history), sessionStorage is lost — but the canonical re-entry point is the magic link /gast-order/:token, from which they can navigate to payment again. Closes #21Verdict: Solid security improvement, correct implementation. Minor suggestions below.
Critical
None.
Warnings
None.
Suggestions
Clean up token after consumption (
GuestPaymentRedirect.vue:82) - AfterpayGuestOrder(token)succeeds,sessionStorage.removeItem('guestToken')before navigating away. While sessionStorage is tab-scoped and cleared on tab close, removing the credential immediately after use reduces the attack window if the user lingers on subsequent pages in the same tab.No test for the backward-compatibility fallback (
GuestPaymentRedirect.spec.ts) - Theroute.query.tokenfallback path is untested. All existing tests either pass a valid token (stored in sessionStorage) or pass an empty string (both sessionStorage AND query empty). Consider adding a test wheresessionStorageis empty butroute.query.tokenis present, to verify the fallback actually works for users with old bookmarked payment URLs.<a href="#">semantics (GuestOrderPage.vue:94) - The click handler replaces<RouterLink>with<a href="#" @click.prevent>, which gives screen readers a link to nowhere (#). A<button>styled as a link (or<RouterLink>with abeforeRouteLeaveequivalent) would be more semantically correct. The existing.guest-order__hint aCSS selector still applies, so visual parity is maintained either way.Looks Good
Refererheaders. This is the right fix for issue #21.GuestPaymentRedirect.vue:15-16) - thesessionStorage.getItem('guestToken') || route.query.tokenchain gracefully handles users who may have bookmarked the old payment URL format. Good transitional measure.router.currentRoute.value.query.tokenand present insessionStorage.sessionStorage.clear()inbeforeEachprevents cross-test pollution.GuestCheckoutPage.vue(new order flow) andGuestOrderPage.vue(magic-link re-entry flow). No path leaves the payment page without setting the token.💡 Suggestion: The backward-compat fallback (
sessionStorage.getItem('guestToken') || route.query.token) is untested. Consider adding a test case wheremountPageis called with the token in the route query but sessionStorage cleared, to verify the fallback works for old bookmarked URLs.💡 Suggestion:
<a href="#" @click.prevent>degrades screen-reader semantics (a link to#/ nowhere). A<button>styled to match, or keeping<RouterLink>with a custom navigation guard, would preserve accessibility without changing the visual result.💡 Suggestion: Consider
sessionStorage.removeItem('guestToken')right afterpayGuestOrder(token)resolves, before therouter.push. The token has been consumed at this point and lingering in sessionStorage only extends the credential's lifetime unnecessarily.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.Merge
Merge the changes and update on Forgejo.Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.