Add extracted source directory and README navigation

This commit is contained in:
Shawn Bot
2026-03-31 14:56:06 +00:00
parent 6252bb6eb5
commit 91e01d755b
4757 changed files with 984951 additions and 0 deletions

View File

@@ -0,0 +1,385 @@
/*! @azure/msal-node v3.8.1 2025-10-29 */
'use strict';
import { Logger, buildStaticAuthorityOptions, AuthenticationScheme, AuthorizationCodeClient, AuthError, RefreshTokenClient, SilentFlowClient, ClientAuthError, ClientAuthErrorCodes, OIDC_DEFAULT_SCOPES, CacheOutcome, createClientAuthError, Constants, getClientAssertion, StringUtils, ServerTelemetryManager, Authority, AuthorityFactory, ResponseMode } from '@azure/msal-common/node';
import { buildAppConfiguration } from '../config/Configuration.mjs';
import { CryptoProvider } from '../crypto/CryptoProvider.mjs';
import { NodeStorage } from '../cache/NodeStorage.mjs';
import { ApiId, Constants as Constants$1 } from '../utils/Constants.mjs';
import { TokenCache } from '../cache/TokenCache.mjs';
import { ClientAssertion } from './ClientAssertion.mjs';
import { name, version } from '../packageMetadata.mjs';
import { NodeAuthError } from '../error/NodeAuthError.mjs';
import { UsernamePasswordClient } from './UsernamePasswordClient.mjs';
import { getAuthCodeRequestUrl } from '../protocol/Authorize.mjs';
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
/**
* Base abstract class for all ClientApplications - public and confidential
* @public
*/
class ClientApplication {
/**
* Constructor for the ClientApplication
*/
constructor(configuration) {
this.config = buildAppConfiguration(configuration);
this.cryptoProvider = new CryptoProvider();
this.logger = new Logger(this.config.system.loggerOptions, name, version);
this.storage = new NodeStorage(this.logger, this.config.auth.clientId, this.cryptoProvider, buildStaticAuthorityOptions(this.config.auth));
this.tokenCache = new TokenCache(this.storage, this.logger, this.config.cache.cachePlugin);
}
/**
* Creates the URL of the authorization request, letting the user input credentials and consent to the
* application. The URL targets the /authorize endpoint of the authority configured in the
* application object.
*
* Once the user inputs their credentials and consents, the authority will send a response to the redirect URI
* sent in the request and should contain an authorization code, which can then be used to acquire tokens via
* `acquireTokenByCode(AuthorizationCodeRequest)`.
*/
async getAuthCodeUrl(request) {
this.logger.info("getAuthCodeUrl called", request.correlationId);
const validRequest = {
...request,
...(await this.initializeBaseRequest(request)),
responseMode: request.responseMode || ResponseMode.QUERY,
authenticationScheme: AuthenticationScheme.BEARER,
state: request.state || "",
nonce: request.nonce || "",
};
const discoveredAuthority = await this.createAuthority(validRequest.authority, validRequest.correlationId, undefined, request.azureCloudOptions);
return getAuthCodeRequestUrl(this.config, discoveredAuthority, validRequest, this.logger);
}
/**
* Acquires a token by exchanging the Authorization Code received from the first step of OAuth2.0
* Authorization Code flow.
*
* `getAuthCodeUrl(AuthorizationCodeUrlRequest)` can be used to create the URL for the first step of OAuth2.0
* Authorization Code flow. Ensure that values for redirectUri and scopes in AuthorizationCodeUrlRequest and
* AuthorizationCodeRequest are the same.
*/
async acquireTokenByCode(request, authCodePayLoad) {
this.logger.info("acquireTokenByCode called");
if (request.state && authCodePayLoad) {
this.logger.info("acquireTokenByCode - validating state");
this.validateState(request.state, authCodePayLoad.state || "");
// eslint-disable-next-line no-param-reassign
authCodePayLoad = { ...authCodePayLoad, state: "" };
}
const validRequest = {
...request,
...(await this.initializeBaseRequest(request)),
authenticationScheme: AuthenticationScheme.BEARER,
};
const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenByCode, validRequest.correlationId);
try {
const discoveredAuthority = await this.createAuthority(validRequest.authority, validRequest.correlationId, undefined, request.azureCloudOptions);
const authClientConfig = await this.buildOauthClientConfiguration(discoveredAuthority, validRequest.correlationId, validRequest.redirectUri, serverTelemetryManager);
const authorizationCodeClient = new AuthorizationCodeClient(authClientConfig);
this.logger.verbose("Auth code client created", validRequest.correlationId);
return await authorizationCodeClient.acquireToken(validRequest, authCodePayLoad);
}
catch (e) {
if (e instanceof AuthError) {
e.setCorrelationId(validRequest.correlationId);
}
serverTelemetryManager.cacheFailedRequest(e);
throw e;
}
}
/**
* Acquires a token by exchanging the refresh token provided for a new set of tokens.
*
* This API is provided only for scenarios where you would like to migrate from ADAL to MSAL. Otherwise, it is
* recommended that you use `acquireTokenSilent()` for silent scenarios. When using `acquireTokenSilent()`, MSAL will
* handle the caching and refreshing of tokens automatically.
*/
async acquireTokenByRefreshToken(request) {
this.logger.info("acquireTokenByRefreshToken called", request.correlationId);
const validRequest = {
...request,
...(await this.initializeBaseRequest(request)),
authenticationScheme: AuthenticationScheme.BEARER,
};
const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenByRefreshToken, validRequest.correlationId);
try {
const discoveredAuthority = await this.createAuthority(validRequest.authority, validRequest.correlationId, undefined, request.azureCloudOptions);
const refreshTokenClientConfig = await this.buildOauthClientConfiguration(discoveredAuthority, validRequest.correlationId, validRequest.redirectUri || "", serverTelemetryManager);
const refreshTokenClient = new RefreshTokenClient(refreshTokenClientConfig);
this.logger.verbose("Refresh token client created", validRequest.correlationId);
return await refreshTokenClient.acquireToken(validRequest);
}
catch (e) {
if (e instanceof AuthError) {
e.setCorrelationId(validRequest.correlationId);
}
serverTelemetryManager.cacheFailedRequest(e);
throw e;
}
}
/**
* Acquires a token silently when a user specifies the account the token is requested for.
*
* This API expects the user to provide an account object and looks into the cache to retrieve the token if present.
* There is also an optional "forceRefresh" boolean the user can send to bypass the cache for access_token and id_token.
* In case the refresh_token is expired or not found, an error is thrown
* and the guidance is for the user to call any interactive token acquisition API (eg: `acquireTokenByCode()`).
*/
async acquireTokenSilent(request) {
const validRequest = {
...request,
...(await this.initializeBaseRequest(request)),
forceRefresh: request.forceRefresh || false,
};
const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenSilent, validRequest.correlationId, validRequest.forceRefresh);
try {
const discoveredAuthority = await this.createAuthority(validRequest.authority, validRequest.correlationId, undefined, request.azureCloudOptions);
const clientConfiguration = await this.buildOauthClientConfiguration(discoveredAuthority, validRequest.correlationId, validRequest.redirectUri || "", serverTelemetryManager);
const silentFlowClient = new SilentFlowClient(clientConfiguration);
this.logger.verbose("Silent flow client created", validRequest.correlationId);
try {
// always overwrite the in-memory cache with the persistence cache (if it exists) before a cache lookup
await this.tokenCache.overwriteCache();
return await this.acquireCachedTokenSilent(validRequest, silentFlowClient, clientConfiguration);
}
catch (error) {
if (error instanceof ClientAuthError &&
error.errorCode ===
ClientAuthErrorCodes.tokenRefreshRequired) {
const refreshTokenClient = new RefreshTokenClient(clientConfiguration);
return refreshTokenClient.acquireTokenByRefreshToken(validRequest);
}
throw error;
}
}
catch (error) {
if (error instanceof AuthError) {
error.setCorrelationId(validRequest.correlationId);
}
serverTelemetryManager.cacheFailedRequest(error);
throw error;
}
}
async acquireCachedTokenSilent(validRequest, silentFlowClient, clientConfiguration) {
const [authResponse, cacheOutcome] = await silentFlowClient.acquireCachedToken({
...validRequest,
scopes: validRequest.scopes?.length
? validRequest.scopes
: [...OIDC_DEFAULT_SCOPES],
});
if (cacheOutcome === CacheOutcome.PROACTIVELY_REFRESHED) {
this.logger.info("ClientApplication:acquireCachedTokenSilent - Cached access token's refreshOn property has been exceeded'. It's not expired, but must be refreshed.");
// refresh the access token in the background
const refreshTokenClient = new RefreshTokenClient(clientConfiguration);
try {
await refreshTokenClient.acquireTokenByRefreshToken(validRequest);
}
catch {
// do nothing, this is running in the background and no action is to be taken upon success or failure
}
}
// return the cached token
return authResponse;
}
/**
* Acquires tokens with password grant by exchanging client applications username and password for credentials
*
* The latest OAuth 2.0 Security Best Current Practice disallows the password grant entirely.
* More details on this recommendation at https://tools.ietf.org/html/draft-ietf-oauth-security-topics-13#section-3.4
* Microsoft's documentation and recommendations are at:
* https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-authentication-flows#usernamepassword
*
* @param request - UsenamePasswordRequest
* @deprecated - Use a more secure flow instead
*/
async acquireTokenByUsernamePassword(request) {
this.logger.info("acquireTokenByUsernamePassword called", request.correlationId);
const validRequest = {
...request,
...(await this.initializeBaseRequest(request)),
};
const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenByUsernamePassword, validRequest.correlationId);
try {
const discoveredAuthority = await this.createAuthority(validRequest.authority, validRequest.correlationId, undefined, request.azureCloudOptions);
const usernamePasswordClientConfig = await this.buildOauthClientConfiguration(discoveredAuthority, validRequest.correlationId, "", serverTelemetryManager);
const usernamePasswordClient = new UsernamePasswordClient(usernamePasswordClientConfig);
this.logger.verbose("Username password client created", validRequest.correlationId);
return await usernamePasswordClient.acquireToken(validRequest);
}
catch (e) {
if (e instanceof AuthError) {
e.setCorrelationId(validRequest.correlationId);
}
serverTelemetryManager.cacheFailedRequest(e);
throw e;
}
}
/**
* Gets the token cache for the application.
*/
getTokenCache() {
this.logger.info("getTokenCache called");
return this.tokenCache;
}
/**
* Validates OIDC state by comparing the user cached state with the state received from the server.
*
* This API is provided for scenarios where you would use OAuth2.0 state parameter to mitigate against
* CSRF attacks.
* For more information about state, visit https://datatracker.ietf.org/doc/html/rfc6819#section-3.6.
* @param state - Unique GUID generated by the user that is cached by the user and sent to the server during the first leg of the flow
* @param cachedState - This string is sent back by the server with the authorization code
*/
validateState(state, cachedState) {
if (!state) {
throw NodeAuthError.createStateNotFoundError();
}
if (state !== cachedState) {
throw createClientAuthError(ClientAuthErrorCodes.stateMismatch);
}
}
/**
* Returns the logger instance
*/
getLogger() {
return this.logger;
}
/**
* Replaces the default logger set in configurations with new Logger with new configurations
* @param logger - Logger instance
*/
setLogger(logger) {
this.logger = logger;
}
/**
* Builds the common configuration to be passed to the common component based on the platform configurarion
* @param authority - user passed authority in configuration
* @param serverTelemetryManager - initializes servertelemetry if passed
*/
async buildOauthClientConfiguration(discoveredAuthority, requestCorrelationId, redirectUri, serverTelemetryManager) {
this.logger.verbose("buildOauthClientConfiguration called", requestCorrelationId);
this.logger.info(`Building oauth client configuration with the following authority: ${discoveredAuthority.tokenEndpoint}.`, requestCorrelationId);
serverTelemetryManager?.updateRegionDiscoveryMetadata(discoveredAuthority.regionDiscoveryMetadata);
const clientConfiguration = {
authOptions: {
clientId: this.config.auth.clientId,
authority: discoveredAuthority,
clientCapabilities: this.config.auth.clientCapabilities,
redirectUri,
},
loggerOptions: {
logLevel: this.config.system.loggerOptions.logLevel,
loggerCallback: this.config.system.loggerOptions.loggerCallback,
piiLoggingEnabled: this.config.system.loggerOptions.piiLoggingEnabled,
correlationId: requestCorrelationId,
},
cacheOptions: {
claimsBasedCachingEnabled: this.config.cache.claimsBasedCachingEnabled,
},
cryptoInterface: this.cryptoProvider,
networkInterface: this.config.system.networkClient,
storageInterface: this.storage,
serverTelemetryManager: serverTelemetryManager,
clientCredentials: {
clientSecret: this.clientSecret,
clientAssertion: await this.getClientAssertion(discoveredAuthority),
},
libraryInfo: {
sku: Constants$1.MSAL_SKU,
version: version,
cpu: process.arch || Constants.EMPTY_STRING,
os: process.platform || Constants.EMPTY_STRING,
},
telemetry: this.config.telemetry,
persistencePlugin: this.config.cache.cachePlugin,
serializableCache: this.tokenCache,
};
return clientConfiguration;
}
async getClientAssertion(authority) {
if (this.developerProvidedClientAssertion) {
this.clientAssertion = ClientAssertion.fromAssertion(await getClientAssertion(this.developerProvidedClientAssertion, this.config.auth.clientId, authority.tokenEndpoint));
}
return (this.clientAssertion && {
assertion: this.clientAssertion.getJwt(this.cryptoProvider, this.config.auth.clientId, authority.tokenEndpoint),
assertionType: Constants$1.JWT_BEARER_ASSERTION_TYPE,
});
}
/**
* Generates a request with the default scopes & generates a correlationId.
* @param authRequest - BaseAuthRequest for initialization
*/
async initializeBaseRequest(authRequest) {
this.logger.verbose("initializeRequestScopes called", authRequest.correlationId);
// Default authenticationScheme to Bearer, log that POP isn't supported yet
if (authRequest.authenticationScheme &&
authRequest.authenticationScheme === AuthenticationScheme.POP) {
this.logger.verbose("Authentication Scheme 'pop' is not supported yet, setting Authentication Scheme to 'Bearer' for request", authRequest.correlationId);
}
authRequest.authenticationScheme = AuthenticationScheme.BEARER;
// Set requested claims hash if claims-based caching is enabled and claims were requested
if (this.config.cache.claimsBasedCachingEnabled &&
authRequest.claims &&
// Checks for empty stringified object "{}" which doesn't qualify as requested claims
!StringUtils.isEmptyObj(authRequest.claims)) {
authRequest.requestedClaimsHash =
await this.cryptoProvider.hashString(authRequest.claims);
}
return {
...authRequest,
scopes: [
...((authRequest && authRequest.scopes) || []),
...OIDC_DEFAULT_SCOPES,
],
correlationId: (authRequest && authRequest.correlationId) ||
this.cryptoProvider.createNewGuid(),
authority: authRequest.authority || this.config.auth.authority,
};
}
/**
* Initializes the server telemetry payload
* @param apiId - Id for a specific request
* @param correlationId - GUID
* @param forceRefresh - boolean to indicate network call
*/
initializeServerTelemetryManager(apiId, correlationId, forceRefresh) {
const telemetryPayload = {
clientId: this.config.auth.clientId,
correlationId: correlationId,
apiId: apiId,
forceRefresh: forceRefresh || false,
};
return new ServerTelemetryManager(telemetryPayload, this.storage);
}
/**
* Create authority instance. If authority not passed in request, default to authority set on the application
* object. If no authority set in application object, then default to common authority.
* @param authorityString - authority from user configuration
*/
async createAuthority(authorityString, requestCorrelationId, azureRegionConfiguration, azureCloudOptions) {
this.logger.verbose("createAuthority called", requestCorrelationId);
// build authority string based on auth params - azureCloudInstance is prioritized if provided
const authorityUrl = Authority.generateAuthority(authorityString, azureCloudOptions || this.config.auth.azureCloudOptions);
const authorityOptions = {
protocolMode: this.config.auth.protocolMode,
knownAuthorities: this.config.auth.knownAuthorities,
cloudDiscoveryMetadata: this.config.auth.cloudDiscoveryMetadata,
authorityMetadata: this.config.auth.authorityMetadata,
azureRegionConfiguration,
skipAuthorityMetadataCache: this.config.auth.skipAuthorityMetadataCache,
};
return AuthorityFactory.createDiscoveredInstance(authorityUrl, this.config.system.networkClient, this.storage, authorityOptions, this.logger, requestCorrelationId);
}
/**
* Clear the cache
*/
clearCache() {
this.storage.clear();
}
}
export { ClientApplication };
//# sourceMappingURL=ClientApplication.mjs.map

View File

