@@ -27,6 +27,7 @@ import {
27
27
generatePortalUrl ,
28
28
Role ,
29
29
GeneratePortalUrlParams ,
30
+ navigateToKinde ,
30
31
} from "@kinde/js-utils" ;
31
32
import * as storeState from "./store" ;
32
33
import React , {
@@ -39,7 +40,7 @@ import React, {
39
40
import { KindeContext , KindeContextProps } from "./KindeContext" ;
40
41
import { getRedirectUrl } from "../utils/getRedirectUrl" ;
41
42
import packageJson from "../../package.json" ;
42
- import { ErrorProps , LogoutOptions } from "./types" ;
43
+ import { ErrorProps , LogoutOptions , PopupOptions } from "./types" ;
43
44
import type { RefreshTokenResult } from "@kinde/js-utils" ;
44
45
// TODO: need to look for old token store and convert.
45
46
storageSettings . keyPrefix = "" ;
@@ -98,6 +99,11 @@ type KindeProviderProps = {
98
99
callbacks ?: KindeCallbacks ;
99
100
scope ?: string ;
100
101
forceChildrenRender ?: boolean ;
102
+ /**
103
+ * When the application is shown in an iFrame, auth will open in a popup window.
104
+ * This is the options for the popup window.
105
+ */
106
+ popupOptions ?: PopupOptions ;
101
107
} ;
102
108
103
109
const defaultCallbacks : KindeCallbacks = {
@@ -131,6 +137,7 @@ export const KindeProvider = ({
131
137
callbacks = { } ,
132
138
logoutUri,
133
139
forceChildrenRender = false ,
140
+ popupOptions = { } ,
134
141
} : KindeProviderProps ) => {
135
142
const mergedCallbacks = { ...defaultCallbacks , ...callbacks } ;
136
143
@@ -189,9 +196,25 @@ export const KindeProvider = ({
189
196
IssuerRouteTypes . login ,
190
197
authProps ,
191
198
) ;
192
- document . location = authUrl . url . toString ( ) ;
199
+
200
+ try {
201
+ navigateToKinde ( {
202
+ url : authUrl . url . toString ( ) ,
203
+ popupOptions,
204
+ handleResult : processAuthResult ,
205
+ } ) ;
206
+ } catch ( error ) {
207
+ mergedCallbacks . onError ?.(
208
+ {
209
+ error : "ERR_POPUP" ,
210
+ errorDescription : ( error as Error ) . message ,
211
+ } ,
212
+ { } ,
213
+ { } as KindeContextProps ,
214
+ ) ;
215
+ }
193
216
} ,
194
- [ audience , clientId , redirectUri ] ,
217
+ [ audience , clientId , redirectUri , popupOptions , mergedCallbacks ] ,
195
218
) ;
196
219
197
220
const register = useCallback (
@@ -231,7 +254,22 @@ export const KindeProvider = ({
231
254
IssuerRouteTypes . register ,
232
255
authProps ,
233
256
) ;
234
- document . location = authUrl . url . toString ( ) ;
257
+ try {
258
+ navigateToKinde ( {
259
+ url : authUrl . url . toString ( ) ,
260
+ popupOptions,
261
+ handleResult : processAuthResult ,
262
+ } ) ;
263
+ } catch ( error ) {
264
+ mergedCallbacks . onError ?.(
265
+ {
266
+ error : "ERR_POPUP" ,
267
+ errorDescription : ( error as Error ) . message ,
268
+ } ,
269
+ { } ,
270
+ { } as KindeContextProps ,
271
+ ) ;
272
+ }
235
273
} catch ( error ) {
236
274
console . error ( "Register error:" , error ) ;
237
275
mergedCallbacks . onError ?.(
@@ -244,7 +282,7 @@ export const KindeProvider = ({
244
282
) ;
245
283
}
246
284
} ,
247
- [ redirectUri ] ,
285
+ [ redirectUri , popupOptions , mergedCallbacks ] ,
248
286
) ;
249
287
250
288
const logout = useCallback ( async ( options ?: string | LogoutOptions ) => {
@@ -275,11 +313,32 @@ export const KindeProvider = ({
275
313
} ) ;
276
314
277
315
await Promise . all ( [
278
- storeState . memoryStorage . destroySession ( ) ,
279
- storeState . localStorage . destroySession ( ) ,
316
+ storeState . memoryStorage . removeSessionItem ( StorageKeys . idToken ) ,
317
+ storeState . memoryStorage . removeSessionItem ( StorageKeys . accessToken ) ,
318
+ storeState . memoryStorage . removeSessionItem ( StorageKeys . refreshToken ) ,
319
+ storeState . localStorage . removeSessionItem ( StorageKeys . refreshToken ) ,
280
320
] ) ;
281
321
282
- document . location = `${ domain } /logout?${ params . toString ( ) } ` ;
322
+ await storeState . localStorage . setSessionItem (
323
+ storeState . LocalKeys . performingLogout ,
324
+ "true" ,
325
+ ) ;
326
+
327
+ try {
328
+ await navigateToKinde ( {
329
+ url : `${ domain } /logout?${ params . toString ( ) } ` ,
330
+ popupOptions,
331
+ } ) ;
332
+ } catch ( error ) {
333
+ mergedCallbacks . onError ?.(
334
+ {
335
+ error : "ERR_POPUP" ,
336
+ errorDescription : ( error as Error ) . message ,
337
+ } ,
338
+ { } ,
339
+ { } as KindeContextProps ,
340
+ ) ;
341
+ }
283
342
} catch ( error ) {
284
343
console . error ( "Logout error:" , error ) ;
285
344
mergedCallbacks . onError ?.(
@@ -390,6 +449,89 @@ export const KindeProvider = ({
390
449
[ mergedCallbacks , contextValue ] ,
391
450
) ;
392
451
452
+ // Function to process authentication result from popup
453
+ const processAuthResult = useCallback (
454
+ async ( searchParams : URLSearchParams ) => {
455
+ const decoded = atob ( searchParams . get ( "state" ) || "" ) ;
456
+ let returnedState : StateWithKinde ;
457
+ let kindeState : KindeState ;
458
+ try {
459
+ returnedState = JSON . parse ( decoded ) ;
460
+ kindeState = Object . assign (
461
+ returnedState . kinde || { event : PromptTypes . login } ,
462
+ ) ;
463
+ } catch ( error ) {
464
+ console . error ( "Error parsing state:" , error ) ;
465
+ mergedCallbacks . onError ?.(
466
+ {
467
+ error : "ERR_STATE_PARSE" ,
468
+ errorDescription : String ( error ) ,
469
+ } ,
470
+ { } ,
471
+ contextValue ,
472
+ ) ;
473
+ returnedState = { } as StateWithKinde ;
474
+ kindeState = { event : AuthEvent . login } ;
475
+ }
476
+ try {
477
+ const codeResponse = await exchangeAuthCode ( {
478
+ urlParams : searchParams ,
479
+ domain,
480
+ clientId,
481
+ redirectURL : getRedirectUrl ( redirectUri ) ,
482
+ autoRefresh : true ,
483
+ onRefresh,
484
+ } ) ;
485
+
486
+ if ( codeResponse . success ) {
487
+ const user = await getUserProfile ( ) ;
488
+ if ( user ) {
489
+ setState ( ( val ) => ( { ...val , user, isAuthenticated : true } ) ) ;
490
+ mergedCallbacks . onSuccess ?.(
491
+ user ,
492
+ {
493
+ ...returnedState ,
494
+ kinde : undefined ,
495
+ } ,
496
+ contextValue ,
497
+ ) ;
498
+ if ( mergedCallbacks . onEvent ) {
499
+ mergedCallbacks . onEvent (
500
+ kindeState . event ,
501
+ {
502
+ ...returnedState ,
503
+ kinde : undefined ,
504
+ } ,
505
+ contextValue ,
506
+ ) ;
507
+ }
508
+ }
509
+ } else {
510
+ mergedCallbacks . onError ?.(
511
+ {
512
+ error : "ERR_CODE_EXCHANGE" ,
513
+ errorDescription : codeResponse . error ,
514
+ } ,
515
+ returnedState ,
516
+ contextValue ,
517
+ ) ;
518
+ }
519
+ } catch ( error ) {
520
+ mergedCallbacks . onError ?.(
521
+ {
522
+ error : "ERR_POPUP_AUTH" ,
523
+ errorDescription : String ( error ) ,
524
+ } ,
525
+ returnedState ,
526
+ contextValue ,
527
+ ) ;
528
+ } finally {
529
+ setState ( ( val ) => ( { ...val , isLoading : false } ) ) ;
530
+ }
531
+ } ,
532
+ [ domain , clientId , redirectUri , onRefresh , mergedCallbacks , contextValue ] ,
533
+ ) ;
534
+
393
535
const handleFocus = useCallback ( ( ) => {
394
536
if ( document . visibilityState === "visible" && state . isAuthenticated ) {
395
537
refreshToken ( { domain, clientId, onRefresh } ) . catch ( ( error ) => {
@@ -412,8 +554,6 @@ export const KindeProvider = ({
412
554
await checkAuth ( { domain, clientId } ) ;
413
555
initRef . current = true ;
414
556
const params = new URLSearchParams ( window . location . search ) ;
415
- let returnedState : StateWithKinde ;
416
- let kindeState : KindeState ;
417
557
418
558
if ( params . has ( "error" ) ) {
419
559
const errorCode = params . get ( "error" ) ;
@@ -428,6 +568,17 @@ export const KindeProvider = ({
428
568
return ;
429
569
}
430
570
571
+ if (
572
+ ( await storeState . localStorage . getSessionItem (
573
+ storeState . LocalKeys . performingLogout ,
574
+ ) ) === "true"
575
+ ) {
576
+ await storeState . localStorage . removeSessionItem (
577
+ storeState . LocalKeys . performingLogout ,
578
+ ) ;
579
+ window . close ( ) ;
580
+ }
581
+
431
582
const hasCode = params . has ( "code" ) ;
432
583
if ( ! hasCode ) {
433
584
try {
@@ -447,77 +598,28 @@ export const KindeProvider = ({
447
598
return ;
448
599
}
449
600
450
- const decoded = atob ( params . get ( "state" ) || "" ) ;
451
-
452
- try {
453
- returnedState = JSON . parse ( decoded ) ;
454
- kindeState = Object . assign (
455
- returnedState . kinde || { event : PromptTypes . login } ,
456
- ) ;
457
- } catch ( error ) {
458
- console . error ( "Error parsing state:" , error ) ;
459
- mergedCallbacks . onError ?.(
601
+ if ( window . opener ) {
602
+ const searchParams = new URLSearchParams ( window . location . search ) ;
603
+ window . opener . postMessage (
460
604
{
461
- error : "ERR_STATE_PARSE " ,
462
- errorDescription : String ( error ) ,
605
+ type : "KINDE_AUTH_RESULT " ,
606
+ result : Object . fromEntries ( searchParams . entries ( ) ) ,
463
607
} ,
464
- { } ,
465
- contextValue ,
608
+ window . location . origin ,
466
609
) ;
467
- returnedState = { } as StateWithKinde ;
468
- kindeState = { event : AuthEvent . login } ;
469
- }
470
- try {
471
- const redirectURL = ( await storeState . memoryStorage . getSessionItem (
472
- storeState . LocalKeys . redirectUri ,
473
- ) ) as string ;
474
-
475
- const codeResponse = await exchangeAuthCode ( {
476
- urlParams : new URLSearchParams ( window . location . search ) ,
477
- domain,
478
- clientId,
479
- redirectURL : getRedirectUrl ( redirectURL || redirectUri ) ,
480
- autoRefresh : true ,
481
- onRefresh,
482
- } ) ;
483
-
484
- if ( codeResponse . success ) {
485
- const user = await getUserProfile ( ) ;
486
- if ( user ) {
487
- setState ( ( val ) => ( { ...val , user, isAuthenticated : true } ) ) ;
488
- mergedCallbacks . onSuccess ?.(
489
- user ,
490
- {
491
- ...returnedState ,
492
- kinde : undefined ,
493
- } ,
494
- contextValue ,
495
- ) ;
496
- if ( mergedCallbacks . onEvent ) {
497
- mergedCallbacks . onEvent (
498
- kindeState . event ,
499
- {
500
- ...returnedState ,
501
- kinde : undefined ,
502
- } ,
503
- contextValue ,
504
- ) ;
505
- }
506
- }
507
- } else {
508
- mergedCallbacks . onError ?.(
509
- {
510
- error : "ERR_CODE_EXCHANGE" ,
511
- errorDescription : codeResponse . error ,
512
- } ,
513
- returnedState ,
514
- contextValue ,
515
- ) ;
516
- }
517
- } finally {
518
- setState ( ( val ) => ( { ...val , isLoading : false } ) ) ;
610
+ window . close ( ) ;
519
611
}
520
- } , [ clientId , domain , redirectUri , mergedCallbacks , contextValue , onRefresh ] ) ;
612
+ await processAuthResult ( new URLSearchParams ( window . location . search ) ) ;
613
+ } , [
614
+ clientId ,
615
+ domain ,
616
+ redirectUri ,
617
+ mergedCallbacks ,
618
+ contextValue ,
619
+ onRefresh ,
620
+ login ,
621
+ processAuthResult ,
622
+ ] ) ;
521
623
522
624
useEffect ( ( ) => {
523
625
const mounted = { current : true } ;
0 commit comments