The 1st decentralized social network for sharing when you're on the toilet. Post a "flush" today! Powered by the AT Protocol.
at main 218 lines 6.9 kB view raw
1'use client'; 2 3import { createContext, useContext, useState, useEffect, ReactNode } from 'react'; 4 5interface AuthContextType { 6 isAuthenticated: boolean; 7 accessToken: string | null; 8 refreshToken: string | null; 9 did: string | null; 10 handle: string | null; 11 serializedKeyPair: string | null; 12 dpopNonce: string | null; 13 pdsEndpoint: string | null; 14 setAuth: (auth: { 15 accessToken: string; 16 refreshToken: string; 17 did: string; 18 handle: string; 19 serializedKeyPair: string; 20 dpopNonce?: string | null; 21 pdsEndpoint?: string | null; 22 }) => void; 23 clearAuth: () => void; 24} 25 26const AuthContext = createContext<AuthContextType | undefined>(undefined); 27 28interface AuthProviderProps { 29 children: ReactNode; 30} 31 32export function AuthProvider({ children }: AuthProviderProps) { 33 const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false); 34 const [accessToken, setAccessToken] = useState<string | null>(null); 35 const [refreshToken, setRefreshToken] = useState<string | null>(null); 36 const [did, setDid] = useState<string | null>(null); 37 const [handle, setHandle] = useState<string | null>(null); 38 const [serializedKeyPair, setSerializedKeyPair] = useState<string | null>(null); 39 const [dpopNonce, setDpopNonce] = useState<string | null>(null); 40 const [pdsEndpoint, setPdsEndpoint] = useState<string | null>(null); 41 const [isClient, setIsClient] = useState(false); 42 const [lastTokenRefresh, setLastTokenRefresh] = useState<number>(0); 43 44 // Function to check token and refresh if needed 45 const checkAndRefreshToken = async () => { 46 if (!accessToken || !refreshToken || !serializedKeyPair || !did || !pdsEndpoint) { 47 return; 48 } 49 50 try { 51 // TEMPORARILY DISABLED TOKEN REFRESH 52 // This fixes issues with third-party PDSs like geese.blue 53 console.log('[AUTH CONTEXT] Token refresh temporarily disabled for third-party PDS compatibility'); 54 55 // Still update the nonce from localStorage if available 56 if (typeof localStorage !== 'undefined') { 57 const latestNonce = localStorage.getItem('dpopNonce'); 58 if (latestNonce && latestNonce !== dpopNonce) { 59 console.log('[AUTH CONTEXT] Updating nonce from localStorage:', latestNonce); 60 setDpopNonce(latestNonce); 61 } 62 } 63 } catch (error) { 64 console.error('[AUTH CONTEXT] Error in checkAndRefreshToken:', error); 65 } 66 }; 67 68 useEffect(() => { 69 // Set isClient to true once the component mounts 70 setIsClient(true); 71 72 // Load auth data from localStorage on initial mount 73 if (typeof window !== 'undefined') { 74 const storedAccessToken = localStorage.getItem('accessToken'); 75 const storedRefreshToken = localStorage.getItem('refreshToken'); 76 const storedDid = localStorage.getItem('did'); 77 const storedHandle = localStorage.getItem('handle'); 78 const storedKeyPair = localStorage.getItem('keyPair'); 79 const storedDpopNonce = localStorage.getItem('dpopNonce'); 80 81 // Special handling for PDS endpoint - check all possible storage locations 82 let storedPdsEndpoint = localStorage.getItem('pdsEndpoint'); 83 84 // If not found, try our auth-prefixed format 85 if (!storedPdsEndpoint) { 86 storedPdsEndpoint = localStorage.getItem('bsky_auth_pdsEndpoint'); 87 } 88 89 // Last resort - check sessionStorage 90 if (!storedPdsEndpoint && typeof sessionStorage !== 'undefined') { 91 try { 92 storedPdsEndpoint = sessionStorage.getItem('pdsEndpoint'); 93 } catch (e) { 94 console.warn('Failed to check sessionStorage for PDS endpoint:', e); 95 } 96 } 97 98 if (storedAccessToken && storedDid && storedKeyPair) { 99 setAccessToken(storedAccessToken); 100 setRefreshToken(storedRefreshToken); 101 setDid(storedDid); 102 setHandle(storedHandle); 103 setSerializedKeyPair(storedKeyPair); 104 setDpopNonce(storedDpopNonce); 105 setPdsEndpoint(storedPdsEndpoint); 106 setIsAuthenticated(true); 107 } 108 } 109 }, []); 110 111 // Effect to check token expiration periodically 112 useEffect(() => { 113 if (!isAuthenticated) return; 114 115 // Check token immediately after login 116 checkAndRefreshToken(); 117 118 // Check token every 10 minutes 119 const tokenCheckInterval = setInterval(() => { 120 checkAndRefreshToken(); 121 }, 10 * 60 * 1000); // 10 minutes 122 123 return () => clearInterval(tokenCheckInterval); 124 }, [isAuthenticated, accessToken, refreshToken, serializedKeyPair, did, pdsEndpoint]); 125 126 const setAuth = ({ 127 accessToken, 128 refreshToken, 129 did, 130 handle, 131 serializedKeyPair, 132 dpopNonce = null, 133 pdsEndpoint = null 134 }: { 135 accessToken: string; 136 refreshToken: string; 137 did: string; 138 handle: string; 139 serializedKeyPair: string; 140 dpopNonce?: string | null; 141 pdsEndpoint?: string | null; 142 }) => { 143 // Store auth data in state 144 setAccessToken(accessToken); 145 setRefreshToken(refreshToken); 146 setDid(did); 147 setHandle(handle); 148 setSerializedKeyPair(serializedKeyPair); 149 setDpopNonce(dpopNonce); 150 setPdsEndpoint(pdsEndpoint); 151 setIsAuthenticated(true); 152 153 // Store auth data in localStorage (only on client) 154 if (typeof window !== 'undefined') { 155 localStorage.setItem('accessToken', accessToken); 156 localStorage.setItem('refreshToken', refreshToken); 157 localStorage.setItem('did', did); 158 localStorage.setItem('handle', handle); 159 localStorage.setItem('keyPair', serializedKeyPair); 160 if (dpopNonce) { 161 localStorage.setItem('dpopNonce', dpopNonce); 162 } 163 if (pdsEndpoint) { 164 localStorage.setItem('pdsEndpoint', pdsEndpoint); 165 } 166 } 167 }; 168 169 const clearAuth = () => { 170 // Clear auth data from state 171 setAccessToken(null); 172 setRefreshToken(null); 173 setDid(null); 174 setHandle(null); 175 setSerializedKeyPair(null); 176 setDpopNonce(null); 177 setPdsEndpoint(null); 178 setIsAuthenticated(false); 179 180 // Clear auth data from localStorage (only on client) 181 if (typeof window !== 'undefined') { 182 localStorage.removeItem('accessToken'); 183 localStorage.removeItem('refreshToken'); 184 localStorage.removeItem('did'); 185 localStorage.removeItem('handle'); 186 localStorage.removeItem('keyPair'); 187 localStorage.removeItem('dpopNonce'); 188 localStorage.removeItem('pdsEndpoint'); 189 } 190 }; 191 192 return ( 193 <AuthContext.Provider 194 value={{ 195 isAuthenticated, 196 accessToken, 197 refreshToken, 198 did, 199 handle, 200 serializedKeyPair, 201 dpopNonce, 202 pdsEndpoint, 203 setAuth, 204 clearAuth 205 }} 206 > 207 {children} 208 </AuthContext.Provider> 209 ); 210} 211 212export function useAuth() { 213 const context = useContext(AuthContext); 214 if (context === undefined) { 215 throw new Error('useAuth must be used within an AuthProvider'); 216 } 217 return context; 218}