@@ -0,0 +1,153 @@
/*! @azure/msal-node v3.8.1 2025-10-29 */
'use strict';
import jwt from 'jsonwebtoken';
import { createClientAuthError, ClientAuthErrorCodes, TimeUtils, EncodingTypes, Constants } from '@azure/msal-common/node';
import { EncodingUtils } from '../utils/EncodingUtils.mjs';
import { JwtConstants } from '../utils/Constants.mjs';
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
/**
* Client assertion of type jwt-bearer used in confidential client flows
* @public
*/
class ClientAssertion {
/**
* Initialize the ClientAssertion class from the clientAssertion passed by the user
* @param assertion - refer https://tools.ietf.org/html/rfc7521
*/
static fromAssertion(assertion) {
const clientAssertion = new ClientAssertion();
clientAssertion.jwt = assertion;
return clientAssertion;
}
/**
* @deprecated Use fromCertificateWithSha256Thumbprint instead, with a SHA-256 thumprint
* Initialize the ClientAssertion class from the certificate passed by the user
* @param thumbprint - identifier of a certificate
* @param privateKey - secret key
* @param publicCertificate - electronic document provided to prove the ownership of the public key
*/
static fromCertificate(thumbprint, privateKey, publicCertificate) {
const clientAssertion = new ClientAssertion();
clientAssertion.privateKey = privateKey;
clientAssertion.thumbprint = thumbprint;
clientAssertion.useSha256 = false;
if (publicCertificate) {
clientAssertion.publicCertificate =
this.parseCertificate(publicCertificate);
}
return clientAssertion;
}
/**
* Initialize the ClientAssertion class from the certificate passed by the user
* @param thumbprint - identifier of a certificate
* @param privateKey - secret key
* @param publicCertificate - electronic document provided to prove the ownership of the public key
*/
static fromCertificateWithSha256Thumbprint(thumbprint, privateKey, publicCertificate) {
const clientAssertion = new ClientAssertion();
clientAssertion.privateKey = privateKey;
clientAssertion.thumbprint = thumbprint;
clientAssertion.useSha256 = true;
if (publicCertificate) {
clientAssertion.publicCertificate =
this.parseCertificate(publicCertificate);
}
return clientAssertion;
}
/**
* Update JWT for certificate based clientAssertion, if passed by the user, uses it as is
* @param cryptoProvider - library's crypto helper
* @param issuer - iss claim
* @param jwtAudience - aud claim
*/
getJwt(cryptoProvider, issuer, jwtAudience) {
// if assertion was created from certificate, check if jwt is expired and create new one.
if (this.privateKey && this.thumbprint) {
if (this.jwt &&
!this.isExpired() &&
issuer === this.issuer &&
jwtAudience === this.jwtAudience) {
return this.jwt;
}
return this.createJwt(cryptoProvider, issuer, jwtAudience);
}
/*
* if assertion was created by caller, then we just append it. It is up to the caller to
* ensure that it contains necessary claims and that it is not expired.
*/
if (this.jwt) {
return this.jwt;
}
throw createClientAuthError(ClientAuthErrorCodes.invalidAssertion);
}
/**
* JWT format and required claims specified: https://tools.ietf.org/html/rfc7523#section-3
*/
createJwt(cryptoProvider, issuer, jwtAudience) {
this.issuer = issuer;
this.jwtAudience = jwtAudience;
const issuedAt = TimeUtils.nowSeconds();
this.expirationTime = issuedAt + 600;
const algorithm = this.useSha256
? JwtConstants.PSS_256
: JwtConstants.RSA_256;
const header = {
alg: algorithm,
};
const thumbprintHeader = this.useSha256
? JwtConstants.X5T_256
: JwtConstants.X5T;
Object.assign(header, {
[thumbprintHeader]: EncodingUtils.base64EncodeUrl(this.thumbprint, EncodingTypes.HEX),
});
if (this.publicCertificate) {
Object.assign(header, {
[JwtConstants.X5C]: this.publicCertificate,
});
}
const payload = {
[JwtConstants.AUDIENCE]: this.jwtAudience,
[JwtConstants.EXPIRATION_TIME]: this.expirationTime,
[JwtConstants.ISSUER]: this.issuer,
[JwtConstants.SUBJECT]: this.issuer,
[JwtConstants.NOT_BEFORE]: issuedAt,
[JwtConstants.JWT_ID]: cryptoProvider.createNewGuid(),
};
this.jwt = jwt.sign(payload, this.privateKey, { header });
return this.jwt;
}
/**
* Utility API to check expiration
*/
isExpired() {
return this.expirationTime < TimeUtils.nowSeconds();
}
/**
* Extracts the raw certs from a given certificate string and returns them in an array.
* @param publicCertificate - electronic document provided to prove the ownership of the public key
*/
static parseCertificate(publicCertificate) {
/**
* This is regex to identify the certs in a given certificate string.
* We want to look for the contents between the BEGIN and END certificate strings, without the associated newlines.
* The information in parens "(.+?)" is the capture group to represent the cert we want isolated.
* "." means any string character, "+" means match 1 or more times, and "?" means the shortest match.
* The "g" at the end of the regex means search the string globally, and the "s" enables the "." to match newlines.
*/
const regexToFindCerts = /-----BEGIN CERTIFICATE-----\r*\n(.+?)\r*\n-----END CERTIFICATE-----/gs;
const certs = [];
let matches;
while ((matches = regexToFindCerts.exec(publicCertificate)) !== null) {
// matches[1] represents the first parens capture group in the regex.
certs.push(matches[1].replace(/\r*\n/g, Constants.EMPTY_STRING));
}
return certs;
}
}
export { ClientAssertion };
//# sourceMappingURL=ClientAssertion.mjs.map

View File

@@ -0,0 +1,202 @@
/*! @azure/msal-node v3.8.1 2025-10-29 */
'use strict';
import { BaseClient, CacheOutcome, TokenCacheContext, ScopeSet, TimeUtils, DEFAULT_TOKEN_RENEWAL_OFFSET_SEC, ResponseHandler, CredentialType, Constants, createClientAuthError, ClientAuthErrorCodes, UrlString, RequestParameterBuilder, GrantType, getClientAssertion, StringUtils, UrlUtils, AuthenticationScheme } from '@azure/msal-common/node';
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
/**
* OAuth2.0 client credential grant
* @public
*/
class ClientCredentialClient extends BaseClient {
constructor(configuration, appTokenProvider) {
super(configuration);
this.appTokenProvider = appTokenProvider;
}
/**
* Public API to acquire a token with ClientCredential Flow for Confidential clients
* @param request - CommonClientCredentialRequest provided by the developer
*/
async acquireToken(request) {
if (request.skipCache || request.claims) {
return this.executeTokenRequest(request, this.authority);
}
const [cachedAuthenticationResult, lastCacheOutcome] = await this.getCachedAuthenticationResult(request, this.config, this.cryptoUtils, this.authority, this.cacheManager, this.serverTelemetryManager);
if (cachedAuthenticationResult) {
// if the token is not expired but must be refreshed; get a new one in the background
if (lastCacheOutcome === CacheOutcome.PROACTIVELY_REFRESHED) {
this.logger.info("ClientCredentialClient:getCachedAuthenticationResult - Cached access token's refreshOn property has been exceeded'. It's not expired, but must be refreshed.");
// refresh the access token in the background
const refreshAccessToken = true;
await this.executeTokenRequest(request, this.authority, refreshAccessToken);
}
// return the cached token
return cachedAuthenticationResult;
}
else {
return this.executeTokenRequest(request, this.authority);
}
}
/**
* looks up cache if the tokens are cached already
*/
async getCachedAuthenticationResult(request, config, cryptoUtils, authority, cacheManager, serverTelemetryManager) {
const clientConfiguration = config;
const managedIdentityConfiguration = config;
let lastCacheOutcome = CacheOutcome.NOT_APPLICABLE;
// read the user-supplied cache into memory, if applicable
let cacheContext;
if (clientConfiguration.serializableCache &&
clientConfiguration.persistencePlugin) {
cacheContext = new TokenCacheContext(clientConfiguration.serializableCache, false);
await clientConfiguration.persistencePlugin.beforeCacheAccess(cacheContext);
}
const cachedAccessToken = this.readAccessTokenFromCache(authority, managedIdentityConfiguration.managedIdentityId?.id ||
clientConfiguration.authOptions.clientId, new ScopeSet(request.scopes || []), cacheManager, request.correlationId);
if (clientConfiguration.serializableCache &&
clientConfiguration.persistencePlugin &&
cacheContext) {
await clientConfiguration.persistencePlugin.afterCacheAccess(cacheContext);
}
// must refresh due to non-existent access_token
if (!cachedAccessToken) {
serverTelemetryManager?.setCacheOutcome(CacheOutcome.NO_CACHED_ACCESS_TOKEN);
return [null, CacheOutcome.NO_CACHED_ACCESS_TOKEN];
}
// must refresh due to the expires_in value
if (TimeUtils.isTokenExpired(cachedAccessToken.expiresOn, clientConfiguration.systemOptions?.tokenRenewalOffsetSeconds ||
DEFAULT_TOKEN_RENEWAL_OFFSET_SEC)) {
serverTelemetryManager?.setCacheOutcome(CacheOutcome.CACHED_ACCESS_TOKEN_EXPIRED);
return [null, CacheOutcome.CACHED_ACCESS_TOKEN_EXPIRED];
}
// must refresh (in the background) due to the refresh_in value
if (cachedAccessToken.refreshOn &&
TimeUtils.isTokenExpired(cachedAccessToken.refreshOn.toString(), 0)) {
lastCacheOutcome = CacheOutcome.PROACTIVELY_REFRESHED;
serverTelemetryManager?.setCacheOutcome(CacheOutcome.PROACTIVELY_REFRESHED);
}
return [
await ResponseHandler.generateAuthenticationResult(cryptoUtils, authority, {
account: null,
idToken: null,
accessToken: cachedAccessToken,
refreshToken: null,
appMetadata: null,
}, true, request),
lastCacheOutcome,
];
}
/**
* Reads access token from the cache
*/
readAccessTokenFromCache(authority, id, scopeSet, cacheManager, correlationId) {
const accessTokenFilter = {
homeAccountId: Constants.EMPTY_STRING,
environment: authority.canonicalAuthorityUrlComponents.HostNameAndPort,
credentialType: CredentialType.ACCESS_TOKEN,
clientId: id,
realm: authority.tenant,
target: ScopeSet.createSearchScopes(scopeSet.asArray()),
};
const accessTokens = cacheManager.getAccessTokensByFilter(accessTokenFilter, correlationId);
if (accessTokens.length < 1) {
return null;
}
else if (accessTokens.length > 1) {
throw createClientAuthError(ClientAuthErrorCodes.multipleMatchingTokens);
}
return accessTokens[0];
}
/**
* Makes a network call to request the token from the service
* @param request - CommonClientCredentialRequest provided by the developer
* @param authority - authority object
*/
async executeTokenRequest(request, authority, refreshAccessToken) {
let serverTokenResponse;
let reqTimestamp;
if (this.appTokenProvider) {
this.logger.info("Using appTokenProvider extensibility.");
const appTokenPropviderParameters = {
correlationId: request.correlationId,
tenantId: this.config.authOptions.authority.tenant,
scopes: request.scopes,
claims: request.claims,
};
reqTimestamp = TimeUtils.nowSeconds();
const appTokenProviderResult = await this.appTokenProvider(appTokenPropviderParameters);
serverTokenResponse = {
access_token: appTokenProviderResult.accessToken,
expires_in: appTokenProviderResult.expiresInSeconds,
refresh_in: appTokenProviderResult.refreshInSeconds,
token_type: AuthenticationScheme.BEARER,
};
}
else {
const queryParametersString = this.createTokenQueryParameters(request);
const endpoint = UrlString.appendQueryString(authority.tokenEndpoint, queryParametersString);
const requestBody = await this.createTokenRequestBody(request);
const headers = this.createTokenRequestHeaders();
const thumbprint = {
clientId: this.config.authOptions.clientId,
authority: request.authority,
scopes: request.scopes,
claims: request.claims,
authenticationScheme: request.authenticationScheme,
resourceRequestMethod: request.resourceRequestMethod,
resourceRequestUri: request.resourceRequestUri,
shrClaims: request.shrClaims,
sshKid: request.sshKid,
};
this.logger.info("Sending token request to endpoint: " + authority.tokenEndpoint);
reqTimestamp = TimeUtils.nowSeconds();
const response = await this.executePostToTokenEndpoint(endpoint, requestBody, headers, thumbprint, request.correlationId);
serverTokenResponse = response.body;
serverTokenResponse.status = response.status;
}
const responseHandler = new ResponseHandler(this.config.authOptions.clientId, this.cacheManager, this.cryptoUtils, this.logger, this.config.serializableCache, this.config.persistencePlugin);
responseHandler.validateTokenResponse(serverTokenResponse, refreshAccessToken);
const tokenResponse = await responseHandler.handleServerTokenResponse(serverTokenResponse, this.authority, reqTimestamp, request);
return tokenResponse;
}
/**
* generate the request to the server in the acceptable format
* @param request - CommonClientCredentialRequest provided by the developer
*/
async createTokenRequestBody(request) {
const parameters = new Map();
RequestParameterBuilder.addClientId(parameters, this.config.authOptions.clientId);
RequestParameterBuilder.addScopes(parameters, request.scopes, false);
RequestParameterBuilder.addGrantType(parameters, GrantType.CLIENT_CREDENTIALS_GRANT);
RequestParameterBuilder.addLibraryInfo(parameters, this.config.libraryInfo);
RequestParameterBuilder.addApplicationTelemetry(parameters, this.config.telemetry.application);
RequestParameterBuilder.addThrottling(parameters);
if (this.serverTelemetryManager) {
RequestParameterBuilder.addServerTelemetry(parameters, this.serverTelemetryManager);
}
const correlationId = request.correlationId ||
this.config.cryptoInterface.createNewGuid();
RequestParameterBuilder.addCorrelationId(parameters, correlationId);
if (this.config.clientCredentials.clientSecret) {
RequestParameterBuilder.addClientSecret(parameters, this.config.clientCredentials.clientSecret);
}
// Use clientAssertion from request, fallback to client assertion in base configuration
const clientAssertion = request.clientAssertion ||
this.config.clientCredentials.clientAssertion;
if (clientAssertion) {
RequestParameterBuilder.addClientAssertion(parameters, await getClientAssertion(clientAssertion.assertion, this.config.authOptions.clientId, request.resourceRequestUri));
RequestParameterBuilder.addClientAssertionType(parameters, clientAssertion.assertionType);
}
if (!StringUtils.isEmptyObj(request.claims) ||
(this.config.authOptions.clientCapabilities &&
this.config.authOptions.clientCapabilities.length > 0)) {
RequestParameterBuilder.addClaims(parameters, request.claims, this.config.authOptions.clientCapabilities);
}
return UrlUtils.mapToQueryString(parameters);
}
}
export { ClientCredentialClient };
//# sourceMappingURL=ClientCredentialClient.mjs.map

View File

