Fix google auth
This commit is contained in:
parent
941d005d6e
commit
e48a5a2d2c
104
backend/main.py
104
backend/main.py
@ -1,6 +1,7 @@
|
||||
from fastapi import FastAPI, HTTPException, Header, Depends, Request
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import RedirectResponse
|
||||
from starlette.middleware.sessions import SessionMiddleware
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
from typing import List, Optional
|
||||
from datetime import datetime
|
||||
@ -35,15 +36,56 @@ oauth.register(
|
||||
|
||||
allowed_origins = ["http://localhost:5173", "https://tasko.dvirlabs.com"]
|
||||
|
||||
# Configure CORS
|
||||
# Environment-aware configuration
|
||||
ENVIRONMENT = os.getenv('ENVIRONMENT', 'development')
|
||||
is_production = ENVIRONMENT == 'production'
|
||||
is_development = ENVIRONMENT == 'development'
|
||||
|
||||
# Configure Session Middleware (required for OAuth state/nonce storage)
|
||||
# Generate a strong SECRET_KEY: python -c "import secrets; print(secrets.token_hex(32))"
|
||||
SESSION_SECRET = os.getenv('SESSION_SECRET', 'dev-secret-change-in-production')
|
||||
|
||||
# Session cookie configuration - environment aware
|
||||
session_config = {
|
||||
"secret_key": SESSION_SECRET,
|
||||
"session_cookie": "tasko_session",
|
||||
"max_age": 3600, # 1 hour
|
||||
"path": "/", # Available on all routes (required for OAuth callback)
|
||||
"same_site": "lax", # Allows OAuth redirects while preventing CSRF
|
||||
"https_only": is_production, # False for HTTP (dev), True for HTTPS (prod)
|
||||
}
|
||||
|
||||
# Development-specific: Ensure cookies work on localhost
|
||||
if is_development:
|
||||
session_config["domain"] = None # Don't set domain for localhost
|
||||
|
||||
app.add_middleware(SessionMiddleware, **session_config)
|
||||
|
||||
# Log session configuration in development
|
||||
if is_development:
|
||||
print(f"🔐 Session Configuration (Development Mode):")
|
||||
print(f" - Cookie Name: {session_config['session_cookie']}")
|
||||
print(f" - Path: {session_config['path']}")
|
||||
print(f" - SameSite: {session_config['same_site']}")
|
||||
print(f" - HTTPS Only: {session_config['https_only']}")
|
||||
print(f" - Domain: {session_config.get('domain', 'None (localhost)')}")
|
||||
|
||||
# Configure CORS - MUST use specific origins (not "*") when allow_credentials=True
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_origins=allowed_origins, # Specific origins required for credentials
|
||||
allow_credentials=True, # Required for session cookies
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
expose_headers=["*"], # Allow frontend to read all response headers
|
||||
)
|
||||
|
||||
# Log startup info
|
||||
if is_development:
|
||||
print(f"🚀 Tasko API starting in DEVELOPMENT mode")
|
||||
print(f" - CORS Origins: {allowed_origins}")
|
||||
print(f" - Allow Credentials: True (session cookies enabled)")
|
||||
|
||||
# Pydantic Models for API
|
||||
class UserResponse(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
@ -228,14 +270,56 @@ def logout(authorization: Optional[str] = Header(None), db: Session = Depends(ge
|
||||
|
||||
@app.get("/auth/google")
|
||||
async def google_login(request: Request):
|
||||
"""Initiate Google OAuth login"""
|
||||
"""Initiate Google OAuth login - DIRECT BROWSER REDIRECT (not fetch)"""
|
||||
redirect_uri = os.getenv('GOOGLE_REDIRECT_URI', 'http://localhost:8000/auth/google/callback')
|
||||
return await oauth.google.authorize_redirect(request, redirect_uri)
|
||||
|
||||
# Debug logging for development
|
||||
if is_development:
|
||||
print(f"\n🔑 OAuth Login initiated (/auth/google):")
|
||||
print(f" - Redirect URI: {redirect_uri}")
|
||||
print(f" - Request URL: {request.url}")
|
||||
print(f" - Request method: {request.method}")
|
||||
print(f" - Request headers Cookie: {request.headers.get('cookie', 'NONE')}")
|
||||
print(f" - Session available: {hasattr(request, 'session')}")
|
||||
if hasattr(request, 'session'):
|
||||
print(f" - Session keys BEFORE: {list(request.session.keys())}")
|
||||
|
||||
# This will set session state and redirect to Google
|
||||
response = await oauth.google.authorize_redirect(request, redirect_uri)
|
||||
|
||||
if is_development:
|
||||
print(f" - Session keys AFTER: {list(request.session.keys())}")
|
||||
print(f" - Response status: {response.status_code}")
|
||||
print(f" - Response Set-Cookie: {response.headers.get('set-cookie', 'NONE')}")
|
||||
print(f" - Response Location: {response.headers.get('location', 'NONE')[:100]}...")
|
||||
|
||||
return response
|
||||
|
||||
@app.get("/auth/google/callback")
|
||||
async def google_callback(request: Request, db: Session = Depends(get_db)):
|
||||
"""Handle Google OAuth callback"""
|
||||
try:
|
||||
# Debug logging for development
|
||||
if is_development:
|
||||
print(f"\n🔄 OAuth Callback received (/auth/google/callback):")
|
||||
print(f" - Request URL: {request.url}")
|
||||
print(f" - Request method: {request.method}")
|
||||
print(f" - Request headers Cookie: {request.headers.get('cookie', 'NONE')}")
|
||||
print(f" - Request query params: {dict(request.query_params)}")
|
||||
print(f" - Cookies from request.cookies: {list(request.cookies.keys())}")
|
||||
print(f" - Session available: {hasattr(request, 'session')}")
|
||||
if hasattr(request, 'session'):
|
||||
session_keys = list(request.session.keys())
|
||||
print(f" - Session keys: {session_keys}")
|
||||
# Print state for debugging
|
||||
for key in session_keys:
|
||||
if 'state' in key.lower():
|
||||
value_str = str(request.session[key])
|
||||
if len(value_str) > 100:
|
||||
print(f" - Session[{key}]: {value_str[:100]}...")
|
||||
else:
|
||||
print(f" - Session[{key}]: {value_str}")
|
||||
|
||||
# Get access token from Google
|
||||
token = await oauth.google.authorize_access_token(request)
|
||||
|
||||
@ -297,11 +381,21 @@ async def google_callback(request: Request, db: Session = Depends(get_db)):
|
||||
db.add(new_token)
|
||||
db.commit()
|
||||
|
||||
if is_development:
|
||||
print(f"✅ OAuth Login SUCCESS!")
|
||||
print(f" - User: {user.email} (ID: {user.id})")
|
||||
print(f" - Token generated: {token_str[:20]}...")
|
||||
print(f" - Redirecting to frontend with token")
|
||||
|
||||
# Redirect to frontend with token
|
||||
frontend_url = os.getenv('FRONTEND_URL', 'http://localhost:5173')
|
||||
return RedirectResponse(url=f"{frontend_url}?token={token_str}&user={user.id}")
|
||||
|
||||
except Exception as e:
|
||||
if is_development:
|
||||
print(f"❌ OAuth Login FAILED: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
raise HTTPException(status_code=400, detail=f"Authentication failed: {str(e)}")
|
||||
|
||||
@app.get("/auth/google/url")
|
||||
|
||||
@ -2,7 +2,8 @@ import { useState, useEffect } from 'react'
|
||||
import Auth from './Auth'
|
||||
import './App.css'
|
||||
|
||||
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000'
|
||||
// Use relative URLs to leverage Vite proxy (same-origin = cookies work!)
|
||||
const API_URL = import.meta.env.VITE_API_URL || '' // Empty string = same-origin
|
||||
|
||||
const AVAILABLE_ICONS = [
|
||||
'📝', '💼', '🏠', '🛒', '🎯', '💪', '📚', '✈️',
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import './Auth.css'
|
||||
|
||||
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000'
|
||||
// Use relative URLs to leverage Vite proxy (same-origin = cookies work!)
|
||||
const API_URL = import.meta.env.VITE_API_URL || '' // Empty string = same-origin
|
||||
|
||||
function Auth({ onLogin }) {
|
||||
const [isLogin, setIsLogin] = useState(true)
|
||||
@ -94,21 +95,11 @@ function Auth({ onLogin }) {
|
||||
setLoading(true)
|
||||
setError('')
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/auth/google/url`)
|
||||
const data = await response.json()
|
||||
|
||||
if (data.url) {
|
||||
window.location.href = data.url
|
||||
} else {
|
||||
setError('Failed to initiate Google login')
|
||||
setLoading(false)
|
||||
}
|
||||
} catch (error) {
|
||||
setError('Failed to connect to Google login')
|
||||
setLoading(false)
|
||||
console.error('Google login error:', error)
|
||||
}
|
||||
// CORRECT OAuth flow: Direct browser redirect to /auth/google
|
||||
// This allows the backend to set session cookie and redirect to Google
|
||||
// DO NOT use fetch() - OAuth requires browser redirects to preserve cookies
|
||||
console.log('Redirecting to Google OAuth...')
|
||||
window.location.href = `${API_URL}/auth/google`
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@ -4,4 +4,34 @@ import react from '@vitejs/plugin-react'
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
proxy: {
|
||||
// Proxy API requests to backend - enables same-origin for session cookies
|
||||
'/auth': {
|
||||
target: 'http://localhost:8000',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
},
|
||||
'/api': {
|
||||
target: 'http://localhost:8000',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
},
|
||||
'/login': {
|
||||
target: 'http://localhost:8000',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
},
|
||||
'/register': {
|
||||
target: 'http://localhost:8000',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
},
|
||||
'/logout': {
|
||||
target: 'http://localhost:8000',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user