UNPKG

ruchy-syntax-tools

Version:

Comprehensive syntax highlighting and language support for the Ruchy programming language

262 lines 10.7 kB
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. import * as msalBrowser from "@azure/msal-browser"; import { defaultLoggerCallback, ensureValidMsalToken, getAuthority, getKnownAuthorities, getMSALLogLevel, handleMsalError, msalToPublic, publicToMsal, } from "../utils.js"; import { AuthenticationRequiredError, CredentialUnavailableError } from "../../errors.js"; import { getLogLevel } from "@azure/logger"; import { formatSuccess } from "../../util/logging.js"; import { processMultiTenantRequest, resolveAdditionallyAllowedTenantIds, resolveTenantId, } from "../../util/tenantIdUtils.js"; import { DefaultTenantId } from "../../constants.js"; // We keep a copy of the redirect hash. // Check if self and location object is defined. const isLocationDefined = typeof self !== "undefined" && self.location !== undefined; /** * Generates a MSAL configuration that generally works for browsers * @internal */ function generateMsalBrowserConfiguration(options) { const tenantId = options.tenantId || DefaultTenantId; const authority = getAuthority(tenantId, options.authorityHost); return { auth: { clientId: options.clientId, authority, knownAuthorities: getKnownAuthorities(tenantId, authority, options.disableInstanceDiscovery), // If the users picked redirect as their login style, // but they didn't provide a redirectUri, // we can try to use the current page we're in as a default value. redirectUri: options.redirectUri || (isLocationDefined ? self.location.origin : undefined), }, cache: { cacheLocation: "sessionStorage", storeAuthStateInCookie: true, // Set to true to improve the experience on IE11 and Edge. }, system: { loggerOptions: { loggerCallback: defaultLoggerCallback(options.logger, "Browser"), logLevel: getMSALLogLevel(getLogLevel()), piiLoggingEnabled: options.loggingOptions?.enableUnsafeSupportLogging, }, }, }; } // We keep a copy of the redirect hash. const redirectHash = isLocationDefined ? self.location.hash : undefined; /** * Uses MSAL Browser 2.X for browser authentication, * which uses the [Auth Code Flow](https://learn.microsoft.com/azure/active-directory/develop/v2-oauth2-auth-code-flow). * @internal */ export function createMsalBrowserClient(options) { const loginStyle = options.loginStyle; if (!options.clientId) { throw new CredentialUnavailableError("A client ID is required in browsers"); } const clientId = options.clientId; const logger = options.logger; const tenantId = resolveTenantId(logger, options.tenantId, options.clientId); const additionallyAllowedTenantIds = resolveAdditionallyAllowedTenantIds(options?.tokenCredentialOptions?.additionallyAllowedTenants); const authorityHost = options.authorityHost; const msalConfig = generateMsalBrowserConfiguration(options); const disableAutomaticAuthentication = options.disableAutomaticAuthentication; const loginHint = options.loginHint; let account; if (options.authenticationRecord) { account = { ...options.authenticationRecord, tenantId, }; } // This variable should only be used through calling `getApp` function let app; /** * Return the MSAL account if not set yet * @returns MSAL application */ async function getApp() { if (!app) { // Prepare the MSAL application app = await msalBrowser.PublicClientApplication.createPublicClientApplication(msalConfig); // setting the account right after the app is created. if (account) { app.setActiveAccount(publicToMsal(account)); } } return app; } /** * Loads the account based on the result of the authentication. * If no result was received, tries to load the account from the cache. * @param result - Result object received from MSAL. */ async function handleBrowserResult(result) { try { const msalApp = await getApp(); if (result && result.account) { logger.info(`MSAL Browser V2 authentication successful.`); msalApp.setActiveAccount(result.account); return msalToPublic(clientId, result.account); } } catch (e) { logger.info(`Failed to acquire token through MSAL. ${e.message}`); } return; } /** * Handles the MSAL authentication result. * If the result has an account, we update the local account reference. * If the token received is invalid, an error will be thrown depending on what's missing. */ function handleResult(scopes, result, getTokenOptions) { if (result?.account) { account = msalToPublic(clientId, result.account); } ensureValidMsalToken(scopes, result, getTokenOptions); logger.getToken.info(formatSuccess(scopes)); return { token: result.accessToken, expiresOnTimestamp: result.expiresOn.getTime(), refreshAfterTimestamp: result.refreshOn?.getTime(), tokenType: "Bearer", }; } /** * Uses MSAL to handle the redirect. */ async function handleRedirect() { const msalApp = await getApp(); return handleBrowserResult((await msalApp.handleRedirectPromise(redirectHash)) || undefined); } /** * Uses MSAL to retrieve the active account. */ async function getActiveAccount() { const msalApp = await getApp(); const activeAccount = msalApp.getActiveAccount(); if (!activeAccount) { return; } return msalToPublic(clientId, activeAccount); } /** * Uses MSAL to trigger a redirect or a popup login. */ async function login(scopes = []) { const arrayScopes = Array.isArray(scopes) ? scopes : [scopes]; const loginRequest = { scopes: arrayScopes, loginHint: loginHint, }; const msalApp = await getApp(); switch (loginStyle) { case "redirect": { await app.loginRedirect(loginRequest); return; } case "popup": return handleBrowserResult(await msalApp.loginPopup(loginRequest)); } } /** * Tries to retrieve the token silently using MSAL. */ async function getTokenSilent(scopes, getTokenOptions) { const activeAccount = await getActiveAccount(); if (!activeAccount) { throw new AuthenticationRequiredError({ scopes, getTokenOptions, message: "Silent authentication failed. We couldn't retrieve an active account from the cache.", }); } const parameters = { authority: getTokenOptions?.authority || msalConfig.auth.authority, correlationId: getTokenOptions?.correlationId, claims: getTokenOptions?.claims, account: publicToMsal(activeAccount), forceRefresh: false, scopes, }; try { logger.info("Attempting to acquire token silently"); const msalApp = await getApp(); const response = await msalApp.acquireTokenSilent(parameters); return handleResult(scopes, response); } catch (err) { throw handleMsalError(scopes, err, options); } } /** * Attempts to retrieve the token in the browser through interactive methods. */ async function getTokenInteractive(scopes, getTokenOptions) { const activeAccount = await getActiveAccount(); if (!activeAccount) { throw new AuthenticationRequiredError({ scopes, getTokenOptions, message: "Silent authentication failed. We couldn't retrieve an active account from the cache.", }); } const parameters = { authority: getTokenOptions?.authority || msalConfig.auth.authority, correlationId: getTokenOptions?.correlationId, claims: getTokenOptions?.claims, account: publicToMsal(activeAccount), loginHint: loginHint, scopes, }; const msalApp = await getApp(); switch (loginStyle) { case "redirect": // This will go out of the page. // Once the InteractiveBrowserCredential is initialized again, // we'll load the MSAL account in the constructor. await msalApp.acquireTokenRedirect(parameters); return { token: "", expiresOnTimestamp: 0, tokenType: "Bearer" }; case "popup": return handleResult(scopes, await app.acquireTokenPopup(parameters)); } } /** * Attempts to get token through the silent flow. * If failed, get token through interactive method with `doGetToken` method. */ async function getToken(scopes, getTokenOptions = {}) { const getTokenTenantId = processMultiTenantRequest(tenantId, getTokenOptions, additionallyAllowedTenantIds) || tenantId; if (!getTokenOptions.authority) { getTokenOptions.authority = getAuthority(getTokenTenantId, authorityHost); } // We ensure that redirection is handled at this point. await handleRedirect(); if (!(await getActiveAccount()) && !disableAutomaticAuthentication) { await login(scopes); } // Attempts to get the token silently; else, falls back to interactive method. try { return await getTokenSilent(scopes, getTokenOptions); } catch (err) { if (err.name !== "AuthenticationRequiredError") { throw err; } if (getTokenOptions?.disableAutomaticAuthentication) { throw new AuthenticationRequiredError({ scopes, getTokenOptions, message: "Automatic authentication has been disabled. You may call the authenticate() method.", }); } logger.info(`Silent authentication failed, falling back to interactive method ${loginStyle}`); return getTokenInteractive(scopes, getTokenOptions); } } return { getActiveAccount, getToken, }; } //# sourceMappingURL=msalBrowserCommon.js.map