@@ -0,0 +1,194 @@
/*! @azure/msal-node v3.8.1 2025-10-29 */
'use strict';
import { ClientApplication } from './ClientApplication.mjs';
import { ClientAssertion } from './ClientAssertion.mjs';
import { Constants, MSAL_FORCE_REGION, REGION_ENVIRONMENT_VARIABLE, ApiId } from '../utils/Constants.mjs';
import { createClientAuthError, ClientAuthErrorCodes, getClientAssertion, OIDC_DEFAULT_SCOPES, UrlString, AADAuthorityConstants, AuthError } from '@azure/msal-common/node';
import { ClientCredentialClient } from './ClientCredentialClient.mjs';
import { OnBehalfOfClient } from './OnBehalfOfClient.mjs';
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
// AADAuthorityConstants
/**
* This class is to be used to acquire tokens for confidential client applications (webApp, webAPI). Confidential client applications
* will configure application secrets, client certificates/assertions as applicable
* @public
*/
class ConfidentialClientApplication extends ClientApplication {
/**
* Constructor for the ConfidentialClientApplication
*
* Required attributes in the Configuration object are:
* - clientID: the application ID of your application. You can obtain one by registering your application with our application registration portal
* - authority: the authority URL for your application.
* - client credential: Must set either client secret, certificate, or assertion for confidential clients. You can obtain a client secret from the application registration portal.
*
* In Azure AD, authority is a URL indicating of the form https://login.microsoftonline.com/\{Enter_the_Tenant_Info_Here\}.
* If your application supports Accounts in one organizational directory, replace "Enter_the_Tenant_Info_Here" value with the Tenant Id or Tenant name (for example, contoso.microsoft.com).
* If your application supports Accounts in any organizational directory, replace "Enter_the_Tenant_Info_Here" value with organizations.
* If your application supports Accounts in any organizational directory and personal Microsoft accounts, replace "Enter_the_Tenant_Info_Here" value with common.
* To restrict support to Personal Microsoft accounts only, replace "Enter_the_Tenant_Info_Here" value with consumers.
*
* In Azure B2C, authority is of the form https://\{instance\}/tfp/\{tenant\}/\{policyName\}/
* Full B2C functionality will be available in this library in future versions.
*
* @param Configuration - configuration object for the MSAL ConfidentialClientApplication instance
*/
constructor(configuration) {
super(configuration);
const clientSecretNotEmpty = !!this.config.auth.clientSecret;
const clientAssertionNotEmpty = !!this.config.auth.clientAssertion;
const certificateNotEmpty = (!!this.config.auth.clientCertificate?.thumbprint ||
!!this.config.auth.clientCertificate?.thumbprintSha256) &&
!!this.config.auth.clientCertificate?.privateKey;
/*
* If app developer configures this callback, they don't need a credential
* i.e. AzureSDK can get token from Managed Identity without a cert / secret
*/
if (this.appTokenProvider) {
return;
}
// Check that at most one credential is set on the application
if ((clientSecretNotEmpty && clientAssertionNotEmpty) ||
(clientAssertionNotEmpty && certificateNotEmpty) ||
(clientSecretNotEmpty && certificateNotEmpty)) {
throw createClientAuthError(ClientAuthErrorCodes.invalidClientCredential);
}
if (this.config.auth.clientSecret) {
this.clientSecret = this.config.auth.clientSecret;
return;
}
if (this.config.auth.clientAssertion) {
this.developerProvidedClientAssertion =
this.config.auth.clientAssertion;
return;
}
if (!certificateNotEmpty) {
throw createClientAuthError(ClientAuthErrorCodes.invalidClientCredential);
}
else {
this.clientAssertion = !!this.config.auth.clientCertificate
.thumbprintSha256
? ClientAssertion.fromCertificateWithSha256Thumbprint(this.config.auth.clientCertificate.thumbprintSha256, this.config.auth.clientCertificate.privateKey, this.config.auth.clientCertificate.x5c)
: ClientAssertion.fromCertificate(
// guaranteed to be a string, due to prior error checking in this function
this.config.auth.clientCertificate.thumbprint, this.config.auth.clientCertificate.privateKey, this.config.auth.clientCertificate.x5c);
}
this.appTokenProvider = undefined;
}
/**
* This extensibility point only works for the client_credential flow, i.e. acquireTokenByClientCredential and
* is meant for Azure SDK to enhance Managed Identity support.
*
* @param IAppTokenProvider - Extensibility interface, which allows the app developer to return a token from a custom source.
*/
SetAppTokenProvider(provider) {
this.appTokenProvider = provider;
}
/**
* Acquires tokens from the authority for the application (not for an end user).
*/
async acquireTokenByClientCredential(request) {
this.logger.info("acquireTokenByClientCredential called", request.correlationId);
// If there is a client assertion present in the request, it overrides the one present in the client configuration
let clientAssertion;
if (request.clientAssertion) {
clientAssertion = {
assertion: await getClientAssertion(request.clientAssertion, this.config.auth.clientId
// tokenEndpoint will be undefined. resourceRequestUri is omitted in ClientCredentialRequest
),
assertionType: Constants.JWT_BEARER_ASSERTION_TYPE,
};
}
const baseRequest = await this.initializeBaseRequest(request);
// valid base request should not contain oidc scopes in this grant type
const validBaseRequest = {
...baseRequest,
scopes: baseRequest.scopes.filter((scope) => !OIDC_DEFAULT_SCOPES.includes(scope)),
};
const validRequest = {
...request,
...validBaseRequest,
clientAssertion,
};
/*
* valid request should not have "common" or "organizations" in lieu of the tenant_id in the authority in the auth configuration
* example authority: "https://login.microsoftonline.com/TenantId",
*/
const authority = new UrlString(validRequest.authority);
const tenantId = authority.getUrlComponents().PathSegments[0];
if (Object.values(AADAuthorityConstants).includes(tenantId)) {
throw createClientAuthError(ClientAuthErrorCodes.missingTenantIdError);
}
/*
* if this env variable is set, and the developer provided region isn't defined and isn't "DisableMsalForceRegion",
* MSAL shall opt-in to ESTS-R with the value of this variable
*/
const ENV_MSAL_FORCE_REGION = process.env[MSAL_FORCE_REGION];
let region;
if (validRequest.azureRegion !== "DisableMsalForceRegion") {
if (!validRequest.azureRegion && ENV_MSAL_FORCE_REGION) {
region = ENV_MSAL_FORCE_REGION;
}
else {
region = validRequest.azureRegion;
}
}
const azureRegionConfiguration = {
azureRegion: region,
environmentRegion: process.env[REGION_ENVIRONMENT_VARIABLE],
};
const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenByClientCredential, validRequest.correlationId, validRequest.skipCache);
try {
const discoveredAuthority = await this.createAuthority(validRequest.authority, validRequest.correlationId, azureRegionConfiguration, request.azureCloudOptions);
const clientCredentialConfig = await this.buildOauthClientConfiguration(discoveredAuthority, validRequest.correlationId, "", serverTelemetryManager);
const clientCredentialClient = new ClientCredentialClient(clientCredentialConfig, this.appTokenProvider);
this.logger.verbose("Client credential client created", validRequest.correlationId);
return await clientCredentialClient.acquireToken(validRequest);
}
catch (e) {
if (e instanceof AuthError) {
e.setCorrelationId(validRequest.correlationId);
}
serverTelemetryManager.cacheFailedRequest(e);
throw e;
}
}
/**
* Acquires tokens from the authority for the application.
*
* Used in scenarios where the current app is a middle-tier service which was called with a token
* representing an end user. The current app can use the token (oboAssertion) to request another
* token to access downstream web API, on behalf of that user.
*
* The current middle-tier app has no user interaction to obtain consent.
* See how to gain consent upfront for your middle-tier app from this article.
* https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow#gaining-consent-for-the-middle-tier-application
*/
async acquireTokenOnBehalfOf(request) {
this.logger.info("acquireTokenOnBehalfOf called", request.correlationId);
const validRequest = {
...request,
...(await this.initializeBaseRequest(request)),
};
try {
const discoveredAuthority = await this.createAuthority(validRequest.authority, validRequest.correlationId, undefined, request.azureCloudOptions);
const onBehalfOfConfig = await this.buildOauthClientConfiguration(discoveredAuthority, validRequest.correlationId, "", undefined);
const oboClient = new OnBehalfOfClient(onBehalfOfConfig);
this.logger.verbose("On behalf of client created", validRequest.correlationId);
return await oboClient.acquireToken(validRequest);
}
catch (e) {
if (e instanceof AuthError) {
e.setCorrelationId(validRequest.correlationId);
}
throw e;
}
}
}
export { ConfidentialClientApplication };
//# sourceMappingURL=ConfidentialClientApplication.mjs.map

View File

@@ -0,0 +1,218 @@
/*! @azure/msal-node v3.8.1 2025-10-29 */
'use strict';
import { BaseClient, TimeUtils, ResponseHandler, UrlString, RequestParameterBuilder, UrlUtils, createClientAuthError, ClientAuthErrorCodes, Constants, createAuthError, AuthErrorCodes, GrantType, StringUtils } from '@azure/msal-common/node';
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
/**
* OAuth2.0 Device code client
* @public
*/
class DeviceCodeClient extends BaseClient {
constructor(configuration) {
super(configuration);
}
/**
* Gets device code from device code endpoint, calls back to with device code response, and
* polls token endpoint to exchange device code for tokens
* @param request - developer provided CommonDeviceCodeRequest
*/
async acquireToken(request) {
const deviceCodeResponse = await this.getDeviceCode(request);
request.deviceCodeCallback(deviceCodeResponse);
const reqTimestamp = TimeUtils.nowSeconds();
const response = await this.acquireTokenWithDeviceCode(request, deviceCodeResponse);
const responseHandler = new ResponseHandler(this.config.authOptions.clientId, this.cacheManager, this.cryptoUtils, this.logger, this.config.serializableCache, this.config.persistencePlugin);
// Validate response. This function throws a server error if an error is returned by the server.
responseHandler.validateTokenResponse(response);
return responseHandler.handleServerTokenResponse(response, this.authority, reqTimestamp, request);
}
/**
* Creates device code request and executes http GET
* @param request - developer provided CommonDeviceCodeRequest
*/
async getDeviceCode(request) {
const queryParametersString = this.createExtraQueryParameters(request);
const endpoint = UrlString.appendQueryString(this.authority.deviceCodeEndpoint, queryParametersString);
const queryString = this.createQueryString(request);
const headers = this.createTokenRequestHeaders();
const thumbprint = {
clientId: this.config.authOptions.clientId,
authority: request.authority,
scopes: request.scopes,
claims: request.claims,
authenticationScheme: request.authenticationScheme,
resourceRequestMethod: request.resourceRequestMethod,
resourceRequestUri: request.resourceRequestUri,
shrClaims: request.shrClaims,
sshKid: request.sshKid,
};
return this.executePostRequestToDeviceCodeEndpoint(endpoint, queryString, headers, thumbprint, request.correlationId);
}
/**
* Creates query string for the device code request
* @param request - developer provided CommonDeviceCodeRequest
*/
createExtraQueryParameters(request) {
const parameters = new Map();
if (request.extraQueryParameters) {
RequestParameterBuilder.addExtraQueryParameters(parameters, request.extraQueryParameters);
}
return UrlUtils.mapToQueryString(parameters);
}
/**
* Executes POST request to device code endpoint
* @param deviceCodeEndpoint - token endpoint
* @param queryString - string to be used in the body of the request
* @param headers - headers for the request
* @param thumbprint - unique request thumbprint
* @param correlationId - correlation id to be used in the request
*/
async executePostRequestToDeviceCodeEndpoint(deviceCodeEndpoint, queryString, headers, thumbprint, correlationId) {
const { body: { user_code: userCode, device_code: deviceCode, verification_uri: verificationUri, expires_in: expiresIn, interval, message, }, } = await this.sendPostRequest(thumbprint, deviceCodeEndpoint, {
body: queryString,
headers: headers,
}, correlationId);
return {
userCode,
deviceCode,
verificationUri,
expiresIn,
interval,
message,
};
}
/**
* Create device code endpoint query parameters and returns string
* @param request - developer provided CommonDeviceCodeRequest
*/
createQueryString(request) {
const parameters = new Map();
RequestParameterBuilder.addScopes(parameters, request.scopes);
RequestParameterBuilder.addClientId(parameters, this.config.authOptions.clientId);
if (request.extraQueryParameters) {
RequestParameterBuilder.addExtraQueryParameters(parameters, request.extraQueryParameters);
}
if (request.claims ||
(this.config.authOptions.clientCapabilities &&
this.config.authOptions.clientCapabilities.length > 0)) {
RequestParameterBuilder.addClaims(parameters, request.claims, this.config.authOptions.clientCapabilities);
}
return UrlUtils.mapToQueryString(parameters);
}
/**
* Breaks the polling with specific conditions
* @param deviceCodeExpirationTime - expiration time for the device code request
* @param userSpecifiedTimeout - developer provided timeout, to be compared against deviceCodeExpirationTime
* @param userSpecifiedCancelFlag - boolean indicating the developer would like to cancel the request
*/
continuePolling(deviceCodeExpirationTime, userSpecifiedTimeout, userSpecifiedCancelFlag) {
if (userSpecifiedCancelFlag) {
this.logger.error("Token request cancelled by setting DeviceCodeRequest.cancel = true");
throw createClientAuthError(ClientAuthErrorCodes.deviceCodePollingCancelled);
}
else if (userSpecifiedTimeout &&
userSpecifiedTimeout < deviceCodeExpirationTime &&
TimeUtils.nowSeconds() > userSpecifiedTimeout) {
this.logger.error(`User defined timeout for device code polling reached. The timeout was set for ${userSpecifiedTimeout}`);
throw createClientAuthError(ClientAuthErrorCodes.userTimeoutReached);
}
else if (TimeUtils.nowSeconds() > deviceCodeExpirationTime) {
if (userSpecifiedTimeout) {
this.logger.verbose(`User specified timeout ignored as the device code has expired before the timeout elapsed. The user specified timeout was set for ${userSpecifiedTimeout}`);
}
this.logger.error(`Device code expired. Expiration time of device code was ${deviceCodeExpirationTime}`);
throw createClientAuthError(ClientAuthErrorCodes.deviceCodeExpired);
}
return true;
}
/**
* Creates token request with device code response and polls token endpoint at interval set by the device code response
* @param request - developer provided CommonDeviceCodeRequest
* @param deviceCodeResponse - DeviceCodeResponse returned by the security token service device code endpoint
*/
async acquireTokenWithDeviceCode(request, deviceCodeResponse) {
const queryParametersString = this.createTokenQueryParameters(request);
const endpoint = UrlString.appendQueryString(this.authority.tokenEndpoint, queryParametersString);
const requestBody = this.createTokenRequestBody(request, deviceCodeResponse);
const headers = this.createTokenRequestHeaders();
const userSpecifiedTimeout = request.timeout
? TimeUtils.nowSeconds() + request.timeout
: undefined;
const deviceCodeExpirationTime = TimeUtils.nowSeconds() + deviceCodeResponse.expiresIn;
const pollingIntervalMilli = deviceCodeResponse.interval * 1000;
/*
* Poll token endpoint while (device code is not expired AND operation has not been cancelled by
* setting CancellationToken.cancel = true). POST request is sent at interval set by pollingIntervalMilli
*/
while (this.continuePolling(deviceCodeExpirationTime, userSpecifiedTimeout, request.cancel)) {
const thumbprint = {
clientId: this.config.authOptions.clientId,
authority: request.authority,
scopes: request.scopes,
claims: request.claims,
authenticationScheme: request.authenticationScheme,
resourceRequestMethod: request.resourceRequestMethod,
resourceRequestUri: request.resourceRequestUri,
shrClaims: request.shrClaims,
sshKid: request.sshKid,
};
const response = await this.executePostToTokenEndpoint(endpoint, requestBody, headers, thumbprint, request.correlationId);
if (response.body && response.body.error) {
// user authorization is pending. Sleep for polling interval and try again
if (response.body.error === Constants.AUTHORIZATION_PENDING) {
this.logger.info("Authorization pending. Continue polling.");
await TimeUtils.delay(pollingIntervalMilli);
}
else {
// for any other error, throw
this.logger.info("Unexpected error in polling from the server");
throw createAuthError(AuthErrorCodes.postRequestFailed, response.body.error);
}
}
else {
this.logger.verbose("Authorization completed successfully. Polling stopped.");
return response.body;
}
}
/*
* The above code should've thrown by this point, but to satisfy TypeScript,
* and in the rare case the conditionals in continuePolling() may not catch everything...
*/
this.logger.error("Polling stopped for unknown reasons.");
throw createClientAuthError(ClientAuthErrorCodes.deviceCodeUnknownError);
}
/**
* Creates query parameters and converts to string.
* @param request - developer provided CommonDeviceCodeRequest
* @param deviceCodeResponse - DeviceCodeResponse returned by the security token service device code endpoint
*/
createTokenRequestBody(request, deviceCodeResponse) {
const parameters = new Map();
RequestParameterBuilder.addScopes(parameters, request.scopes);
RequestParameterBuilder.addClientId(parameters, this.config.authOptions.clientId);
RequestParameterBuilder.addGrantType(parameters, GrantType.DEVICE_CODE_GRANT);
RequestParameterBuilder.addDeviceCode(parameters, deviceCodeResponse.deviceCode);
const correlationId = request.correlationId ||
this.config.cryptoInterface.createNewGuid();
RequestParameterBuilder.addCorrelationId(parameters, correlationId);
RequestParameterBuilder.addClientInfo(parameters);
RequestParameterBuilder.addLibraryInfo(parameters, this.config.libraryInfo);
RequestParameterBuilder.addApplicationTelemetry(parameters, this.config.telemetry.application);
RequestParameterBuilder.addThrottling(parameters);
if (this.serverTelemetryManager) {
RequestParameterBuilder.addServerTelemetry(parameters, this.serverTelemetryManager);
}
if (!StringUtils.isEmptyObj(request.claims) ||
(this.config.authOptions.clientCapabilities &&
this.config.authOptions.clientCapabilities.length > 0)) {
RequestParameterBuilder.addClaims(parameters, request.claims, this.config.authOptions.clientCapabilities);
}
return UrlUtils.mapToQueryString(parameters);
}
}
export { DeviceCodeClient };
//# sourceMappingURL=DeviceCodeClient.mjs.map

View File

