mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-25 00:36:31 +02:00
feat: implement redirect-based OAuth authorization for Google login
- Added a new endpoint `/auth/google/authorize-redirect` to handle OAuth authorization via server-side redirect, addressing CSRF cookie issues in Firefox/Safari. - Updated the `GoogleLoginButton` component to use the new redirect endpoint instead of the previous JSON-based authorization method. - Enhanced CSRF cookie handling by explicitly setting the cookie domain and ensuring compatibility with cross-origin requests.
This commit is contained in:
parent
a6b0400858
commit
9029f5c621
2 changed files with 78 additions and 25 deletions
|
|
@ -1,6 +1,6 @@
|
|||
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 sqlalchemy.ext.asyncio import AsyncSession
|
||||
from uvicorn.middleware.proxy_headers import ProxyHeadersMiddleware
|
||||
|
|
@ -107,6 +107,8 @@ app.include_router(
|
|||
)
|
||||
|
||||
if config.AUTH_TYPE == "GOOGLE":
|
||||
from fastapi.responses import RedirectResponse
|
||||
|
||||
from app.users import google_oauth_client
|
||||
|
||||
# Determine if we're in a secure context (HTTPS) or local development (HTTP)
|
||||
|
|
@ -119,6 +121,15 @@ if config.AUTH_TYPE == "GOOGLE":
|
|||
# For same-origin or local development, use SameSite=Lax (default)
|
||||
csrf_cookie_samesite = "none" if is_secure_context else "lax"
|
||||
|
||||
# Extract the domain from BACKEND_URL for cookie domain setting
|
||||
# This helps with cross-site cookie issues in Firefox/Safari
|
||||
csrf_cookie_domain = None
|
||||
if config.BACKEND_URL:
|
||||
from urllib.parse import urlparse
|
||||
|
||||
parsed_url = urlparse(config.BACKEND_URL)
|
||||
csrf_cookie_domain = parsed_url.hostname
|
||||
|
||||
app.include_router(
|
||||
fastapi_users.get_oauth_router(
|
||||
google_oauth_client,
|
||||
|
|
@ -127,6 +138,7 @@ if config.AUTH_TYPE == "GOOGLE":
|
|||
is_verified_by_default=True,
|
||||
csrf_token_cookie_secure=is_secure_context,
|
||||
csrf_token_cookie_samesite=csrf_cookie_samesite,
|
||||
csrf_token_cookie_httponly=False, # Required for cross-site OAuth in Firefox/Safari
|
||||
)
|
||||
if not config.BACKEND_URL
|
||||
else fastapi_users.get_oauth_router(
|
||||
|
|
@ -137,6 +149,8 @@ if config.AUTH_TYPE == "GOOGLE":
|
|||
redirect_url=f"{config.BACKEND_URL}/auth/google/callback",
|
||||
csrf_token_cookie_secure=is_secure_context,
|
||||
csrf_token_cookie_samesite=csrf_cookie_samesite,
|
||||
csrf_token_cookie_httponly=False, # Required for cross-site OAuth in Firefox/Safari
|
||||
csrf_token_cookie_domain=csrf_cookie_domain, # Explicitly set cookie domain
|
||||
),
|
||||
prefix="/auth/google",
|
||||
tags=["auth"],
|
||||
|
|
@ -145,6 +159,62 @@ if config.AUTH_TYPE == "GOOGLE":
|
|||
], # blocks OAuth registration when disabled
|
||||
)
|
||||
|
||||
# Add a redirect-based authorize endpoint for Firefox/Safari compatibility
|
||||
# This endpoint performs a server-side redirect instead of returning JSON
|
||||
# which fixes cross-site cookie issues where browsers don't send cookies
|
||||
# set via cross-origin fetch requests on subsequent redirects
|
||||
@app.get("/auth/google/authorize-redirect", tags=["auth"])
|
||||
async def google_authorize_redirect(
|
||||
request: Request,
|
||||
):
|
||||
"""
|
||||
Redirect-based OAuth authorization endpoint.
|
||||
|
||||
Unlike the standard /auth/google/authorize endpoint that returns JSON,
|
||||
this endpoint directly redirects the browser to Google's OAuth page.
|
||||
This fixes CSRF cookie issues in Firefox and Safari where cookies set
|
||||
via cross-origin fetch requests are not sent on subsequent redirects.
|
||||
"""
|
||||
import secrets
|
||||
|
||||
from fastapi_users.router.oauth import generate_state_token
|
||||
|
||||
# Generate CSRF token
|
||||
csrf_token = secrets.token_urlsafe(32)
|
||||
|
||||
# Build state token
|
||||
state_data = {"csrftoken": csrf_token}
|
||||
state = generate_state_token(state_data, SECRET, lifetime_seconds=3600)
|
||||
|
||||
# Get the callback URL
|
||||
if config.BACKEND_URL:
|
||||
redirect_url = f"{config.BACKEND_URL}/auth/google/callback"
|
||||
else:
|
||||
redirect_url = str(request.url_for("oauth:google.jwt.callback"))
|
||||
|
||||
# Get authorization URL from Google
|
||||
authorization_url = await google_oauth_client.get_authorization_url(
|
||||
redirect_url,
|
||||
state,
|
||||
scope=["openid", "email", "profile"],
|
||||
)
|
||||
|
||||
# Create redirect response and set CSRF cookie
|
||||
response = RedirectResponse(url=authorization_url, status_code=302)
|
||||
response.set_cookie(
|
||||
key="fastapiusersoauthcsrf",
|
||||
value=csrf_token,
|
||||
max_age=3600,
|
||||
path="/",
|
||||
domain=csrf_cookie_domain,
|
||||
secure=is_secure_context,
|
||||
httponly=False, # Required for cross-site OAuth in Firefox/Safari
|
||||
samesite=csrf_cookie_samesite,
|
||||
)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
app.include_router(crud_router, prefix="/api/v1", tags=["crud"])
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue