Keep four redirect decisions separate
- Client request
- The supported SDK sends
redirect_toon the actual signup URL. An options object or JSON body alone does not prove Auth received it. - Auth selection
- Current Auth prefers an accepted explicit
redirect_to, then a validReferer, then configuredSiteURL. A cross-origin Referer can contain only the origin and silently lose/repository/. - Email template
{{ .ConfirmationURL }}includes the selected redirect. A template based on{{ .SiteURL }}or a hand-built link can discard it.- Final navigation
- The verify endpoint, any link rewriter, application callback, router base, and GitHub Pages hosting behavior can still change the destination after the email is rendered.
Browser-local redirect planner
Locate the first lost path
No email address, project reference, confirmation link, token, OTP, API key, cookie, authorization header, or full network capture is requested or transmitted.
- First boundary
- Decision
- Next action
- Exit condition
- Safety rule
What the 2.110.7 client and Auth source prove
In signUp() in auth-js 2.110.7, options.emailRedirectTo is passed to the request helper as redirectTo. The request helper serializes it as the redirect_to query parameter.
In reviewed Auth commit 67c55c0, GetReferrer first checks redirect_to from the request header or query/form data. If that is absent or invalid, it accepts a valid HTTP Referer before falling back to SiteURL. A body field named email_redirect_to is not read by that signup path.
For a cross-origin browser POST, a policy such as strict-origin-when-cross-origin commonly sends only https://user.github.io/ as the Referer. Because that origin can still pass redirect validation, current Auth may select it before a configured SiteURL containing /repository/. Open PR 2629 proposes preferring the path-bearing Site URL in that case and accepting signup JSON body fields, but it was not merged as of this review and must not be treated as deployed behavior.
The confirmation mailer places that selected referrer in both ConfirmationURL and RedirectTo. That lets one private message inspection distinguish an Auth fallback from a later template or browser problem.
Password recovery reaching the correct path does not prove signup used the same request URL, template, or fresh link. Compare the two flows at each boundary instead of treating one as a control for the whole chain.
Run one bounded correction
- Record the exact
@supabase/auth-jspackage version used by the browser bundle. - Inspect the actual signup request URL and decode only the
redirect_tohost and path. Record the request Referer as only origin versus full path. Do not rely on the JavaScript options object or request body view. - Use an exact production Redirect URL entry when possible. With patterns,
*does not cross/;**does. - Create one fresh controlled signup. In the newest private confirmation link, classify whether decoded
redirect_tohas the repository path, the origin-only Referer, or Site URL fallback. - Check whether the button uses unmodified
{{ .ConfirmationURL }}. If a custom endpoint is required, carry and revalidate{{ .RedirectTo }}. - Open the newest link once and compare the verification response Location path with the final browser route.
Direct HTTP requests use a different field boundary
For a direct request, send the target on the URL:
POST /auth/v1/signup?redirect_to=https%3A%2F%2Fexample.github.io%2Frepository%2F
Content-Type: application/json
{"email":"controlled-address","password":"controlled-password"}
The example shows placement, not values to post publicly. Do not add email_redirect_to to the JSON and assume the Auth referrer was set.
Regression contract
- Assert the outgoing signup URL contains the complete encoded
redirect_topath. - Assert the generated
ConfirmationURLdecodes to the same path. - Assert the delivered button preserves that value without provider link rewriting.
- Assert one fresh verification succeeds and its Location retains the repository path.
- Assert an unlisted origin, wrong scheme, wrong port, and an unmatched path fall back or fail safely.
- Assert replay and expired links remain rejected without changing redirect policy.
Keep redirect correctness separate from message generation, provider processing, receiver visibility, and one-time token completion.
Primary references
- Supabase Auth issue 2634: signup emailRedirectTo loses a GitHub Pages repository path
- Open Supabase Auth PR 2629: prefer path-bearing Site URL over an origin-only Referer
- Supabase JavaScript signUp reference
- Supabase Redirect URLs and wildcard semantics
- Supabase email template variables
- auth-js 2.110.7 signUp implementation
- auth-js 2.110.7 request helper
- Supabase Auth redirect validation and SiteURL fallback
- Supabase Auth confirmation mailer