@@ -0,0 +1,134 @@
/*! @azure/msal-node v3.8.1 2025-10-29 */
'use strict';
import { Logger, DEFAULT_CRYPTO_IMPLEMENTATION, Constants, Authority, ProtocolMode, createClientConfigurationError, ClientConfigurationErrorCodes, EncodingTypes, CacheOutcome } from '@azure/msal-common/node';
import { buildManagedIdentityConfiguration } from '../config/Configuration.mjs';
import { name, version } from '../packageMetadata.mjs';
import { CryptoProvider } from '../crypto/CryptoProvider.mjs';
import { ClientCredentialClient } from './ClientCredentialClient.mjs';
import { ManagedIdentityClient } from './ManagedIdentityClient.mjs';
import { NodeStorage } from '../cache/NodeStorage.mjs';
import { DEFAULT_AUTHORITY_FOR_MANAGED_IDENTITY, ManagedIdentitySourceNames } from '../utils/Constants.mjs';
import { HashUtils } from '../crypto/HashUtils.mjs';
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
const SOURCES_THAT_SUPPORT_TOKEN_REVOCATION = [ManagedIdentitySourceNames.SERVICE_FABRIC];
/**
* Class to initialize a managed identity and identify the service
* @public
*/
class ManagedIdentityApplication {
constructor(configuration) {
// undefined config means the managed identity is system-assigned
this.config = buildManagedIdentityConfiguration(configuration || {});
this.logger = new Logger(this.config.system.loggerOptions, name, version);
const fakeStatusAuthorityOptions = {
canonicalAuthority: Constants.DEFAULT_AUTHORITY,
};
if (!ManagedIdentityApplication.nodeStorage) {
ManagedIdentityApplication.nodeStorage = new NodeStorage(this.logger, this.config.managedIdentityId.id, DEFAULT_CRYPTO_IMPLEMENTATION, fakeStatusAuthorityOptions);
}
this.networkClient = this.config.system.networkClient;
this.cryptoProvider = new CryptoProvider();
const fakeAuthorityOptions = {
protocolMode: ProtocolMode.AAD,
knownAuthorities: [DEFAULT_AUTHORITY_FOR_MANAGED_IDENTITY],
cloudDiscoveryMetadata: "",
authorityMetadata: "",
};
this.fakeAuthority = new Authority(DEFAULT_AUTHORITY_FOR_MANAGED_IDENTITY, this.networkClient, ManagedIdentityApplication.nodeStorage, fakeAuthorityOptions, this.logger, this.cryptoProvider.createNewGuid(), // correlationID
undefined, true);
this.fakeClientCredentialClient = new ClientCredentialClient({
authOptions: {
clientId: this.config.managedIdentityId.id,
authority: this.fakeAuthority,
},
});
this.managedIdentityClient = new ManagedIdentityClient(this.logger, ManagedIdentityApplication.nodeStorage, this.networkClient, this.cryptoProvider, this.config.disableInternalRetries);
this.hashUtils = new HashUtils();
}
/**
* Acquire an access token from the cache or the managed identity
* @param managedIdentityRequest - the ManagedIdentityRequestParams object passed in by the developer
* @returns the access token
*/
async acquireToken(managedIdentityRequestParams) {
if (!managedIdentityRequestParams.resource) {
throw createClientConfigurationError(ClientConfigurationErrorCodes.urlEmptyError);
}
const managedIdentityRequest = {
forceRefresh: managedIdentityRequestParams.forceRefresh,
resource: managedIdentityRequestParams.resource.replace("/.default", ""),
scopes: [
managedIdentityRequestParams.resource.replace("/.default", ""),
],
authority: this.fakeAuthority.canonicalAuthority,
correlationId: this.cryptoProvider.createNewGuid(),
claims: managedIdentityRequestParams.claims,
clientCapabilities: this.config.clientCapabilities,
};
if (managedIdentityRequest.forceRefresh) {
return this.acquireTokenFromManagedIdentity(managedIdentityRequest, this.config.managedIdentityId, this.fakeAuthority);
}
const [cachedAuthenticationResult, lastCacheOutcome] = await this.fakeClientCredentialClient.getCachedAuthenticationResult(managedIdentityRequest, this.config, this.cryptoProvider, this.fakeAuthority, ManagedIdentityApplication.nodeStorage);
/*
* Check if claims are present in the managed identity request.
* If so, the cached token will not be used.
*/
if (managedIdentityRequest.claims) {
const sourceName = this.managedIdentityClient.getManagedIdentitySource();
/*
* Check if there is a cached token and if the Managed Identity source supports token revocation.
* If so, hash the cached access token and add it to the request.
*/
if (cachedAuthenticationResult &&
SOURCES_THAT_SUPPORT_TOKEN_REVOCATION.includes(sourceName)) {
const revokedTokenSha256Hash = this.hashUtils
.sha256(cachedAuthenticationResult.accessToken)
.toString(EncodingTypes.HEX);
managedIdentityRequest.revokedTokenSha256Hash =
revokedTokenSha256Hash;
}
return this.acquireTokenFromManagedIdentity(managedIdentityRequest, this.config.managedIdentityId, this.fakeAuthority);
}
if (cachedAuthenticationResult) {
// if the token is not expired but must be refreshed; get a new one in the background
if (lastCacheOutcome === CacheOutcome.PROACTIVELY_REFRESHED) {
this.logger.info("ClientCredentialClient:getCachedAuthenticationResult - Cached access token's refreshOn property has been exceeded'. It's not expired, but must be refreshed.");
// force refresh; will run in the background
const refreshAccessToken = true;
await this.acquireTokenFromManagedIdentity(managedIdentityRequest, this.config.managedIdentityId, this.fakeAuthority, refreshAccessToken);
}
return cachedAuthenticationResult;
}
else {
return this.acquireTokenFromManagedIdentity(managedIdentityRequest, this.config.managedIdentityId, this.fakeAuthority);
}
}
/**
* Acquires a token from a managed identity endpoint.
*
* @param managedIdentityRequest - The request object containing parameters for the managed identity token request.
* @param managedIdentityId - The identifier for the managed identity (e.g., client ID or resource ID).
* @param fakeAuthority - A placeholder authority used for the token request.
* @param refreshAccessToken - Optional flag indicating whether to force a refresh of the access token.
* @returns A promise that resolves to an AuthenticationResult containing the acquired token and related information.
*/
async acquireTokenFromManagedIdentity(managedIdentityRequest, managedIdentityId, fakeAuthority, refreshAccessToken) {
// make a network call to the managed identity
return this.managedIdentityClient.sendManagedIdentityTokenRequest(managedIdentityRequest, managedIdentityId, fakeAuthority, refreshAccessToken);
}
/**
* Determine the Managed Identity Source based on available environment variables. This API is consumed by Azure Identity SDK.
* @returns ManagedIdentitySourceNames - The Managed Identity source's name
*/
getManagedIdentitySource() {
return (ManagedIdentityClient.sourceName ||
this.managedIdentityClient.getManagedIdentitySource());
}
}
export { ManagedIdentityApplication };
//# sourceMappingURL=ManagedIdentityApplication.mjs.map

View File

@@ -0,0 +1,79 @@
/*! @azure/msal-node v3.8.1 2025-10-29 */
'use strict';
import { AppService } from './ManagedIdentitySources/AppService.mjs';
import { AzureArc } from './ManagedIdentitySources/AzureArc.mjs';
import { CloudShell } from './ManagedIdentitySources/CloudShell.mjs';
import { Imds } from './ManagedIdentitySources/Imds.mjs';
import { ServiceFabric } from './ManagedIdentitySources/ServiceFabric.mjs';
import { createManagedIdentityError } from '../error/ManagedIdentityError.mjs';
import { ManagedIdentitySourceNames } from '../utils/Constants.mjs';
import { MachineLearning } from './ManagedIdentitySources/MachineLearning.mjs';
import { unableToCreateSource } from '../error/ManagedIdentityErrorCodes.mjs';
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
/*
* Class to initialize a managed identity and identify the service.
* Original source of code: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity/src/ManagedIdentityClient.cs
*/
class ManagedIdentityClient {
constructor(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries) {
this.logger = logger;
this.nodeStorage = nodeStorage;
this.networkClient = networkClient;
this.cryptoProvider = cryptoProvider;
this.disableInternalRetries = disableInternalRetries;
}
async sendManagedIdentityTokenRequest(managedIdentityRequest, managedIdentityId, fakeAuthority, refreshAccessToken) {
if (!ManagedIdentityClient.identitySource) {
ManagedIdentityClient.identitySource =
this.selectManagedIdentitySource(this.logger, this.nodeStorage, this.networkClient, this.cryptoProvider, this.disableInternalRetries, managedIdentityId);
}
return ManagedIdentityClient.identitySource.acquireTokenWithManagedIdentity(managedIdentityRequest, managedIdentityId, fakeAuthority, refreshAccessToken);
}
allEnvironmentVariablesAreDefined(environmentVariables) {
return Object.values(environmentVariables).every((environmentVariable) => {
return environmentVariable !== undefined;
});
}
/**
* Determine the Managed Identity Source based on available environment variables. This API is consumed by ManagedIdentityApplication's getManagedIdentitySource.
* @returns ManagedIdentitySourceNames - The Managed Identity source's name
*/
getManagedIdentitySource() {
ManagedIdentityClient.sourceName =
this.allEnvironmentVariablesAreDefined(ServiceFabric.getEnvironmentVariables())
? ManagedIdentitySourceNames.SERVICE_FABRIC
: this.allEnvironmentVariablesAreDefined(AppService.getEnvironmentVariables())
? ManagedIdentitySourceNames.APP_SERVICE
: this.allEnvironmentVariablesAreDefined(MachineLearning.getEnvironmentVariables())
? ManagedIdentitySourceNames.MACHINE_LEARNING
: this.allEnvironmentVariablesAreDefined(CloudShell.getEnvironmentVariables())
? ManagedIdentitySourceNames.CLOUD_SHELL
: this.allEnvironmentVariablesAreDefined(AzureArc.getEnvironmentVariables())
? ManagedIdentitySourceNames.AZURE_ARC
: ManagedIdentitySourceNames.DEFAULT_TO_IMDS;
return ManagedIdentityClient.sourceName;
}
/**
* Tries to create a managed identity source for all sources
* @returns the managed identity Source
*/
selectManagedIdentitySource(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries, managedIdentityId) {
const source = ServiceFabric.tryCreate(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries, managedIdentityId) ||
AppService.tryCreate(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries) ||
MachineLearning.tryCreate(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries) ||
CloudShell.tryCreate(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries, managedIdentityId) ||
AzureArc.tryCreate(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries, managedIdentityId) ||
Imds.tryCreate(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries);
if (!source) {
throw createManagedIdentityError(unableToCreateSource);
}
return source;
}
}
export { ManagedIdentityClient };
//# sourceMappingURL=ManagedIdentityClient.mjs.map

View File

@@ -0,0 +1,109 @@
/*! @azure/msal-node v3.8.1 2025-10-29 */
'use strict';
import { BaseManagedIdentitySource } from './BaseManagedIdentitySource.mjs';
import { ManagedIdentityEnvironmentVariableNames, ManagedIdentitySourceNames, ManagedIdentityHeaders, ManagedIdentityQueryParameters, ManagedIdentityIdType, HttpMethod } from '../../utils/Constants.mjs';
import { ManagedIdentityRequestParameters } from '../../config/ManagedIdentityRequestParameters.mjs';
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
// MSI Constants. Docs for MSI are available here https://docs.microsoft.com/azure/app-service/overview-managed-identity
const APP_SERVICE_MSI_API_VERSION = "2019-08-01";
/**
* Azure App Service Managed Identity Source implementation.
*
* This class provides managed identity authentication for applications running in Azure App Service.
* It uses the local metadata service endpoint available within App Service environments to obtain
* access tokens without requiring explicit credentials.
*
* Original source of code: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity/src/AppServiceManagedIdentitySource.cs
*/
class AppService extends BaseManagedIdentitySource {
/**
* Creates a new instance of the AppService managed identity source.
*
* @param logger - Logger instance for diagnostic output
* @param nodeStorage - Node.js storage implementation for caching
* @param networkClient - Network client for making HTTP requests
* @param cryptoProvider - Cryptographic operations provider
* @param disableInternalRetries - Whether to disable internal retry logic
* @param identityEndpoint - The App Service identity endpoint URL
* @param identityHeader - The secret header value required for authentication
*/
constructor(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries, identityEndpoint, identityHeader) {
super(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries);
this.identityEndpoint = identityEndpoint;
this.identityHeader = identityHeader;
}
/**
* Retrieves the required environment variables for App Service managed identity.
*
* App Service managed identity requires two environment variables:
* - IDENTITY_ENDPOINT: The URL of the local metadata service
* - IDENTITY_HEADER: A secret header value for authentication
*
* @returns An array containing [identityEndpoint, identityHeader] values from environment variables.
* Either value may be undefined if the environment variable is not set.
*/
static getEnvironmentVariables() {
const identityEndpoint = process.env[ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT];
const identityHeader = process.env[ManagedIdentityEnvironmentVariableNames.IDENTITY_HEADER];
return [identityEndpoint, identityHeader];
}
/**
* Attempts to create an AppService managed identity source if the environment supports it.
*
* This method checks for the presence of required environment variables and validates
* the identity endpoint URL. If the environment is not suitable for App Service managed
* identity (missing environment variables or invalid endpoint), it returns null.
*
* @param logger - Logger instance for diagnostic output
* @param nodeStorage - Node.js storage implementation for caching
* @param networkClient - Network client for making HTTP requests
* @param cryptoProvider - Cryptographic operations provider
* @param disableInternalRetries - Whether to disable internal retry logic
*
* @returns A new AppService instance if the environment is suitable, null otherwise
*/
static tryCreate(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries) {
const [identityEndpoint, identityHeader] = AppService.getEnvironmentVariables();
// if either of the identity endpoint or identity header variables are undefined, this MSI provider is unavailable.
if (!identityEndpoint || !identityHeader) {
logger.info(`[Managed Identity] ${ManagedIdentitySourceNames.APP_SERVICE} managed identity is unavailable because one or both of the '${ManagedIdentityEnvironmentVariableNames.IDENTITY_HEADER}' and '${ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT}' environment variables are not defined.`);
return null;
}
const validatedIdentityEndpoint = AppService.getValidatedEnvVariableUrlString(ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT, identityEndpoint, ManagedIdentitySourceNames.APP_SERVICE, logger);
logger.info(`[Managed Identity] Environment variables validation passed for ${ManagedIdentitySourceNames.APP_SERVICE} managed identity. Endpoint URI: ${validatedIdentityEndpoint}. Creating ${ManagedIdentitySourceNames.APP_SERVICE} managed identity.`);
return new AppService(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries, identityEndpoint, identityHeader);
}
/**
* Creates a managed identity token request for the App Service environment.
*
* This method constructs an HTTP GET request to the App Service identity endpoint
* with the required headers, query parameters, and managed identity configuration.
* The request includes the secret header for authentication and appropriate API version.
*
* @param resource - The target resource/scope for which to request an access token (e.g., "https://graph.microsoft.com/.default")
* @param managedIdentityId - The managed identity configuration specifying whether to use system-assigned or user-assigned identity
*
* @returns A configured ManagedIdentityRequestParameters object ready for network execution
*/
createRequest(resource, managedIdentityId) {
const request = new ManagedIdentityRequestParameters(HttpMethod.GET, this.identityEndpoint);
request.headers[ManagedIdentityHeaders.APP_SERVICE_SECRET_HEADER_NAME] =
this.identityHeader;
request.queryParameters[ManagedIdentityQueryParameters.API_VERSION] =
APP_SERVICE_MSI_API_VERSION;
request.queryParameters[ManagedIdentityQueryParameters.RESOURCE] =
resource;
if (managedIdentityId.idType !== ManagedIdentityIdType.SYSTEM_ASSIGNED) {
request.queryParameters[this.getManagedIdentityUserAssignedIdQueryParameterKey(managedIdentityId.idType)] = managedIdentityId.id;
}
// bodyParameters calculated in BaseManagedIdentity.acquireTokenWithManagedIdentity
return request;
}
}
export { AppService };
//# sourceMappingURL=AppService.mjs.map

View File

