@@ -219,6 +219,45 @@ describe('createClientFromRequest', () => {
219219 // Should throw error for empty headers instead of continuing silently
220220 expect ( ( ) => createClientFromRequest ( mockRequest ) ) . toThrow ( 'Invalid authorization header format. Expected "Bearer <token>"' ) ;
221221 } ) ;
222+
223+ test ( 'should propagate Base44-Client-IP header when present' , ( ) => {
224+ const mockRequest = {
225+ headers : {
226+ get : ( name ) => {
227+ const headers = {
228+ 'Base44-App-Id' : 'test-app-id' ,
229+ 'Base44-Client-IP' : '192.168.1.100'
230+ } ;
231+ return headers [ name ] || null ;
232+ }
233+ }
234+ } ;
235+
236+ const client = createClientFromRequest ( mockRequest ) ;
237+
238+ expect ( client ) . toBeDefined ( ) ;
239+ const config = client . getConfig ( ) ;
240+ expect ( config . appId ) . toBe ( 'test-app-id' ) ;
241+ } ) ;
242+
243+ test ( 'should work without Base44-Client-IP header' , ( ) => {
244+ const mockRequest = {
245+ headers : {
246+ get : ( name ) => {
247+ const headers = {
248+ 'Base44-App-Id' : 'test-app-id'
249+ } ;
250+ return headers [ name ] || null ;
251+ }
252+ }
253+ } ;
254+
255+ const client = createClientFromRequest ( mockRequest ) ;
256+
257+ expect ( client ) . toBeDefined ( ) ;
258+ const config = client . getConfig ( ) ;
259+ expect ( config . appId ) . toBe ( 'test-app-id' ) ;
260+ } ) ;
222261} ) ;
223262
224263
@@ -415,4 +454,100 @@ describe('Service Role Authorization Headers', () => {
415454 expect ( scope . isDone ( ) ) . toBe ( true ) ;
416455 } ) ;
417456
457+ test ( 'should propagate Base44-Client-IP header in API requests when created from request' , async ( ) => {
458+ const clientIp = '192.168.1.100' ;
459+
460+ const mockRequest = {
461+ headers : {
462+ get : ( name ) => {
463+ const headers = {
464+ 'Authorization' : 'Bearer user-token-123' ,
465+ 'Base44-App-Id' : appId ,
466+ 'Base44-Api-Url' : serverUrl ,
467+ 'Base44-Client-IP' : clientIp
468+ } ;
469+ return headers [ name ] || null ;
470+ }
471+ }
472+ } ;
473+
474+ const client = createClientFromRequest ( mockRequest ) ;
475+
476+ // Mock entities request and verify Base44-Client-IP header is present
477+ scope . get ( `/api/apps/${ appId } /entities/Todo` )
478+ . matchHeader ( 'Base44-Client-IP' , clientIp )
479+ . matchHeader ( 'Authorization' , 'Bearer user-token-123' )
480+ . reply ( 200 , { items : [ ] , total : 0 } ) ;
481+
482+ // Make request
483+ await client . entities . Todo . list ( ) ;
484+
485+ // Verify all mocks were called (including header match)
486+ expect ( scope . isDone ( ) ) . toBe ( true ) ;
487+ } ) ;
488+
489+ test ( 'should not include Base44-Client-IP header when not present in original request' , async ( ) => {
490+ const mockRequest = {
491+ headers : {
492+ get : ( name ) => {
493+ const headers = {
494+ 'Authorization' : 'Bearer user-token-123' ,
495+ 'Base44-App-Id' : appId ,
496+ 'Base44-Api-Url' : serverUrl
497+ } ;
498+ return headers [ name ] || null ;
499+ }
500+ }
501+ } ;
502+
503+ const client = createClientFromRequest ( mockRequest ) ;
504+
505+ // Mock entities request and verify Base44-Client-IP header is NOT present
506+ scope . get ( `/api/apps/${ appId } /entities/Todo` )
507+ . matchHeader ( 'Base44-Client-IP' , ( val ) => ! val ) // Should not have this header
508+ . matchHeader ( 'Authorization' , 'Bearer user-token-123' )
509+ . reply ( 200 , { items : [ ] , total : 0 } ) ;
510+
511+ // Make request
512+ await client . entities . Todo . list ( ) ;
513+
514+ // Verify all mocks were called
515+ expect ( scope . isDone ( ) ) . toBe ( true ) ;
516+ } ) ;
517+
518+ test ( 'should propagate Base44-Client-IP header in service role API requests' , async ( ) => {
519+ const clientIp = '10.0.0.50' ;
520+
521+ const mockRequest = {
522+ headers : {
523+ get : ( name ) => {
524+ const headers = {
525+ 'Base44-Service-Authorization' : 'Bearer service-token-123' ,
526+ 'Base44-App-Id' : appId ,
527+ 'Base44-Api-Url' : serverUrl ,
528+ 'Base44-Client-IP' : clientIp
529+ } ;
530+ return headers [ name ] || null ;
531+ }
532+ }
533+ } ;
534+
535+ const client = createClientFromRequest ( mockRequest ) ;
536+
537+ // Mock service role entities request and verify Base44-Client-IP header is present
538+ scope . get ( `/api/apps/${ appId } /entities/User/123` )
539+ . matchHeader ( 'Base44-Client-IP' , clientIp )
540+ . matchHeader ( 'Authorization' , 'Bearer service-token-123' )
541+ . reply ( 200 , { id : '123' , name : 'Test User' } ) ;
542+
543+ // Make request using service role
544+ const result = await client . asServiceRole . entities . User . get ( '123' ) ;
545+
546+ // Verify response
547+ expect ( result . id ) . toBe ( '123' ) ;
548+
549+ // Verify all mocks were called (including header match)
550+ expect ( scope . isDone ( ) ) . toBe ( true ) ;
551+ } ) ;
552+
418553} ) ;
0 commit comments