mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-24 14:51:12 -05:00
Implement Auth0 migration
This commit is contained in:
148
src/pages/Auth0Callback.tsx
Normal file
148
src/pages/Auth0Callback.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user