A deep link with a scope selection now reaches Google

Signed out, open a dashboard link carrying prop=*@org, click Sign in with Google: before, Better Auth refused the callbackURL; after, the browser lands on Google's sign-in.

18 Sept 2026 · branch login/k1-callback-url-encode · captured against propflow-stage · at 443bc0623f

Data note. Every name and balance below comes from propflow-stage, which is anonymized by construction (ADR-0097/0110 — phones in the impossible +1000 NPA, emails @example.test). The identical capture against prod would carry real tenants' names and balances and is not safe to attach anywhere, which is why the generator refuses to run against one.

1 · Before — the Google button answers "Invalid callbackURL"

This is origin/main without the fix, served by a dev server bound to propflow-stage. The page is exactly what middleware produces for the reported link: /login?from=/dashboard?org=…&prop=*%40…. Clicking Sign in with Google POSTs the vetted from value to Better Auth as callbackURL, and Better Auth answers 403 {"code":"INVALID_CALLBACK_URL"} — its relative-callbackURL regex admits no * in a query. The page renders the message and nothing else happens.

Asserted in the captured DOMSign in with Google · Invalid callbackURL. The error copy is asserted in rendered text, in the state the screenshot shows — after the click.

Before — the Google button answers "Invalid callbackURL"
Provenance. Captured from commit da79dfaf12, read from the worktree the server was started from (/Users/miniclaw/code/PropFlow/.wt-k2) — the server could not report its own commit, so this is the weaker of the two sources.

2 · After — the same click lands on Google

Same page, same click, on this branch. toAuthCallbackURL rewrites the vetted value to /dashboard?org=…&prop=%2A%40… — the one character Better Auth refuses is percent-encoded and the existing %40 passes through untouched. Better Auth answers 200 {"url":"https://accounts.google.com/…"} and the browser navigates there: Google's own Sign in to continue to Propflow screen, captured cross-origin. No "Invalid callbackURL" anywhere on the path. On the way back, useSearchParams().get('prop') decodes %2A%40 to *@, so the landing selection is the one the link carried.

Asserted in the captured DOMto continue to · Propflow · Email or phone. Asserted on Google's own page text after the cross-origin navigation (its heading splits "Sign in" / "to continue to" / "Propflow" across elements) — the proof is that the browser got there at all.

After — the same click lands on Google

What the two captures pin

The BEFORE is the bug as reported (Gera, 2026-09-18, walking onboarding signed out). The AFTER is the same browser, the same click, reaching Google. The unit tests in auth-return-to.test.ts pin the encoder against a copy of Better Auth's regex; login-google-signin-hang.test.tsx pins that both Better Auth call sites on /login use it. This page pins the thing neither can: that the real Better Auth in the real server accepts the result.

PropFlow Docs