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)

444 lines (441 loc) 19.6 kB
/*! @azure/msal-node-extensions v1.5.9 2025-03-25 */ 'use strict'; import { msalNodeRuntime, ErrorStatus, LogLevel } from '@azure/msal-node-runtime'; import { ErrorCodes } from '../utils/Constants.mjs'; import { NativeAuthError } from '../error/NativeAuthError.mjs'; import { name, version } from '../packageMetadata.mjs'; import { Logger } from '../lib/msal-common/dist/logger/Logger.mjs'; import { Constants, PromptValue, AuthenticationScheme } from '../lib/msal-common/dist/utils/Constants.mjs'; import { createClientAuthError } from '../lib/msal-common/dist/error/ClientAuthError.mjs'; import { ServerTelemetryManager } from '../lib/msal-common/dist/telemetry/server/ServerTelemetryManager.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 { X_CLIENT_EXTRA_SKU } from '../lib/msal-common/dist/constants/AADServerParamKeys.mjs'; import { toDateFromSeconds } from '../lib/msal-common/dist/utils/TimeUtils.mjs'; import { noAccountFound, 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) => { switch (logLevel) { case LogLevel.Trace: if (containsPii) { this.logger.tracePii(message); } else { this.logger.trace(message); } break; case LogLevel.Debug: if (containsPii) { this.logger.tracePii(message); } else { this.logger.trace(message); } break; case LogLevel.Info: if (containsPii) { this.logger.infoPii(message); } else { this.logger.info(message); } break; case LogLevel.Warning: if (containsPii) { this.logger.warningPii(message); } else { this.logger.warning(message); } break; case LogLevel.Error: if (containsPii) { this.logger.errorPii(message); } else { this.logger.error(message); } break; case LogLevel.Fatal: if (containsPii) { this.logger.errorPii(message); } else { this.logger.error(message); } break; default: if (containsPii) { this.logger.infoPii(message); } else { this.logger.info(message); } 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 authParams = this.generateRequestParameters(request); const account = await this.getAccount(request); 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(request, result); resolve(authenticationResult); }; try { if (account) { msalNodeRuntime.AcquireTokenSilentlyAsync(authParams, request.correlationId, account, resultCallback); } else { msalNodeRuntime.SignInSilentlyAsync(authParams, request.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 authParams = this.generateRequestParameters(request); const account = await this.getAccount(request); 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(request, result); resolve(authenticationResult); }; try { switch (request.prompt) { case PromptValue.LOGIN: case PromptValue.SELECT_ACCOUNT: case PromptValue.CREATE: this.logger.info("Calling native interop SignInInteractively API", request.correlationId); const loginHint = request.loginHint || Constants.EMPTY_STRING; msalNodeRuntime.SignInInteractivelyAsync(windowHandle, authParams, request.correlationId, loginHint, resultCallback); break; case PromptValue.NONE: if (account) { this.logger.info("Calling native interop AcquireTokenSilently API", request.correlationId); msalNodeRuntime.AcquireTokenSilentlyAsync(authParams, request.correlationId, account, resultCallback); } else { this.logger.info("Calling native interop SignInSilently API", request.correlationId); msalNodeRuntime.SignInSilentlyAsync(authParams, request.correlationId, resultCallback); } break; default: if (account) { this.logger.info("Calling native interop AcquireTokenInteractively API", request.correlationId); msalNodeRuntime.AcquireTokenInteractivelyAsync(windowHandle, authParams, request.correlationId, account, resultCallback); } else { this.logger.info("Calling native interop SignIn API", request.correlationId); const loginHint = request.loginHint || Constants.EMPTY_STRING; msalNodeRuntime.SignInAsync(windowHandle, authParams, request.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.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; } 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, fromNativeBroker: true, }; 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; switch (errorStatus) { case ErrorStatus.InteractionRequired: case ErrorStatus.AccountUnusable: return new InteractionRequiredAuthError(ErrorCodes.INTERATION_REQUIRED_ERROR_CODE, errorContext); case ErrorStatus.NoNetwork: case ErrorStatus.NetworkTemporarilyUnavailable: return createClientAuthError(noNetworkConnectivity); case ErrorStatus.ServerTemporarilyUnavailable: return new ServerError(ErrorCodes.SERVER_UNAVAILABLE, errorContext); case ErrorStatus.UserCanceled: return createClientAuthError(userCanceled); case ErrorStatus.AuthorityUntrusted: return createClientConfigurationError(untrustedAuthority); 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: return createClientAuthError(noAccountFound); default: return new NativeAuthError(ErrorStatus[errorStatus], errorContext, errorCode, errorTag); } } throw error; } } export { NativeBrokerPlugin }; //# sourceMappingURL=NativeBrokerPlugin.mjs.map