The 1st decentralized social network for sharing when you're on the toilet. Post a "flush" today! Powered by the AT Protocol.
at main 148 lines 4.6 kB view raw
1'use client'; 2 3import { useEffect, useState, Suspense } from 'react'; 4import { useRouter } from 'next/navigation'; 5import { useAuth } from '@/lib/auth-context'; 6import styles from './callback.module.css'; 7 8// Loading component to show while waiting 9function CallbackLoader() { 10 return ( 11 <div className={styles.container}> 12 <div className={styles.loaderContainer}> 13 <div className={styles.loader}></div> 14 <p>Processing login...</p> 15 </div> 16 </div> 17 ); 18} 19 20// Main callback handler component 21function CallbackHandler() { 22 const router = useRouter(); 23 const { session, isLoading } = useAuth(); 24 const [error, setError] = useState<string | null>(null); 25 const [processed, setProcessed] = useState(false); 26 27 useEffect(() => { 28 // Prevent double processing 29 if (processed) return; 30 31 // The OAuth client handles the callback automatically during initialization 32 // We just need to wait for the session to be available or an error to occur 33 34 // Set a timeout to handle potential issues 35 const timeout = setTimeout(() => { 36 if (!session && !isLoading) { 37 console.error('OAuth callback processing timed out'); 38 setError('Login process timed out. Please try again.'); 39 } 40 }, 30000); // 30 second timeout 41 42 // Clean up timeout if component unmounts 43 return () => { 44 clearTimeout(timeout); 45 setProcessed(true); 46 }; 47 }, [session, isLoading, processed]); 48 49 // Once we have a session, redirect to home 50 useEffect(() => { 51 if (session && !isLoading) { 52 console.log(`Successfully authenticated user: ${session.sub}`); 53 54 // Small delay to show success state 55 setTimeout(() => { 56 router.push('/'); 57 }, 1000); 58 } 59 }, [session, isLoading, router]); 60 61 // Handle cases where the OAuth flow fails 62 useEffect(() => { 63 // Check for error parameters in the URL 64 if (typeof window !== 'undefined') { 65 const urlParams = new URLSearchParams(window.location.search); 66 const urlError = urlParams.get('error'); 67 const errorDescription = urlParams.get('error_description'); 68 69 if (urlError) { 70 console.error(`OAuth error in URL: ${urlError} - ${errorDescription}`); 71 setError(`Authentication error: ${errorDescription || urlError}`); 72 return; 73 } 74 75 // Also check hash params (since we use fragment mode) 76 const hashParams = new URLSearchParams(window.location.hash.substring(1)); 77 const hashError = hashParams.get('error'); 78 const hashErrorDescription = hashParams.get('error_description'); 79 80 if (hashError) { 81 console.error(`OAuth error in hash: ${hashError} - ${hashErrorDescription}`); 82 setError(`Authentication error: ${hashErrorDescription || hashError}`); 83 return; 84 } 85 } 86 87 // If we're not loading and don't have a session after a reasonable time, 88 // something went wrong 89 if (!isLoading && !session) { 90 const timer = setTimeout(() => { 91 if (!session) { 92 console.error('No session available after callback processing'); 93 setError('Failed to complete authentication. Please try again.'); 94 } 95 }, 5000); // Wait 5 seconds for session to appear 96 97 return () => clearTimeout(timer); 98 } 99 }, [isLoading, session]); 100 101 if (error) { 102 return ( 103 <div className={styles.container}> 104 <div className={styles.errorContainer}> 105 <h1>Authentication Error</h1> 106 <p className={styles.error}>{error}</p> 107 <button onClick={() => router.push('/auth/login')} className={styles.button}> 108 Try Again 109 </button> 110 <button onClick={() => router.push('/')} className={styles.button}> 111 Back to Home 112 </button> 113 </div> 114 </div> 115 ); 116 } 117 118 if (session) { 119 return ( 120 <div className={styles.container}> 121 <div className={styles.successContainer}> 122 <div className={styles.checkmark}></div> 123 <h1>Welcome back!</h1> 124 <p>Successfully signed in! Redirecting...</p> 125 <p>Redirecting to home page...</p> 126 </div> 127 </div> 128 ); 129 } 130 131 return ( 132 <div className={styles.container}> 133 <div className={styles.loaderContainer}> 134 <div className={styles.loader}></div> 135 <p>Completing authentication...</p> 136 </div> 137 </div> 138 ); 139} 140 141// Main export with Suspense boundary 142export default function CallbackPage() { 143 return ( 144 <Suspense fallback={<CallbackLoader />}> 145 <CallbackHandler /> 146 </Suspense> 147 ); 148}