Summary
The __session cookie set by /runtime/oauth/start carries no SameSite attribute, so Chrome applies its SameSite=Lax default. The web redirect flow uses response_mode=form_post, meaning the identity provider POSTs cross-site to /runtime/oauth/callback — and Chrome does not send Lax cookies on cross-site POSTs. The callback therefore answers:
{"type":"oauth-error","error":"Missing cookie."}
It presents as intermittent because of Chrome's "Lax + POST" mitigation, which still sends cookies less than two minutes old on top-level cross-site POSTs. A sign-in that completes quickly (an already-authenticated account chooser) succeeds; one that takes longer than two minutes — 2FA, or a passkey prompt — fails. That mitigation is being phased out, so the failure rate should be expected to rise.
Reproduction
The cookie, as sent today:
$ curl -s -D - -o /dev/null \
"https://api.instantdb.com/runtime/oauth/start?app_id=<APP_ID>&client_name=google&redirect_uri=https%3A%2F%2Fexample.com%2F" \
| grep -i set-cookie
set-cookie: __session=instantdb_3f0f8c95-...; HttpOnly; Secure; Expires=...; Path=/runtime/oauth
No SameSite. The same response's Location selects the form-post mode:
location: https://accounts.google.com/o/oauth2/v2/auth?...&response_mode=form_post&...
Isolating the cookie as the only variable, using a genuine state from that same start request (the invalid code is deliberate — the point is which check is reached):
# With the cookie: passes the cookie check, fails later on the bad code.
curl -s -X POST https://api.instantdb.com/runtime/oauth/callback \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H "Cookie: __session=instantdb_<from start>" \
-d "code=probe-invalid&state=<from start>"
# (no "Missing cookie" error)
# Without it — what Chrome sends on a cross-site POST:
curl -s -X POST https://api.instantdb.com/runtime/oauth/callback \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d "code=probe-invalid&state=<from start>"
{"type":"oauth-error","error":"Missing cookie."}
Expected
SameSite=None; Secure on the __session cookie, which is what a form_post flow requires. Secure is already set, so this should be a one-attribute change.
Alternatively, response_mode=query would keep the callback a top-level GET, where Lax cookies are sent — though that puts the authorization code in the URL, so the cookie attribute seems the better fix.
Environment
- Chrome 152.0.7977.83, macOS
@instantdb/react / @instantdb/core 0.22.185 (client version is incidental — the cookie is set server-side by api.instantdb.com)
- Web redirect flow via
db.auth.createAuthorizationURL({ clientName, redirectURL }), per the Google OAuth web-redirect docs
Impact
Users who take longer than two minutes to authenticate — which includes anyone using a passkey or 2FA — cannot sign in with the web redirect flow at all, and the error surfaces as a raw JSON body rather than a redirect back to the app.
Summary
The
__sessioncookie set by/runtime/oauth/startcarries noSameSiteattribute, so Chrome applies itsSameSite=Laxdefault. The web redirect flow usesresponse_mode=form_post, meaning the identity provider POSTs cross-site to/runtime/oauth/callback— and Chrome does not sendLaxcookies on cross-site POSTs. The callback therefore answers:{"type":"oauth-error","error":"Missing cookie."}It presents as intermittent because of Chrome's "Lax + POST" mitigation, which still sends cookies less than two minutes old on top-level cross-site POSTs. A sign-in that completes quickly (an already-authenticated account chooser) succeeds; one that takes longer than two minutes — 2FA, or a passkey prompt — fails. That mitigation is being phased out, so the failure rate should be expected to rise.
Reproduction
The cookie, as sent today:
No
SameSite. The same response'sLocationselects the form-post mode:Isolating the cookie as the only variable, using a genuine
statefrom that same start request (the invalidcodeis deliberate — the point is which check is reached):Expected
SameSite=None; Secureon the__sessioncookie, which is what aform_postflow requires.Secureis already set, so this should be a one-attribute change.Alternatively,
response_mode=querywould keep the callback a top-level GET, whereLaxcookies are sent — though that puts the authorization code in the URL, so the cookie attribute seems the better fix.Environment
@instantdb/react/@instantdb/core0.22.185 (client version is incidental — the cookie is set server-side byapi.instantdb.com)db.auth.createAuthorizationURL({ clientName, redirectURL }), per the Google OAuth web-redirect docsImpact
Users who take longer than two minutes to authenticate — which includes anyone using a passkey or 2FA — cannot sign in with the web redirect flow at all, and the error surfaces as a raw JSON body rather than a redirect back to the app.