Skip to content

Commit c9603bd

Browse files
author
Prithvi Kanherkar
committed
Revert "Revert "Merge branch 'dev' into add-beachball-config""
This reverts commit be0aa70.
1 parent 12f49cd commit c9603bd

38 files changed

+677
-125
lines changed

experimental/msal-react-native-android-poc/package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/msal-browser/src/app/PublicClientApplication.ts

+72-34
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ import {
2828
SilentFlowClient,
2929
EndSessionRequest,
3030
BaseAuthRequest,
31-
Logger
31+
Logger,
32+
ServerAuthorizationCodeResponse
3233
} from "@azure/msal-common";
3334
import { buildConfiguration, Configuration } from "../config/Configuration";
3435
import { BrowserStorage } from "../cache/BrowserStorage";
@@ -37,13 +38,14 @@ import { RedirectHandler } from "../interaction_handler/RedirectHandler";
3738
import { PopupHandler } from "../interaction_handler/PopupHandler";
3839
import { SilentHandler } from "../interaction_handler/SilentHandler";
3940
import { BrowserAuthError } from "../error/BrowserAuthError";
40-
import { BrowserConstants, TemporaryCacheKeys, DEFAULT_REQUEST } from "../utils/BrowserConstants";
41+
import { BrowserConstants, TemporaryCacheKeys, DEFAULT_REQUEST, InteractionType } from "../utils/BrowserConstants";
4142
import { BrowserUtils } from "../utils/BrowserUtils";
4243
import { version } from "../../package.json";
4344
import { IPublicClientApplication } from "./IPublicClientApplication";
4445
import { RedirectRequest } from "../request/RedirectRequest";
4546
import { PopupRequest } from "../request/PopupRequest";
4647
import { SilentRequest } from "../request/SilentRequest";
48+
import { BrowserProtocolUtils, BrowserStateObject } from "../utils/BrowserProtocolUtils";
4749

4850
/**
4951
* 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 {
132134
* - if true, performs logic to cache and navigate
133135
* - if false, handles hash string and parses response
134136
*/
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+
}
141143

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;
143146
const loginRequestUrlNormalized = UrlString.removeHashFromUrl(loginRequestUrl || "");
147+
const currentUrlNormalized = UrlString.removeHashFromUrl(window.location.href);
148+
144149
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+
}
145154
// 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()) {
158159
// Returned from authority using redirect - need to perform navigation before processing response
160+
// Cache the hash to be retrieved after the next redirect
159161
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") {
162164
// 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);
163168
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);
165170
} else {
166-
// Navigate to target url
171+
// Navigate to page that initiated the redirect request
167172
BrowserUtils.navigateWindow(loginRequestUrl, true);
168173
}
169174
}
170175

