Implement Auth0 migration

This commit is contained in:
gpt-engineer-app[bot]
2025-11-01 01:08:11 +00:00
parent 858320cd03
commit b2bf9a6e20
17 changed files with 1430 additions and 7 deletions

148
src/pages/Auth0Callback.tsx Normal file
View File

@@ -0,0 +1,148 @@
/**
* Auth0 Callback Page
*
* Handles Auth0 authentication callback and syncs user to Supabase
*/
import { useEffect, useState } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { useAuth0 } from '@auth0/auth0-react';
import { supabase } from '@/integrations/supabase/client';
import { Header } from '@/components/layout/Header';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { Button } from '@/components/ui/button';
import { Loader2, CheckCircle, XCircle, Shield } from 'lucide-react';
import { useToast } from '@/hooks/use-toast';
type SyncStatus = 'processing' | 'success' | 'error';
export default function Auth0Callback() {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const { isAuthenticated, isLoading, user, getAccessTokenSilently } = useAuth0();
const [syncStatus, setSyncStatus] = useState<SyncStatus>('processing');
const [errorMessage, setErrorMessage] = useState<string>('');
const { toast } = useToast();
useEffect(() => {
const syncUserToSupabase = async () => {
if (isLoading) return;
if (!isAuthenticated || !user) {
setSyncStatus('error');
setErrorMessage('Authentication failed. Please try again.');
return;
}
try {
console.log('[Auth0Callback] Syncing user to Supabase:', user.sub);
// Get Auth0 access token
const accessToken = await getAccessTokenSilently();
// Call sync edge function
const { data, error } = await supabase.functions.invoke('auth0-sync-user', {
headers: {
Authorization: `Bearer ${accessToken}`,
},
body: {
email: user.email,
name: user.name,
picture: user.picture,
email_verified: user.email_verified,
},
});
if (error || !data?.success) {
throw new Error(data?.error || error?.message || 'Sync failed');
}
console.log('[Auth0Callback] User synced successfully:', data.profile);
setSyncStatus('success');
toast({
title: 'Welcome back!',
description: 'You\'ve been signed in successfully.',
});
// Redirect after brief delay
setTimeout(() => {
const redirectTo = searchParams.get('redirect') || '/';
navigate(redirectTo);
}, 1500);
} catch (error) {
console.error('[Auth0Callback] Sync error:', error);
setSyncStatus('error');
setErrorMessage(error instanceof Error ? error.message : 'Failed to sync user data');
}
};
syncUserToSupabase();
}, [isAuthenticated, isLoading, user, getAccessTokenSilently, navigate, searchParams, toast]);
return (
<div className="min-h-screen bg-background">
<Header />
<main className="container mx-auto px-4 py-16">
<div className="max-w-md mx-auto">
<Card>
<CardHeader>
<div className="flex items-center justify-center mb-4">
{syncStatus === 'processing' && (
<Loader2 className="h-12 w-12 text-primary animate-spin" />
)}
{syncStatus === 'success' && (
<CheckCircle className="h-12 w-12 text-green-500" />
)}
{syncStatus === 'error' && (
<XCircle className="h-12 w-12 text-destructive" />
)}
</div>
<CardTitle className="text-center">
{syncStatus === 'processing' && 'Completing Sign In...'}
{syncStatus === 'success' && 'Sign In Successful!'}
{syncStatus === 'error' && 'Sign In Error'}
</CardTitle>
<CardDescription className="text-center">
{syncStatus === 'processing' && 'Please wait while we set up your account'}
{syncStatus === 'success' && 'Redirecting you to ThrillWiki...'}
{syncStatus === 'error' && 'Something went wrong during authentication'}
</CardDescription>
</CardHeader>
{syncStatus === 'error' && (
<CardContent>
<Alert variant="destructive">
<AlertDescription>
{errorMessage || 'An unexpected error occurred. Please try signing in again.'}
</AlertDescription>
</Alert>
<div className="mt-4 space-y-2">
<Button
onClick={() => navigate('/auth')}
className="w-full"
>
Return to Sign In
</Button>
</div>
</CardContent>
)}
{syncStatus === 'processing' && (
<CardContent>
<div className="space-y-2 text-sm text-muted-foreground text-center">
<p>Syncing your profile...</p>
<p>This should only take a moment</p>
</div>
</CardContent>
)}
</Card>
</div>
</main>
</div>
);
}