77import { Log , User , UserManager } from 'oidc-client' ;
88import { UserManagerMock } from './UserManagerMock' ;
99import {
10+ resetAuthenticationRouterError ,
1011 setLoggedUser ,
12+ setLogoutError ,
13+ setShowAuthenticationRouterLogin ,
1114 setSignInCallbackError ,
1215 setUnauthorizedUserInfo ,
13- setLogoutError ,
1416 setUserValidationError ,
15- resetAuthenticationRouterError ,
16- setShowAuthenticationRouterLogin ,
1717} from '../redux/actions' ;
1818import { jwtDecode } from 'jwt-decode' ;
1919import { Dispatch } from 'react' ;
2020import { NavigateFunction } from 'react-router-dom' ;
2121
2222type UserValidationFunc = ( user : User ) => Promise < boolean > ;
23+ type IdpSettingsGetter = ( ) => Promise < IdpSettings > ;
24+
25+ export type IdpSettings = {
26+ authority : string ;
27+ client_id : string ;
28+ redirect_uri : string ;
29+ post_logout_redirect_uri : string ;
30+ silent_redirect_uri : string ;
31+ scope : string ;
32+ maxExpiresIn ?: number ;
33+ } ;
2334
2435type CustomUserManager = UserManager & {
25- authorizationCodeFlowEnabled ?: boolean ;
2636 idpSettings ?: {
2737 maxExpiresIn ?: number ;
2838 } ;
@@ -36,7 +46,7 @@ const hackAuthorityKey = 'oidc.hack.authority';
3646const oidcHackReloadedKey = 'gridsuite-oidc-hack-reloaded' ;
3747const pathKey = 'powsybl-gridsuite-current-path' ;
3848
39- function isIssuerErrorForCodeFlow ( error : Error ) {
49+ function isIssuerError ( error : Error ) {
4050 return error . message . includes ( 'Invalid issuer in token' ) ;
4151}
4252
@@ -60,7 +70,6 @@ function reloadTimerOnExpiresIn(
6070 userManager : UserManager ,
6171 expiresIn : number
6272) {
63- // TODO: Can we stop doing it in the hash for implicit flow ? To make it common for both flows
6473 // Not allowed by TS because expires_in is supposed to be readonly
6574 // @ts -ignore
6675 user . expires_in = expiresIn ;
@@ -77,24 +86,16 @@ function handleSigninSilent(
7786 if ( user == null || getIdTokenExpiresIn ( user ) < 0 ) {
7887 return userManager . signinSilent ( ) . catch ( ( error : Error ) => {
7988 dispatch ( setShowAuthenticationRouterLogin ( true ) ) ;
80- const errorIssuerCodeFlow = isIssuerErrorForCodeFlow ( error ) ;
81- const errorIssuerImplicitFlow =
82- error . message ===
83- 'authority mismatch on settings vs. signin state' ;
84- if ( errorIssuerCodeFlow ) {
85- // Replacing authority for code flow only because it's done in the hash for implicit flow
86- // TODO: Can we stop doing it in the hash for implicit flow ? To make it common here for both flows
89+ if ( isIssuerError ( error ) ) {
8790 extractIssuerToSessionStorage ( error ) ;
88- }
89- if ( errorIssuerCodeFlow || errorIssuerImplicitFlow ) {
9091 reload ( ) ;
9192 }
9293 } ) ;
9394 }
9495 } ) ;
9596}
9697
97- function initializeAuthenticationDev (
98+ export async function initializeAuthenticationDev (
9899 dispatch : Dispatch < unknown > ,
99100 isSilentRenew : boolean ,
100101 validateUser : UserValidationFunc ,
@@ -109,113 +110,49 @@ function initializeAuthenticationDev(
109110 handleSigninSilent ( dispatch , userManager ) ;
110111 }
111112 }
112- return Promise . resolve ( userManager ) ;
113+ return userManager ;
113114}
114115
115116const accessTokenExpiringNotificationTime = 60 ; // seconds
116117
117- function initializeAuthenticationProd (
118+ export async function initializeAuthenticationProd (
118119 dispatch : Dispatch < unknown > ,
119120 isSilentRenew : boolean ,
120- idpSettings : Promise < Response > ,
121+ idpSettingsGetter : IdpSettingsGetter ,
121122 validateUser : UserValidationFunc ,
122- authorizationCodeFlowEnabled : boolean ,
123123 isSigninCallback : boolean
124124) {
125- return idpSettings
126- . then ( ( r ) => r . json ( ) )
127- . then ( ( idpSettings ) => {
128- /* hack to ignore the iss check. XXX TODO to remove */
129- const regextoken = / i d _ t o k e n = [ ^ & ] * / ;
130- const regexstate = / s t a t e = [ ^ & ] * / ;
131- const regexexpires = / e x p i r e s _ i n = [ ^ & ] * / ;
132- let authority : string | undefined ;
133- if ( window . location . hash ) {
134- const matched_id_token = window . location . hash . match ( regextoken ) ;
135- const matched_state = window . location . hash . match ( regexstate ) ;
136- if ( matched_id_token != null && matched_state != null ) {
137- const id_token = matched_id_token [ 0 ] . split ( '=' ) [ 1 ] ;
138- const state = matched_state [ 0 ] . split ( '=' ) [ 1 ] ;
139- const strState = localStorage . getItem ( 'oidc.' + state ) ;
140- if ( strState != null ) {
141- const decoded = jwtDecode ( id_token ) ;
142- authority = decoded . iss ;
143- const storedState = JSON . parse ( strState ) ;
144- console . debug (
145- 'Replacing authority in storedState. Before: ' ,
146- storedState . authority ,
147- 'after: ' ,
148- authority
149- ) ;
150- storedState . authority = authority ;
151- localStorage . setItem (
152- 'oidc.' + state ,
153- JSON . stringify ( storedState )
154- ) ;
155- if ( authority !== undefined ) {
156- sessionStorage . setItem ( hackAuthorityKey , authority ) ;
157- }
158- const matched_expires =
159- window . location . hash . match ( regexexpires ) ;
160- if ( matched_expires != null ) {
161- const expires_in = parseInt (
162- matched_expires [ 0 ] . split ( '=' ) [ 1 ]
163- ) ;
164- window . location . hash = window . location . hash . replace (
165- matched_expires [ 0 ] ,
166- 'expires_in=' +
167- computeMinExpiresIn (
168- expires_in ,
169- id_token ,
170- idpSettings . maxExpiresIn
171- )
172- ) ;
173- }
174- }
175- }
176- }
177- authority =
178- authority ||
125+ const idpSettings = await idpSettingsGetter ( ) ;
126+ try {
127+ const settings = {
128+ authority :
179129 sessionStorage . getItem ( hackAuthorityKey ) ||
180- idpSettings . authority ;
181-
182- const responseSettings = authorizationCodeFlowEnabled
183- ? { response_type : 'code' }
184- : {
185- response_type : 'id_token token' ,
186- response_mode : 'fragment' ,
187- } ;
188- const settings = {
189- authority,
190- client_id : idpSettings . client_id ,
191- redirect_uri : idpSettings . redirect_uri ,
192- post_logout_redirect_uri : idpSettings . post_logout_redirect_uri ,
193- silent_redirect_uri : idpSettings . silent_redirect_uri ,
194- scope : idpSettings . scope ,
195- automaticSilentRenew : ! isSilentRenew ,
196- accessTokenExpiringNotificationTime :
197- accessTokenExpiringNotificationTime ,
198- ...responseSettings ,
199- } ;
200- let userManager : CustomUserManager = new UserManager ( settings ) ;
201- // Hack to enrich UserManager object
202- userManager . idpSettings = idpSettings ; //store our settings in there as well to use it later
203- // Hack to enrich UserManager object
204- userManager . authorizationCodeFlowEnabled =
205- authorizationCodeFlowEnabled ;
206- if ( ! isSilentRenew ) {
207- handleUser ( dispatch , userManager , validateUser ) ;
208- if ( ! isSigninCallback ) {
209- handleSigninSilent ( dispatch , userManager ) ;
210- }
130+ idpSettings . authority ,
131+ client_id : idpSettings . client_id ,
132+ redirect_uri : idpSettings . redirect_uri ,
133+ post_logout_redirect_uri : idpSettings . post_logout_redirect_uri ,
134+ silent_redirect_uri : idpSettings . silent_redirect_uri ,
135+ scope : idpSettings . scope ,
136+ automaticSilentRenew : ! isSilentRenew ,
137+ accessTokenExpiringNotificationTime :
138+ accessTokenExpiringNotificationTime ,
139+ response_type : 'code' ,
140+ } ;
141+ let userManager : CustomUserManager = new UserManager ( settings ) ;
142+ // Hack to enrich UserManager object
143+ userManager . idpSettings = idpSettings ; //store our settings in there as well to use it later
144+ if ( ! isSilentRenew ) {
145+ handleUser ( dispatch , userManager , validateUser ) ;
146+ if ( ! isSigninCallback ) {
147+ handleSigninSilent ( dispatch , userManager ) ;
211148 }
212- return userManager ;
213- } )
214- . catch ( ( error ) => {
215- console . debug ( 'error when importing the idp settings' , error ) ;
216- dispatch ( setShowAuthenticationRouterLogin ( true ) ) ;
217- throw error ;
218- } ) ;
149+ }
150+ return userManager ;
151+ } catch ( error : unknown ) {
152+ console . debug ( 'error when importing the idp settings' , error ) ;
153+ dispatch ( setShowAuthenticationRouterLogin ( true ) ) ;
154+ throw error ;
155+ }
219156}
220157
221158function computeMinExpiresIn (
@@ -257,14 +194,17 @@ function computeMinExpiresIn(
257194 return newExpiresIn ;
258195}
259196
260- function login ( location : Location , userManagerInstance : UserManager ) {
197+ export function login ( location : Location , userManagerInstance : UserManager ) {
261198 sessionStorage . setItem ( pathKey , location . pathname + location . search ) ;
262199 return userManagerInstance
263200 . signinRedirect ( )
264201 . then ( ( ) => console . debug ( 'login' ) ) ;
265202}
266203
267- function logout ( dispatch : Dispatch < unknown > , userManagerInstance : UserManager ) {
204+ export function logout (
205+ dispatch : Dispatch < unknown > ,
206+ userManagerInstance : UserManager
207+ ) {
268208 sessionStorage . removeItem ( hackAuthorityKey ) ; //To remove when hack is removed
269209 return userManagerInstance . getUser ( ) . then ( ( user ) => {
270210 if ( user ) {
@@ -301,7 +241,7 @@ function getIdTokenExpiresIn(user: User) {
301241 return exp - now ;
302242}
303243
304- function dispatchUser (
244+ export function dispatchUser (
305245 dispatch : Dispatch < unknown > ,
306246 userManagerInstance : CustomUserManager ,
307247 validateUser : UserValidationFunc
@@ -333,20 +273,17 @@ function dispatchUser(
333273 console . debug (
334274 'User has been successfully loaded from store.'
335275 ) ;
336-
337276 // In authorization code flow we have to make the oidc-client lib re-evaluate the date of the token renewal timers
338277 // because it is not hacked at page loading on the fragment before oidc-client lib initialization
339- if ( userManagerInstance . authorizationCodeFlowEnabled ) {
340- reloadTimerOnExpiresIn (
341- user ,
342- userManagerInstance ,
343- computeMinExpiresIn (
344- user . expires_in ,
345- user . id_token ,
346- userManagerInstance . idpSettings ?. maxExpiresIn
347- )
348- ) ;
349- }
278+ reloadTimerOnExpiresIn (
279+ user ,
280+ userManagerInstance ,
281+ computeMinExpiresIn (
282+ user . expires_in ,
283+ user . id_token ,
284+ userManagerInstance . idpSettings ?. maxExpiresIn
285+ )
286+ ) ;
350287 return dispatch ( setLoggedUser ( user ) ) ;
351288 } )
352289 . catch ( ( e ) => {
@@ -363,7 +300,7 @@ function dispatchUser(
363300 } ) ;
364301}
365302
366- function getPreLoginPath ( ) {
303+ export function getPreLoginPath ( ) {
367304 return sessionStorage . getItem ( pathKey ) ;
368305}
369306
@@ -374,7 +311,7 @@ function navigateToPreLoginPath(navigate: NavigateFunction) {
374311 }
375312}
376313
377- function handleSigninCallback (
314+ export function handleSigninCallback (
378315 dispatch : Dispatch < unknown > ,
379316 navigate : NavigateFunction ,
380317 userManagerInstance : UserManager
@@ -383,9 +320,7 @@ function handleSigninCallback(
383320 userManagerInstance
384321 . signinRedirectCallback ( )
385322 . catch ( function ( e ) {
386- if ( isIssuerErrorForCodeFlow ( e ) ) {
387- // Replacing authority for code flow only because it's done in the hash for implicit flow
388- // TODO: Can we also do it here for the implicit flow ? To make it common here for both flows
323+ if ( isIssuerError ( e ) ) {
389324 extractIssuerToSessionStorage ( e ) ;
390325 // After navigate, location will be out of a redirection route (sign-in-silent or sign-in-callback) so reloading the page will attempt a silent signin
391326 // It will reload the user manager based on hacked authority at initialization with the new authority
@@ -409,7 +344,7 @@ function handleSigninCallback(
409344 } ) ;
410345}
411346
412- function handleSilentRenewCallback ( userManagerInstance : UserManager ) {
347+ export function handleSilentRenewCallback ( userManagerInstance : UserManager ) {
413348 userManagerInstance . signinSilentCallback ( ) ;
414349}
415350
@@ -507,14 +442,3 @@ function handleUser(
507442 console . debug ( 'dispatch user' ) ;
508443 dispatchUser ( dispatch , userManager , validateUser ) ;
509444}
510-
511- export {
512- initializeAuthenticationDev ,
513- initializeAuthenticationProd ,
514- handleSilentRenewCallback ,
515- login ,
516- logout ,
517- dispatchUser ,
518- handleSigninCallback ,
519- getPreLoginPath ,
520- } ;
0 commit comments