@@ -28,7 +28,8 @@ import {
28
28
SilentFlowClient ,
29
29
EndSessionRequest ,
30
30
BaseAuthRequest ,
31
- Logger
31
+ Logger ,
32
+ ServerAuthorizationCodeResponse
32
33
} from "@azure/msal-common" ;
33
34
import { buildConfiguration , Configuration } from "../config/Configuration" ;
34
35
import { BrowserStorage } from "../cache/BrowserStorage" ;
@@ -37,13 +38,14 @@ import { RedirectHandler } from "../interaction_handler/RedirectHandler";
37
38
import { PopupHandler } from "../interaction_handler/PopupHandler" ;
38
39
import { SilentHandler } from "../interaction_handler/SilentHandler" ;
39
40
import { BrowserAuthError } from "../error/BrowserAuthError" ;
40
- import { BrowserConstants , TemporaryCacheKeys , DEFAULT_REQUEST } from "../utils/BrowserConstants" ;
41
+ import { BrowserConstants , TemporaryCacheKeys , DEFAULT_REQUEST , InteractionType } from "../utils/BrowserConstants" ;
41
42
import { BrowserUtils } from "../utils/BrowserUtils" ;
42
43
import { version } from "../../package.json" ;
43
44
import { IPublicClientApplication } from "./IPublicClientApplication" ;
44
45
import { RedirectRequest } from "../request/RedirectRequest" ;
45
46
import { PopupRequest } from "../request/PopupRequest" ;
46
47
import { SilentRequest } from "../request/SilentRequest" ;
48
+ import { BrowserProtocolUtils , BrowserStateObject } from "../utils/BrowserProtocolUtils" ;
47
49
48
50
/**
49
51
* The PublicClientApplication class is the object exposed by the library to perform authentication and authorization functions in Single Page Applications
@@ -132,45 +134,76 @@ export class PublicClientApplication implements IPublicClientApplication {
132
134
* - if true, performs logic to cache and navigate
133
135
* - if false, handles hash string and parses response
134
136
*/
135
- private async handleRedirectResponse ( ) : Promise < AuthenticationResult > {
136
- // Get current location hash from window or cache.
137
- const { location : { hash } } = window ;
138
- const cachedHash = this . browserStorage . getItem ( this . browserStorage . generateCacheKey ( TemporaryCacheKeys . URL_HASH ) , CacheSchemaType . TEMPORARY ) as string ;
139
- const isResponseHash = UrlString . hashContainsKnownProperties ( hash ) ;
140
- const loginRequestUrl = this . browserStorage . getItem ( this . browserStorage . generateCacheKey ( TemporaryCacheKeys . ORIGIN_URI ) , CacheSchemaType . TEMPORARY ) as string ;
137
+ private async handleRedirectResponse ( ) : Promise < AuthenticationResult | null > {
138
+ const responseHash = this . getRedirectResponseHash ( ) ;
139
+ if ( ! responseHash ) {
140
+ // Not a recognized server response hash or hash not associated with a redirect request
141
+ return null ;
142
+ }
141
143
142
- const currentUrlNormalized = UrlString . removeHashFromUrl ( window . location . href ) ;
144
+ // If navigateToLoginRequestUrl is true, get the url where the redirect request was initiated
145
+ const loginRequestUrl = this . browserStorage . getItem ( this . browserStorage . generateCacheKey ( TemporaryCacheKeys . ORIGIN_URI ) , CacheSchemaType . TEMPORARY ) as string ;
143
146
const loginRequestUrlNormalized = UrlString . removeHashFromUrl ( loginRequestUrl || "" ) ;
147
+ const currentUrlNormalized = UrlString . removeHashFromUrl ( window . location . href ) ;
148
+
144
149
if ( loginRequestUrlNormalized === currentUrlNormalized && this . config . auth . navigateToLoginRequestUrl ) {
150
+ if ( loginRequestUrl . indexOf ( "#" ) > - 1 ) {
151
+ // Replace current hash with non-msal hash, if present
152
+ BrowserUtils . replaceHash ( loginRequestUrl ) ;
153
+ }
145
154
// We are on the page we need to navigate to - handle hash
146
- // Replace current hash with non-msal hash, if present
147
- BrowserUtils . replaceHash ( loginRequestUrl ) ;
148
- return this . handleHash ( isResponseHash ? hash : cachedHash ) ;
149
- }
150
-
151
- if ( ! this . config . auth . navigateToLoginRequestUrl ) {
152
- // We don't need to navigate - handle hash
153
- BrowserUtils . clearHash ( ) ;
154
- return this . handleHash ( isResponseHash ? hash : cachedHash ) ;
155
- }
156
-
157
- if ( isResponseHash && ! BrowserUtils . isInIframe ( ) ) {
155
+ return this . handleHash ( responseHash ) ;
156
+ } else if ( ! this . config . auth . navigateToLoginRequestUrl ) {
157
+ return this . handleHash ( responseHash ) ;
158
+ } else if ( ! BrowserUtils . isInIframe ( ) ) {
158
159
// Returned from authority using redirect - need to perform navigation before processing response
160
+ // Cache the hash to be retrieved after the next redirect
159
161
const hashKey = this . browserStorage . generateCacheKey ( TemporaryCacheKeys . URL_HASH ) ;
160
- this . browserStorage . setItem ( hashKey , hash , CacheSchemaType . TEMPORARY ) ;
161
- if ( StringUtils . isEmpty ( loginRequestUrl ) || loginRequestUrl === "null" ) {
162
+ this . browserStorage . setItem ( hashKey , responseHash , CacheSchemaType . TEMPORARY ) ;
163
+ if ( ! loginRequestUrl || loginRequestUrl === "null" ) {
162
164
// Redirect to home page if login request url is null (real null or the string null)
165
+ const homepage = BrowserUtils . getHomepage ( ) ;
166
+ // Cache the homepage under ORIGIN_URI to ensure cached hash is processed on homepage
167
+ this . browserStorage . setItem ( this . browserStorage . generateCacheKey ( TemporaryCacheKeys . ORIGIN_URI ) , homepage , CacheSchemaType . TEMPORARY ) ;
163
168
this . logger . warning ( "Unable to get valid login request url from cache, redirecting to home page" ) ;
164
- BrowserUtils . navigateWindow ( "/" , true ) ;
169
+ BrowserUtils . navigateWindow ( homepage , true ) ;
165
170
} else {
166
- // Navigate to target url
171
+ // Navigate to page that initiated the redirect request
167
172
BrowserUtils . navigateWindow ( loginRequestUrl , true ) ;
168
173
}
169
174
}
170
175
171
176
return null ;
172
177
}
173
178
179
+ /**
180
+ * Gets the response hash for a redirect request
181
+ * Returns null if interactionType in the state value is not "redirect" or the hash does not contain known properties
182
+ * @returns {string }
183
+ */
184
+ private getRedirectResponseHash ( ) : string {
185
+ // Get current location hash from window or cache.
186
+ const { location : { hash } } = window ;
187
+ const isResponseHash = UrlString . hashContainsKnownProperties ( hash ) ;
188
+ const cachedHash = this . browserStorage . getItem ( this . browserStorage . generateCacheKey ( TemporaryCacheKeys . URL_HASH ) , CacheSchemaType . TEMPORARY ) as string ;
189
+ this . browserStorage . removeItem ( this . browserStorage . generateCacheKey ( TemporaryCacheKeys . URL_HASH ) ) ;
190
+
191
+ const responseHash = isResponseHash ? hash : cachedHash ;
192
+ if ( responseHash ) {
193
+ // Deserialize hash fragment response parameters.
194
+ const serverParams : ServerAuthorizationCodeResponse = UrlString . getDeserializedHash ( responseHash ) ;
195
+ const platformStateObj : BrowserStateObject = BrowserProtocolUtils . extractBrowserRequestState ( this . browserCrypto , serverParams . state ) ;
196
+ if ( platformStateObj . interactionType !== InteractionType . REDIRECT ) {
197
+ return null ;
198
+ } else {
199
+ BrowserUtils . clearHash ( ) ;
200
+ return responseHash ;
201
+ }
202
+ }
203
+
204
+ return null ;
205
+ }
206
+
174
207
/**
175
208
* Checks if hash exists and handles in window.
176
209
* @param responseHash
@@ -215,7 +248,7 @@ export class PublicClientApplication implements IPublicClientApplication {
215
248
async acquireTokenRedirect ( request : RedirectRequest ) : Promise < void > {
216
249
try {
217
250
// Preflight request
218
- const validRequest : AuthorizationUrlRequest = this . preflightInteractiveRequest ( request ) ;
251
+ const validRequest : AuthorizationUrlRequest = this . preflightInteractiveRequest ( request , InteractionType . REDIRECT ) ;
219
252
220
253
// Create auth code request and generate PKCE params
221
254
const authCodeRequest : AuthorizationCodeRequest = await this . initializeAuthorizationCodeRequest ( validRequest ) ;
@@ -262,7 +295,7 @@ export class PublicClientApplication implements IPublicClientApplication {
262
295
async acquireTokenPopup ( request : PopupRequest ) : Promise < AuthenticationResult > {
263
296
try {
264
297
// Preflight request
265
- const validRequest : AuthorizationUrlRequest = this . preflightInteractiveRequest ( request ) ;
298
+ const validRequest : AuthorizationUrlRequest = this . preflightInteractiveRequest ( request , InteractionType . POPUP ) ;
266
299
267
300
// Create auth code request and generate PKCE params
268
301
const authCodeRequest : AuthorizationCodeRequest = await this . initializeAuthorizationCodeRequest ( validRequest ) ;
@@ -333,7 +366,7 @@ export class PublicClientApplication implements IPublicClientApplication {
333
366
const silentRequest : AuthorizationUrlRequest = this . initializeAuthorizationRequest ( {
334
367
...request ,
335
368
prompt : PromptValue . NONE
336
- } ) ;
369
+ } , InteractionType . SILENT ) ;
337
370
338
371
// Create auth code request and generate PKCE params
339
372
const authCodeRequest : AuthorizationCodeRequest = await this . initializeAuthorizationCodeRequest ( silentRequest ) ;
@@ -383,7 +416,7 @@ export class PublicClientApplication implements IPublicClientApplication {
383
416
...silentRequest ,
384
417
redirectUri : request . redirectUri ,
385
418
prompt : PromptValue . NONE
386
- } ) ;
419
+ } , InteractionType . SILENT ) ;
387
420
388
421
// Create auth code request and generate PKCE params
389
422
const authCodeRequest : AuthorizationCodeRequest = await this . initializeAuthorizationCodeRequest ( silentAuthUrlRequest ) ;
@@ -566,7 +599,7 @@ export class PublicClientApplication implements IPublicClientApplication {
566
599
/**
567
600
* Helper to validate app environment before making a request.
568
601
*/
569
- private preflightInteractiveRequest ( request : RedirectRequest | PopupRequest ) : AuthorizationUrlRequest {
602
+ private preflightInteractiveRequest ( request : RedirectRequest | PopupRequest , interactionType : InteractionType ) : AuthorizationUrlRequest {
570
603
// block the reload if it occurred inside a hidden iframe
571
604
BrowserUtils . blockReloadInHiddenIframes ( ) ;
572
605
@@ -575,7 +608,7 @@ export class PublicClientApplication implements IPublicClientApplication {
575
608
throw BrowserAuthError . createInteractionInProgressError ( ) ;
576
609
}
577
610
578
- return this . initializeAuthorizationRequest ( request ) ;
611
+ return this . initializeAuthorizationRequest ( request , interactionType ) ;
579
612
}
580
613
581
614
/**
@@ -611,7 +644,7 @@ export class PublicClientApplication implements IPublicClientApplication {
611
644
* Helper to initialize required request parameters for interactive APIs and ssoSilent()
612
645
* @param request
613
646
*/
614
- private initializeAuthorizationRequest ( request : AuthorizationUrlRequest | RedirectRequest | PopupRequest ) : AuthorizationUrlRequest {
647
+ private initializeAuthorizationRequest ( request : AuthorizationUrlRequest | RedirectRequest | PopupRequest , interactionType : InteractionType ) : AuthorizationUrlRequest {
615
648
let validatedRequest : AuthorizationUrlRequest = {
616
649
...request
617
650
} ;
@@ -631,15 +664,20 @@ export class PublicClientApplication implements IPublicClientApplication {
631
664
}
632
665
}
633
666
667
+ const browserState : BrowserStateObject = {
668
+ interactionType : interactionType
669
+ } ;
670
+
634
671
validatedRequest . state = ProtocolUtils . setRequestState (
672
+ this . browserCrypto ,
635
673
( request && request . state ) || "" ,
636
- this . browserCrypto
674
+ browserState
637
675
) ;
638
676
639
677
if ( StringUtils . isEmpty ( validatedRequest . nonce ) ) {
640
678
validatedRequest . nonce = this . browserCrypto . createNewGuid ( ) ;
641
679
}
642
-
680
+
643
681
validatedRequest . responseMode = ResponseMode . FRAGMENT ;
644
682
645
683
validatedRequest = {
0 commit comments