@azure/msal-node-extensions
Version:
 
487 lines (484 loc) • 22.1 kB
JavaScript
/*! @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