171176
return null;
172177
}
173178

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+
174207
/**
175208
* Checks if hash exists and handles in window.
176209
* @param responseHash
@@ -215,7 +248,7 @@ export class PublicClientApplication implements IPublicClientApplication {
215248
async acquireTokenRedirect(request: RedirectRequest): Promise<void> {
216249
try {
217250
// Preflight request
218-
const validRequest: AuthorizationUrlRequest = this.preflightInteractiveRequest(request);
251+
const validRequest: AuthorizationUrlRequest = this.preflightInteractiveRequest(request, InteractionType.REDIRECT);
219252

220253
// Create auth code request and generate PKCE params
221254
const authCodeRequest: AuthorizationCodeRequest = await this.initializeAuthorizationCodeRequest(validRequest);
@@ -262,7 +295,7 @@ export class PublicClientApplication implements IPublicClientApplication {
262295
async acquireTokenPopup(request: PopupRequest): Promise<AuthenticationResult> {
263296
try {
264297
// Preflight request
265-
const validRequest: AuthorizationUrlRequest = this.preflightInteractiveRequest(request);
298+
const validRequest: AuthorizationUrlRequest = this.preflightInteractiveRequest(request, InteractionType.POPUP);
266299

267300
// Create auth code request and generate PKCE params
268301
const authCodeRequest: AuthorizationCodeRequest = await this.initializeAuthorizationCodeRequest(validRequest);
@@ -333,7 +366,7 @@ export class PublicClientApplication implements IPublicClientApplication {
333366
const silentRequest: AuthorizationUrlRequest = this.initializeAuthorizationRequest({
334367
...request,
335368
prompt: PromptValue.NONE
336-
});
369+
}, InteractionType.SILENT);
337370

338371
// Create auth code request and generate PKCE params
339372
const authCodeRequest: AuthorizationCodeRequest = await this.initializeAuthorizationCodeRequest(silentRequest);
@@ -383,7 +416,7 @@ export class PublicClientApplication implements IPublicClientApplication {
383416
...silentRequest,
384417
redirectUri: request.redirectUri,
385418
prompt: PromptValue.NONE
386-
});
419+
}, InteractionType.SILENT);
387420

388421
// Create auth code request and generate PKCE params
389422
const authCodeRequest: AuthorizationCodeRequest = await this.initializeAuthorizationCodeRequest(silentAuthUrlRequest);
@@ -566,7 +599,7 @@ export class PublicClientApplication implements IPublicClientApplication {
566599
/**
567600
* Helper to validate app environment before making a request.
568601
*/
569-
private preflightInteractiveRequest(request: RedirectRequest|PopupRequest): AuthorizationUrlRequest {
602+
private preflightInteractiveRequest(request: RedirectRequest|PopupRequest, interactionType: InteractionType): AuthorizationUrlRequest {
570603
// block the reload if it occurred inside a hidden iframe
571604
BrowserUtils.blockReloadInHiddenIframes();
572605

@@ -575,7 +608,7 @@ export class PublicClientApplication implements IPublicClientApplication {
575608
throw BrowserAuthError.createInteractionInProgressError();
576609
}
577610

578-
return this.initializeAuthorizationRequest(request);
611+
return this.initializeAuthorizationRequest(request, interactionType);
579612
}
580613

581614
/**
@@ -611,7 +644,7 @@ export class PublicClientApplication implements IPublicClientApplication {
611644
* Helper to initialize required request parameters for interactive APIs and ssoSilent()
612645
* @param request
613646
*/
614-
private initializeAuthorizationRequest(request: AuthorizationUrlRequest|RedirectRequest|PopupRequest): AuthorizationUrlRequest {
647+
private initializeAuthorizationRequest(request: AuthorizationUrlRequest|RedirectRequest|PopupRequest, interactionType: InteractionType): AuthorizationUrlRequest {
615648
let validatedRequest: AuthorizationUrlRequest = {
616649
...request
617650
};
@@ -631,15 +664,20 @@ export class PublicClientApplication implements IPublicClientApplication {
631664
}
632665
}
633666

667+
const browserState: BrowserStateObject = {
668+
interactionType: interactionType
669+
};
670+
634671
validatedRequest.state = ProtocolUtils.setRequestState(
672+
this.browserCrypto,
635673
(request && request.state) || "",
636-
this.browserCrypto
674+
browserState
637675
);
638676

639677
if (StringUtils.isEmpty(validatedRequest.nonce)) {
640678
validatedRequest.nonce = this.browserCrypto.createNewGuid();
641679
}
642-
680+
643681
validatedRequest.responseMode = ResponseMode.FRAGMENT;
644682

645683
validatedRequest = {

lib/msal-browser/src/interaction_handler/RedirectHandler.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,9 @@ export class RedirectHandler extends InteractionHandler {
6565
this.authCodeRequest = this.browserStorage.getCachedRequest(requestState, browserCrypto);
6666
this.authCodeRequest.code = authCode;
6767

68-
// Hash was processed successfully - remove from cache
69-
this.browserStorage.removeItem(this.browserStorage.generateCacheKey(TemporaryCacheKeys.URL_HASH));
70-
7168
// Acquire token with retrieved code.
7269
const tokenResponse = await this.authModule.acquireToken(this.authCodeRequest, cachedNonce, requestState);
70+
7371
this.browserStorage.cleanRequest();
7472
return tokenResponse;
7573
}

lib/msal-browser/src/utils/BrowserConstants.ts

+9
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ export enum TemporaryCacheKeys {
5353
SCOPES = "scopes"
5454
}
5555

56+
/**
57+
* Interaction type of the API - used for state and telemetry
58+
*/
59+
export enum InteractionType {
60+
REDIRECT = "redirect",
61+
POPUP = "popup",
62+
SILENT = "silent"
63+
}
64+
5665
export const DEFAULT_REQUEST: AuthorizationUrlRequest = {
5766
scopes: [Constants.OPENID_SCOPE, Constants.PROFILE_SCOPE]
5867
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License.
4+
*/
5+
6+
import { InteractionType } from "./BrowserConstants";
7+
import { StringUtils, ClientAuthError, ICrypto, RequestStateObject, ProtocolUtils } from "@azure/msal-common";
8+
9+
export type BrowserStateObject = {
10+
interactionType: InteractionType
11+
};
12+
13+
export class BrowserProtocolUtils {
14+
15+
/**
16+
* Extracts the BrowserStateObject from the state string.
17+
* @param browserCrypto
18+
* @param state
19+
*/
20+
static extractBrowserRequestState(browserCrypto: ICrypto, state: string): BrowserStateObject {
21+
if (StringUtils.isEmpty(state)) {
22+
return null;
23+
}
24+
25+
try {
26+
const requestStateObj: RequestStateObject = ProtocolUtils.parseRequestState(browserCrypto, state);
27+
return requestStateObj.libraryState.meta as BrowserStateObject;
28+
} catch (e) {
29+
throw ClientAuthError.createInvalidStateError(state, e);
30+
}
31+
}
32+
}

lib/msal-browser/src/utils/BrowserUtils.ts

+9
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ export class BrowserUtils {
6060
return window.location.href.split("?")[0].split("#")[0];
6161
}
6262

63+
/**
64+
* Gets the homepage url for the current window location.
65+
*/
66+
static getHomepage(): string {
67+
const currentUrl = new UrlString(window.location.href);
68+
const urlComponents = currentUrl.getUrlComponents();
69+
return `${urlComponents.Protocol}//${urlComponents.HostNameAndPort}/`;
70+
}
71+
6372
/**
6473
* Returns best compatible network client object.
6574
*/

0 commit comments

Comments
 (0)