Something went wrong. Try again.
perform your own verifications verifier.psingletary.com/verifier
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106import React, { createContext, useContext, useState, useEffect, useRef, useCallback } from 'react';import { BrowserOAuthClient } from '@atproto/oauth-client-browser';
export const AuthContext = createContext(null);
export const AuthProvider = ({ children }) => { const [client, setClient] = useState(null); const [session, setSession] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const initializing = useRef(false);
useEffect(() => { const initializeAuth = async () => { if (initializing.current || client) return; initializing.current = true; setLoading(true); setError(null);
try { // P6: single source of truth — load client metadata at startup const metadataResponse = await fetch('/client-metadata.json'); if (!metadataResponse.ok) { throw new Error(`Failed to load client metadata: ${metadataResponse.status}`); } const clientMetadata = await metadataResponse.json();
const oauthClient = new BrowserOAuthClient({ clientMetadata, handleResolver: 'https://public.api.bsky.app', plcDirectoryUrl: 'https://plc.directory', });
setClient(oauthClient); const initResult = await oauthClient.init();
if (initResult?.session) { setSession(initResult.session); } else { setSession(null); } } catch (err) { console.error('Auth initialization failed:', err); setError('Authentication initialization failed. Please try refreshing.'); setSession(null); } finally { setLoading(false); initializing.current = false; } };
initializeAuth(); }, [client]);
const login = useCallback(async (handle, returnUrl = '/') => { if (!client) { setError("Client not initialized."); return; } try { const stateData = JSON.stringify({ returnUrl }); await client.signIn(handle, { state: stateData }); } catch (err) { console.error('Login failed:', err); setError('Login initiation failed or was cancelled.'); } }, [client]);
const logout = useCallback(async () => { if (!session) return; try { await session.signOut(); setSession(null); window.location.href = '/'; } catch (err) { console.error('Logout failed:', err); setSession(null); window.location.href = '/'; } }, [session]);
return ( <AuthContext.Provider value={{ client, session, loading, error, isAuthenticated: !!session, login, logout, }} > {children} </AuthContext.Provider> );};
export const useAuth = () => { const context = useContext(AuthContext); if (context === null) { throw new Error('useAuth must be used within an AuthProvider'); } return context;};