@@ -63,12 +63,32 @@ export interface AuthorizeService {
63
63
64
64
class OidcAuthorizeService implements AuthorizeService {
65
65
private _userManager : UserManager ;
66
-
66
+ private _intialSilentSignIn : Promise < void > | undefined ;
67
67
constructor ( userManager : UserManager ) {
68
68
this . _userManager = userManager ;
69
69
}
70
70
71
+ async trySilentSignIn ( ) {
72
+ if ( ! this . _intialSilentSignIn ) {
73
+ this . _intialSilentSignIn = ( async ( ) => {
74
+ try {
75
+ await this . _userManager . signinSilent ( ) ;
76
+ return ;
77
+ } catch ( e ) {
78
+ }
79
+ } ) ( ) ;
80
+ }
81
+
82
+ return this . _intialSilentSignIn ;
83
+ }
84
+
71
85
async getUser ( ) {
86
+ if ( window . parent == window && ! window . opener && ! window . frameElement &&
87
+ ! location . href . startsWith ( this . _userManager . settings . redirect_uri ! ) ) {
88
+ // If we are not inside a hidden iframe, try authenticating silently.
89
+ await AuthenticationService . instance . trySilentSignIn ( ) ;
90
+ }
91
+
72
92
const user = await this . _userManager . getUser ( ) ;
73
93
return user && user . profile ;
74
94
}
@@ -253,21 +273,61 @@ class OidcAuthorizeService implements AuthorizeService {
253
273
export class AuthenticationService {
254
274
255
275
static _infrastructureKey = 'Microsoft.AspNetCore.Components.WebAssembly.Authentication' ;
256
- static _initialized : Promise < void > ;
276
+ static _initialized : Promise < void > ;
257
277
static instance : OidcAuthorizeService ;
278
+ static _pendingOperations : { [ key : string ] : Promise < AuthenticationResult > | undefined } = { }
258
279
259
280
public static async init ( settings : UserManagerSettings & AuthorizeServiceSettings ) {
260
281
// Multiple initializations can start concurrently and we want to avoid that.
261
282
// In order to do so, we create an initialization promise and the first call to init
262
283
// tries to initialize the app and sets up a promise other calls can await on.
263
284
if ( ! AuthenticationService . _initialized ) {
264
- this . _initialized = ( async ( ) => {
265
- const userManager = await this . createUserManager ( settings ) ;
285
+ AuthenticationService . _initialized = AuthenticationService . InitializeCore ( settings ) ;
286
+
287
+ await AuthenticationService . _initialized ;
288
+ }
289
+
290
+ return AuthenticationService . _initialized ;
291
+ }
292
+
293
+ public static handleCallback ( ) {
294
+ return AuthenticationService . InitializeCore ( ) ;
295
+ }
296
+
297
+ private static async InitializeCore ( settings ?: UserManagerSettings & AuthorizeServiceSettings ) {
298
+ let finalSettings = settings || AuthenticationService . resolveCachedSettings ( ) ;
299
+ if ( ! settings && finalSettings ) {
300
+ const userManager = AuthenticationService . createUserManagerCore ( finalSettings ) ;
301
+
302
+ if ( window . parent != window && ! window . opener && ( window . frameElement &&
303
+ location . href . startsWith ( userManager . settings . redirect_uri ! ) ) ) {
304
+ // If we are inside a hidden iframe, try completing the sign in early.
266
305
AuthenticationService . instance = new OidcAuthorizeService ( userManager ) ;
267
- } ) ( ) ;
306
+ AuthenticationService . _initialized = ( async ( ) : Promise < void > => {
307
+ await AuthenticationService . instance . completeSignIn ( location . href ) ;
308
+ return ;
309
+ } ) ( ) ;
310
+ }
311
+
312
+ return ;
313
+ } else if ( settings ) {
314
+ const userManager = await AuthenticationService . createUserManager ( settings ) ;
315
+ AuthenticationService . instance = new OidcAuthorizeService ( userManager ) ;
316
+ } else {
317
+ // HandleCallback gets called unconditionally, so we do nothing for normal paths.
318
+ // Cached settings are only used on handling the redirect_uri path and if the settings are not there
319
+ // the app will fallback to the default logic for handling the redirect.
268
320
}
321
+ }
269
322
270
- await this . _initialized ;
323
+ private static resolveCachedSettings ( ) : UserManagerSettings | undefined {
324
+ let finalSettings : UserManagerSettings | undefined ;
325
+ const cachedSettings = window . sessionStorage . getItem ( `${ AuthenticationService . _infrastructureKey } .CachedAuthSettings` ) ;
326
+ if ( cachedSettings ) {
327
+ finalSettings = JSON . parse ( cachedSettings ) ;
328
+ }
329
+
330
+ return finalSettings ;
271
331
}
272
332
273
333
public static getUser ( ) {
@@ -282,16 +342,30 @@ export class AuthenticationService {
282
342
return AuthenticationService . instance . signIn ( state ) ;
283
343
}
284
344
285
- public static completeSignIn ( url : string ) {
286
- return AuthenticationService . instance . completeSignIn ( url ) ;
345
+ public static async completeSignIn ( url : string ) {
346
+ let operation = this . _pendingOperations [ url ] ;
347
+ if ( ! operation ) {
348
+ operation = AuthenticationService . instance . completeSignIn ( url ) ;
349
+ await operation ;
350
+ this . _pendingOperations [ url ] = undefined ;
351
+ }
352
+
353
+ return operation ;
287
354
}
288
355
289
356
public static signOut ( state : any ) {
290
357
return AuthenticationService . instance . signOut ( state ) ;
291
358
}
292
359
293
- public static completeSignOut ( url : string ) {
294
- return AuthenticationService . instance . completeSignOut ( url ) ;
360
+ public static async completeSignOut ( url : string ) {
361
+ let operation = this . _pendingOperations [ url ] ;
362
+ if ( ! operation ) {
363
+ operation = AuthenticationService . instance . completeSignOut ( url ) ;
364
+ await operation ;
365
+ this . _pendingOperations [ url ] = undefined ;
366
+ }
367
+
368
+ return operation ;
295
369
}
296
370
297
371
private static async createUserManager ( settings : OidcAuthorizeServiceSettings ) : Promise < UserManager > {
@@ -304,11 +378,6 @@ export class AuthenticationService {
304
378
305
379
const downloadedSettings = await response . json ( ) ;
306
380
307
- window . sessionStorage . setItem ( `${ AuthenticationService . _infrastructureKey } .CachedAuthSettings` , JSON . stringify ( settings ) ) ;
308
-
309
- downloadedSettings . automaticSilentRenew = true ;
310
- downloadedSettings . includeIdTokenInSilentRenew = true ;
311
-
312
381
finalSettings = downloadedSettings ;
313
382
} else {
314
383
if ( ! settings . scope ) {
@@ -323,12 +392,16 @@ export class AuthenticationService {
323
392
finalSettings = settings ;
324
393
}
325
394
326
- const userManager = new UserManager ( finalSettings ) ;
395
+ window . sessionStorage . setItem ( ` ${ AuthenticationService . _infrastructureKey } .CachedAuthSettings` , JSON . stringify ( finalSettings ) ) ;
327
396
397
+ return AuthenticationService . createUserManagerCore ( finalSettings ) ;
398
+ }
399
+
400
+ private static createUserManagerCore ( finalSettings : UserManagerSettings ) {
401
+ const userManager = new UserManager ( finalSettings ) ;
328
402
userManager . events . addUserSignedOut ( async ( ) => {
329
- await userManager . removeUser ( ) ;
403
+ userManager . removeUser ( ) ;
330
404
} ) ;
331
-
332
405
return userManager ;
333
406
}
334
407
}
@@ -337,4 +410,6 @@ declare global {
337
410
interface Window { AuthenticationService : AuthenticationService ; }
338
411
}
339
412
413
+ AuthenticationService . handleCallback ( ) ;
414
+
340
415
window . AuthenticationService = AuthenticationService ;
0 commit comments