fix: navigate directly to Google authorize endpoints#44
Merged
Conversation
`handleGoogleLogin` and the Google branch of `handleConnect` both called
`api.get('.../authorize').json()` and then navigated to the returned
`authorization_url`. In production the authorize endpoint returns a 302
to accounts.google.com, which Ky's fetch follows automatically — and the
browser blocks the cross-origin call because accounts.google.com isn't
in our CSP connect-src.
Switch both to direct navigation (mirroring the existing Steam pattern
in c8d1e2d). The browser navigates to the API, the API 302s, the browser
follows the redirect — CSP connect-src doesn't apply to navigation.
API code (app/routers/auth_providers.py) confirms login_authorize and
associate_authorize share the same dev-JSON / prod-302 behavior across
both providers, so this aligns Google with how Steam already works.
✅ Deploy Preview for criticalbit-auth-web ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
6 tasks
amrtgaber
added a commit
that referenced
this pull request
May 27, 2026
Replaces 6 `window.location.href` calls with TanStack Router's
`useNavigate` across login, register, logout, oauth callback, profile
delete, and associate-complete. No more white flash between auth state
changes.
## The race that wasn't
`login-page.tsx:36-37` previously documented:
> Hard redirect — reloads the page so AuthProvider picks up the new
> cookie cleanly. Client-side navigate has a race condition with the
> onUnauthorized handler.
E2E testing (Playwright against real API + Postgres) shows the race
does NOT manifest with `await auth.checkAuth() + await navigate()`:
React 18's batching plus TanStack Router's context propagation (via
`<RouterProvider context={{ auth }} />` in `main.tsx:105`) means the
updated context is visible by the time `beforeLoad` runs. No
`router.invalidate()` needed.
The pattern by call site:
- **Login / register**: `await auth.checkAuth() → await navigate(...)`.
Refreshes the AuthContext with the new cookie's identity before the
destination's beforeLoad checks `isAuthenticated`.
- **Logout / delete**: `await auth.logout() → await navigate("/login")`.
Clears local state before navigating so /login's "redirect
authenticated users to /profile" guard doesn't bounce the user back.
- **OAuth-complete**: keeps a direct `api.get("auth/me")` (so the
routing decision sees current data without waiting for a render)
PLUS `await auth.checkAuth()` (so the AuthContext is fresh for the
destination route).
- **Associate-complete**: pure in-app nav with no auth state change.
## Preserved hard-nav cases
- Cross-origin redirects (consumer apps like hera-streamer-…) still use
`window.location.href` — useNavigate is for in-app routes only.
- Outbound to provider OAuth (Google / Steam authorize) keeps direct
navigation (CSP + the dev/prod JSON-vs-302 split, fixed in #44).
## Test plan
- [x] `pnpm test:run` — 45 tests pass.
- [x] `pnpm exec tsc -b` clean.
- [x] Live E2E: register → /profile, logout → /login, login → /profile,
delete → /login. No bounce, no white flash. Backend log confirms
request chain (register/login/accept-tos/me/connections; logout;
delete/logout).
Closes #43.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Symptom
Production Google sign-in is broken with a CSP violation:
Same blast radius hits the "Connect Google" button on /profile (the associate flow).
Cause
`handleGoogleLogin` and `handleConnect("google")` both did `api.get('.../authorize').json()` and then navigated to the returned `authorization_url`. In production the authorize endpoint returns a 302 to accounts.google.com; Ky's fetch follows that redirect automatically, and the browser blocks the cross-origin call because `accounts.google.com` isn't in `connect-src`. The fix already exists in this codebase for Steam — commit `c8d1e2d fix: navigate directly to Steam authorize endpoint` documents exactly this trap.
API code (`app/routers/auth_providers.py:294-310,369-382`) confirms `login_authorize` and `associate_authorize` share the same dev-JSON / prod-302 behavior across both providers, so this aligns Google's handlers with how Steam already works.
Fix
Two call sites, both switched from fetch-then-navigate to direct navigation. The browser navigates to the API, the API 302s, the browser follows the navigation redirect — `connect-src` doesn't apply to navigation.
```diff
-async function handleGoogleLogin() {
-}
+function handleGoogleLogin() {
+}
```
Same shape for the Google branch of `handleConnect` in profile-page.tsx.
Test plan
Related