@@ -0,0 +1,248 @@
/*! @azure/msal-node v3.8.1 2025-10-29 */
'use strict';
import { HttpStatus, EncodingTypes, AuthError, createClientAuthError, ClientAuthErrorCodes } from '@azure/msal-common/node';
import { ManagedIdentityRequestParameters } from '../../config/ManagedIdentityRequestParameters.mjs';
import { BaseManagedIdentitySource } from './BaseManagedIdentitySource.mjs';
import { createManagedIdentityError } from '../../error/ManagedIdentityError.mjs';
import { ManagedIdentityEnvironmentVariableNames, ManagedIdentitySourceNames, ManagedIdentityIdType, HttpMethod, ManagedIdentityHeaders, ManagedIdentityQueryParameters, AZURE_ARC_SECRET_FILE_MAX_SIZE_BYTES } from '../../utils/Constants.mjs';
import { accessSync, constants, statSync, readFileSync } from 'fs';
import path from 'path';
import { unableToCreateAzureArc, wwwAuthenticateHeaderMissing, wwwAuthenticateHeaderUnsupportedFormat, platformNotSupported, invalidFileExtension, invalidFilePath, unableToReadSecretFile, invalidSecret } from '../../error/ManagedIdentityErrorCodes.mjs';
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
const ARC_API_VERSION = "2019-11-01";
const DEFAULT_AZURE_ARC_IDENTITY_ENDPOINT = "http://127.0.0.1:40342/metadata/identity/oauth2/token";
const HIMDS_EXECUTABLE_HELPER_STRING = "N/A: himds executable exists";
const SUPPORTED_AZURE_ARC_PLATFORMS = {
win32: `${process.env["ProgramData"]}\\AzureConnectedMachineAgent\\Tokens\\`,
linux: "/var/opt/azcmagent/tokens/",
};
const AZURE_ARC_FILE_DETECTION = {
win32: `${process.env["ProgramFiles"]}\\AzureConnectedMachineAgent\\himds.exe`,
linux: "/opt/azcmagent/bin/himds",
};
/**
* Azure Arc managed identity source implementation for acquiring tokens from Azure Arc-enabled servers.
*
* This class provides managed identity authentication for applications running on Azure Arc-enabled servers
* by communicating with the local Hybrid Instance Metadata Service (HIMDS). It supports both environment
* variable-based configuration and automatic detection through the HIMDS executable.
*
* Original source of code: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity/src/AzureArcManagedIdentitySource.cs
*/
class AzureArc extends BaseManagedIdentitySource {
/**
* Creates a new instance of the AzureArc managed identity source.
*
* @param logger - Logger instance for capturing telemetry and diagnostic information
* @param nodeStorage - Storage implementation for caching tokens and metadata
* @param networkClient - Network client for making HTTP requests to the identity endpoint
* @param cryptoProvider - Cryptographic operations provider for token validation and encryption
* @param disableInternalRetries - Flag to disable automatic retry logic for failed requests
* @param identityEndpoint - The Azure Arc identity endpoint URL for token requests
*/
constructor(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries, identityEndpoint) {
super(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries);
this.identityEndpoint = identityEndpoint;
}
/**
* Retrieves and validates Azure Arc environment variables for managed identity configuration.
*
* This method checks for IDENTITY_ENDPOINT and IMDS_ENDPOINT environment variables.
* If either is missing, it attempts to detect the Azure Arc environment by checking for
* the HIMDS executable at platform-specific paths. On successful detection, it returns
* the default identity endpoint and a helper string indicating file-based detection.
*
* @returns An array containing [identityEndpoint, imdsEndpoint] where both values are
* strings if Azure Arc is available, or undefined if not available.
*/
static getEnvironmentVariables() {
let identityEndpoint = process.env[ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT];
let imdsEndpoint = process.env[ManagedIdentityEnvironmentVariableNames.IMDS_ENDPOINT];
// if either of the identity or imds endpoints are undefined, check if the himds executable exists
if (!identityEndpoint || !imdsEndpoint) {
// get the expected Windows or Linux file path of the himds executable
const fileDetectionPath = AZURE_ARC_FILE_DETECTION[process.platform];
try {
/*
* check if the himds executable exists and its permissions allow it to be read
* returns undefined if true, throws an error otherwise
*/
accessSync(fileDetectionPath, constants.F_OK | constants.R_OK);
identityEndpoint = DEFAULT_AZURE_ARC_IDENTITY_ENDPOINT;
imdsEndpoint = HIMDS_EXECUTABLE_HELPER_STRING;
}
catch (err) {
/*
* do nothing
* accessSync returns undefined on success, and throws an error on failure
*/
}
}
return [identityEndpoint, imdsEndpoint];
}
/**
* Attempts to create an AzureArc managed identity source instance.
*
* Validates the Azure Arc environment by checking environment variables
* and performing file-based detection. It ensures that only system-assigned managed identities
* are supported for Azure Arc scenarios. The method performs comprehensive validation of
* endpoint URLs and logs detailed information about the detection process.
*
* @param logger - Logger instance for capturing creation and validation steps
* @param nodeStorage - Storage implementation for the managed identity source
* @param networkClient - Network client for HTTP communication
* @param cryptoProvider - Cryptographic operations provider
* @param disableInternalRetries - Whether to disable automatic retry mechanisms
* @param managedIdentityId - The managed identity configuration, must be system-assigned
*
* @returns AzureArc instance if the environment supports Azure Arc managed identity, null otherwise
*
* @throws {ManagedIdentityError} When a user-assigned managed identity is specified (not supported for Azure Arc)
*/
static tryCreate(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries, managedIdentityId) {
const [identityEndpoint, imdsEndpoint] = AzureArc.getEnvironmentVariables();
// if either of the identity or imds endpoints are undefined (even after himds file detection)
if (!identityEndpoint || !imdsEndpoint) {
logger.info(`[Managed Identity] ${ManagedIdentitySourceNames.AZURE_ARC} managed identity is unavailable through environment variables because one or both of '${ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT}' and '${ManagedIdentityEnvironmentVariableNames.IMDS_ENDPOINT}' are not defined. ${ManagedIdentitySourceNames.AZURE_ARC} managed identity is also unavailable through file detection.`);
return null;
}
// check if the imds endpoint is set to the default for file detection
if (imdsEndpoint === HIMDS_EXECUTABLE_HELPER_STRING) {
logger.info(`[Managed Identity] ${ManagedIdentitySourceNames.AZURE_ARC} managed identity is available through file detection. Defaulting to known ${ManagedIdentitySourceNames.AZURE_ARC} endpoint: ${DEFAULT_AZURE_ARC_IDENTITY_ENDPOINT}. Creating ${ManagedIdentitySourceNames.AZURE_ARC} managed identity.`);
}
else {
// otherwise, both the identity and imds endpoints are defined without file detection; validate them
const validatedIdentityEndpoint = AzureArc.getValidatedEnvVariableUrlString(ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT, identityEndpoint, ManagedIdentitySourceNames.AZURE_ARC, logger);
// remove trailing slash
validatedIdentityEndpoint.endsWith("/")
? validatedIdentityEndpoint.slice(0, -1)
: validatedIdentityEndpoint;
AzureArc.getValidatedEnvVariableUrlString(ManagedIdentityEnvironmentVariableNames.IMDS_ENDPOINT, imdsEndpoint, ManagedIdentitySourceNames.AZURE_ARC, logger);
logger.info(`[Managed Identity] Environment variables validation passed for ${ManagedIdentitySourceNames.AZURE_ARC} managed identity. Endpoint URI: ${validatedIdentityEndpoint}. Creating ${ManagedIdentitySourceNames.AZURE_ARC} managed identity.`);
}
if (managedIdentityId.idType !== ManagedIdentityIdType.SYSTEM_ASSIGNED) {
throw createManagedIdentityError(unableToCreateAzureArc);
}
return new AzureArc(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries, identityEndpoint);
}
/**
* Creates a properly formatted HTTP request for acquiring tokens from the Azure Arc identity endpoint.
*
* This method constructs a GET request to the Azure Arc HIMDS endpoint with the required metadata header
* and query parameters. The endpoint URL is normalized to use 127.0.0.1 instead of localhost for
* consistency. Additional body parameters are calculated by the base class during token acquisition.
*
* @param resource - The target resource/scope for which to request an access token (e.g., "https://graph.microsoft.com/.default")
*
* @returns A configured ManagedIdentityRequestParameters object ready for network execution
*/
createRequest(resource) {
const request = new ManagedIdentityRequestParameters(HttpMethod.GET, this.identityEndpoint.replace("localhost", "127.0.0.1"));
request.headers[ManagedIdentityHeaders.METADATA_HEADER_NAME] = "true";
request.queryParameters[ManagedIdentityQueryParameters.API_VERSION] =
ARC_API_VERSION;
request.queryParameters[ManagedIdentityQueryParameters.RESOURCE] =
resource;
// bodyParameters calculated in BaseManagedIdentity.acquireTokenWithManagedIdentity
return request;
}
/**
* Processes the server response and handles Azure Arc-specific authentication challenges.
*
* This method implements the Azure Arc authentication flow which may require reading a secret file
* for authorization. When the initial request returns HTTP 401 Unauthorized, it extracts the file
* path from the WWW-Authenticate header, validates the file location and size, reads the secret,
* and retries the request with Basic authentication. The method includes comprehensive security
* validations to prevent path traversal and ensure file integrity.
*
* @param originalResponse - The initial HTTP response from the identity endpoint
* @param networkClient - Network client for making the retry request if needed
* @param networkRequest - The original request parameters (modified with auth header for retry)
* @param networkRequestOptions - Additional options for network requests
*
* @returns A promise that resolves to the server token response with access token and metadata
*
* @throws {ManagedIdentityError} When:
* - WWW-Authenticate header is missing or has unsupported format
* - Platform is not supported (not Windows or Linux)
* - Secret file has invalid extension (not .key)
* - Secret file path doesn't match expected platform path
* - Secret file cannot be read or is too large (>4096 bytes)
* @throws {ClientAuthError} When network errors occur during retry request
*/
async getServerTokenResponseAsync(originalResponse, networkClient, networkRequest, networkRequestOptions) {
let retryResponse;
if (originalResponse.status === HttpStatus.UNAUTHORIZED) {
const wwwAuthHeader = originalResponse.headers["www-authenticate"];
if (!wwwAuthHeader) {
throw createManagedIdentityError(wwwAuthenticateHeaderMissing);
}
if (!wwwAuthHeader.includes("Basic realm=")) {
throw createManagedIdentityError(wwwAuthenticateHeaderUnsupportedFormat);
}
const secretFilePath = wwwAuthHeader.split("Basic realm=")[1];
// throw an error if the managed identity application is not being run on Windows or Linux
if (!SUPPORTED_AZURE_ARC_PLATFORMS.hasOwnProperty(process.platform)) {
throw createManagedIdentityError(platformNotSupported);
}
// get the expected Windows or Linux file path
const expectedSecretFilePath = SUPPORTED_AZURE_ARC_PLATFORMS[process.platform];
// throw an error if the file in the file path is not a .key file
const fileName = path.basename(secretFilePath);
if (!fileName.endsWith(".key")) {
throw createManagedIdentityError(invalidFileExtension);
}
/*
* throw an error if the file path from the www-authenticate header does not match the
* expected file path for the platform (Windows or Linux) the managed identity application
* is running on
*/
if (expectedSecretFilePath + fileName !== secretFilePath) {
throw createManagedIdentityError(invalidFilePath);
}
let secretFileSize;
// attempt to get the secret file's size, in bytes
try {
secretFileSize = await statSync(secretFilePath).size;
}
catch (e) {
throw createManagedIdentityError(unableToReadSecretFile);
}
// throw an error if the secret file's size is greater than 4096 bytes
if (secretFileSize > AZURE_ARC_SECRET_FILE_MAX_SIZE_BYTES) {
throw createManagedIdentityError(invalidSecret);
}
// attempt to read the contents of the secret file
let secret;
try {
secret = readFileSync(secretFilePath, EncodingTypes.UTF8);
}
catch (e) {
throw createManagedIdentityError(unableToReadSecretFile);
}
const authHeaderValue = `Basic ${secret}`;
this.logger.info(`[Managed Identity] Adding authorization header to the request.`);
networkRequest.headers[ManagedIdentityHeaders.AUTHORIZATION_HEADER_NAME] = authHeaderValue;
try {
retryResponse =
await networkClient.sendGetRequestAsync(networkRequest.computeUri(), networkRequestOptions);
}
catch (error) {
if (error instanceof AuthError) {
throw error;
}
else {
throw createClientAuthError(ClientAuthErrorCodes.networkError);
}
}
}
return this.getServerTokenResponse(retryResponse || originalResponse);
}
}
export { ARC_API_VERSION, AZURE_ARC_FILE_DETECTION, AzureArc, DEFAULT_AZURE_ARC_IDENTITY_ENDPOINT, SUPPORTED_AZURE_ARC_PLATFORMS };
//# sourceMappingURL=AzureArc.mjs.map

View File

@@ -0,0 +1,244 @@
/*! @azure/msal-node v3.8.1 2025-10-29 */
'use strict';
import { TimeUtils, Constants, HeaderNames, AuthError, createClientAuthError, ClientAuthErrorCodes, ResponseHandler, UrlString } from '@azure/msal-common/node';
import { ManagedIdentityQueryParameters, HttpMethod, ManagedIdentityIdType } from '../../utils/Constants.mjs';
import { createManagedIdentityError } from '../../error/ManagedIdentityError.mjs';
import { isIso8601 } from '../../utils/TimeUtils.mjs';
import { HttpClientWithRetries } from '../../network/HttpClientWithRetries.mjs';
import { invalidManagedIdentityIdType, MsiEnvironmentVariableUrlMalformedErrorCodes } from '../../error/ManagedIdentityErrorCodes.mjs';
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
/**
* Managed Identity User Assigned Id Query Parameter Names
*/
const ManagedIdentityUserAssignedIdQueryParameterNames = {
MANAGED_IDENTITY_CLIENT_ID_2017: "clientid",
MANAGED_IDENTITY_CLIENT_ID: "client_id",
MANAGED_IDENTITY_OBJECT_ID: "object_id",
MANAGED_IDENTITY_RESOURCE_ID_IMDS: "msi_res_id",
MANAGED_IDENTITY_RESOURCE_ID_NON_IMDS: "mi_res_id",
};
/**
* Base class for all Managed Identity sources. Provides common functionality for
* authenticating with Azure Managed Identity endpoints across different Azure services
* including IMDS, App Service, Azure Arc, Service Fabric, Cloud Shell, and Machine Learning.
*
* This abstract class handles token acquisition, response processing, and network communication
* while allowing concrete implementations to define source-specific request creation logic.
*/
class BaseManagedIdentitySource {
/**
* Creates an instance of BaseManagedIdentitySource.
*
* @param logger - Logger instance for diagnostic information
* @param nodeStorage - Storage interface for caching tokens
* @param networkClient - Network client for making HTTP requests
* @param cryptoProvider - Cryptographic provider for token operations
* @param disableInternalRetries - Whether to disable automatic retry logic
*/
constructor(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries) {
this.logger = logger;
this.nodeStorage = nodeStorage;
this.networkClient = networkClient;
this.cryptoProvider = cryptoProvider;
this.disableInternalRetries = disableInternalRetries;
}
/**
* Processes the network response and converts it to a standardized server token response.
* This async version allows for source-specific response processing logic while maintaining
* backward compatibility with the synchronous version.
*
* @param response - The network response containing the managed identity token
* @param _networkClient - Network client used for the request (unused in base implementation)
* @param _networkRequest - The original network request parameters (unused in base implementation)
* @param _networkRequestOptions - The network request options (unused in base implementation)
*
* @returns Promise resolving to a standardized server authorization token response
*/
async getServerTokenResponseAsync(response,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_networkClient,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_networkRequest,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_networkRequestOptions) {
return this.getServerTokenResponse(response);
}
/**
* Converts a managed identity token response to a standardized server authorization token response.
* Handles time format conversion, expiration calculation, and error mapping to ensure
* compatibility with the MSAL response handling pipeline.
*
* @param response - The network response containing the managed identity token
*
* @returns Standardized server authorization token response with normalized fields
*/
getServerTokenResponse(response) {
let refreshIn, expiresIn;
if (response.body.expires_on) {
// if the expires_on field in the response body is a string and in ISO 8601 format, convert it to a Unix timestamp (seconds since epoch)
if (isIso8601(response.body.expires_on)) {
response.body.expires_on =
new Date(response.body.expires_on).getTime() / 1000;
}
expiresIn = response.body.expires_on - TimeUtils.nowSeconds();
// compute refresh_in as 1/2 of expires_in, but only if expires_in > 2h
if (expiresIn > 2 * 3600) {
refreshIn = expiresIn / 2;
}
}
const serverTokenResponse = {
status: response.status,
// success
access_token: response.body.access_token,
expires_in: expiresIn,
scope: response.body.resource,
token_type: response.body.token_type,
refresh_in: refreshIn,
// error
correlation_id: response.body.correlation_id || response.body.correlationId,
error: typeof response.body.error === "string"
? response.body.error
: response.body.error?.code,
error_description: response.body.message ||
(typeof response.body.error === "string"
? response.body.error_description
: response.body.error?.message),
error_codes: response.body.error_codes,
timestamp: response.body.timestamp,
trace_id: response.body.trace_id,
};
return serverTokenResponse;
}
/**
* Acquires an access token using the managed identity endpoint for the specified resource.
* This is the primary method for token acquisition, handling the complete flow from
* request creation through response processing and token caching.
*
* @param managedIdentityRequest - The managed identity request containing resource and optional parameters
* @param managedIdentityId - The managed identity configuration (system or user-assigned)
* @param fakeAuthority - Authority instance used for token caching (managed identity uses a placeholder authority)
* @param refreshAccessToken - Whether this is a token refresh operation
*
* @returns Promise resolving to an authentication result containing the access token and metadata
*
* @throws {AuthError} When network requests fail or token validation fails
* @throws {ClientAuthError} When network errors occur during the request
*/
async acquireTokenWithManagedIdentity(managedIdentityRequest, managedIdentityId, fakeAuthority, refreshAccessToken) {
const networkRequest = this.createRequest(managedIdentityRequest.resource, managedIdentityId);
if (managedIdentityRequest.revokedTokenSha256Hash) {
this.logger.info(`[Managed Identity] The following claims are present in the request: ${managedIdentityRequest.claims}`);
networkRequest.queryParameters[ManagedIdentityQueryParameters.SHA256_TOKEN_TO_REFRESH] = managedIdentityRequest.revokedTokenSha256Hash;
}
if (managedIdentityRequest.clientCapabilities?.length) {
const clientCapabilities = managedIdentityRequest.clientCapabilities.toString();
this.logger.info(`[Managed Identity] The following client capabilities are present in the request: ${clientCapabilities}`);
networkRequest.queryParameters[ManagedIdentityQueryParameters.XMS_CC] = clientCapabilities;
}
const headers = networkRequest.headers;
headers[HeaderNames.CONTENT_TYPE] = Constants.URL_FORM_CONTENT_TYPE;
const networkRequestOptions = { headers };
if (Object.keys(networkRequest.bodyParameters).length) {
networkRequestOptions.body =
networkRequest.computeParametersBodyString();
}
/**
* Initializes the network client helper based on the retry policy configuration.
* If internal retries are disabled, it uses the provided network client directly.
* Otherwise, it wraps the network client with an HTTP client that supports retries.
*/
const networkClientHelper = this.disableInternalRetries
? this.networkClient
: new HttpClientWithRetries(this.networkClient, networkRequest.retryPolicy, this.logger);
const reqTimestamp = TimeUtils.nowSeconds();
let response;
try {
// Sources that send POST requests: Cloud Shell
if (networkRequest.httpMethod === HttpMethod.POST) {
response =
await networkClientHelper.sendPostRequestAsync(networkRequest.computeUri(), networkRequestOptions);
// Sources that send GET requests: App Service, Azure Arc, IMDS, Service Fabric
}
else {
response =
await networkClientHelper.sendGetRequestAsync(networkRequest.computeUri(), networkRequestOptions);
}
}
catch (error) {
if (error instanceof AuthError) {
throw error;
}
else {
throw createClientAuthError(ClientAuthErrorCodes.networkError);
}
}
const responseHandler = new ResponseHandler(managedIdentityId.id, this.nodeStorage, this.cryptoProvider, this.logger, null, null);
const serverTokenResponse = await this.getServerTokenResponseAsync(response, networkClientHelper, networkRequest, networkRequestOptions);
responseHandler.validateTokenResponse(serverTokenResponse, refreshAccessToken);
// caches the token
return responseHandler.handleServerTokenResponse(serverTokenResponse, fakeAuthority, reqTimestamp, managedIdentityRequest);
}
/**
* Determines the appropriate query parameter name for user-assigned managed identity
* based on the identity type, API version, and endpoint characteristics.
* Different Azure services and API versions use different parameter names for the same identity types.
*
* @param managedIdentityIdType - The type of user-assigned managed identity (client ID, object ID, or resource ID)
* @param isImds - Whether the request is being made to the IMDS (Instance Metadata Service) endpoint
* @param usesApi2017 - Whether the endpoint uses the 2017-09-01 API version (affects client ID parameter name)
*
* @returns The correct query parameter name for the specified identity type and endpoint
*
* @throws {ManagedIdentityError} When an invalid managed identity ID type is provided
*/
getManagedIdentityUserAssignedIdQueryParameterKey(managedIdentityIdType, isImds, usesApi2017) {
switch (managedIdentityIdType) {
case ManagedIdentityIdType.USER_ASSIGNED_CLIENT_ID:
this.logger.info(`[Managed Identity] [API version ${usesApi2017 ? "2017+" : "2019+"}] Adding user assigned client id to the request.`);
// The Machine Learning source uses the 2017-09-01 API version, which uses "clientid" instead of "client_id"
return usesApi2017
? ManagedIdentityUserAssignedIdQueryParameterNames.MANAGED_IDENTITY_CLIENT_ID_2017
: ManagedIdentityUserAssignedIdQueryParameterNames.MANAGED_IDENTITY_CLIENT_ID;
case ManagedIdentityIdType.USER_ASSIGNED_RESOURCE_ID:
this.logger.info("[Managed Identity] Adding user assigned resource id to the request.");
return isImds
? ManagedIdentityUserAssignedIdQueryParameterNames.MANAGED_IDENTITY_RESOURCE_ID_IMDS
: ManagedIdentityUserAssignedIdQueryParameterNames.MANAGED_IDENTITY_RESOURCE_ID_NON_IMDS;
case ManagedIdentityIdType.USER_ASSIGNED_OBJECT_ID:
this.logger.info("[Managed Identity] Adding user assigned object id to the request.");
return ManagedIdentityUserAssignedIdQueryParameterNames.MANAGED_IDENTITY_OBJECT_ID;
default:
throw createManagedIdentityError(invalidManagedIdentityIdType);
}
}
}
/**
* Validates and normalizes an environment variable containing a URL string.
* This static utility method ensures that environment variables used for managed identity
* endpoints contain properly formatted URLs and provides informative error messages when validation fails.
*
* @param envVariableStringName - The name of the environment variable being validated (for error reporting)
* @param envVariable - The environment variable value containing the URL string
* @param sourceName - The name of the managed identity source (for error reporting)
* @param logger - Logger instance for diagnostic information
*
* @returns The validated and normalized URL string
*
* @throws {ManagedIdentityError} When the environment variable contains a malformed URL
*/
BaseManagedIdentitySource.getValidatedEnvVariableUrlString = (envVariableStringName, envVariable, sourceName, logger) => {
try {
return new UrlString(envVariable).urlString;
}
catch (error) {
logger.info(`[Managed Identity] ${sourceName} managed identity is unavailable because the '${envVariableStringName}' environment variable is malformed.`);
throw createManagedIdentityError(MsiEnvironmentVariableUrlMalformedErrorCodes[envVariableStringName]);
}
};
export { BaseManagedIdentitySource, ManagedIdentityUserAssignedIdQueryParameterNames };
//# sourceMappingURL=BaseManagedIdentitySource.mjs.map

