This repository has no description
0

Configure Feed

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

fix third paryy pds again

+127 -40
+19 -1
app/src/lib/auth-context.tsx
··· 88 88 89 89 // Refresh the token with enhanced error handling 90 90 try { 91 + // CRITICAL FIX: Follow the same server selection logic as in refreshAccessToken 92 + let refreshAuthServer = pdsEndpoint; 93 + 94 + // For bsky.network PDSes, use bsky.social 95 + if (pdsEndpoint.includes('bsky.network')) { 96 + console.log('[AUTH CONTEXT] Using bsky.social for token refresh with bsky.network PDS'); 97 + refreshAuthServer = 'https://bsky.social'; 98 + } else if (pdsEndpoint.includes('bsky.social')) { 99 + // Already using bsky.social 100 + console.log('[AUTH CONTEXT] Using bsky.social directly for token refresh'); 101 + } else { 102 + // For third-party PDSes, use their own endpoint 103 + console.log('[AUTH CONTEXT] Using third-party PDS\'s own endpoint for token refresh:', pdsEndpoint); 104 + } 105 + 91 106 const { accessToken: newAccessToken, refreshToken: newRefreshToken, dpopNonce: newNonce } = 92 - await refreshAccessToken(refreshToken, keyPair, pdsEndpoint); 107 + await refreshAccessToken(refreshToken, keyPair, refreshAuthServer); 93 108 94 109 // Update state 95 110 setAccessToken(newAccessToken); ··· 101 116 localStorage.setItem('accessToken', newAccessToken); 102 117 localStorage.setItem('refreshToken', newRefreshToken); 103 118 if (newNonce) localStorage.setItem('dpopNonce', newNonce); 119 + // Ensure we have the PDS endpoint stored consistently 120 + localStorage.setItem('pdsEndpoint', pdsEndpoint); 121 + localStorage.setItem('bsky_auth_pdsEndpoint', pdsEndpoint); 104 122 } 105 123 106 124 setLastTokenRefresh(Date.now());
+108 -39
app/src/lib/bluesky-api.ts
··· 92 92 // Endpoint for token refresh 93 93 const tokenEndpoint = `${authServer}/oauth/token`; 94 94 95 - // First, ALWAYS get a fresh nonce before attempting token refresh 95 + // For third-party PDS, directly get nonce from PDS endpoint 96 + // This is critical because third-party PDSes need their own specific nonce 96 97 let dpopNonce = null; 97 - try { 98 - // Try server-side nonce retrieval first 99 - console.log('[TOKEN REFRESH] Getting fresh nonce from server API'); 100 - const nonceResponse = await fetch('/api/auth/nonce', { 101 - method: 'POST', 102 - headers: { 'Content-Type': 'application/json' }, 103 - body: JSON.stringify({ pdsEndpoint }) 104 - }); 105 - 106 - if (nonceResponse.ok) { 107 - const nonceData = await nonceResponse.json(); 108 - if (nonceData.nonce) { 109 - dpopNonce = nonceData.nonce; 110 - console.log('[TOKEN REFRESH] Got fresh nonce from server API:', dpopNonce); 98 + 99 + // Special handling for third-party PDS token refresh 100 + if (!authServer.includes('bsky.social') && !authServer.includes('bsky.network')) { 101 + try { 102 + // For third-party PDS, use a two-step approach to get the valid nonce: 103 + console.log('[TOKEN REFRESH] Direct nonce retrieval from third-party PDS'); 104 + 105 + // Step 1: Send an empty token refresh request to get a nonce error 106 + // This ensures we get the exact format of nonce the PDS expects 107 + console.log('[TOKEN REFRESH] Step 1: Sending probe request to get nonce'); 108 + const probeResponse = await fetch(tokenEndpoint, { 109 + method: 'POST', 110 + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 111 + body: new URLSearchParams({ 112 + 'grant_type': 'refresh_token', 113 + 'refresh_token': refreshToken, 114 + 'client_id': 'https://flushes.app/client-metadata.json' 115 + }) 116 + }); 117 + 118 + // Get the nonce from the error response 119 + const probeNonce = probeResponse.headers.get('DPoP-Nonce'); 120 + if (probeNonce) { 121 + console.log('[TOKEN REFRESH] Got DPoP-Nonce from probe response:', probeNonce); 122 + dpopNonce = probeNonce; 123 + } else { 124 + // Try to parse the response body for a nonce in the error message 125 + try { 126 + const probeData = await probeResponse.json(); 127 + if (probeData.error === 'use_dpop_nonce' && probeData.nonce) { 128 + console.log('[TOKEN REFRESH] Got nonce from error body:', probeData.nonce); 129 + dpopNonce = probeData.nonce; 130 + } 131 + } catch (e) { 132 + console.warn('[TOKEN REFRESH] Failed to parse probe response:', e); 133 + } 111 134 } 135 + } catch (directError) { 136 + console.warn('[TOKEN REFRESH] Direct nonce retrieval failed:', directError); 112 137 } 113 - 114 - // If server-side retrieval fails, try client-side 115 - if (!dpopNonce) { 116 - console.log('[TOKEN REFRESH] Trying HEAD request for nonce'); 117 - const headResponse = await fetch(tokenEndpoint, { method: 'HEAD' }); 118 - dpopNonce = headResponse.headers.get('DPoP-Nonce'); 119 - } 120 - 121 - // If still no nonce, try POST probe 122 - if (!dpopNonce) { 123 - console.log('[TOKEN REFRESH] Trying POST probe for nonce'); 124 - const probeResponse = await fetch(tokenEndpoint, { 138 + } 139 + 140 + // Fall back to standard nonce retrieval methods if direct method failed 141 + if (!dpopNonce) { 142 + try { 143 + // Try server-side nonce retrieval first 144 + console.log('[TOKEN REFRESH] Getting fresh nonce from server API'); 145 + const nonceResponse = await fetch('/api/auth/nonce', { 125 146 method: 'POST', 126 - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 127 - body: new URLSearchParams({}) // Empty body to trigger error response with nonce 147 + headers: { 'Content-Type': 'application/json' }, 148 + body: JSON.stringify({ pdsEndpoint: authServer }) // Use the correct server 128 149 }); 129 - dpopNonce = probeResponse.headers.get('DPoP-Nonce'); 150 + 151 + if (nonceResponse.ok) { 152 + const nonceData = await nonceResponse.json(); 153 + if (nonceData.nonce) { 154 + dpopNonce = nonceData.nonce; 155 + console.log('[TOKEN REFRESH] Got fresh nonce from server API:', dpopNonce); 156 + } 157 + } 158 + 159 + // If server-side retrieval fails, try client-side 160 + if (!dpopNonce) { 161 + console.log('[TOKEN REFRESH] Trying HEAD request for nonce'); 162 + const headResponse = await fetch(tokenEndpoint, { method: 'HEAD' }); 163 + dpopNonce = headResponse.headers.get('DPoP-Nonce'); 164 + } 165 + 166 + // If still no nonce, try POST probe 167 + if (!dpopNonce) { 168 + console.log('[TOKEN REFRESH] Trying POST probe for nonce'); 169 + const probeResponse = await fetch(tokenEndpoint, { 170 + method: 'POST', 171 + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 172 + body: new URLSearchParams({}) // Empty body to trigger error response with nonce 173 + }); 174 + dpopNonce = probeResponse.headers.get('DPoP-Nonce'); 175 + } 176 + } catch (nonceError) { 177 + console.warn('[TOKEN REFRESH] Failed to get initial nonce:', nonceError); 130 178 } 131 - } catch (nonceError) { 132 - console.warn('[TOKEN REFRESH] Failed to get initial nonce:', nonceError); 133 179 } 134 180 135 181 if (!dpopNonce) { ··· 165 211 }); 166 212 167 213 // Handle nonce error explicitly - this is the critical part! 168 - if (response.status === 401) { 214 + if (response.status === 401 || response.status === 400) { 169 215 let responseBody; 170 216 try { 171 217 responseBody = await response.json(); ··· 173 219 responseBody = {}; 174 220 } 175 221 176 - const newNonce = response.headers.get('DPoP-Nonce'); 222 + // Try to get nonce from multiple sources 223 + let newNonce = response.headers.get('DPoP-Nonce'); 224 + 225 + // Also check for nonce in the response body (some PDSes return it there) 226 + if (!newNonce && responseBody.nonce) { 227 + newNonce = responseBody.nonce; 228 + console.log('[TOKEN REFRESH] Found nonce in response body:', newNonce); 229 + } 230 + 231 + // Some servers use DPoP-Nonce header instead of nonce in body 232 + if (!newNonce && response.headers.get('DPoP-Nonce')) { 233 + newNonce = response.headers.get('DPoP-Nonce'); 234 + console.log('[TOKEN REFRESH] Found DPoP-Nonce in response headers:', newNonce); 235 + } 177 236 178 237 // Check for DPoP nonce error 179 - if ( 180 - (responseBody.error === 'use_dpop_nonce' || 181 - (responseBody.error_description && responseBody.error_description.includes('nonce'))) && 182 - newNonce 183 - ) { 238 + const isNonceError = 239 + responseBody.error === 'use_dpop_nonce' || 240 + responseBody.error === 'invalid_dpop_proof' || 241 + (responseBody.error_description && ( 242 + responseBody.error_description.includes('nonce') || 243 + responseBody.error_description.includes('DPoP') 244 + )); 245 + 246 + if (isNonceError && newNonce) { 184 247 console.log('[TOKEN REFRESH] Received nonce error, retrying with new nonce:', newNonce); 185 248 186 249 // Generate new DPoP token with the provided nonce ··· 414 477 // For third-party PDSes, use their own endpoint 415 478 console.log('[AUTH CHECK] Will use third-party PDS\'s own endpoint:', pdsEndpoint); 416 479 // Keep refreshAuthServer as the original PDS endpoint 480 + 481 + // Ensure we update the PDS endpoint everywhere 482 + if (typeof localStorage !== 'undefined') { 483 + localStorage.setItem('pdsEndpoint', pdsEndpoint); 484 + localStorage.setItem('bsky_auth_pdsEndpoint', pdsEndpoint); 485 + } 417 486 } 418 487 419 488 const { accessToken: newAccessToken, refreshToken: newRefreshToken, dpopNonce: newNonce } =