UNPKG

@azure/msal-node-extensions

Version:

![npm (scoped)](https://img.shields.io/npm/v/@azure/msal-node-extensions) ![npm](https://img.shields.io/npm/dw/@azure/msal-node-extensions)

487 lines (484 loc) 22.1 kB
/*! @azure/msal-node-extensions v5.2.2 2026-05-19 */ 'use strict'; import { msalNodeRuntime, ErrorStatus, LogLevel } from '@azure/msal-node-runtime'; import { ErrorCodes } from '../utils/Constants.mjs'; import { name, version } from '../packageMetadata.mjs'; import { Logger } from '../lib/msal-common/dist/logger/Logger.mjs'; import { createClientAuthError } from '../lib/msal-common/dist/error/ClientAuthError.mjs'; import { ServerTelemetryManager } from '../lib/msal-common/dist/telemetry/server/ServerTelemetryManager.mjs'; import { PlatformBrokerError } from '../lib/msal-common/dist/error/PlatformBrokerError.mjs'; import { createClientConfigurationError } from '../lib/msal-common/dist/error/ClientConfigurationError.mjs'; import { ServerError } from '../lib/msal-common/dist/error/ServerError.mjs'; import { InteractionRequiredAuthError } from '../lib/msal-common/dist/error/InteractionRequiredAuthError.mjs'; import { PromptValue, AuthenticationScheme } from '../lib/msal-common/dist/utils/Constants.mjs'; import { RESOURCE, X_CLIENT_EXTRA_SKU } from '../lib/msal-common/dist/constants/AADServerParamKeys.mjs'; import { toDateFromSeconds } from '../lib/msal-common/dist/utils/TimeUtils.mjs'; import { noAccountFound, platformBrokerError, userCanceled, noNetworkConnectivity } from '../lib/msal-common/dist/error/ClientAuthErrorCodes.mjs'; import { untrustedAuthority } from '../lib/msal-common/dist/error/ClientConfigurationErrorCodes.mjs'; /* * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. */ class NativeBrokerPlugin { constructor() { const defaultLoggerOptions = { loggerCallback: () => { // Empty logger callback }, piiLoggingEnabled: false, }; this.logger = new Logger(defaultLoggerOptions, name, version); // Default logger this.isBrokerAvailable = msalNodeRuntime.StartupError ? false : true; } setLogger(loggerOptions) { this.logger = new Logger(loggerOptions, name, version); const logCallback = (message, logLevel, containsPii, correlationId = "") => { switch (logLevel) { case LogLevel.Trace: if (containsPii) { this.logger.tracePii(message, correlationId); } else { this.logger.trace(message, correlationId); } break; case LogLevel.Debug: if (containsPii) { this.logger.tracePii(message, correlationId); } else { this.logger.trace(message, correlationId); } break; case LogLevel.Info: if (containsPii) { this.logger.infoPii(message, correlationId); } else { this.logger.info(message, correlationId); } break; case LogLevel.Warning: if (containsPii) { this.logger.warningPii(message, correlationId); } else { this.logger.warning(message, correlationId); } break; case LogLevel.Error: if (containsPii) { this.logger.errorPii(message, correlationId); } else { this.logger.error(message, correlationId); } break; case LogLevel.Fatal: if (containsPii) { this.logger.errorPii(message, correlationId); } else { this.logger.error(message, correlationId); } break; default: if (containsPii) { this.logger.infoPii(message, correlationId); } else { this.logger.info(message, correlationId); } break; } }; try { msalNodeRuntime.RegisterLogger(logCallback, loggerOptions.piiLoggingEnabled || false); } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { throw wrappedError; } } } async getAccountById(accountId, correlationId) { this.logger.trace("NativeBrokerPlugin - getAccountById called", correlationId); const readAccountResult = await this.readAccountById(accountId, correlationId); return this.generateAccountInfo(readAccountResult.account); } async getAllAccounts(clientId, correlationId) { this.logger.trace("NativeBrokerPlugin - getAllAccounts called", correlationId); return new Promise((resolve, reject) => { const resultCallback = (result) => { try { result.CheckError(); } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { reject(wrappedError); return; } } const accountInfoResult = []; result.accounts.forEach((account) => { accountInfoResult.push(this.generateAccountInfo(account)); }); resolve(accountInfoResult); }; try { msalNodeRuntime.DiscoverAccountsAsync(clientId, correlationId, resultCallback); } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { reject(wrappedError); } } }); } async acquireTokenSilent(request) { this.logger.trace("NativeBrokerPlugin - acquireTokenSilent called", request.correlationId); const platformRequest = request; if (!platformRequest.redirectUri) { platformRequest.redirectUri = this.chooseRedirectUriByPlatform(platformRequest); this.logger.info("NativeBrokerPlugin - No Redirect URI provided, using default", platformRequest.correlationId); } const authParams = this.generateRequestParameters(platformRequest); const account = await this.getAccount(platformRequest); return new Promise((resolve, reject) => { const resultCallback = (result) => { try { result.CheckError(); } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { reject(wrappedError); return; } } const authenticationResult = this.getAuthenticationResult(platformRequest, result); resolve(authenticationResult); }; try { if (account) { msalNodeRuntime.AcquireTokenSilentlyAsync(authParams, platformRequest.correlationId, account, resultCallback); } else { msalNodeRuntime.SignInSilentlyAsync(authParams, platformRequest.correlationId, resultCallback); } } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { reject(wrappedError); } } }); } async acquireTokenInteractive(request, providedWindowHandle) { this.logger.trace("NativeBrokerPlugin - acquireTokenInteractive called", request.correlationId); const platformRequest = request; if (!platformRequest.redirectUri) { platformRequest.redirectUri = this.chooseRedirectUriByPlatform(platformRequest); this.logger.info("NativeBrokerPlugin - No Redirect URI provided, using default", platformRequest.correlationId); } const authParams = this.generateRequestParameters(platformRequest); const account = await this.getAccount(platformRequest); const windowHandle = providedWindowHandle || Buffer.from([0]); return new Promise((resolve, reject) => { const resultCallback = (result) => { try { result.CheckError(); } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { reject(wrappedError); return; } } const authenticationResult = this.getAuthenticationResult(platformRequest, result); resolve(authenticationResult); }; try { switch (platformRequest.prompt) { case PromptValue.LOGIN: case PromptValue.SELECT_ACCOUNT: case PromptValue.CREATE: this.logger.info("Calling native interop SignInInteractively API", platformRequest.correlationId); const loginHint = platformRequest.loginHint || ""; msalNodeRuntime.SignInInteractivelyAsync(windowHandle, authParams, platformRequest.correlationId, loginHint, resultCallback); break; case PromptValue.NONE: if (account) { this.logger.info("Calling native interop AcquireTokenSilently API", platformRequest.correlationId); msalNodeRuntime.AcquireTokenSilentlyAsync(authParams, platformRequest.correlationId, account, resultCallback); } else { this.logger.info("Calling native interop SignInSilently API", platformRequest.correlationId); msalNodeRuntime.SignInSilentlyAsync(authParams, platformRequest.correlationId, resultCallback); } break; default: if (account) { this.logger.info("Calling native interop AcquireTokenInteractively API", platformRequest.correlationId); msalNodeRuntime.AcquireTokenInteractivelyAsync(windowHandle, authParams, platformRequest.correlationId, account, resultCallback); } else { this.logger.info("Calling native interop SignIn API", platformRequest.correlationId); const loginHint = platformRequest.loginHint || ""; msalNodeRuntime.SignInAsync(windowHandle, authParams, platformRequest.correlationId, loginHint, resultCallback); } break; } } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { reject(wrappedError); } } }); } async signOut(request) { this.logger.trace("NativeBrokerPlugin - signOut called", request.correlationId); const account = await this.getAccount(request); if (!account) { throw createClientAuthError(noAccountFound); } return new Promise((resolve, reject) => { const resultCallback = (result) => { try { result.CheckError(); } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { reject(wrappedError); return; } } resolve(); }; try { msalNodeRuntime.SignOutSilentlyAsync(request.clientId, request.correlationId, account, resultCallback); } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { reject(wrappedError); } } }); } async getAccount(request) { if (request.accountId) { const readAccountResult = await this.readAccountById(request.accountId, request.correlationId); return readAccountResult.account; } return null; } async readAccountById(accountId, correlationId) { this.logger.trace("NativeBrokerPlugin - readAccountById called", correlationId); return new Promise((resolve, reject) => { const resultCallback = (result) => { try { result.CheckError(); } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { reject(wrappedError); return; } } resolve(result); }; try { msalNodeRuntime.ReadAccountByIdAsync(accountId, correlationId, resultCallback); } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { reject(wrappedError); } } }); } generateRequestParameters(request) { this.logger.trace("NativeBrokerPlugin - generateRequestParameters called", request.correlationId); const authParams = new msalNodeRuntime.AuthParameters(); try { authParams.CreateAuthParameters(request.clientId, request.authority); authParams.SetRedirectUri(request.redirectUri); authParams.SetRequestedScopes(request.scopes.join(" ")); if (request.claims) { authParams.SetDecodedClaims(request.claims); } if (request.authenticationScheme === AuthenticationScheme.POP) { if (!request.resourceRequestMethod || !request.resourceRequestUri) { throw new Error("Authentication Scheme set to POP but one or more of the following parameters are missing: resourceRequestMethod, resourceRequestUri"); } const resourceUrl = new URL(request.resourceRequestUri); authParams.SetPopParams(request.resourceRequestMethod, resourceUrl.host, resourceUrl.pathname, request.shrNonce || ""); } if (request.resource) { authParams.SetAdditionalParameter(RESOURCE, request.resource); } if (request.extraParameters) { Object.entries(request.extraParameters).forEach(([key, value]) => { authParams.SetAdditionalParameter(key, value); }); } const skus = request.extraParameters && request.extraParameters[X_CLIENT_EXTRA_SKU] ?.length ? request.extraParameters[X_CLIENT_EXTRA_SKU] : ""; authParams.SetAdditionalParameter(X_CLIENT_EXTRA_SKU, ServerTelemetryManager.makeExtraSkuString({ skus, extensionName: "msal.node.ext", extensionVersion: version, })); } catch (e) { const wrappedError = this.wrapError(e); if (wrappedError) { throw wrappedError; } } return authParams; } chooseRedirectUriByPlatform(request) { this.logger.trace("NativeBrokerPlugin - chooseRedirectUriByPlatform called", request.correlationId); let redirectUri; switch (process.platform) { case "darwin": redirectUri = "msauth.com.msauth.unsignedapp://auth"; break; case "win32": redirectUri = `ms-appx-web://Microsoft.AAD.BrokerPlugin/${request.clientId}`; break; default: redirectUri = "https://login.microsoftonline.com/common/oauth2/nativeclient"; } return redirectUri; } getAuthenticationResult(request, authResult) { this.logger.trace("NativeBrokerPlugin - getAuthenticationResult called", request.correlationId); let fromCache = false; try { const telemetryJSON = JSON.parse(authResult.telemetryData); fromCache = !!telemetryJSON["is_cache"]; } catch (e) { this.logger.error("NativeBrokerPlugin: getAuthenticationResult - Error parsing telemetry data. Could not determine if response came from cache.", request.correlationId); } let idTokenClaims; try { idTokenClaims = JSON.parse(authResult.idToken); } catch (e) { throw new Error("Unable to parse idToken claims"); } const accountInfo = this.generateAccountInfo(authResult.account, idTokenClaims); let accessToken; let tokenType; if (authResult.isPopAuthorization) { // Header includes 'pop ' prefix accessToken = authResult.authorizationHeader.split(" ")[1]; tokenType = AuthenticationScheme.POP; } else { accessToken = authResult.accessToken; tokenType = AuthenticationScheme.BEARER; } const result = { authority: request.authority, uniqueId: idTokenClaims.oid || idTokenClaims.sub || "", tenantId: idTokenClaims.tid || "", scopes: authResult.grantedScopes.split(" "), account: accountInfo, idToken: authResult.rawIdToken, idTokenClaims: idTokenClaims, accessToken: accessToken, fromCache: fromCache, // MsalRuntime expiresOn returned in seconds, converting to Date for AuthenticationResult expiresOn: toDateFromSeconds(authResult.expiresOn), tokenType: tokenType, correlationId: request.correlationId, fromPlatformBroker: true, ...(request.resource && { resource: request.resource }), }; return result; } generateAccountInfo(account, idTokenClaims) { this.logger.trace("NativeBrokerPlugin - generateAccountInfo called", ""); const accountInfo = { homeAccountId: account.homeAccountId, environment: account.environment, tenantId: account.realm, username: account.username, localAccountId: account.localAccountId, name: account.displayName, idTokenClaims: idTokenClaims, nativeAccountId: account.accountId, }; return accountInfo; } isMsalRuntimeError(result) { return (result.hasOwnProperty("errorCode") || result.hasOwnProperty("errorStatus") || result.hasOwnProperty("errorContext") || result.hasOwnProperty("errorTag")); } wrapError(error) { if (error && typeof error === "object" && this.isMsalRuntimeError(error)) { const { errorCode, errorStatus, errorContext, errorTag } = error; const msalNodeRuntimeError = new PlatformBrokerError(ErrorStatus[errorStatus], errorContext, errorCode, errorTag); let wrappedError; switch (errorStatus) { case ErrorStatus.InteractionRequired: case ErrorStatus.AccountUnusable: wrappedError = new InteractionRequiredAuthError(ErrorCodes.INTERATION_REQUIRED_ERROR_CODE, msalNodeRuntimeError.message); break; case ErrorStatus.NoNetwork: case ErrorStatus.NetworkTemporarilyUnavailable: wrappedError = createClientAuthError(noNetworkConnectivity); break; case ErrorStatus.ServerTemporarilyUnavailable: wrappedError = new ServerError(ErrorCodes.SERVER_UNAVAILABLE, msalNodeRuntimeError.message); break; case ErrorStatus.UserCanceled: wrappedError = createClientAuthError(userCanceled); break; case ErrorStatus.AuthorityUntrusted: wrappedError = createClientConfigurationError(untrustedAuthority); break; case ErrorStatus.UserSwitched: // Not an error case, if there's customer demand we can surface this as a response property return null; case ErrorStatus.AccountNotFound: wrappedError = createClientAuthError(noAccountFound); break; default: wrappedError = createClientAuthError(platformBrokerError); } wrappedError.platformBrokerError = msalNodeRuntimeError; return wrappedError; } throw error; } } export { NativeBrokerPlugin }; //# sourceMappingURL=NativeBrokerPlugin.mjs.map