UNPKG

@azure/msal-browser

Version:
214 lines (191 loc) 6.75 kB
/* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ import { UrlString, invoke, invokeAsync } from "@azure/msal-common/browser"; import { createBrowserAuthError, BrowserAuthErrorCodes, } from "../error/BrowserAuthError.js"; import { BrowserConstants, BrowserCacheLocation } from "./BrowserConstants.js"; import * as BrowserCrypto from "../crypto/BrowserCrypto.js"; import { BrowserConfigurationAuthErrorCodes, createBrowserConfigurationAuthError, } from "../error/BrowserConfigurationAuthError.js"; import type { BrowserConfiguration } from "../config/Configuration.js"; /** * Clears hash from window url. */ export function clearHash(contentWindow: Window): void { // Office.js sets history.replaceState to null contentWindow.location.hash = ""; if (typeof contentWindow.history.replaceState === "function") { // Full removes "#" from url contentWindow.history.replaceState( null, "", `${contentWindow.location.origin}${contentWindow.location.pathname}${contentWindow.location.search}` ); } } /** * Replaces current hash with hash from provided url */ export function replaceHash(url: string): void { const urlParts = url.split("#"); urlParts.shift(); // Remove part before the hash window.location.hash = urlParts.length > 0 ? urlParts.join("#") : ""; } /** * Returns boolean of whether the current window is in an iframe or not. */ export function isInIframe(): boolean { return window.parent !== window; } /** * Returns boolean of whether or not the current window is a popup opened by msal */ export function isInPopup(): boolean { return ( typeof window !== "undefined" && !!window.opener && window.opener !== window && typeof window.name === "string" && window.name.indexOf(`${BrowserConstants.POPUP_NAME_PREFIX}.`) === 0 ); } // #endregion /** * Returns current window URL as redirect uri */ export function getCurrentUri(): string { return typeof window !== "undefined" && window.location ? window.location.href.split("?")[0].split("#")[0] : ""; } /** * Gets the homepage url for the current window location. */ export function getHomepage(): string { const currentUrl = new UrlString(window.location.href); const urlComponents = currentUrl.getUrlComponents(); return `${urlComponents.Protocol}//${urlComponents.HostNameAndPort}/`; } /** * Throws error if we have completed an auth and are * attempting another auth request inside an iframe. */ export function blockReloadInHiddenIframes(): void { const isResponseHash = UrlString.hashContainsKnownProperties( window.location.hash ); // return an error if called from the hidden iframe created by the msal js silent calls if (isResponseHash && isInIframe()) { throw createBrowserAuthError(BrowserAuthErrorCodes.blockIframeReload); } } /** * Block redirect operations in iframes unless explicitly allowed * @param interactionType Interaction type for the request * @param allowRedirectInIframe Config value to allow redirects when app is inside an iframe */ export function blockRedirectInIframe(allowRedirectInIframe: boolean): void { if (isInIframe() && !allowRedirectInIframe) { // If we are not in top frame, we shouldn't redirect. This is also handled by the service. throw createBrowserAuthError(BrowserAuthErrorCodes.redirectInIframe); } } /** * Block redirectUri loaded in popup from calling AcquireToken APIs */ export function blockAcquireTokenInPopups(): void { // Popups opened by msal popup APIs are given a name that starts with "msal." if (isInPopup()) { throw createBrowserAuthError(BrowserAuthErrorCodes.blockNestedPopups); } } /** * Throws error if token requests are made in non-browser environment * @param isBrowserEnvironment Flag indicating if environment is a browser. */ export function blockNonBrowserEnvironment(): void { if (typeof window === "undefined") { throw createBrowserAuthError( BrowserAuthErrorCodes.nonBrowserEnvironment ); } } /** * Throws error if initialize hasn't been called * @param initialized */ export function blockAPICallsBeforeInitialize(initialized: boolean): void { if (!initialized) { throw createBrowserAuthError( BrowserAuthErrorCodes.uninitializedPublicClientApplication ); } } /** * Helper to validate app environment before making an auth request * @param initialized */ export function preflightCheck(initialized: boolean): void { // Block request if not in browser environment blockNonBrowserEnvironment(); // Block auth requests inside a hidden iframe blockReloadInHiddenIframes(); // Block redirectUri opened in a popup from calling MSAL APIs blockAcquireTokenInPopups(); // Block token acquisition before initialize has been called blockAPICallsBeforeInitialize(initialized); } /** * Helper to validate app enviornment before making redirect request * @param initialized * @param config */ export function redirectPreflightCheck( initialized: boolean, config: BrowserConfiguration ): void { preflightCheck(initialized); blockRedirectInIframe(config.system.allowRedirectInIframe); // Block redirects if memory storage is enabled but storeAuthStateInCookie is not if ( config.cache.cacheLocation === BrowserCacheLocation.MemoryStorage && !config.cache.storeAuthStateInCookie ) { throw createBrowserConfigurationAuthError( BrowserConfigurationAuthErrorCodes.inMemRedirectUnavailable ); } } /** * Adds a preconnect link element to the header which begins DNS resolution and SSL connection in anticipation of the /token request * @param loginDomain Authority domain, including https protocol e.g. https://login.microsoftonline.com * @returns */ export function preconnect(authority: string): void { const link = document.createElement("link"); link.rel = "preconnect"; link.href = new URL(authority).origin; link.crossOrigin = "anonymous"; document.head.appendChild(link); // The browser will close connection if not used within a few seconds, remove element from the header after 10s window.setTimeout(() => { try { document.head.removeChild(link); } catch {} }, 10000); // 10s Timeout } /** * Wrapper function that creates a UUID v7 from the current timestamp. * @returns {string} */ export function createGuid(): string { return BrowserCrypto.createNewGuid(); } export { invoke }; export { invokeAsync };