33 * Bridges between injected window.nostr and background script
44 */
55
6+ // CRITICAL: Set up fetch/XHR interception IMMEDIATELY, synchronously
7+ // This must run before ANY other code, including page scripts
8+ ( function setupInterceptionImmediately ( ) {
9+ 'use strict' ;
10+
11+ // Store original functions
12+ const originalFetch = window . fetch ;
13+ const originalXHROpen = XMLHttpRequest . prototype . open ;
14+ const originalXHRSend = XMLHttpRequest . prototype . send ;
15+
16+ // Helper to get auth header (will be async, but we'll handle that)
17+ let getAuthHeaderFn = null ;
18+
19+ // Set up the async auth header function (will be defined below)
20+ function setAuthHeaderFn ( fn ) {
21+ getAuthHeaderFn = fn ;
22+ }
23+
24+ // Intercept fetch - synchronous wrapper, async implementation
25+ window . fetch = function ( url , options = { } ) {
26+ const urlString = typeof url === 'string' ? url : url . toString ( ) ;
27+ console . log ( '[Podkey] 🔍 fetch() called:' , urlString , options . method || 'GET' ) ;
28+
29+ // If we have the auth function, use it
30+ if ( getAuthHeaderFn ) {
31+ console . log ( '[Podkey] Auth function available, adding header...' ) ;
32+ const promise = ( async ( ) => {
33+ try {
34+ const authHeader = await getAuthHeaderFn ( url , options . method || 'GET' , options . body ) ;
35+ if ( authHeader ) {
36+ options = options || { } ;
37+ options . headers = options . headers || { } ;
38+ if ( options . headers instanceof Headers ) {
39+ options . headers . set ( 'Authorization' , authHeader ) ;
40+ console . log ( '[Podkey] ✅ Added NIP-98 auth header (Headers)' ) ;
41+ } else {
42+ options . headers [ 'Authorization' ] = authHeader ;
43+ console . log ( '[Podkey] ✅ Added NIP-98 auth header (object)' ) ;
44+ }
45+ } else {
46+ console . log ( '[Podkey] ⚠️ No auth header returned (will retry on 401)' ) ;
47+ }
48+ } catch ( e ) {
49+ console . error ( '[Podkey] Error in fetch interceptor:' , e ) ;
50+ }
51+ return originalFetch . call ( this , url , options ) ;
52+ } ) ( ) ;
53+
54+ // Handle 401 retry
55+ return promise . then ( response => {
56+ if ( response . status === 401 && getAuthHeaderFn ) {
57+ console . log ( '[Podkey] 🔄 401 detected, retrying with auth...' ) ;
58+ return ( async ( ) => {
59+ try {
60+ const authHeader = await getAuthHeaderFn ( url , options . method || 'GET' , options . body ) ;
61+ if ( authHeader ) {
62+ const retryOptions = { ...options } ;
63+ retryOptions . headers = retryOptions . headers || { } ;
64+ if ( retryOptions . headers instanceof Headers ) {
65+ retryOptions . headers . set ( 'Authorization' , authHeader ) ;
66+ } else {
67+ retryOptions . headers [ 'Authorization' ] = authHeader ;
68+ }
69+ console . log ( '[Podkey] 🔄 Retrying with NIP-98 auth...' ) ;
70+ const retryResponse = await originalFetch . call ( this , url , retryOptions ) ;
71+ if ( retryResponse . status === 200 || retryResponse . status === 201 ) {
72+ console . log ( '[Podkey] ✅✅ NIP-98 auth retry successful!' ) ;
73+ } else {
74+ console . log ( '[Podkey] ⚠️ Retry still failed:' , retryResponse . status ) ;
75+ }
76+ return retryResponse ;
77+ }
78+ } catch ( e ) {
79+ console . error ( '[Podkey] Error in 401 retry:' , e ) ;
80+ }
81+ return response ;
82+ } ) ( ) ;
83+ }
84+ return response ;
85+ } ) ;
86+ }
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 ) ;
91+ } ;
92+
93+ // Intercept XMLHttpRequest
94+ XMLHttpRequest . prototype . open = function ( method , url , ...args ) {
95+ this . _podkeyMethod = method ;
96+ this . _podkeyUrl = url ;
97+ return originalXHROpen . apply ( this , [ method , url , ...args ] ) ;
98+ } ;
99+
100+ XMLHttpRequest . prototype . send = function ( body ) {
101+ if ( getAuthHeaderFn && this . _podkeyUrl ) {
102+ ( async ( ) => {
103+ try {
104+ const authHeader = await getAuthHeaderFn ( this . _podkeyUrl , this . _podkeyMethod , body ) ;
105+ if ( authHeader ) {
106+ this . setRequestHeader ( 'Authorization' , authHeader ) ;
107+ }
108+ } catch ( e ) {
109+ console . error ( '[Podkey] Error in XHR interceptor:' , e ) ;
110+ }
111+ } ) ( ) ;
112+ }
113+ return originalXHRSend . apply ( this , [ body ] ) ;
114+ } ;
115+
116+ // Expose setter for the auth function
117+ window . __podkey_setAuthFn = setAuthHeaderFn ;
118+
119+ console . log ( '[Podkey] ✅ Synchronous interception setup complete' ) ;
120+ } ) ( ) ;
121+
6122// Inject the nostr provider script into the page
7123const script = document . createElement ( 'script' ) ;
8124script . src = chrome . runtime . getURL ( 'src/nostr-provider.js' ) ;
@@ -67,110 +183,10 @@ window.addEventListener('podkey-request', async (event) => {
67183 }
68184} ) ;
69185
70- // NIP-98 auto-auth: Intercept fetch and XMLHttpRequest
71- ( function interceptHttpRequests ( ) {
72- // Intercept fetch
73- const originalFetch = window . fetch ;
74- window . fetch = async function ( url , options = { } ) {
75- try {
76- const authHeader = await getNip98AuthHeader ( url , options . method || 'GET' , options . body ) ;
77- if ( authHeader ) {
78- options . headers = options . headers || { } ;
79- if ( options . headers instanceof Headers ) {
80- options . headers . set ( 'Authorization' , authHeader ) ;
81- } else {
82- options . headers [ 'Authorization' ] = authHeader ;
83- }
84- }
85- } catch ( error ) {
86- console . error ( '[Podkey] Error adding NIP-98 auth to fetch:' , error ) ;
87- }
88-
89- const response = await originalFetch ( url , options ) ;
90-
91- // Handle 401 responses - retry with NIP-98 auth
92- if ( response . status === 401 ) {
93- console . log ( '[Podkey] 401 detected, attempting NIP-98 auth retry for:' , url ) ;
94- try {
95- // Clone response to read body if needed, but for retry we'll make a new request
96- const authHeader = await getNip98AuthHeader ( url , options . method || 'GET' , options . body ) ;
97- if ( authHeader ) {
98- // Retry with auth
99- const retryOptions = { ...options } ;
100- retryOptions . headers = retryOptions . headers || { } ;
101-
102- // Handle Headers object
103- if ( retryOptions . headers instanceof Headers ) {
104- retryOptions . headers . set ( 'Authorization' , authHeader ) ;
105- } else if ( retryOptions . headers instanceof Object ) {
106- retryOptions . headers [ 'Authorization' ] = authHeader ;
107- } else {
108- retryOptions . headers = { 'Authorization' : authHeader } ;
109- }
110-
111- console . log ( '[Podkey] Retrying request with NIP-98 auth' ) ;
112- const retryResponse = await originalFetch ( url , retryOptions ) ;
113-
114- if ( retryResponse . status === 200 || retryResponse . status === 201 ) {
115- console . log ( '[Podkey] ✅ NIP-98 auth successful!' ) ;
116- } else {
117- console . log ( '[Podkey] ⚠️ Retry still failed with status:' , retryResponse . status ) ;
118- }
119-
120- return retryResponse ;
121- } else {
122- console . log ( '[Podkey] No NIP-98 auth header available for retry' ) ;
123- }
124- } catch ( error ) {
125- console . error ( '[Podkey] Error retrying fetch with NIP-98 auth:' , error ) ;
126- }
127- }
128-
129- return response ;
130- } ;
131-
132- // Intercept XMLHttpRequest
133- const originalOpen = XMLHttpRequest . prototype . open ;
134- const originalSend = XMLHttpRequest . prototype . send ;
135-
136- XMLHttpRequest . prototype . open = function ( method , url , ...args ) {
137- this . _podkeyMethod = method ;
138- this . _podkeyUrl = url ;
139- return originalOpen . apply ( this , [ method , url , ...args ] ) ;
140- } ;
141-
142- XMLHttpRequest . prototype . send = async function ( body ) {
143- try {
144- const authHeader = await getNip98AuthHeader ( this . _podkeyUrl , this . _podkeyMethod , body ) ;
145- if ( authHeader ) {
146- this . setRequestHeader ( 'Authorization' , authHeader ) ;
147- }
148- } catch ( error ) {
149- console . error ( '[Podkey] Error adding NIP-98 auth to XHR:' , error ) ;
150- }
151-
152- // Handle 401 responses
153- this . addEventListener ( 'load' , async function ( ) {
154- if ( this . status === 401 ) {
155- try {
156- const authHeader = await getNip98AuthHeader ( this . _podkeyUrl , this . _podkeyMethod , body ) ;
157- if ( authHeader ) {
158- // Retry with auth
159- const retryXhr = new XMLHttpRequest ( ) ;
160- retryXhr . open ( this . _podkeyMethod , this . _podkeyUrl ) ;
161- retryXhr . setRequestHeader ( 'Authorization' , authHeader ) ;
162- // Copy other headers if needed
163- retryXhr . send ( body ) ;
164- // Note: This is a simplified retry - in practice, you'd want to handle the response properly
165- }
166- } catch ( error ) {
167- console . error ( '[Podkey] Error retrying XHR with NIP-98 auth:' , error ) ;
168- }
169- }
170- } ) ;
171-
172- return originalSend . apply ( this , [ body ] ) ;
173- } ;
186+ // NIP-98 auto-auth: Set up the async auth header function
187+ // This connects to the synchronous interceptor above
188+ ( function setupAuthFunction ( ) {
189+ console . log ( '[Podkey] Setting up NIP-98 auth function...' ) ;
174190
175191 async function getNip98AuthHeader ( url , method , body ) {
176192 try {
@@ -190,9 +206,9 @@ window.addEventListener('podkey-request', async (event) => {
190206 }
191207
192208 if ( response ) {
193- console . log ( '[Podkey] Got NIP-98 auth header' ) ;
209+ console . log ( '[Podkey] ✅ Got NIP-98 auth header' ) ;
194210 } else {
195- console . log ( '[Podkey] No NIP-98 auth header (origin not trusted, auto-sign disabled, or no keypair)' ) ;
211+ console . log ( '[Podkey] ❌ No NIP-98 auth header (origin not trusted, auto-sign disabled, or no keypair)' ) ;
196212 }
197213
198214 return response || null ;
@@ -201,6 +217,14 @@ window.addEventListener('podkey-request', async (event) => {
201217 return null ;
202218 }
203219 }
220+
221+ // Connect the auth function to the synchronous interceptor
222+ if ( window . __podkey_setAuthFn ) {
223+ window . __podkey_setAuthFn ( getNip98AuthHeader ) ;
224+ console . log ( '[Podkey] ✅ Auth function connected' ) ;
225+ } else {
226+ console . error ( '[Podkey] ❌ Could not connect auth function - interceptor not ready' ) ;
227+ }
204228} ) ( ) ;
205229
206230console . log ( '[Podkey] Content script loaded with NIP-98 auto-auth interception' ) ;
0 commit comments