Initial
This commit is contained in:
179
node_modules/@azure/msal-browser/src/interaction_handler/InteractionHandler.ts
generated
vendored
Normal file
179
node_modules/@azure/msal-browser/src/interaction_handler/InteractionHandler.ts
generated
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import {
|
||||
AuthorizationCodePayload,
|
||||
CommonAuthorizationCodeRequest,
|
||||
AuthorizationCodeClient,
|
||||
CcsCredential,
|
||||
Logger,
|
||||
ServerError,
|
||||
IPerformanceClient,
|
||||
PerformanceEvents,
|
||||
invokeAsync,
|
||||
CcsCredentialType,
|
||||
AuthorizeResponse,
|
||||
AuthorizeProtocol,
|
||||
CommonAuthorizationUrlRequest,
|
||||
} from "@azure/msal-common/browser";
|
||||
|
||||
import { BrowserCacheManager } from "../cache/BrowserCacheManager.js";
|
||||
import {
|
||||
createBrowserAuthError,
|
||||
BrowserAuthErrorCodes,
|
||||
} from "../error/BrowserAuthError.js";
|
||||
import { AuthenticationResult } from "../response/AuthenticationResult.js";
|
||||
|
||||
/**
|
||||
* Abstract class which defines operations for a browser interaction handling class.
|
||||
*/
|
||||
export class InteractionHandler {
|
||||
protected authModule: AuthorizationCodeClient;
|
||||
protected browserStorage: BrowserCacheManager;
|
||||
protected authCodeRequest: CommonAuthorizationCodeRequest;
|
||||
protected logger: Logger;
|
||||
protected performanceClient: IPerformanceClient;
|
||||
|
||||
constructor(
|
||||
authCodeModule: AuthorizationCodeClient,
|
||||
storageImpl: BrowserCacheManager,
|
||||
authCodeRequest: CommonAuthorizationCodeRequest,
|
||||
logger: Logger,
|
||||
performanceClient: IPerformanceClient
|
||||
) {
|
||||
this.authModule = authCodeModule;
|
||||
this.browserStorage = storageImpl;
|
||||
this.authCodeRequest = authCodeRequest;
|
||||
this.logger = logger;
|
||||
this.performanceClient = performanceClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to handle response parameters from hash.
|
||||
* @param locationHash
|
||||
*/
|
||||
async handleCodeResponse(
|
||||
response: AuthorizeResponse,
|
||||
request: CommonAuthorizationUrlRequest
|
||||
): Promise<AuthenticationResult> {
|
||||
this.performanceClient.addQueueMeasurement(
|
||||
PerformanceEvents.HandleCodeResponse,
|
||||
request.correlationId
|
||||
);
|
||||
|
||||
let authCodeResponse;
|
||||
try {
|
||||
authCodeResponse = AuthorizeProtocol.getAuthorizationCodePayload(
|
||||
response,
|
||||
request.state
|
||||
);
|
||||
} catch (e) {
|
||||
if (
|
||||
e instanceof ServerError &&
|
||||
e.subError === BrowserAuthErrorCodes.userCancelled
|
||||
) {
|
||||
// Translate server error caused by user closing native prompt to corresponding first class MSAL error
|
||||
throw createBrowserAuthError(
|
||||
BrowserAuthErrorCodes.userCancelled
|
||||
);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
return invokeAsync(
|
||||
this.handleCodeResponseFromServer.bind(this),
|
||||
PerformanceEvents.HandleCodeResponseFromServer,
|
||||
this.logger,
|
||||
this.performanceClient,
|
||||
request.correlationId
|
||||
)(authCodeResponse, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process auth code response from AAD
|
||||
* @param authCodeResponse
|
||||
* @param state
|
||||
* @param authority
|
||||
* @param networkModule
|
||||
* @returns
|
||||
*/
|
||||
async handleCodeResponseFromServer(
|
||||
authCodeResponse: AuthorizationCodePayload,
|
||||
request: CommonAuthorizationUrlRequest,
|
||||
validateNonce: boolean = true
|
||||
): Promise<AuthenticationResult> {
|
||||
this.performanceClient.addQueueMeasurement(
|
||||
PerformanceEvents.HandleCodeResponseFromServer,
|
||||
request.correlationId
|
||||
);
|
||||
this.logger.trace(
|
||||
"InteractionHandler.handleCodeResponseFromServer called"
|
||||
);
|
||||
|
||||
// Assign code to request
|
||||
this.authCodeRequest.code = authCodeResponse.code;
|
||||
|
||||
// Check for new cloud instance
|
||||
if (authCodeResponse.cloud_instance_host_name) {
|
||||
await invokeAsync(
|
||||
this.authModule.updateAuthority.bind(this.authModule),
|
||||
PerformanceEvents.UpdateTokenEndpointAuthority,
|
||||
this.logger,
|
||||
this.performanceClient,
|
||||
request.correlationId
|
||||
)(authCodeResponse.cloud_instance_host_name, request.correlationId);
|
||||
}
|
||||
|
||||
// Nonce validation not needed when redirect not involved (e.g. hybrid spa, renewing token via rt)
|
||||
if (validateNonce) {
|
||||
// TODO: Assigning "response nonce" to "request nonce" is confusing. Refactor the function doing validation to accept request nonce directly
|
||||
authCodeResponse.nonce = request.nonce || undefined;
|
||||
}
|
||||
|
||||
authCodeResponse.state = request.state;
|
||||
|
||||
// Add CCS parameters if available
|
||||
if (authCodeResponse.client_info) {
|
||||
this.authCodeRequest.clientInfo = authCodeResponse.client_info;
|
||||
} else {
|
||||
const ccsCred = this.createCcsCredentials(request);
|
||||
if (ccsCred) {
|
||||
this.authCodeRequest.ccsCredential = ccsCred;
|
||||
}
|
||||
}
|
||||
|
||||
// Acquire token with retrieved code.
|
||||
const tokenResponse = (await invokeAsync(
|
||||
this.authModule.acquireToken.bind(this.authModule),
|
||||
PerformanceEvents.AuthClientAcquireToken,
|
||||
this.logger,
|
||||
this.performanceClient,
|
||||
request.correlationId
|
||||
)(this.authCodeRequest, authCodeResponse)) as AuthenticationResult;
|
||||
return tokenResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build ccs creds if available
|
||||
*/
|
||||
protected createCcsCredentials(
|
||||
request: CommonAuthorizationUrlRequest
|
||||
): CcsCredential | null {
|
||||
if (request.account) {
|
||||
return {
|
||||
credential: request.account.homeAccountId,
|
||||
type: CcsCredentialType.HOME_ACCOUNT_ID,
|
||||
};
|
||||
} else if (request.loginHint) {
|
||||
return {
|
||||
credential: request.loginHint,
|
||||
type: CcsCredentialType.UPN,
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
273
node_modules/@azure/msal-browser/src/interaction_handler/SilentHandler.ts
generated
vendored
Normal file
273
node_modules/@azure/msal-browser/src/interaction_handler/SilentHandler.ts
generated
vendored
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import {
|
||||
Logger,
|
||||
IPerformanceClient,
|
||||
PerformanceEvents,
|
||||
invokeAsync,
|
||||
invoke,
|
||||
ServerResponseType,
|
||||
Authority,
|
||||
CommonAuthorizationUrlRequest,
|
||||
} from "@azure/msal-common/browser";
|
||||
import {
|
||||
createBrowserAuthError,
|
||||
BrowserAuthErrorCodes,
|
||||
} from "../error/BrowserAuthError.js";
|
||||
import {
|
||||
BrowserConfiguration,
|
||||
DEFAULT_IFRAME_TIMEOUT_MS,
|
||||
} from "../config/Configuration.js";
|
||||
import { getCodeForm, getEARForm } from "../protocol/Authorize.js";
|
||||
|
||||
/**
|
||||
* Creates a hidden iframe to given URL using user-requested scopes as an id.
|
||||
* @param urlNavigate
|
||||
* @param userRequestScopes
|
||||
*/
|
||||
export async function initiateCodeRequest(
|
||||
requestUrl: string,
|
||||
performanceClient: IPerformanceClient,
|
||||
logger: Logger,
|
||||
correlationId: string,
|
||||
navigateFrameWait?: number
|
||||
): Promise<HTMLIFrameElement> {
|
||||
performanceClient.addQueueMeasurement(
|
||||
PerformanceEvents.SilentHandlerInitiateAuthRequest,
|
||||
correlationId
|
||||
);
|
||||
|
||||
if (!requestUrl) {
|
||||
// Throw error if request URL is empty.
|
||||
logger.info("Navigate url is empty");
|
||||
throw createBrowserAuthError(BrowserAuthErrorCodes.emptyNavigateUri);
|
||||
}
|
||||
if (navigateFrameWait) {
|
||||
return invokeAsync(
|
||||
loadFrame,
|
||||
PerformanceEvents.SilentHandlerLoadFrame,
|
||||
logger,
|
||||
performanceClient,
|
||||
correlationId
|
||||
)(requestUrl, navigateFrameWait, performanceClient, correlationId);
|
||||
}
|
||||
return invoke(
|
||||
loadFrameSync,
|
||||
PerformanceEvents.SilentHandlerLoadFrameSync,
|
||||
logger,
|
||||
performanceClient,
|
||||
correlationId
|
||||
)(requestUrl);
|
||||
}
|
||||
|
||||
export async function initiateCodeFlowWithPost(
|
||||
config: BrowserConfiguration,
|
||||
authority: Authority,
|
||||
request: CommonAuthorizationUrlRequest,
|
||||
logger: Logger,
|
||||
performanceClient: IPerformanceClient
|
||||
): Promise<HTMLIFrameElement> {
|
||||
const frame = createHiddenIframe();
|
||||
if (!frame.contentDocument) {
|
||||
throw "No document associated with iframe!";
|
||||
}
|
||||
const form = await getCodeForm(
|
||||
frame.contentDocument,
|
||||
config,
|
||||
authority,
|
||||
request,
|
||||
logger,
|
||||
performanceClient
|
||||
);
|
||||
form.submit();
|
||||
return frame;
|
||||
}
|
||||
|
||||
export async function initiateEarRequest(
|
||||
config: BrowserConfiguration,
|
||||
authority: Authority,
|
||||
request: CommonAuthorizationUrlRequest,
|
||||
logger: Logger,
|
||||
performanceClient: IPerformanceClient
|
||||
): Promise<HTMLIFrameElement> {
|
||||
const frame = createHiddenIframe();
|
||||
if (!frame.contentDocument) {
|
||||
throw "No document associated with iframe!";
|
||||
}
|
||||
const form = await getEARForm(
|
||||
frame.contentDocument,
|
||||
config,
|
||||
authority,
|
||||
request,
|
||||
logger,
|
||||
performanceClient
|
||||
);
|
||||
form.submit();
|
||||
return frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitors an iframe content window until it loads a url with a known hash, or hits a specified timeout.
|
||||
* @param iframe
|
||||
* @param timeout
|
||||
*/
|
||||
export async function monitorIframeForHash(
|
||||
iframe: HTMLIFrameElement,
|
||||
timeout: number,
|
||||
pollIntervalMilliseconds: number,
|
||||
performanceClient: IPerformanceClient,
|
||||
logger: Logger,
|
||||
correlationId: string,
|
||||
responseType: ServerResponseType
|
||||
): Promise<string> {
|
||||
performanceClient.addQueueMeasurement(
|
||||
PerformanceEvents.SilentHandlerMonitorIframeForHash,
|
||||
correlationId
|
||||
);
|
||||
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
if (timeout < DEFAULT_IFRAME_TIMEOUT_MS) {
|
||||
logger.warning(
|
||||
`system.loadFrameTimeout or system.iframeHashTimeout set to lower (${timeout}ms) than the default (${DEFAULT_IFRAME_TIMEOUT_MS}ms). This may result in timeouts.`
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Polling for iframes can be purely timing based,
|
||||
* since we don't need to account for interaction.
|
||||
*/
|
||||
const timeoutId = window.setTimeout(() => {
|
||||
window.clearInterval(intervalId);
|
||||
reject(
|
||||
createBrowserAuthError(
|
||||
BrowserAuthErrorCodes.monitorWindowTimeout
|
||||
)
|
||||
);
|
||||
}, timeout);
|
||||
|
||||
const intervalId = window.setInterval(() => {
|
||||
let href: string = "";
|
||||
const contentWindow = iframe.contentWindow;
|
||||
try {
|
||||
/*
|
||||
* Will throw if cross origin,
|
||||
* which should be caught and ignored
|
||||
* since we need the interval to keep running while on STS UI.
|
||||
*/
|
||||
href = contentWindow ? contentWindow.location.href : "";
|
||||
} catch (e) {}
|
||||
|
||||
if (!href || href === "about:blank") {
|
||||
return;
|
||||
}
|
||||
|
||||
let responseString = "";
|
||||
if (contentWindow) {
|
||||
if (responseType === ServerResponseType.QUERY) {
|
||||
responseString = contentWindow.location.search;
|
||||
} else {
|
||||
responseString = contentWindow.location.hash;
|
||||
}
|
||||
}
|
||||
window.clearTimeout(timeoutId);
|
||||
window.clearInterval(intervalId);
|
||||
resolve(responseString);
|
||||
}, pollIntervalMilliseconds);
|
||||
}).finally(() => {
|
||||
invoke(
|
||||
removeHiddenIframe,
|
||||
PerformanceEvents.RemoveHiddenIframe,
|
||||
logger,
|
||||
performanceClient,
|
||||
correlationId
|
||||
)(iframe);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* Loads iframe with authorization endpoint URL
|
||||
* @ignore
|
||||
* @deprecated
|
||||
*/
|
||||
function loadFrame(
|
||||
urlNavigate: string,
|
||||
navigateFrameWait: number,
|
||||
performanceClient: IPerformanceClient,
|
||||
correlationId: string
|
||||
): Promise<HTMLIFrameElement> {
|
||||
performanceClient.addQueueMeasurement(
|
||||
PerformanceEvents.SilentHandlerLoadFrame,
|
||||
correlationId
|
||||
);
|
||||
|
||||
/*
|
||||
* This trick overcomes iframe navigation in IE
|
||||
* IE does not load the page consistently in iframe
|
||||
*/
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const frameHandle = createHiddenIframe();
|
||||
|
||||
window.setTimeout(() => {
|
||||
if (!frameHandle) {
|
||||
reject("Unable to load iframe");
|
||||
return;
|
||||
}
|
||||
|
||||
frameHandle.src = urlNavigate;
|
||||
|
||||
resolve(frameHandle);
|
||||
}, navigateFrameWait);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @hidden
|
||||
* Loads the iframe synchronously when the navigateTimeFrame is set to `0`
|
||||
* @param urlNavigate
|
||||
* @param frameName
|
||||
* @param logger
|
||||
*/
|
||||
function loadFrameSync(urlNavigate: string): HTMLIFrameElement {
|
||||
const frameHandle = createHiddenIframe();
|
||||
|
||||
frameHandle.src = urlNavigate;
|
||||
|
||||
return frameHandle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* Creates a new hidden iframe or gets an existing one for silent token renewal.
|
||||
* @ignore
|
||||
*/
|
||||
function createHiddenIframe(): HTMLIFrameElement {
|
||||
const authFrame = document.createElement("iframe");
|
||||
|
||||
authFrame.className = "msalSilentIframe";
|
||||
authFrame.style.visibility = "hidden";
|
||||
authFrame.style.position = "absolute";
|
||||
authFrame.style.width = authFrame.style.height = "0";
|
||||
authFrame.style.border = "0";
|
||||
authFrame.setAttribute(
|
||||
"sandbox",
|
||||
"allow-scripts allow-same-origin allow-forms"
|
||||
);
|
||||
document.body.appendChild(authFrame);
|
||||
|
||||
return authFrame;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* Removes a hidden iframe from the page.
|
||||
* @ignore
|
||||
*/
|
||||
function removeHiddenIframe(iframe: HTMLIFrameElement): void {
|
||||
if (document.body === iframe.parentNode) {
|
||||
document.body.removeChild(iframe);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user