View File

@@ -0,0 +1,103 @@
/*! @azure/msal-node v3.8.1 2025-10-29 */
'use strict';
import { ManagedIdentityRequestParameters } from '../../config/ManagedIdentityRequestParameters.mjs';
import { BaseManagedIdentitySource } from './BaseManagedIdentitySource.mjs';
import { ManagedIdentityEnvironmentVariableNames, ManagedIdentitySourceNames, ManagedIdentityIdType, ManagedIdentityHeaders, ManagedIdentityQueryParameters, HttpMethod } from '../../utils/Constants.mjs';
import { createManagedIdentityError } from '../../error/ManagedIdentityError.mjs';
import { unableToCreateCloudShell } from '../../error/ManagedIdentityErrorCodes.mjs';
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
/**
* Azure Cloud Shell managed identity source implementation.
*
* This class handles authentication for applications running in Azure Cloud Shell environment.
* Cloud Shell provides a browser-accessible shell for managing Azure resources and includes
* a pre-configured managed identity for authentication.
*
* Original source of code: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity/src/CloudShellManagedIdentitySource.cs
*/
class CloudShell extends BaseManagedIdentitySource {
/**
* Creates a new CloudShell managed identity source instance.
*
* @param logger - Logger instance for diagnostic logging
* @param nodeStorage - Node.js storage implementation for caching
* @param networkClient - HTTP client for making requests to the managed identity endpoint
* @param cryptoProvider - Cryptographic operations provider
* @param disableInternalRetries - Whether to disable automatic retry logic for failed requests
* @param msiEndpoint - The MSI endpoint URL obtained from environment variables
*/
constructor(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries, msiEndpoint) {
super(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries);
this.msiEndpoint = msiEndpoint;
}
/**
* Retrieves the required environment variables for Cloud Shell managed identity.
*
* Cloud Shell requires the MSI_ENDPOINT environment variable to be set, which
* contains the URL of the managed identity service endpoint.
*
* @returns An array containing the MSI_ENDPOINT environment variable value (or undefined if not set)
*/
static getEnvironmentVariables() {
const msiEndpoint = process.env[ManagedIdentityEnvironmentVariableNames.MSI_ENDPOINT];
return [msiEndpoint];
}
/**
* Attempts to create a CloudShell managed identity source instance.
*
* This method validates that the required environment variables are present and
* creates a CloudShell instance if the environment is properly configured.
* Cloud Shell only supports system-assigned managed identities.
*
* @param logger - Logger instance for diagnostic logging
* @param nodeStorage - Node.js storage implementation for caching
* @param networkClient - HTTP client for making requests
* @param cryptoProvider - Cryptographic operations provider
* @param disableInternalRetries - Whether to disable automatic retry logic
* @param managedIdentityId - The managed identity configuration (must be system-assigned)
*
* @returns A CloudShell instance if the environment is valid, null otherwise
*
* @throws {ManagedIdentityError} When a user-assigned managed identity is requested,
* as Cloud Shell only supports system-assigned identities
*/
static tryCreate(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries, managedIdentityId) {
const [msiEndpoint] = CloudShell.getEnvironmentVariables();
// if the msi endpoint environment variable is undefined, this MSI provider is unavailable.
if (!msiEndpoint) {
logger.info(`[Managed Identity] ${ManagedIdentitySourceNames.CLOUD_SHELL} managed identity is unavailable because the '${ManagedIdentityEnvironmentVariableNames.MSI_ENDPOINT} environment variable is not defined.`);
return null;
}
const validatedMsiEndpoint = CloudShell.getValidatedEnvVariableUrlString(ManagedIdentityEnvironmentVariableNames.MSI_ENDPOINT, msiEndpoint, ManagedIdentitySourceNames.CLOUD_SHELL, logger);
logger.info(`[Managed Identity] Environment variable validation passed for ${ManagedIdentitySourceNames.CLOUD_SHELL} managed identity. Endpoint URI: ${validatedMsiEndpoint}. Creating ${ManagedIdentitySourceNames.CLOUD_SHELL} managed identity.`);
if (managedIdentityId.idType !== ManagedIdentityIdType.SYSTEM_ASSIGNED) {
throw createManagedIdentityError(unableToCreateCloudShell);
}
return new CloudShell(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries, msiEndpoint);
}
/**
* Creates an HTTP request to acquire an access token from the Cloud Shell managed identity endpoint.
*
* This method constructs a POST request to the MSI endpoint with the required headers and
* body parameters for Cloud Shell authentication. The request includes the target resource
* for which the access token is being requested.
*
* @param resource - The target resource/scope for which to request an access token (e.g., "https://graph.microsoft.com/.default")
*
* @returns A configured ManagedIdentityRequestParameters object ready for network execution
*/
createRequest(resource) {
const request = new ManagedIdentityRequestParameters(HttpMethod.POST, this.msiEndpoint);
request.headers[ManagedIdentityHeaders.METADATA_HEADER_NAME] = "true";
request.bodyParameters[ManagedIdentityQueryParameters.RESOURCE] =
resource;
return request;
}
}
export { CloudShell };
//# sourceMappingURL=CloudShell.mjs.map

View File

@@ -0,0 +1,108 @@
/*! @azure/msal-node v3.8.1 2025-10-29 */
'use strict';
import { ManagedIdentityRequestParameters } from '../../config/ManagedIdentityRequestParameters.mjs';
import { BaseManagedIdentitySource } from './BaseManagedIdentitySource.mjs';
import { ManagedIdentityEnvironmentVariableNames, ManagedIdentitySourceNames, ManagedIdentityHeaders, ManagedIdentityQueryParameters, ManagedIdentityIdType, HttpMethod } from '../../utils/Constants.mjs';
import { ImdsRetryPolicy } from '../../retry/ImdsRetryPolicy.mjs';
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
// Documentation for IMDS is available at https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token#get-a-token-using-http
const IMDS_TOKEN_PATH = "/metadata/identity/oauth2/token";
const DEFAULT_IMDS_ENDPOINT = `http://169.254.169.254${IMDS_TOKEN_PATH}`;
const IMDS_API_VERSION = "2018-02-01";
/**
* Managed Identity source implementation for Azure Instance Metadata Service (IMDS).
*
* IMDS is available on Azure Virtual Machines and Virtual Machine Scale Sets and provides
* a REST endpoint to obtain OAuth tokens for managed identities. This implementation
* handles both system-assigned and user-assigned managed identities.
*
* Original source of code: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity/src/ImdsManagedIdentitySource.cs
*/
class Imds extends BaseManagedIdentitySource {
/**
* Constructs an Imds instance with the specified configuration.
*
* @param logger - Logger instance for recording debug information and errors
* @param nodeStorage - NodeStorage instance used for token caching operations
* @param networkClient - Network client implementation for making HTTP requests to IMDS
* @param cryptoProvider - CryptoProvider for generating correlation IDs and other cryptographic operations
* @param disableInternalRetries - When true, disables the built-in retry logic for IMDS requests
* @param identityEndpoint - The complete IMDS endpoint URL including the token path
*/
constructor(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries, identityEndpoint) {
super(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries);
this.identityEndpoint = identityEndpoint;
}
/**
* Creates an Imds instance with the appropriate endpoint configuration.
*
* This method checks for the presence of the AZURE_POD_IDENTITY_AUTHORITY_HOST environment
* variable, which is used in Azure Kubernetes Service (AKS) environments with Azure AD
* Pod Identity. If found, it uses that endpoint; otherwise, it falls back to the standard
* IMDS endpoint (169.254.169.254).
*
* @param logger - Logger instance for recording endpoint discovery and validation
* @param nodeStorage - NodeStorage instance for token caching
* @param networkClient - Network client for HTTP requests
* @param cryptoProvider - CryptoProvider for cryptographic operations
* @param disableInternalRetries - Whether to disable built-in retry logic
*
* @returns A configured Imds instance ready to make token requests
*/
static tryCreate(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries) {
let validatedIdentityEndpoint;
if (process.env[ManagedIdentityEnvironmentVariableNames
.AZURE_POD_IDENTITY_AUTHORITY_HOST]) {
logger.info(`[Managed Identity] Environment variable ${ManagedIdentityEnvironmentVariableNames.AZURE_POD_IDENTITY_AUTHORITY_HOST} for ${ManagedIdentitySourceNames.IMDS} returned endpoint: ${process.env[ManagedIdentityEnvironmentVariableNames
.AZURE_POD_IDENTITY_AUTHORITY_HOST]}`);
validatedIdentityEndpoint = Imds.getValidatedEnvVariableUrlString(ManagedIdentityEnvironmentVariableNames.AZURE_POD_IDENTITY_AUTHORITY_HOST, `${process.env[ManagedIdentityEnvironmentVariableNames
.AZURE_POD_IDENTITY_AUTHORITY_HOST]}${IMDS_TOKEN_PATH}`, ManagedIdentitySourceNames.IMDS, logger);
}
else {
logger.info(`[Managed Identity] Unable to find ${ManagedIdentityEnvironmentVariableNames.AZURE_POD_IDENTITY_AUTHORITY_HOST} environment variable for ${ManagedIdentitySourceNames.IMDS}, using the default endpoint.`);
validatedIdentityEndpoint = DEFAULT_IMDS_ENDPOINT;
}
return new Imds(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries, validatedIdentityEndpoint);
}
/**
* Creates a properly configured HTTP request for acquiring an access token from IMDS.
*
* This method builds a complete request object with all necessary headers, query parameters,
* and retry policies required by the Azure Instance Metadata Service.
*
* Key request components:
* - HTTP GET method to the IMDS token endpoint
* - Metadata header set to "true" (required by IMDS)
* - API version parameter (currently "2018-02-01")
* - Resource parameter specifying the target audience
* - Identity-specific parameters for user-assigned managed identities
* - IMDS-specific retry policy
*
* @param resource - The target resource/scope for which to request an access token (e.g., "https://graph.microsoft.com/.default")
* @param managedIdentityId - The managed identity configuration specifying whether to use system-assigned or user-assigned identity
*
* @returns A configured ManagedIdentityRequestParameters object ready for network execution
*/
createRequest(resource, managedIdentityId) {
const request = new ManagedIdentityRequestParameters(HttpMethod.GET, this.identityEndpoint);
request.headers[ManagedIdentityHeaders.METADATA_HEADER_NAME] = "true";
request.queryParameters[ManagedIdentityQueryParameters.API_VERSION] =
IMDS_API_VERSION;
request.queryParameters[ManagedIdentityQueryParameters.RESOURCE] =
resource;
if (managedIdentityId.idType !== ManagedIdentityIdType.SYSTEM_ASSIGNED) {
request.queryParameters[this.getManagedIdentityUserAssignedIdQueryParameterKey(managedIdentityId.idType, true // indicates source is IMDS
)] = managedIdentityId.id;
}
// The bodyParameters are calculated in BaseManagedIdentity.acquireTokenWithManagedIdentity.
request.retryPolicy = new ImdsRetryPolicy();
return request;
}
}
export { Imds };
//# sourceMappingURL=Imds.mjs.map

View File

@@ -0,0 +1,128 @@
/*! @azure/msal-node v3.8.1 2025-10-29 */
'use strict';
import { BaseManagedIdentitySource, ManagedIdentityUserAssignedIdQueryParameterNames } from './BaseManagedIdentitySource.mjs';
import { ManagedIdentityEnvironmentVariableNames, ManagedIdentitySourceNames, ManagedIdentityHeaders, ManagedIdentityQueryParameters, ManagedIdentityIdType, HttpMethod } from '../../utils/Constants.mjs';
import { ManagedIdentityRequestParameters } from '../../config/ManagedIdentityRequestParameters.mjs';
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
const MACHINE_LEARNING_MSI_API_VERSION = "2017-09-01";
const MANAGED_IDENTITY_MACHINE_LEARNING_UNSUPPORTED_ID_TYPE_ERROR = `Only client id is supported for user-assigned managed identity in ${ManagedIdentitySourceNames.MACHINE_LEARNING}.`; // referenced in unit test
/**
* Machine Learning Managed Identity Source implementation for Azure Machine Learning environments.
*
* This class handles managed identity authentication specifically for Azure Machine Learning services.
* It supports both system-assigned and user-assigned managed identities, using the MSI_ENDPOINT
* and MSI_SECRET environment variables that are automatically provided in Azure ML environments.
*/
class MachineLearning extends BaseManagedIdentitySource {
/**
* Creates a new MachineLearning managed identity source instance.
*
* @param logger - Logger instance for diagnostic information
* @param nodeStorage - Node storage implementation for caching
* @param networkClient - Network client for making HTTP requests
* @param cryptoProvider - Cryptographic operations provider
* @param disableInternalRetries - Whether to disable automatic request retries
* @param msiEndpoint - The MSI endpoint URL from environment variables
* @param secret - The MSI secret from environment variables
*/
constructor(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries, msiEndpoint, secret) {
super(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries);
this.msiEndpoint = msiEndpoint;
this.secret = secret;
}
/**
* Retrieves the required environment variables for Azure Machine Learning managed identity.
*
* This method checks for the presence of MSI_ENDPOINT and MSI_SECRET environment variables
* that are automatically set by the Azure Machine Learning platform when managed identity
* is enabled for the compute instance or cluster.
*
* @returns An array containing [msiEndpoint, secret] where either value may be undefined
* if the corresponding environment variable is not set
*/
static getEnvironmentVariables() {
const msiEndpoint = process.env[ManagedIdentityEnvironmentVariableNames.MSI_ENDPOINT];
const secret = process.env[ManagedIdentityEnvironmentVariableNames.MSI_SECRET];
return [msiEndpoint, secret];
}
/**
* Attempts to create a MachineLearning managed identity source.
*
* This method validates the Azure Machine Learning environment by checking for the required
* MSI_ENDPOINT and MSI_SECRET environment variables. If both are present and valid,
* it creates and returns a MachineLearning instance. If either is missing or invalid,
* it returns null, indicating that this managed identity source is not available
* in the current environment.
*
* @param logger - Logger instance for diagnostic information
* @param nodeStorage - Node storage implementation for caching
* @param networkClient - Network client for making HTTP requests
* @param cryptoProvider - Cryptographic operations provider
* @param disableInternalRetries - Whether to disable automatic request retries
*
* @returns A new MachineLearning instance if the environment is valid, null otherwise
*/
static tryCreate(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries) {
const [msiEndpoint, secret] = MachineLearning.getEnvironmentVariables();
// if either of the MSI endpoint or MSI secret variables are undefined, this MSI provider is unavailable.
if (!msiEndpoint || !secret) {
logger.info(`[Managed Identity] ${ManagedIdentitySourceNames.MACHINE_LEARNING} managed identity is unavailable because one or both of the '${ManagedIdentityEnvironmentVariableNames.MSI_ENDPOINT}' and '${ManagedIdentityEnvironmentVariableNames.MSI_SECRET}' environment variables are not defined.`);
return null;
}
const validatedMsiEndpoint = MachineLearning.getValidatedEnvVariableUrlString(ManagedIdentityEnvironmentVariableNames.MSI_ENDPOINT, msiEndpoint, ManagedIdentitySourceNames.MACHINE_LEARNING, logger);
logger.info(`[Managed Identity] Environment variables validation passed for ${ManagedIdentitySourceNames.MACHINE_LEARNING} managed identity. Endpoint URI: ${validatedMsiEndpoint}. Creating ${ManagedIdentitySourceNames.MACHINE_LEARNING} managed identity.`);
return new MachineLearning(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries, msiEndpoint, secret);
}
/**
* Creates a managed identity token request for Azure Machine Learning environments.
*
* This method constructs the HTTP request parameters needed to acquire an access token
* from the Azure Machine Learning managed identity endpoint. It handles both system-assigned
* and user-assigned managed identities with specific logic for each type:
*
* - System-assigned: Uses the DEFAULT_IDENTITY_CLIENT_ID environment variable
* - User-assigned: Only supports client ID-based identification (not object ID or resource ID)
*
* The request uses the 2017-09-01 API version and includes the required secret header
* for authentication with the MSI endpoint.
*
* @param resource - The target resource/scope for which to request an access token (e.g., "https://graph.microsoft.com/.default")
* @param managedIdentityId - The managed identity configuration specifying whether to use system-assigned or user-assigned identity
*
* @returns A configured ManagedIdentityRequestParameters object ready for network execution
*
* @throws Error if an unsupported managed identity ID type is specified (only client ID is supported for user-assigned)
*/
createRequest(resource, managedIdentityId) {
const request = new ManagedIdentityRequestParameters(HttpMethod.GET, this.msiEndpoint);
request.headers[ManagedIdentityHeaders.METADATA_HEADER_NAME] = "true";
request.headers[ManagedIdentityHeaders.ML_AND_SF_SECRET_HEADER_NAME] =
this.secret;
request.queryParameters[ManagedIdentityQueryParameters.API_VERSION] =
MACHINE_LEARNING_MSI_API_VERSION;
request.queryParameters[ManagedIdentityQueryParameters.RESOURCE] =
resource;
if (managedIdentityId.idType === ManagedIdentityIdType.SYSTEM_ASSIGNED) {
request.queryParameters[ManagedIdentityUserAssignedIdQueryParameterNames.MANAGED_IDENTITY_CLIENT_ID_2017] = process.env[ManagedIdentityEnvironmentVariableNames
.DEFAULT_IDENTITY_CLIENT_ID]; // this environment variable is always set in an Azure Machine Learning source
}
else if (managedIdentityId.idType ===
ManagedIdentityIdType.USER_ASSIGNED_CLIENT_ID) {
request.queryParameters[this.getManagedIdentityUserAssignedIdQueryParameterKey(managedIdentityId.idType, false, // isIMDS
true // uses2017API
)] = managedIdentityId.id;
}
else {
throw new Error(MANAGED_IDENTITY_MACHINE_LEARNING_UNSUPPORTED_ID_TYPE_ERROR);
}
// bodyParameters calculated in BaseManagedIdentity.acquireTokenWithManagedIdentity
return request;
}
}
export { MANAGED_IDENTITY_MACHINE_LEARNING_UNSUPPORTED_ID_TYPE_ERROR, MachineLearning };
//# sourceMappingURL=MachineLearning.mjs.map

