This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

fix

+193 -52
+41 -19
src/components/ProtectedRoute.js
··· 9 9 const { isAuthenticated, loading, session, checkAuthStatus } = useAuth(); 10 10 const location = useLocation(); 11 11 const [redirecting, setRedirecting] = useState(false); 12 + const [checkingStatus, setCheckingStatus] = useState(false); 12 13 const checkCount = useRef(0); 13 14 const maxChecks = 3; // Maximum number of checks to prevent infinite loops 14 15 16 + // Perform an immediate auth check when the component mounts 15 17 useEffect(() => { 16 - // Prevent excessive auth checks 17 - if (checkCount.current >= maxChecks) { 18 - console.error("Maximum auth check attempts reached. Stopping to prevent infinite loop."); 19 - return; 20 - } 18 + const checkAuth = async () => { 19 + if (checkCount.current >= maxChecks) { 20 + console.error("Maximum auth check attempts reached. Stopping to prevent infinite loop."); 21 + return; 22 + } 23 + 24 + // Only proceed if not already checking, not already redirecting, and not loading 25 + if (!isAuthenticated && !checkingStatus && !redirecting && !loading) { 26 + try { 27 + console.log("ProtectedRoute: Checking authentication status"); 28 + setCheckingStatus(true); 29 + checkCount.current += 1; 30 + await checkAuthStatus(); 31 + } catch (error) { 32 + console.error("ProtectedRoute: Auth check failed:", error); 33 + } finally { 34 + setCheckingStatus(false); 35 + } 36 + } 37 + }; 21 38 22 - // Only check if not already authenticated and not already redirecting 23 - if (!isAuthenticated && !redirecting && !loading) { 24 - checkCount.current += 1; 25 - checkAuthStatus(); 26 - } 39 + // Call immediately on mount or when dependency values change 40 + checkAuth(); 27 41 28 - // Set up interval for periodic checks - but only if authenticated 29 - // This prevents constantly checking while unauthenticated 42 + // Set up interval for periodic checks only if authenticated 30 43 let interval; 31 - if (isAuthenticated) { 32 - interval = setInterval(checkAuthStatus, 30000); // Check every 30 seconds 44 + if (isAuthenticated && session) { 45 + console.log("ProtectedRoute: Setting up periodic auth checks"); 46 + interval = setInterval(() => { 47 + checkAuthStatus().catch(err => { 48 + console.error("Error in periodic auth check:", err); 49 + }); 50 + }, 30000); // Check every 30 seconds 33 51 } 34 52 35 53 return () => { 36 - if (interval) clearInterval(interval); 54 + if (interval) { 55 + console.log("ProtectedRoute: Clearing periodic auth checks"); 56 + clearInterval(interval); 57 + } 37 58 }; 38 - }, [isAuthenticated, checkAuthStatus, redirecting, loading]); 59 + }, [isAuthenticated, checkAuthStatus, redirecting, loading, checkingStatus, session]); 39 60 40 61 // Show loading state while authentication is being checked 41 - if (loading) { 62 + if (loading || checkingStatus) { 42 63 return <Loading message="Checking authentication..." />; 43 64 } 44 65 45 66 // If not authenticated, redirect to login with return URL 46 67 if (!isAuthenticated && !redirecting) { 68 + console.log("ProtectedRoute: Not authenticated, redirecting to login"); 47 69 setRedirecting(true); // Prevent multiple redirects 48 70 const returnUrl = encodeURIComponent(location.pathname); 49 71 return <Navigate to={`/login?returnUrl=${returnUrl}`} replace />; 50 72 } 51 73 52 74 // Check if user is allowed 53 - // Only check if we have detailed user info 54 - // If we're using server-side sessions, we might not need this check 55 75 if (session && session.handle && !isAccountAllowed(session)) { 76 + console.log("ProtectedRoute: User not in allowlist, redirecting to supporter page"); 56 77 return <Navigate to="/supporter" replace />; 57 78 } 58 79 ··· 60 81 checkCount.current = 0; 61 82 62 83 // Render children if authenticated and allowed 84 + console.log("ProtectedRoute: Authentication successful, rendering protected content"); 63 85 return children; 64 86 }; 65 87
+31 -9
src/config/allowlist.js
··· 6 6 7 7 // Helper function to check if an account is allowed 8 8 export const isAccountAllowed = (session) => { 9 - if (!session) return false; 9 + console.log('Checking if account is allowed:', session); 10 10 11 - // For Bluesky OAuth session 12 - if (session.sub && session.handle) { 13 - return ALLOWED_ACCOUNTS.includes(session.sub) || 14 - ALLOWED_ACCOUNTS.includes(session.handle); 11 + if (!session) { 12 + console.log('No session provided, denying access'); 13 + return false; 15 14 } 16 15 17 - // For server-side session 18 - if (session.did && session.handle) { 19 - return ALLOWED_ACCOUNTS.includes(session.did) || 20 - ALLOWED_ACCOUNTS.includes(session.handle); 16 + // Extract DID from various possible session formats 17 + const did = session.did || session.sub || null; 18 + 19 + // Extract handle from various possible session formats 20 + const handle = session.handle || null; 21 + 22 + console.log(`Checking permissions for DID: ${did}, handle: ${handle}`); 23 + 24 + // Check if either did or handle is in the allowlist 25 + if (did && ALLOWED_ACCOUNTS.includes(did)) { 26 + console.log('DID is in allowlist, granting access'); 27 + return true; 21 28 } 22 29 30 + if (handle && ALLOWED_ACCOUNTS.includes(handle)) { 31 + console.log('Handle is in allowlist, granting access'); 32 + return true; 33 + } 34 + 35 + // Also check if the handle (without domain) is in the allowlist 36 + if (handle && handle.includes('.')) { 37 + const handleWithoutDomain = handle.split('.')[0]; 38 + if (ALLOWED_ACCOUNTS.includes(handleWithoutDomain)) { 39 + console.log('Handle (without domain) is in allowlist, granting access'); 40 + return true; 41 + } 42 + } 43 + 44 + console.log('Account not in allowlist, denying access'); 23 45 return false; 24 46 };
+121 -24
src/contexts/AuthContext.js
··· 32 32 const [error, setError] = useState(null); 33 33 const lastAuthCheck = useRef(0); 34 34 const authCheckInProgress = useRef(false); 35 + const didInitialCheck = useRef(false); 35 36 36 37 // Initialize the OAuth client 37 38 useEffect(() => { 38 39 const initializeAuth = async () => { 40 + if (didInitialCheck.current) return; 41 + didInitialCheck.current = true; 42 + 39 43 try { 40 44 // First check server-side authentication status 45 + console.log('Checking server authentication status'); 41 46 const serverAuthResponse = await fetch('/api/auth/status', { 42 47 credentials: 'include' 43 48 }); 44 49 45 - const serverAuthData = await serverAuthResponse.json(); 46 - 47 - if (serverAuthData.isAuthenticated) { 48 - setSession(serverAuthData.user); 49 - setLoading(false); 50 - return; 50 + if (!serverAuthResponse.ok) { 51 + console.error('Server auth check failed with status:', serverAuthResponse.status); 52 + } else { 53 + const serverAuthData = await serverAuthResponse.json(); 54 + console.log('Server auth status:', serverAuthData); 55 + 56 + if (serverAuthData.isAuthenticated || serverAuthData.authenticated) { 57 + console.log('Already authenticated on server, setting session'); 58 + setSession(serverAuthData.user); 59 + setLoading(false); 60 + return; 61 + } 51 62 } 52 63 53 64 // If not authenticated on the server, check client OAuth 65 + console.log('Not authenticated on server, initializing OAuth client'); 54 66 const oauthClient = new BrowserOAuthClient({ 55 67 clientMetadata, 56 68 handleResolver: 'https://bsky.social', ··· 65 77 if (result?.session) { 66 78 console.log('Found existing OAuth session:', result.session); 67 79 68 - // If client has session but server doesn't, we need to sync them 80 + // Check if atproto_session exists in localStorage as a backup 81 + const atprotoSession = localStorage.getItem('atproto_session'); 82 + console.log('atproto_session in localStorage:', atprotoSession ? 'exists' : 'not found'); 83 + 84 + // Format session data for our internal use and sync with server 85 + const sessionData = { 86 + did: result.session.sub, 87 + handle: result.session.handle 88 + }; 89 + 90 + console.log('Syncing session with server:', sessionData); 91 + 92 + // Try to sync with server 69 93 try { 70 94 const syncResponse = await fetch('/api/sync-session', { 71 95 method: 'POST', 72 96 headers: { 73 97 'Content-Type': 'application/json' 74 98 }, 75 - body: JSON.stringify({ 76 - did: result.session.sub, 77 - handle: result.session.handle 78 - }), 99 + body: JSON.stringify(sessionData), 79 100 credentials: 'include' 80 101 }); 81 102 82 103 if (syncResponse.ok) { 83 104 const syncData = await syncResponse.json(); 84 - console.log('Session sync successful:', syncData); 85 - // Use the server session data which may have more info 105 + console.log('Initial session sync successful:', syncData); 86 106 setSession(syncData.user); 87 107 } else { 88 - console.warn('Session sync failed, using client session'); 89 - // Still use the client session if sync fails 90 - setSession(result.session); 108 + console.warn('Initial session sync failed:', await syncResponse.text()); 109 + 110 + // If sync fails, use client session in our internal format 111 + console.log('Using client session as fallback'); 112 + setSession({ 113 + did: result.session.sub, 114 + handle: result.session.handle, 115 + displayName: result.session.handle 116 + }); 91 117 } 92 118 } catch (syncError) { 93 - console.error('Error syncing session:', syncError); 94 - // If sync fails, still use the client session 95 - setSession(result.session); 119 + console.error('Error syncing initial session:', syncError); 120 + 121 + // If sync fails, still use client session in our internal format 122 + setSession({ 123 + did: result.session.sub, 124 + handle: result.session.handle, 125 + displayName: result.session.handle 126 + }); 96 127 } 128 + } else { 129 + console.log('No existing OAuth session found'); 97 130 } 98 131 99 132 // Listen for session deletion events 100 133 oauthClient.addEventListener('deleted', (event) => { 101 - if (event.data.did === session?.sub || event.data.did === session?.did) { 134 + console.log('Session deletion event received:', event.data); 135 + 136 + // Get current session DID at the time of event 137 + const currentSession = session; 138 + const sessionDid = currentSession?.did || currentSession?.sub; 139 + 140 + if (event.data.did === sessionDid) { 141 + console.log('Current session was deleted, logging out'); 102 142 setSession(null); 143 + 103 144 // Also logout from server 104 145 fetch('/api/logout', { 105 146 method: 'POST', 106 147 credentials: 'include' 148 + }).catch(err => { 149 + console.error('Error during server logout after deletion:', err); 107 150 }); 108 151 } 109 152 }); ··· 224 267 const response = await fetch('/api/auth/status', { 225 268 credentials: 'include' 226 269 }); 270 + 271 + if (!response.ok) { 272 + console.error('Auth status check failed with status:', response.status); 273 + authCheckInProgress.current = false; 274 + return !!session; // Return current state on error 275 + } 276 + 227 277 const data = await response.json(); 278 + console.log('Auth status check response:', data); 228 279 229 - if (data.isAuthenticated) { 230 - setSession(data.user); 280 + const isAuthenticated = data.isAuthenticated || data.authenticated; 281 + 282 + if (isAuthenticated && data.user) { 283 + // If server session is different from current session, update it 284 + const currentSessionJSON = session ? JSON.stringify(session) : ''; 285 + const newSessionJSON = JSON.stringify(data.user); 286 + 287 + if (currentSessionJSON !== newSessionJSON) { 288 + console.log('Updating session from server data'); 289 + setSession(data.user); 290 + } 291 + 231 292 authCheckInProgress.current = false; 232 293 return true; 233 294 } else { 295 + // If server says not authenticated but we have a client session, 296 + // try to synchronize sessions 297 + if (session && client) { 298 + try { 299 + console.log('Server says not authenticated but we have a client session, trying to sync'); 300 + 301 + // Format session data properly 302 + const sessionData = { 303 + did: session.did || session.sub, 304 + handle: session.handle 305 + }; 306 + 307 + // Try to sync one more time 308 + const syncResponse = await fetch('/api/sync-session', { 309 + method: 'POST', 310 + headers: { 311 + 'Content-Type': 'application/json' 312 + }, 313 + body: JSON.stringify(sessionData), 314 + credentials: 'include' 315 + }); 316 + 317 + if (syncResponse.ok) { 318 + console.log('Session sync successful during status check'); 319 + const syncData = await syncResponse.json(); 320 + setSession(syncData.user); 321 + authCheckInProgress.current = false; 322 + return true; 323 + } 324 + } catch (syncError) { 325 + console.error('Error syncing during status check:', syncError); 326 + } 327 + } 328 + 329 + // If all attempts failed and the server says we're not authenticated 330 + console.log('Server says not authenticated, clearing session'); 234 331 setSession(null); 235 332 authCheckInProgress.current = false; 236 333 return false; ··· 238 335 } catch (err) { 239 336 console.error('Error checking auth status:', err); 240 337 authCheckInProgress.current = false; 241 - return false; 338 + return !!session; // Fall back to current session state 242 339 } 243 - }, [session]); 340 + }, [session, client]); 244 341 245 342 return ( 246 343 <AuthContext.Provider