11/*---------------------------------------------------------------------------------------------
2- * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
3- * See LICENSE.md in the project root for license terms and full copyright notice.
4- *--------------------------------------------------------------------------------------------*/
2+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
3+ * See LICENSE.md in the project root for license terms and full copyright notice.
4+ *--------------------------------------------------------------------------------------------*/
55
66/** @packageDocumentation
77 * @module Authorization
@@ -15,20 +15,34 @@ import { BrowserAuthorizationLoggerCategory } from "./LoggerCategory";
1515import { getImsAuthority } from "./utils" ;
1616import type { AuthorizationClient } from "@itwin/core-common" ;
1717import type { User , UserManagerSettings } from "oidc-client-ts" ;
18- import type { BrowserAuthorizationClientConfiguration , BrowserAuthorizationClientConfigurationOptions , BrowserAuthorizationClientRedirectState , BrowserAuthorizationClientRequestOptions , SettingsInStorage } from "./types" ;
18+ import type {
19+ BrowserAuthorizationClientConfiguration ,
20+ BrowserAuthorizationClientConfigurationOptions ,
21+ BrowserAuthorizationClientRedirectState ,
22+ BrowserAuthorizationClientRequestOptions ,
23+ SettingsInStorage ,
24+ } from "./types" ;
1925
2026/** BrowserAuthorization type guard.
2127 * @beta
2228 */
23- export const isBrowserAuthorizationClient = ( client : AuthorizationClient | undefined ) : client is BrowserAuthorizationClient => {
24- return client !== undefined && ( client as BrowserAuthorizationClient ) . signIn !== undefined && ( client as BrowserAuthorizationClient ) . signOut !== undefined ;
29+ export const isBrowserAuthorizationClient = (
30+ client : AuthorizationClient | undefined ,
31+ ) : client is BrowserAuthorizationClient => {
32+ return (
33+ client !== undefined &&
34+ ( client as BrowserAuthorizationClient ) . signIn !== undefined &&
35+ ( client as BrowserAuthorizationClient ) . signOut !== undefined
36+ ) ;
2537} ;
2638
2739/**
2840 * @beta
2941 */
3042export class BrowserAuthorizationClient implements AuthorizationClient {
31- public readonly onAccessTokenChanged = new BeEvent < ( token : AccessToken ) => void > ( ) ;
43+ public readonly onAccessTokenChanged = new BeEvent <
44+ ( token : AccessToken ) => void
45+ > ( ) ;
3246 protected _userManager ?: UserManager ;
3347
3448 protected _basicSettings : BrowserAuthorizationClientConfigurationOptions ;
@@ -69,15 +83,29 @@ export class BrowserAuthorizationClient implements AuthorizationClient {
6983 return this . _userManager ;
7084 }
7185
72- const settings = await this . getUserManagerSettings ( this . _basicSettings , this . _advancedSettings ) ;
86+ const settings = await this . getUserManagerSettings (
87+ this . _basicSettings ,
88+ this . _advancedSettings ,
89+ ) ;
7390 this . _userManager = this . createUserManager ( settings ) ;
7491 return this . _userManager ;
7592 }
7693
7794 /**
7895 * Merges the basic and advanced settings into a single configuration object consumable by the internal userManager.
7996 */
80- protected async getUserManagerSettings ( basicSettings : BrowserAuthorizationClientConfiguration , advancedSettings ?: UserManagerSettings ) : Promise < UserManagerSettings > {
97+ protected async getUserManagerSettings (
98+ basicSettings : BrowserAuthorizationClientConfiguration ,
99+ advancedSettings ?: UserManagerSettings ,
100+ ) : Promise < UserManagerSettings > {
101+ if ( ! basicSettings . silentRedirectUri ) {
102+ Logger . logWarning (
103+ BrowserAuthorizationLoggerCategory . Authorization ,
104+ "silentRedirectUri not configured. Automatic silent token renewal will not work. " +
105+ "Provide a silentRedirectUri pointing to a lightweight HTML page for best performance. See README for details." ,
106+ ) ;
107+ }
108+
81109 let userManagerSettings : UserManagerSettings = {
82110 authority : this . authorityUrl ,
83111 redirect_uri : basicSettings . redirectUri , // eslint-disable-line @typescript-eslint/naming-convention
@@ -132,7 +160,10 @@ export class BrowserAuthorizationClient implements AuthorizationClient {
132160 * @param successRedirectUrl - (optional) path to redirect to after a successful authorization
133161 * @param args (optional) additional BrowserAuthorizationClientRequestOptions passed to signIn methods
134162 */
135- public async signInRedirect ( successRedirectUrl ?: string , args ?: BrowserAuthorizationClientRequestOptions ) : Promise < void > {
163+ public async signInRedirect (
164+ successRedirectUrl ?: string ,
165+ args ?: BrowserAuthorizationClientRequestOptions ,
166+ ) : Promise < void > {
136167 const user = await this . nonInteractiveSignIn ( args ) ;
137168 if ( user ) {
138169 return ;
@@ -151,7 +182,9 @@ export class BrowserAuthorizationClient implements AuthorizationClient {
151182 * Attempts a sign-in via popup with the authorization provider
152183 * @param args - @see BrowserAuthorizationClientRequestOptions
153184 */
154- public async signInPopup ( args ?: BrowserAuthorizationClientRequestOptions ) : Promise < void > {
185+ public async signInPopup (
186+ args ?: BrowserAuthorizationClientRequestOptions ,
187+ ) : Promise < void > {
155188 let user = await this . nonInteractiveSignIn ( args ) ;
156189 if ( user ) {
157190 return ;
@@ -160,7 +193,9 @@ export class BrowserAuthorizationClient implements AuthorizationClient {
160193 const userManager = await this . getUserManager ( ) ;
161194 user = await userManager . signinPopup ( args ) ;
162195 if ( ! user || user . expired )
163- throw new Error ( "Expected userManager.signinPopup to always resolve to an authorized user" ) ;
196+ throw new Error (
197+ "Expected userManager.signinPopup to always resolve to an authorized user" ,
198+ ) ;
164199 return ;
165200 }
166201
@@ -179,11 +214,17 @@ export class BrowserAuthorizationClient implements AuthorizationClient {
179214 * - tries to load the user from storage
180215 * - tries to silently sign-in the user
181216 */
182- protected async nonInteractiveSignIn ( args ?: BrowserAuthorizationClientRequestOptions ) : Promise < User | undefined > {
217+ protected async nonInteractiveSignIn (
218+ args ?: BrowserAuthorizationClientRequestOptions ,
219+ ) : Promise < User | undefined > {
183220 const userManager = await this . getUserManager ( ) ;
184- const settingsPromptRequired = userManager . settings . prompt !== undefined && userManager . settings . prompt !== "none" ;
185- const argsPromptRequired = args ?. prompt !== undefined && args . prompt !== "none" ;
186- if ( settingsPromptRequired || argsPromptRequired ) { // No need to even try a silent sign in if we know the prompt will force its failure.
221+ const settingsPromptRequired =
222+ userManager . settings . prompt !== undefined &&
223+ userManager . settings . prompt !== "none" ;
224+ const argsPromptRequired =
225+ args ?. prompt !== undefined && args . prompt !== "none" ;
226+ if ( settingsPromptRequired || argsPromptRequired ) {
227+ // No need to even try a silent sign in if we know the prompt will force its failure.
187228 return undefined ;
188229 }
189230
@@ -194,7 +235,7 @@ export class BrowserAuthorizationClient implements AuthorizationClient {
194235
195236 // Attempt a silent sign-in
196237 try {
197- user = await userManager . signinSilent ( ) ?? undefined ; // calls events
238+ user = ( await userManager . signinSilent ( ) ) ?? undefined ; // calls events
198239 return user ;
199240 } catch ( err ) {
200241 return undefined ;
@@ -225,7 +266,9 @@ export class BrowserAuthorizationClient implements AuthorizationClient {
225266 return ;
226267 }
227268 this . _accessToken = `Bearer ${ user . access_token } ` ;
228- this . _expiresAt = user . expires_at ? new Date ( user . expires_at * 1000 ) : undefined ;
269+ this . _expiresAt = user . expires_at
270+ ? new Date ( user . expires_at * 1000 )
271+ : undefined ;
229272 }
230273
231274 /**
@@ -254,8 +297,7 @@ export class BrowserAuthorizationClient implements AuthorizationClient {
254297 * @returns an AccessToken
255298 */
256299 public async getAccessToken ( ) : Promise < AccessToken > {
257- if ( this . _accessToken )
258- return this . _accessToken ;
300+ if ( this . _accessToken ) return this . _accessToken ;
259301 throw new Error ( "Authorization error: Not signed in." ) ;
260302 }
261303
@@ -268,7 +310,8 @@ export class BrowserAuthorizationClient implements AuthorizationClient {
268310 const userManager = await this . getUserManager ( ) ;
269311 try {
270312 await userManager . querySessionStatus ( ) ;
271- } catch ( err ) { // Access token is no longer valid in this session
313+ } catch ( err ) {
314+ // Access token is no longer valid in this session
272315 await userManager . removeUser ( ) ;
273316 return false ;
274317 }
@@ -281,7 +324,11 @@ export class BrowserAuthorizationClient implements AuthorizationClient {
281324 try {
282325 this . onAccessTokenChanged . raiseEvent ( this . _accessToken ) ;
283326 } catch ( err : any ) {
284- Logger . logError ( BrowserAuthorizationLoggerCategory . Authorization , "Error thrown when handing BrowserAuthorizationClient.onUserStateChanged event" , ( ) => ( { message : err . message } ) ) ;
327+ Logger . logError (
328+ BrowserAuthorizationLoggerCategory . Authorization ,
329+ "Error thrown when handing BrowserAuthorizationClient.onUserStateChanged event" ,
330+ ( ) => ( { message : err . message } ) ,
331+ ) ;
285332 }
286333 } ;
287334
@@ -303,8 +350,7 @@ export class BrowserAuthorizationClient implements AuthorizationClient {
303350 /**
304351 * Raised prior to the access token expiring
305352 */
306- protected _onAccessTokenExpiring = async ( ) => {
307- } ;
353+ protected _onAccessTokenExpiring = async ( ) => { } ;
308354
309355 /**
310356 * Raised after the access token has expired.
@@ -316,8 +362,7 @@ export class BrowserAuthorizationClient implements AuthorizationClient {
316362 /**
317363 * Raised when the automatic silent renew has failed.
318364 */
319- protected _onSilentRenewError = ( ) => {
320- } ;
365+ protected _onSilentRenewError = ( ) => { } ;
321366
322367 /**
323368 * Raised when the user's sign-in status at the OP has changed.
@@ -330,8 +375,12 @@ export class BrowserAuthorizationClient implements AuthorizationClient {
330375 public dispose ( ) : void {
331376 if ( this . _userManager ) {
332377 this . _userManager . events . removeUserLoaded ( this . _onUserLoaded ) ;
333- this . _userManager . events . removeAccessTokenExpiring ( this . _onAccessTokenExpiring ) ;
334- this . _userManager . events . removeAccessTokenExpired ( this . _onAccessTokenExpired ) ;
378+ this . _userManager . events . removeAccessTokenExpiring (
379+ this . _onAccessTokenExpiring ,
380+ ) ;
381+ this . _userManager . events . removeAccessTokenExpired (
382+ this . _onAccessTokenExpired ,
383+ ) ;
335384 this . _userManager . events . removeUserUnloaded ( this . _onUserUnloaded ) ;
336385 this . _userManager . events . removeSilentRenewError ( this . _onSilentRenewError ) ;
337386 this . _userManager . events . removeUserSignedOut ( this . _onUserSignedOut ) ;
@@ -347,7 +396,9 @@ export class BrowserAuthorizationClient implements AuthorizationClient {
347396 */
348397 public setAdvancedSettings ( settings : UserManagerSettings ) : void {
349398 if ( this . _userManager ) {
350- throw new Error ( "Cannot supply advanced settings to BrowserAuthorizationClient after the underlying UserManager has already been created." ) ;
399+ throw new Error (
400+ "Cannot supply advanced settings to BrowserAuthorizationClient after the underlying UserManager has already been created." ,
401+ ) ;
351402 }
352403
353404 this . _advancedSettings = settings ;
@@ -359,22 +410,26 @@ export class BrowserAuthorizationClient implements AuthorizationClient {
359410 * @param responseMode - Defines how OIDC auth reponse parameters are encoded.
360411 * @throws [[Error]] when this attempt fails for any reason.
361412 */
362- private async handleSigninCallbackInternal ( responseMode : "query" | "fragment" ) : Promise < void > {
413+ private async handleSigninCallbackInternal (
414+ responseMode : "query" | "fragment" ,
415+ ) : Promise < void > {
363416 const userManager = await this . getUserManager ( ) ;
364417 // oidc-client-js uses an over-eager regex to parse the url, which may match values from the hash string when targeting the query string (and vice-versa)
365418 // To ensure that this mismatching doesn't occur, we strip the unnecessary portion away here first.
366- const urlSuffix = responseMode === "query"
367- ? window . location . search
368- : window . location . hash ;
419+ const urlSuffix =
420+ responseMode === "query" ? window . location . search : window . location . hash ;
369421 const url = `${ window . location . origin } ${ window . location . pathname } ${ urlSuffix } ` ;
370422
371423 const user = await userManager . signinCallback ( url ) ; // For silent or popup callbacks, execution effectively ends here, since the context will be destroyed.
372424 if ( ! user || user . expired )
373- throw new Error ( "Authorization error: userManager.signinRedirectCallback does not resolve to authorized user" ) ;
425+ throw new Error (
426+ "Authorization error: userManager.signinRedirectCallback does not resolve to authorized user" ,
427+ ) ;
374428
375429 if ( user . state ) {
376430 const state = user . state as BrowserAuthorizationClientRedirectState ;
377- if ( state . successRedirectUrl ) { // Special case for signin via redirect used to return to the original location
431+ if ( state . successRedirectUrl ) {
432+ // Special case for signin via redirect used to return to the original location
378433 window . location . replace ( state . successRedirectUrl ) ;
379434 }
380435 }
@@ -386,8 +441,7 @@ export class BrowserAuthorizationClient implements AuthorizationClient {
386441 */
387442 public async handleSigninCallback ( ) : Promise < void > {
388443 const url = new URL ( this . _basicSettings . redirectUri ) ;
389- if ( url . pathname !== window . location . pathname )
390- return ;
444+ if ( url . pathname !== window . location . pathname ) return ;
391445
392446 let errorMessage = "" ;
393447
@@ -405,7 +459,8 @@ export class BrowserAuthorizationClient implements AuthorizationClient {
405459 errorMessage += `${ err . message } \n` ;
406460 }
407461
408- if ( window . self !== window . top ) { // simply destroy the window if a failure is detected in an iframe.
462+ if ( window . self !== window . top ) {
463+ // simply destroy the window if a failure is detected in an iframe.
409464 window . close ( ) ;
410465 return ;
411466 }
@@ -422,7 +477,9 @@ export class BrowserAuthorizationClient implements AuthorizationClient {
422477 * @param store - A Storage object such as sessionStorage which stores configuration. Defaults to localStorage
423478 * which is also the default stateStore for this library. These stores should match.
424479 */
425- public static async handleSignInCallback ( store : Storage = window . localStorage ) {
480+ public static async handleSignInCallback (
481+ store : Storage = window . localStorage ,
482+ ) {
426483 const staticClient = new BrowserAuthorizationClient ( { } as any ) ;
427484 this . loadSettingsFromStorage ( staticClient , store ) ;
428485 await staticClient . handleSigninCallback ( ) ;
@@ -437,7 +494,9 @@ export class BrowserAuthorizationClient implements AuthorizationClient {
437494
438495 const storageEntry = store . getItem ( `oidc.${ nonce } ` ) ;
439496 if ( ! storageEntry )
440- throw new Error ( "Could not load oidc settings from local storage. Ensure the client is configured properly" ) ;
497+ throw new Error (
498+ "Could not load oidc settings from local storage. Ensure the client is configured properly" ,
499+ ) ;
441500
442501 const storageObject : SettingsInStorage = JSON . parse ( storageEntry ) ;
443502
0 commit comments