From 79e4ad55ed4f134a9db5558aa96ee0308b7a9fd0 Mon Sep 17 00:00:00 2001 From: Xin Wang Date: Mon, 30 Jun 2025 18:22:32 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=84=E7=90=86=E5=9B=9E=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/auth/google/callback/route.ts | 10 ++++ app/page.tsx | 3 ++ components/ui/oauth-callback-handler.tsx | 65 ++++++++++++++++++++++++ lib/auth.ts | 2 +- 4 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 components/ui/oauth-callback-handler.tsx diff --git a/app/api/auth/google/callback/route.ts b/app/api/auth/google/callback/route.ts index fc86a22..00b49e8 100644 --- a/app/api/auth/google/callback/route.ts +++ b/app/api/auth/google/callback/route.ts @@ -16,6 +16,7 @@ export async function GET(request: NextRequest) { const searchParams = request.nextUrl.searchParams; const code = searchParams.get('code'); const error = searchParams.get('error'); + const state = searchParams.get('state'); // Handle errors from Google if (error) { @@ -28,6 +29,10 @@ export async function GET(request: NextRequest) { return NextResponse.redirect(new URL('/login?error=no_code', request.url)); } + // The state parameter validation will happen client-side + // since we're storing the original state in sessionStorage + // We'll add the state to the redirect URL so the client can validate it + try { // In a real app, you would exchange the code for tokens // and validate the tokens here @@ -51,6 +56,11 @@ export async function GET(request: NextRequest) { redirectUrl.searchParams.set('session', 'demo-session-token'); redirectUrl.searchParams.set('user', encodeURIComponent(JSON.stringify(mockUser))); + // Pass the state back to the client for validation + if (state) { + redirectUrl.searchParams.set('state', state); + } + return NextResponse.redirect(redirectUrl); } catch (error) { console.error('Failed to process Google authentication:', error); diff --git a/app/page.tsx b/app/page.tsx index b6a4c01..6de4c32 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,10 +1,13 @@ import { DashboardLayout } from '@/components/layout/dashboard-layout'; // import { HomePage } from '@/components/pages/home-page'; import { HomePage2 } from '@/components/pages/home-page2'; +import { OAuthCallbackHandler } from '@/components/ui/oauth-callback-handler'; export default function Home() { return ( + {/* Handle OAuth callbacks */} + ); diff --git a/components/ui/oauth-callback-handler.tsx b/components/ui/oauth-callback-handler.tsx new file mode 100644 index 0000000..80a19dd --- /dev/null +++ b/components/ui/oauth-callback-handler.tsx @@ -0,0 +1,65 @@ +'use client'; + +import { useEffect } from 'react'; +import { useRouter, useSearchParams } from 'next/navigation'; +import { validateOAuthState } from '@/lib/auth'; +import { toast } from '@/hooks/use-toast'; + +export function OAuthCallbackHandler() { + const searchParams = useSearchParams(); + const router = useRouter(); + + useEffect(() => { + // Check if this is an OAuth callback + const state = searchParams.get('state'); + const session = searchParams.get('session'); + const userJson = searchParams.get('user'); + + // If we have state and session, this might be an OAuth callback + if (state && session) { + // Validate the state parameter to prevent CSRF + const isValid = validateOAuthState(state); + + if (!isValid) { + // State validation failed, possible CSRF attack + toast({ + title: 'Authentication Error', + description: 'Security validation failed. Please try signing in again.', + variant: 'destructive', + }); + router.push('/login?error=invalid_state'); + return; + } + + // State is valid, process the login + if (userJson) { + try { + const user = JSON.parse(decodeURIComponent(userJson)); + + // Store the user in session + sessionStorage.setItem('currentUser', JSON.stringify(user)); + + // Show success message + toast({ + title: 'Signed in successfully', + description: `Welcome ${user.name}!`, + }); + + // Remove the query parameters from the URL + router.replace('/'); + } catch (error) { + console.error('Failed to parse user data', error); + toast({ + title: 'Authentication Error', + description: 'Failed to process authentication data', + variant: 'destructive', + }); + router.push('/login?error=invalid_user_data'); + } + } + } + }, [searchParams, router]); + + // This is a utility component that doesn't render anything + return null; +} \ No newline at end of file diff --git a/lib/auth.ts b/lib/auth.ts index e185c42..2e5ab46 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -1,7 +1,7 @@ // Mock Google OAuth configuration const GOOGLE_CLIENT_ID = '1016208801816-qtvcvki2jobmcin1g4e7u4sotr0p8g3u.apps.googleusercontent.com'; const GOOGLE_REDIRECT_URI = typeof window !== 'undefined' - ? `${window.location.origin}/api/auth/google/callback` + ? `${window.location.origin}/users/oauth/callback` : ''; /**