|
21 | 21 | getAuthHeaderFn = fn; |
22 | 22 | } |
23 | 23 |
|
24 | | - // Intercept fetch - synchronous wrapper, async implementation |
| 24 | + // Intercept fetch - MUST replace immediately to catch all calls |
| 25 | + // This runs synchronously, so it catches fetch even if called immediately |
25 | 26 | window.fetch = function (url, options = {}) { |
26 | 27 | const urlString = typeof url === 'string' ? url : url.toString(); |
27 | | - console.log('[Podkey] 🔍 fetch() called:', urlString, options.method || 'GET'); |
28 | | - |
| 28 | + const method = options?.method || 'GET'; |
| 29 | + console.log('[Podkey] 🔍 fetch() intercepted:', urlString, method); |
| 30 | + |
29 | 31 | // If we have the auth function, use it |
30 | | - if (getAuthHeaderFn) { |
31 | | - console.log('[Podkey] Auth function available, adding header...'); |
| 32 | + if (authFunctionReady && getAuthHeaderFn) { |
| 33 | + console.log('[Podkey] ✅ Auth function ready, adding header...'); |
32 | 34 | const promise = (async () => { |
33 | 35 | try { |
34 | 36 | const authHeader = await getAuthHeaderFn(url, options.method || 'GET', options.body); |
|
50 | 52 | } |
51 | 53 | return originalFetch.call(this, url, options); |
52 | 54 | })(); |
53 | | - |
| 55 | + |
54 | 56 | // Handle 401 retry |
55 | 57 | return promise.then(response => { |
56 | 58 | if (response.status === 401 && getAuthHeaderFn) { |
|
84 | 86 | return response; |
85 | 87 | }); |
86 | 88 | } |
87 | | - |
88 | | - // Fallback if auth function not ready yet |
89 | | - console.log('[Podkey] ⚠️ Auth function not ready yet, making request without auth'); |
90 | | - return originalFetch.call(this, url, options); |
| 89 | + |
| 90 | + // Fallback if auth function not ready yet - but still try to get auth |
| 91 | + console.log('[Podkey] ⚠️ Auth function not ready yet, making request...'); |
| 92 | + |
| 93 | + // Even if not ready, try to get auth header asynchronously and retry on 401 |
| 94 | + const requestPromise = originalFetch.call(this, url, options); |
| 95 | + |
| 96 | + // If we get a 401 and auth becomes available, retry |
| 97 | + return requestPromise.then(response => { |
| 98 | + if (response.status === 401) { |
| 99 | + console.log('[Podkey] 🔄 Got 401, checking if auth function is ready now...'); |
| 100 | + // Wait a bit for auth function to be ready, then retry |
| 101 | + return new Promise((resolve) => { |
| 102 | + const checkAuth = () => { |
| 103 | + if (authFunctionReady && getAuthHeaderFn) { |
| 104 | + console.log('[Podkey] 🔄 Auth function now ready, retrying with NIP-98...'); |
| 105 | + getAuthHeaderFn(url, method, options?.body).then(authHeader => { |
| 106 | + if (authHeader) { |
| 107 | + const retryOptions = { ...options }; |
| 108 | + retryOptions.headers = retryOptions.headers || {}; |
| 109 | + if (retryOptions.headers instanceof Headers) { |
| 110 | + retryOptions.headers.set('Authorization', authHeader); |
| 111 | + } else { |
| 112 | + retryOptions.headers['Authorization'] = authHeader; |
| 113 | + } |
| 114 | + originalFetch.call(this, url, retryOptions).then(resolve); |
| 115 | + } else { |
| 116 | + resolve(response); |
| 117 | + } |
| 118 | + }); |
| 119 | + } else { |
| 120 | + // Check again in 100ms |
| 121 | + setTimeout(checkAuth, 100); |
| 122 | + } |
| 123 | + }; |
| 124 | + checkAuth(); |
| 125 | + }); |
| 126 | + } |
| 127 | + return response; |
| 128 | + }); |
91 | 129 | }; |
92 | 130 |
|
93 | 131 | // Intercept XMLHttpRequest |
|
119 | 157 | console.log('[Podkey] ✅ Synchronous interception setup complete'); |
120 | 158 | })(); |
121 | 159 |
|
| 160 | +// Inject NIP-98 interceptor FIRST (must run before any page code) |
| 161 | +const interceptorScript = document.createElement('script'); |
| 162 | +interceptorScript.src = chrome.runtime.getURL('src/nip98-interceptor.js'); |
| 163 | +interceptorScript.onload = function () { |
| 164 | + this.remove(); |
| 165 | +}; |
| 166 | +interceptorScript.onerror = function () { |
| 167 | + console.error('[Podkey] Failed to load nip98-interceptor.js'); |
| 168 | +}; |
| 169 | +(document.head || document.documentElement).appendChild(interceptorScript); |
| 170 | + |
| 171 | +// Listen for NIP-98 auth requests from page context |
| 172 | +window.addEventListener('podkey-nip98-request', async (event) => { |
| 173 | + const { id, url, method, body } = event.detail; |
| 174 | + |
| 175 | + try { |
| 176 | + const response = await chrome.runtime.sendMessage({ |
| 177 | + type: 'CREATE_NIP98_AUTH_HEADER', |
| 178 | + url, |
| 179 | + method, |
| 180 | + body |
| 181 | + }); |
| 182 | + |
| 183 | + if (chrome.runtime.lastError) { |
| 184 | + throw new Error(chrome.runtime.lastError.message); |
| 185 | + } |
| 186 | + |
| 187 | + // Send response back to page context |
| 188 | + window.dispatchEvent(new CustomEvent('podkey-nip98-response', { |
| 189 | + detail: { |
| 190 | + id, |
| 191 | + result: response || null |
| 192 | + } |
| 193 | + })); |
| 194 | + } catch (error) { |
| 195 | + console.error('[Podkey] Error handling NIP-98 request:', error); |
| 196 | + window.dispatchEvent(new CustomEvent('podkey-nip98-response', { |
| 197 | + detail: { |
| 198 | + id, |
| 199 | + result: null |
| 200 | + } |
| 201 | + })); |
| 202 | + } |
| 203 | +}); |
| 204 | + |
122 | 205 | // Inject the nostr provider script into the page |
123 | 206 | const script = document.createElement('script'); |
124 | 207 | script.src = chrome.runtime.getURL('src/nostr-provider.js'); |
|
0 commit comments