View File

@@ -0,0 +1,122 @@
/*! @azure/msal-node v3.8.1 2025-10-29 */
'use strict';
import { ManagedIdentityRequestParameters } from '../../config/ManagedIdentityRequestParameters.mjs';
import { BaseManagedIdentitySource } from './BaseManagedIdentitySource.mjs';
import { ManagedIdentityEnvironmentVariableNames, ManagedIdentitySourceNames, ManagedIdentityIdType, ManagedIdentityHeaders, ManagedIdentityQueryParameters, HttpMethod } from '../../utils/Constants.mjs';
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
const SERVICE_FABRIC_MSI_API_VERSION = "2019-07-01-preview";
/**
* Original source of code: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity/src/ServiceFabricManagedIdentitySource.cs
*/
class ServiceFabric extends BaseManagedIdentitySource {
/**
* Constructs a new ServiceFabric managed identity source for acquiring tokens from Azure Service Fabric clusters.
*
* Service Fabric managed identity allows applications running in Service Fabric clusters to authenticate
* without storing credentials in code. This source handles token acquisition using the Service Fabric
* Managed Identity Token Service (MITS).
*
* @param logger - Logger instance for logging authentication events and debugging information
* @param nodeStorage - NodeStorage instance for caching tokens and other authentication artifacts
* @param networkClient - Network client for making HTTP requests to the Service Fabric identity endpoint
* @param cryptoProvider - Crypto provider for cryptographic operations like token validation
* @param disableInternalRetries - Whether to disable internal retry logic for failed requests
* @param identityEndpoint - The Service Fabric managed identity endpoint URL
* @param identityHeader - The Service Fabric managed identity secret header value
*/
constructor(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries, identityEndpoint, identityHeader) {
super(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries);
this.identityEndpoint = identityEndpoint;
this.identityHeader = identityHeader;
}
/**
* Retrieves the environment variables required for Service Fabric managed identity authentication.
*
* Service Fabric managed identity requires three specific environment variables to be set by the
* Service Fabric runtime:
* - IDENTITY_ENDPOINT: The endpoint URL for the Managed Identity Token Service (MITS)
* - IDENTITY_HEADER: A secret value used for authentication with the MITS
* - IDENTITY_SERVER_THUMBPRINT: The thumbprint of the MITS server certificate for secure communication
*
* @returns An array containing the identity endpoint, identity header, and identity server thumbprint values.
* Elements will be undefined if the corresponding environment variables are not set.
*/
static getEnvironmentVariables() {
const identityEndpoint = process.env[ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT];
const identityHeader = process.env[ManagedIdentityEnvironmentVariableNames.IDENTITY_HEADER];
const identityServerThumbprint = process.env[ManagedIdentityEnvironmentVariableNames
.IDENTITY_SERVER_THUMBPRINT];
return [identityEndpoint, identityHeader, identityServerThumbprint];
}
/**
* Attempts to create a ServiceFabric managed identity source if the runtime environment supports it.
*
* Checks for the presence of all required Service Fabric environment variables
* and validates the endpoint URL format. It will only create a ServiceFabric instance if the application
* is running in a properly configured Service Fabric cluster with managed identity enabled.
*
* Note: User-assigned managed identities must be configured at the cluster level, not at runtime.
* This method will log a warning if a user-assigned identity is requested.
*
* @param logger - Logger instance for logging creation events and validation results
* @param nodeStorage - NodeStorage instance for caching tokens and authentication artifacts
* @param networkClient - Network client for making HTTP requests to the identity endpoint
* @param cryptoProvider - Crypto provider for cryptographic operations
* @param disableInternalRetries - Whether to disable internal retry logic for failed requests
* @param managedIdentityId - Managed identity identifier specifying system-assigned or user-assigned identity
*
* @returns A ServiceFabric instance if all environment variables are valid and present, otherwise null
*/
static tryCreate(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries, managedIdentityId) {
const [identityEndpoint, identityHeader, identityServerThumbprint] = ServiceFabric.getEnvironmentVariables();
if (!identityEndpoint || !identityHeader || !identityServerThumbprint) {
logger.info(`[Managed Identity] ${ManagedIdentitySourceNames.SERVICE_FABRIC} managed identity is unavailable because one or all of the '${ManagedIdentityEnvironmentVariableNames.IDENTITY_HEADER}', '${ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT}' or '${ManagedIdentityEnvironmentVariableNames.IDENTITY_SERVER_THUMBPRINT}' environment variables are not defined.`);
return null;
}
const validatedIdentityEndpoint = ServiceFabric.getValidatedEnvVariableUrlString(ManagedIdentityEnvironmentVariableNames.IDENTITY_ENDPOINT, identityEndpoint, ManagedIdentitySourceNames.SERVICE_FABRIC, logger);
logger.info(`[Managed Identity] Environment variables validation passed for ${ManagedIdentitySourceNames.SERVICE_FABRIC} managed identity. Endpoint URI: ${validatedIdentityEndpoint}. Creating ${ManagedIdentitySourceNames.SERVICE_FABRIC} managed identity.`);
if (managedIdentityId.idType !== ManagedIdentityIdType.SYSTEM_ASSIGNED) {
logger.warning(`[Managed Identity] ${ManagedIdentitySourceNames.SERVICE_FABRIC} user assigned managed identity is configured in the cluster, not during runtime. See also: https://learn.microsoft.com/en-us/azure/service-fabric/configure-existing-cluster-enable-managed-identity-token-service.`);
}
return new ServiceFabric(logger, nodeStorage, networkClient, cryptoProvider, disableInternalRetries, identityEndpoint, identityHeader);
}
/**
* Creates HTTP request parameters for acquiring an access token from the Service Fabric Managed Identity Token Service (MITS).
*
* This method constructs a properly formatted HTTP GET request that includes:
* - The secret header for authentication with MITS
* - API version parameter for the Service Fabric MSI endpoint
* - Resource parameter specifying the target Azure service
* - Optional identity parameters for user-assigned managed identities
*
* The request follows the Service Fabric managed identity protocol and uses the 2019-07-01-preview API version.
* For user-assigned identities, the appropriate query parameter (client_id, object_id, or resource_id) is added
* based on the identity type.
*
* @param resource - The Azure resource URI for which the access token is requested (e.g., "https://vault.azure.net/")
* @param managedIdentityId - The managed identity configuration specifying system-assigned or user-assigned identity details
*
* @returns A configured ManagedIdentityRequestParameters object ready for network execution
*/
createRequest(resource, managedIdentityId) {
const request = new ManagedIdentityRequestParameters(HttpMethod.GET, this.identityEndpoint);
request.headers[ManagedIdentityHeaders.ML_AND_SF_SECRET_HEADER_NAME] =
this.identityHeader;
request.queryParameters[ManagedIdentityQueryParameters.API_VERSION] =
SERVICE_FABRIC_MSI_API_VERSION;
request.queryParameters[ManagedIdentityQueryParameters.RESOURCE] =
resource;
if (managedIdentityId.idType !== ManagedIdentityIdType.SYSTEM_ASSIGNED) {
request.queryParameters[this.getManagedIdentityUserAssignedIdQueryParameterKey(managedIdentityId.idType)] = managedIdentityId.id;
}
// bodyParameters calculated in BaseManagedIdentity.acquireTokenWithManagedIdentity
return request;
}
}
export { ServiceFabric };
//# sourceMappingURL=ServiceFabric.mjs.map

View File

@@ -0,0 +1,210 @@
/*! @azure/msal-node v3.8.1 2025-10-29 */
'use strict';
import { BaseClient, ScopeSet, CacheOutcome, createClientAuthError, ClientAuthErrorCodes, TimeUtils, AuthToken, Constants, ResponseHandler, CredentialType, AuthenticationScheme, UrlString, RequestParameterBuilder, GrantType, AADServerParamKeys, getClientAssertion, UrlUtils } from '@azure/msal-common/node';
import { EncodingUtils } from '../utils/EncodingUtils.mjs';
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
/**
* On-Behalf-Of client
* @public
*/
class OnBehalfOfClient extends BaseClient {
constructor(configuration) {
super(configuration);
}
/**
* Public API to acquire tokens with on behalf of flow
* @param request - developer provided CommonOnBehalfOfRequest
*/
async acquireToken(request) {
this.scopeSet = new ScopeSet(request.scopes || []);
// generate the user_assertion_hash for OBOAssertion
this.userAssertionHash = await this.cryptoUtils.hashString(request.oboAssertion);
if (request.skipCache || request.claims) {
return this.executeTokenRequest(request, this.authority, this.userAssertionHash);
}
try {
return await this.getCachedAuthenticationResult(request);
}
catch (e) {
// Any failure falls back to interactive request, once we implement distributed cache, we plan to handle `createRefreshRequiredError` to refresh using the RT
return await this.executeTokenRequest(request, this.authority, this.userAssertionHash);
}
}
/**
* look up cache for tokens
* Find idtoken in the cache
* Find accessToken based on user assertion and account info in the cache
* Please note we are not yet supported OBO tokens refreshed with long lived RT. User will have to send a new assertion if the current access token expires
* This is to prevent security issues when the assertion changes over time, however, longlived RT helps retaining the session
* @param request - developer provided CommonOnBehalfOfRequest
*/
async getCachedAuthenticationResult(request) {
// look in the cache for the access_token which matches the incoming_assertion
const cachedAccessToken = this.readAccessTokenFromCacheForOBO(this.config.authOptions.clientId, request);
if (!cachedAccessToken) {
// Must refresh due to non-existent access_token.
this.serverTelemetryManager?.setCacheOutcome(CacheOutcome.NO_CACHED_ACCESS_TOKEN);
this.logger.info("SilentFlowClient:acquireCachedToken - No access token found in cache for the given properties.");
throw createClientAuthError(ClientAuthErrorCodes.tokenRefreshRequired);
}
else if (TimeUtils.isTokenExpired(cachedAccessToken.expiresOn, this.config.systemOptions.tokenRenewalOffsetSeconds)) {
// Access token expired, will need to renewed
this.serverTelemetryManager?.setCacheOutcome(CacheOutcome.CACHED_ACCESS_TOKEN_EXPIRED);
this.logger.info(`OnbehalfofFlow:getCachedAuthenticationResult - Cached access token is expired or will expire within ${this.config.systemOptions.tokenRenewalOffsetSeconds} seconds.`);
throw createClientAuthError(ClientAuthErrorCodes.tokenRefreshRequired);
}
// fetch the idToken from cache
const cachedIdToken = this.readIdTokenFromCacheForOBO(cachedAccessToken.homeAccountId, request.correlationId);
let idTokenClaims;
let cachedAccount = null;
if (cachedIdToken) {
idTokenClaims = AuthToken.extractTokenClaims(cachedIdToken.secret, EncodingUtils.base64Decode);
const localAccountId = idTokenClaims.oid || idTokenClaims.sub;
const accountInfo = {
homeAccountId: cachedIdToken.homeAccountId,
environment: cachedIdToken.environment,
tenantId: cachedIdToken.realm,
username: Constants.EMPTY_STRING,
localAccountId: localAccountId || Constants.EMPTY_STRING,
};
cachedAccount = this.cacheManager.getAccount(this.cacheManager.generateAccountKey(accountInfo), request.correlationId);
}
// increment telemetry cache hit counter
if (this.config.serverTelemetryManager) {
this.config.serverTelemetryManager.incrementCacheHits();
}
return ResponseHandler.generateAuthenticationResult(this.cryptoUtils, this.authority, {
account: cachedAccount,
accessToken: cachedAccessToken,
idToken: cachedIdToken,
refreshToken: null,
appMetadata: null,
}, true, request, idTokenClaims);
}
/**
* read idtoken from cache, this is a specific implementation for OBO as the requirements differ from a generic lookup in the cacheManager
* Certain use cases of OBO flow do not expect an idToken in the cache/or from the service
* @param atHomeAccountId - account id
*/
readIdTokenFromCacheForOBO(atHomeAccountId, correlationId) {
const idTokenFilter = {
homeAccountId: atHomeAccountId,
environment: this.authority.canonicalAuthorityUrlComponents.HostNameAndPort,
credentialType: CredentialType.ID_TOKEN,
clientId: this.config.authOptions.clientId,
realm: this.authority.tenant,
};
const idTokenMap = this.cacheManager.getIdTokensByFilter(idTokenFilter, correlationId);
// When acquiring a token on behalf of an application, there might not be an id token in the cache
if (Object.values(idTokenMap).length < 1) {
return null;
}
return Object.values(idTokenMap)[0];
}
/**
* Fetches the cached access token based on incoming assertion
* @param clientId - client id
* @param request - developer provided CommonOnBehalfOfRequest
*/
readAccessTokenFromCacheForOBO(clientId, request) {
const authScheme = request.authenticationScheme || AuthenticationScheme.BEARER;
/*
* Distinguish between Bearer and PoP/SSH token cache types
* Cast to lowercase to handle "bearer" from ADFS
*/
const credentialType = authScheme &&
authScheme.toLowerCase() !==
AuthenticationScheme.BEARER.toLowerCase()
? CredentialType.ACCESS_TOKEN_WITH_AUTH_SCHEME
: CredentialType.ACCESS_TOKEN;
const accessTokenFilter = {
credentialType: credentialType,
clientId,
target: ScopeSet.createSearchScopes(this.scopeSet.asArray()),
tokenType: authScheme,
keyId: request.sshKid,
requestedClaimsHash: request.requestedClaimsHash,
userAssertionHash: this.userAssertionHash,
};
const accessTokens = this.cacheManager.getAccessTokensByFilter(accessTokenFilter, request.correlationId);
const numAccessTokens = accessTokens.length;
if (numAccessTokens < 1) {
return null;
}
else if (numAccessTokens > 1) {
throw createClientAuthError(ClientAuthErrorCodes.multipleMatchingTokens);
}
return accessTokens[0];
}
/**
* Make a network call to the server requesting credentials
* @param request - developer provided CommonOnBehalfOfRequest
* @param authority - authority object
*/
async executeTokenRequest(request, authority, userAssertionHash) {
const queryParametersString = this.createTokenQueryParameters(request);
const endpoint = UrlString.appendQueryString(authority.tokenEndpoint, queryParametersString);
const requestBody = await this.createTokenRequestBody(request);
const headers = this.createTokenRequestHeaders();
const thumbprint = {
clientId: this.config.authOptions.clientId,
authority: request.authority,
scopes: request.scopes,
claims: request.claims,
authenticationScheme: request.authenticationScheme,
resourceRequestMethod: request.resourceRequestMethod,
resourceRequestUri: request.resourceRequestUri,
shrClaims: request.shrClaims,
sshKid: request.sshKid,
};
const reqTimestamp = TimeUtils.nowSeconds();
const response = await this.executePostToTokenEndpoint(endpoint, requestBody, headers, thumbprint, request.correlationId);
const responseHandler = new ResponseHandler(this.config.authOptions.clientId, this.cacheManager, this.cryptoUtils, this.logger, this.config.serializableCache, this.config.persistencePlugin);
responseHandler.validateTokenResponse(response.body);
const tokenResponse = await responseHandler.handleServerTokenResponse(response.body, this.authority, reqTimestamp, request, undefined, userAssertionHash);
return tokenResponse;
}
/**
* generate a server request in accepable format
* @param request - developer provided CommonOnBehalfOfRequest
*/
async createTokenRequestBody(request) {
const parameters = new Map();
RequestParameterBuilder.addClientId(parameters, this.config.authOptions.clientId);
RequestParameterBuilder.addScopes(parameters, request.scopes);
RequestParameterBuilder.addGrantType(parameters, GrantType.JWT_BEARER);
RequestParameterBuilder.addClientInfo(parameters);
RequestParameterBuilder.addLibraryInfo(parameters, this.config.libraryInfo);
RequestParameterBuilder.addApplicationTelemetry(parameters, this.config.telemetry.application);
RequestParameterBuilder.addThrottling(parameters);
if (this.serverTelemetryManager) {
RequestParameterBuilder.addServerTelemetry(parameters, this.serverTelemetryManager);
}
const correlationId = request.correlationId ||
this.config.cryptoInterface.createNewGuid();
RequestParameterBuilder.addCorrelationId(parameters, correlationId);
RequestParameterBuilder.addRequestTokenUse(parameters, AADServerParamKeys.ON_BEHALF_OF);
RequestParameterBuilder.addOboAssertion(parameters, request.oboAssertion);
if (this.config.clientCredentials.clientSecret) {
RequestParameterBuilder.addClientSecret(parameters, this.config.clientCredentials.clientSecret);
}
const clientAssertion = this.config.clientCredentials.clientAssertion;
if (clientAssertion) {
RequestParameterBuilder.addClientAssertion(parameters, await getClientAssertion(clientAssertion.assertion, this.config.authOptions.clientId, request.resourceRequestUri));
RequestParameterBuilder.addClientAssertionType(parameters, clientAssertion.assertionType);
}
if (request.claims ||
(this.config.authOptions.clientCapabilities &&
this.config.authOptions.clientCapabilities.length > 0)) {
RequestParameterBuilder.addClaims(parameters, request.claims, this.config.authOptions.clientCapabilities);
}
return UrlUtils.mapToQueryString(parameters);
}
}
export { OnBehalfOfClient };
//# sourceMappingURL=OnBehalfOfClient.mjs.map

