Merge pull request #599 from MODSetter/dev

hotfix(try): OAUTH_INVALID_STATE
This commit is contained in:
Rohan Verma 2025-12-19 12:50:10 -08:00 committed by GitHub
commit 50febcb4a6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 38 additions and 19 deletions

View file

@ -1,7 +1,8 @@
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from fastapi import Depends, FastAPI, HTTPException, status from fastapi import Depends, FastAPI, HTTPException, Request, status
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import RedirectResponse
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware
@ -89,6 +90,28 @@ if config.AUTH_TYPE == "GOOGLE":
], # blocks OAuth registration when disabled ], # blocks OAuth registration when disabled
) )
# Convenience endpoint for browser-based login:
# - FastAPI Users `/auth/google/authorize` returns JSON {authorization_url: ...}
# - In production, it's more reliable to do a top-level navigation that
# immediately redirects to Google (avoids cross-site XHR/CORS pitfalls).
from fastapi import Query
from fastapi_users.router.oauth import generate_state_token
@app.get("/auth/google/start", include_in_schema=False)
async def google_oauth_start(
request: Request, scopes: list[str] = Query(None)
) -> RedirectResponse:
base = (config.BACKEND_URL or str(request.base_url)).rstrip("/")
callback_url = f"{base}/auth/google/callback"
state = generate_state_token({}, SECRET)
authorization_url = await google_oauth_client.get_authorization_url(
callback_url,
state,
scopes,
)
return RedirectResponse(authorization_url, status_code=302)
app.include_router(crud_router, prefix="/api/v1", tags=["crud"]) app.include_router(crud_router, prefix="/api/v1", tags=["crud"])

View file

@ -9,24 +9,20 @@ export function GoogleLoginButton() {
const t = useTranslations("auth"); const t = useTranslations("auth");
const handleGoogleLogin = () => { const handleGoogleLogin = () => {
// Redirect to Google OAuth authorization URL // IMPORTANT:
fetch(`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/auth/google/authorize`) // FastAPI Users OAuth stores the "state" in a cookie.
.then((response) => { // Doing a cross-origin fetch() (www.surfsense.com -> backend.ssbacktemp.xyz)
if (!response.ok) { // will NOT persist Set-Cookie unless you use credentials + non-wildcard CORS.
throw new Error("Failed to get authorization URL"); // The simplest/most reliable approach is a top-level navigation to the backend
} // authorize endpoint so the cookie is set as first-party.
return response.json(); const backendUrl = process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL;
}) if (!backendUrl) {
.then((data) => { console.error("Missing NEXT_PUBLIC_FASTAPI_BACKEND_URL");
if (data.authorization_url) { return;
window.location.href = data.authorization_url; }
} else {
console.error("No authorization URL received"); // This endpoint performs a 302 to Google (more reliable than fetching JSON).
} window.location.href = `${backendUrl}/auth/google/start`;
})
.catch((error) => {
console.error("Error during Google login:", error);
});
}; };
return ( return (
<div className="relative w-full overflow-hidden"> <div className="relative w-full overflow-hidden">