View File

@@ -0,0 +1,266 @@
/*! @azure/msal-node v3.8.1 2025-10-29 */
'use strict';
import { Constants, ApiId, LOOPBACK_SERVER_CONSTANTS } from '../utils/Constants.mjs';
import { ServerTelemetryManager, AuthError, OIDC_DEFAULT_SCOPES, CodeChallengeMethodValues, ResponseMode, ServerError, Constants as Constants$1, AADServerParamKeys } from '@azure/msal-common/node';
import { ClientApplication } from './ClientApplication.mjs';
import { NodeAuthError, NodeAuthErrorMessage } from '../error/NodeAuthError.mjs';
import { LoopbackClient } from '../network/LoopbackClient.mjs';
import { DeviceCodeClient } from './DeviceCodeClient.mjs';
import { version } from '../packageMetadata.mjs';
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
/**
* This class is to be used to acquire tokens for public client applications (desktop, mobile). Public client applications
* are not trusted to safely store application secrets, and therefore can only request tokens in the name of an user.
* @public
*/
class PublicClientApplication extends ClientApplication {
/**
* Important attributes in the Configuration object for auth are:
* - clientID: the application ID of your application. You can obtain one by registering your application with our Application registration portal.
* - authority: the authority URL for your application.
*
* AAD authorities are of the form https://login.microsoftonline.com/\{Enter_the_Tenant_Info_Here\}.
* - If your application supports Accounts in one organizational directory, replace "Enter_the_Tenant_Info_Here" value with the Tenant Id or Tenant name (for example, contoso.microsoft.com).
* - If your application supports Accounts in any organizational directory, replace "Enter_the_Tenant_Info_Here" value with organizations.
* - If your application supports Accounts in any organizational directory and personal Microsoft accounts, replace "Enter_the_Tenant_Info_Here" value with common.
* - To restrict support to Personal Microsoft accounts only, replace "Enter_the_Tenant_Info_Here" value with consumers.
*
* Azure B2C authorities are of the form https://\{instance\}/\{tenant\}/\{policy\}. Each policy is considered
* its own authority. You will have to set the all of the knownAuthorities at the time of the client application
* construction.
*
* ADFS authorities are of the form https://\{instance\}/adfs.
*/
constructor(configuration) {
super(configuration);
if (this.config.broker.nativeBrokerPlugin) {
if (this.config.broker.nativeBrokerPlugin.isBrokerAvailable) {
this.nativeBrokerPlugin = this.config.broker.nativeBrokerPlugin;
this.nativeBrokerPlugin.setLogger(this.config.system.loggerOptions);
}
else {
this.logger.warning("NativeBroker implementation was provided but the broker is unavailable.");
}
}
this.skus = ServerTelemetryManager.makeExtraSkuString({
libraryName: Constants.MSAL_SKU,
libraryVersion: version,
});
}
/**
* Acquires a token from the authority using OAuth2.0 device code flow.
* This flow is designed for devices that do not have access to a browser or have input constraints.
* The authorization server issues a DeviceCode object with a verification code, an end-user code,
* and the end-user verification URI. The DeviceCode object is provided through a callback, and the end-user should be
* instructed to use another device to navigate to the verification URI to input credentials.
* Since the client cannot receive incoming requests, it polls the authorization server repeatedly
* until the end-user completes input of credentials.
*/
async acquireTokenByDeviceCode(request) {
this.logger.info("acquireTokenByDeviceCode called", request.correlationId);
const validRequest = Object.assign(request, await this.initializeBaseRequest(request));
const serverTelemetryManager = this.initializeServerTelemetryManager(ApiId.acquireTokenByDeviceCode, validRequest.correlationId);
try {
const discoveredAuthority = await this.createAuthority(validRequest.authority, validRequest.correlationId, undefined, request.azureCloudOptions);
const deviceCodeConfig = await this.buildOauthClientConfiguration(discoveredAuthority, validRequest.correlationId, "", serverTelemetryManager);
const deviceCodeClient = new DeviceCodeClient(deviceCodeConfig);
this.logger.verbose("Device code client created", validRequest.correlationId);
return await deviceCodeClient.acquireToken(validRequest);
}
catch (e) {
if (e instanceof AuthError) {
e.setCorrelationId(validRequest.correlationId);
}
serverTelemetryManager.cacheFailedRequest(e);
throw e;
}
}
/**
* Acquires a token interactively via the browser by requesting an authorization code then exchanging it for a token.
*/
async acquireTokenInteractive(request) {
const correlationId = request.correlationId || this.cryptoProvider.createNewGuid();
this.logger.trace("acquireTokenInteractive called", correlationId);
const { openBrowser, successTemplate, errorTemplate, windowHandle, loopbackClient: customLoopbackClient, ...remainingProperties } = request;
if (this.nativeBrokerPlugin) {
const brokerRequest = {
...remainingProperties,
clientId: this.config.auth.clientId,
scopes: request.scopes || OIDC_DEFAULT_SCOPES,
redirectUri: request.redirectUri || "",
authority: request.authority || this.config.auth.authority,
correlationId: correlationId,
extraParameters: {
...remainingProperties.extraQueryParameters,
...remainingProperties.tokenQueryParameters,
[AADServerParamKeys.X_CLIENT_EXTRA_SKU]: this.skus,
},
accountId: remainingProperties.account?.nativeAccountId,
};
return this.nativeBrokerPlugin.acquireTokenInteractive(brokerRequest, windowHandle);
}
if (request.redirectUri) {
// If its not a broker fallback scenario, we throw a error
if (!this.config.broker.nativeBrokerPlugin) {
throw NodeAuthError.createRedirectUriNotSupportedError();
}
// If a redirect URI is provided for a broker flow but MSAL runtime startup failed, we fall back to the browser flow and will ignore the redirect URI provided for the broker flow
request.redirectUri = "";
}
const { verifier, challenge } = await this.cryptoProvider.generatePkceCodes();
const loopbackClient = customLoopbackClient || new LoopbackClient();
let authCodeResponse = {};
let authCodeListenerError = null;
try {
const authCodeListener = loopbackClient
.listenForAuthCode(successTemplate, errorTemplate)
.then((response) => {
authCodeResponse = response;
})
.catch((e) => {
// Store the promise instead of throwing so we can control when its thrown
authCodeListenerError = e;
});
// Wait for server to be listening
const redirectUri = await this.waitForRedirectUri(loopbackClient);
const validRequest = {
...remainingProperties,
correlationId: correlationId,
scopes: request.scopes || OIDC_DEFAULT_SCOPES,
redirectUri: redirectUri,
responseMode: ResponseMode.QUERY,
codeChallenge: challenge,
codeChallengeMethod: CodeChallengeMethodValues.S256,
};
const authCodeUrl = await this.getAuthCodeUrl(validRequest);
await openBrowser(authCodeUrl);
await authCodeListener;
if (authCodeListenerError) {
throw authCodeListenerError;
}
if (authCodeResponse.error) {
throw new ServerError(authCodeResponse.error, authCodeResponse.error_description, authCodeResponse.suberror);
}
else if (!authCodeResponse.code) {
throw NodeAuthError.createNoAuthCodeInResponseError();
}
const clientInfo = authCodeResponse.client_info;
const tokenRequest = {
code: authCodeResponse.code,
codeVerifier: verifier,
clientInfo: clientInfo || Constants$1.EMPTY_STRING,
...validRequest,
};
return await this.acquireTokenByCode(tokenRequest); // Await this so the server doesn't close prematurely
}
finally {
loopbackClient.closeServer();
}
}
/**
* Returns a token retrieved either from the cache or by exchanging the refresh token for a fresh access token. If brokering is enabled the token request will be serviced by the broker.
* @param request - developer provided SilentFlowRequest
* @returns
*/
async acquireTokenSilent(request) {
const correlationId = request.correlationId || this.cryptoProvider.createNewGuid();
this.logger.trace("acquireTokenSilent called", correlationId);
if (this.nativeBrokerPlugin) {
const brokerRequest = {
...request,
clientId: this.config.auth.clientId,
scopes: request.scopes || OIDC_DEFAULT_SCOPES,
redirectUri: request.redirectUri || "",
authority: request.authority || this.config.auth.authority,
correlationId: correlationId,
extraParameters: {
...request.tokenQueryParameters,
[AADServerParamKeys.X_CLIENT_EXTRA_SKU]: this.skus,
},
accountId: request.account.nativeAccountId,
forceRefresh: request.forceRefresh || false,
};
return this.nativeBrokerPlugin.acquireTokenSilent(brokerRequest);
}
if (request.redirectUri) {
// If its not a broker fallback scenario, we throw a error
if (!this.config.broker.nativeBrokerPlugin) {
throw NodeAuthError.createRedirectUriNotSupportedError();
}
request.redirectUri = "";
}
return super.acquireTokenSilent(request);
}
/**
* Removes cache artifacts associated with the given account
* @param request - developer provided SignOutRequest
* @returns
*/
async signOut(request) {
if (this.nativeBrokerPlugin && request.account.nativeAccountId) {
const signoutRequest = {
clientId: this.config.auth.clientId,
accountId: request.account.nativeAccountId,
correlationId: request.correlationId ||
this.cryptoProvider.createNewGuid(),
};
await this.nativeBrokerPlugin.signOut(signoutRequest);
}
await this.getTokenCache().removeAccount(request.account, request.correlationId);
}
/**
* Returns all cached accounts for this application. If brokering is enabled this request will be serviced by the broker.
* @returns
*/
async getAllAccounts() {
if (this.nativeBrokerPlugin) {
const correlationId = this.cryptoProvider.createNewGuid();
return this.nativeBrokerPlugin.getAllAccounts(this.config.auth.clientId, correlationId);
}
return this.getTokenCache().getAllAccounts();
}
/**
* Attempts to retrieve the redirectUri from the loopback server. If the loopback server does not start listening for requests within the timeout this will throw.
* @param loopbackClient - developer provided custom loopback server implementation
* @returns
*/
async waitForRedirectUri(loopbackClient) {
return new Promise((resolve, reject) => {
let ticks = 0;
const id = setInterval(() => {
if (LOOPBACK_SERVER_CONSTANTS.TIMEOUT_MS /
LOOPBACK_SERVER_CONSTANTS.INTERVAL_MS <
ticks) {
clearInterval(id);
reject(NodeAuthError.createLoopbackServerTimeoutError());
return;
}
try {
const r = loopbackClient.getRedirectUri();
clearInterval(id);
resolve(r);
return;
}
catch (e) {
if (e instanceof AuthError &&
e.errorCode ===
NodeAuthErrorMessage.noLoopbackServerExists.code) {
// Loopback server is not listening yet
ticks++;
return;
}
clearInterval(id);
reject(e);
return;
}
}, LOOPBACK_SERVER_CONSTANTS.INTERVAL_MS);
});
}
}
export { PublicClientApplication };
//# sourceMappingURL=PublicClientApplication.mjs.map

View File

@@ -0,0 +1,104 @@
/*! @azure/msal-node v3.8.1 2025-10-29 */
'use strict';
import { BaseClient, TimeUtils, ResponseHandler, UrlString, CcsCredentialType, RequestParameterBuilder, OAuthResponseType, GrantType, getClientAssertion, StringUtils, UrlUtils } from '@azure/msal-common/node';
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
/**
* Oauth2.0 Password grant client
* Note: We are only supporting public clients for password grant and for purely testing purposes
* @public
* @deprecated - Use a more secure flow instead
*/
class UsernamePasswordClient extends BaseClient {
constructor(configuration) {
super(configuration);
}
/**
* API to acquire a token by passing the username and password to the service in exchage of credentials
* password_grant
* @param request - CommonUsernamePasswordRequest
*/
async acquireToken(request) {
this.logger.info("in acquireToken call in username-password client");
const reqTimestamp = TimeUtils.nowSeconds();
const response = await this.executeTokenRequest(this.authority, request);
const responseHandler = new ResponseHandler(this.config.authOptions.clientId, this.cacheManager, this.cryptoUtils, this.logger, this.config.serializableCache, this.config.persistencePlugin);
// Validate response. This function throws a server error if an error is returned by the server.
responseHandler.validateTokenResponse(response.body);
const tokenResponse = responseHandler.handleServerTokenResponse(response.body, this.authority, reqTimestamp, request);
return tokenResponse;
}
/**
* Executes POST request to token endpoint
* @param authority - authority object
* @param request - CommonUsernamePasswordRequest provided by the developer
*/
async executeTokenRequest(authority, request) {
const queryParametersString = this.createTokenQueryParameters(request);
const endpoint = UrlString.appendQueryString(authority.tokenEndpoint, queryParametersString);
const requestBody = await this.createTokenRequestBody(request);
const headers = this.createTokenRequestHeaders({
credential: request.username,
type: CcsCredentialType.UPN,
});
const thumbprint = {
clientId: this.config.authOptions.clientId,
authority: authority.canonicalAuthority,
scopes: request.scopes,
claims: request.claims,
authenticationScheme: request.authenticationScheme,
resourceRequestMethod: request.resourceRequestMethod,
resourceRequestUri: request.resourceRequestUri,
shrClaims: request.shrClaims,
sshKid: request.sshKid,
};
return this.executePostToTokenEndpoint(endpoint, requestBody, headers, thumbprint, request.correlationId);
}
/**
* Generates a map for all the params to be sent to the service
* @param request - CommonUsernamePasswordRequest provided by the developer
*/
async createTokenRequestBody(request) {
const parameters = new Map();
RequestParameterBuilder.addClientId(parameters, this.config.authOptions.clientId);
RequestParameterBuilder.addUsername(parameters, request.username);
RequestParameterBuilder.addPassword(parameters, request.password);
RequestParameterBuilder.addScopes(parameters, request.scopes);
RequestParameterBuilder.addResponseType(parameters, OAuthResponseType.IDTOKEN_TOKEN);
RequestParameterBuilder.addGrantType(parameters, GrantType.RESOURCE_OWNER_PASSWORD_GRANT);
RequestParameterBuilder.addClientInfo(parameters);
RequestParameterBuilder.addLibraryInfo(parameters, this.config.libraryInfo);
RequestParameterBuilder.addApplicationTelemetry(parameters, this.config.telemetry.application);
RequestParameterBuilder.addThrottling(parameters);
if (this.serverTelemetryManager) {
RequestParameterBuilder.addServerTelemetry(parameters, this.serverTelemetryManager);
}
const correlationId = request.correlationId ||
this.config.cryptoInterface.createNewGuid();
RequestParameterBuilder.addCorrelationId(parameters, correlationId);
if (this.config.clientCredentials.clientSecret) {
RequestParameterBuilder.addClientSecret(parameters, this.config.clientCredentials.clientSecret);
}
const clientAssertion = this.config.clientCredentials.clientAssertion;
if (clientAssertion) {
RequestParameterBuilder.addClientAssertion(parameters, await getClientAssertion(clientAssertion.assertion, this.config.authOptions.clientId, request.resourceRequestUri));
RequestParameterBuilder.addClientAssertionType(parameters, clientAssertion.assertionType);
}
if (!StringUtils.isEmptyObj(request.claims) ||
(this.config.authOptions.clientCapabilities &&
this.config.authOptions.clientCapabilities.length > 0)) {
RequestParameterBuilder.addClaims(parameters, request.claims, this.config.authOptions.clientCapabilities);
}
if (this.config.systemOptions.preventCorsPreflight &&
request.username) {
RequestParameterBuilder.addCcsUpn(parameters, request.username);
}
return UrlUtils.mapToQueryString(parameters);
}
}
export { UsernamePasswordClient };
//# sourceMappingURL=UsernamePasswordClient.mjs.map