Estructura inicial del proyecto

This commit is contained in:
2026-06-02 16:57:08 +00:00
commit 8b306b9afc
9864 changed files with 1435687 additions and 0 deletions

View File

@ -0,0 +1,419 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { CommonAuthorizationUrlRequest } from "../request/CommonAuthorizationUrlRequest.js";
import * as RequestParameterBuilder from "../request/RequestParameterBuilder.js";
import { IPerformanceClient } from "../telemetry/performance/IPerformanceClient.js";
import * as AADServerParamKeys from "../constants/AADServerParamKeys.js";
import { AuthOptions } from "../config/ClientConfiguration.js";
import { PromptValue } from "../utils/Constants.js";
import { AccountInfo } from "../account/AccountInfo.js";
import { Logger } from "../logger/Logger.js";
import { buildClientInfoFromHomeAccountId } from "../account/ClientInfo.js";
import { Authority } from "../authority/Authority.js";
import { mapToQueryString } from "../utils/UrlUtils.js";
import { UrlString } from "../url/UrlString.js";
import { AuthorizationCodePayload } from "../response/AuthorizationCodePayload.js";
import { AuthorizeResponse } from "../response/AuthorizeResponse.js";
import {
ClientAuthErrorCodes,
createClientAuthError,
} from "../error/ClientAuthError.js";
import {
InteractionRequiredAuthError,
isInteractionRequiredError,
} from "../error/InteractionRequiredAuthError.js";
import { ServerError } from "../error/ServerError.js";
/**
* Returns map of parameters that are applicable to all calls to /authorize whether using PKCE or EAR
* @param config
* @param request
* @param logger
* @param performanceClient
* @returns
*/
export function getStandardAuthorizeRequestParameters(
authOptions: AuthOptions,
request: CommonAuthorizationUrlRequest,
logger: Logger,
performanceClient?: IPerformanceClient
): Map<string, string> {
// generate the correlationId if not set by the user and add
const correlationId = request.correlationId;
const parameters = new Map<string, string>();
RequestParameterBuilder.addClientId(
parameters,
request.embeddedClientId ||
request.extraQueryParameters?.[AADServerParamKeys.CLIENT_ID] ||
authOptions.clientId
);
const requestScopes = [
...(request.scopes || []),
...(request.extraScopesToConsent || []),
];
RequestParameterBuilder.addScopes(
parameters,
requestScopes,
true,
authOptions.authority.options.OIDCOptions?.defaultScopes
);
RequestParameterBuilder.addResource(parameters, request.resource);
RequestParameterBuilder.addRedirectUri(parameters, request.redirectUri);
RequestParameterBuilder.addCorrelationId(parameters, correlationId);
// add response_mode. If not passed in it defaults to query.
RequestParameterBuilder.addResponseMode(parameters, request.responseMode);
// add client_info=1
RequestParameterBuilder.addClientInfo(parameters);
// add clidata=1
RequestParameterBuilder.addCliData(parameters);
if (request.prompt) {
RequestParameterBuilder.addPrompt(parameters, request.prompt);
performanceClient?.addFields({ prompt: request.prompt }, correlationId);
}
if (request.domainHint) {
RequestParameterBuilder.addDomainHint(parameters, request.domainHint);
performanceClient?.addFields(
{ domainHintFromRequest: true },
correlationId
);
}
// Add sid or loginHint with preference for login_hint claim (in request) -> sid -> loginHint (upn/email) -> username of AccountInfo object
if (request.prompt !== PromptValue.SELECT_ACCOUNT) {
// AAD will throw if prompt=select_account is passed with an account hint
if (request.sid && request.prompt === PromptValue.NONE) {
// SessionID is only used in silent calls
logger.verbose(
"createAuthCodeUrlQueryString: Prompt is none, adding sid from request",
request.correlationId
);
RequestParameterBuilder.addSid(parameters, request.sid);
performanceClient?.addFields(
{ sidFromRequest: true },
correlationId
);
} else if (request.account) {
const accountSid = extractAccountSid(request.account);
let accountLoginHintClaim = extractLoginHint(request.account);
if (accountLoginHintClaim && request.domainHint) {
logger.warning(
`AuthorizationCodeClient.createAuthCodeUrlQueryString: "domainHint" param is set, skipping opaque "login_hint" claim. Please consider not passing domainHint`,
request.correlationId
);
accountLoginHintClaim = null;
}
// If login_hint claim is present, use it over sid/username
if (accountLoginHintClaim) {
logger.verbose(
"createAuthCodeUrlQueryString: login_hint claim present on account",
request.correlationId
);
RequestParameterBuilder.addLoginHint(
parameters,
accountLoginHintClaim
);
performanceClient?.addFields(
{ loginHintFromClaim: true },
correlationId
);
try {
const clientInfo = buildClientInfoFromHomeAccountId(
request.account.homeAccountId
);
RequestParameterBuilder.addCcsOid(parameters, clientInfo);
} catch (e) {
logger.verbose(
"createAuthCodeUrlQueryString: Could not parse home account ID for CCS Header",
request.correlationId
);
}
} else if (accountSid && request.prompt === PromptValue.NONE) {
/*
* If account and loginHint are provided, we will check account first for sid before adding loginHint
* SessionId is only used in silent calls
*/
logger.verbose(
"createAuthCodeUrlQueryString: Prompt is none, adding sid from account",
request.correlationId
);
RequestParameterBuilder.addSid(parameters, accountSid);
performanceClient?.addFields(
{ sidFromClaim: true },
correlationId
);
try {
const clientInfo = buildClientInfoFromHomeAccountId(
request.account.homeAccountId
);
RequestParameterBuilder.addCcsOid(parameters, clientInfo);
} catch (e) {
logger.verbose(
"createAuthCodeUrlQueryString: Could not parse home account ID for CCS Header",
request.correlationId
);
}
} else if (request.loginHint) {
logger.verbose(
"createAuthCodeUrlQueryString: Adding login_hint from request",
request.correlationId
);
RequestParameterBuilder.addLoginHint(
parameters,
request.loginHint
);
RequestParameterBuilder.addCcsUpn(
parameters,
request.loginHint
);
performanceClient?.addFields(
{ loginHintFromRequest: true },
correlationId
);
} else if (request.account.username) {
// Fallback to account username if provided
logger.verbose(
"createAuthCodeUrlQueryString: Adding login_hint from account",
request.correlationId
);
RequestParameterBuilder.addLoginHint(
parameters,
request.account.username
);
performanceClient?.addFields(
{ loginHintFromUpn: true },
correlationId
);
try {
const clientInfo = buildClientInfoFromHomeAccountId(
request.account.homeAccountId
);
RequestParameterBuilder.addCcsOid(parameters, clientInfo);
} catch (e) {
logger.verbose(
"createAuthCodeUrlQueryString: Could not parse home account ID for CCS Header",
request.correlationId
);
}
}
} else if (request.loginHint) {
logger.verbose(
"createAuthCodeUrlQueryString: No account, adding login_hint from request",
request.correlationId
);
RequestParameterBuilder.addLoginHint(parameters, request.loginHint);
RequestParameterBuilder.addCcsUpn(parameters, request.loginHint);
performanceClient?.addFields(
{ loginHintFromRequest: true },
correlationId
);
}
} else {
logger.verbose(
"createAuthCodeUrlQueryString: Prompt is select_account, ignoring account hints",
request.correlationId
);
}
if (request.nonce) {
RequestParameterBuilder.addNonce(parameters, request.nonce);
}
if (request.state) {
RequestParameterBuilder.addState(parameters, request.state);
}
if (request.embeddedClientId) {
RequestParameterBuilder.addBrokerParameters(
parameters,
authOptions.clientId,
authOptions.redirectUri
);
}
RequestParameterBuilder.addClaims(
parameters,
request.claims,
authOptions.clientCapabilities,
request.skipBrokerClaims
);
// If extraQueryParameters includes instance_aware its value will be added when extraQueryParameters are added
if (
authOptions.instanceAware &&
(!request.extraQueryParameters ||
!Object.keys(request.extraQueryParameters).includes(
AADServerParamKeys.INSTANCE_AWARE
))
) {
RequestParameterBuilder.addInstanceAware(parameters);
}
return parameters;
}
/**
* Returns authorize endpoint with given request parameters in the query string
* @param authority
* @param requestParameters
* @returns
*/
export function getAuthorizeUrl(
authority: Authority,
requestParameters: Map<string, string>
): string {
const queryString = mapToQueryString(requestParameters);
return UrlString.appendQueryString(
authority.authorizationEndpoint,
queryString
);
}
/**
* Handles the hash fragment response from public client code request. Returns a code response used by
* the client to exchange for a token in acquireToken.
* @param serverParams
* @param cachedState
*/
export function getAuthorizationCodePayload(
serverParams: AuthorizeResponse,
cachedState: string
): AuthorizationCodePayload {
// Get code response
validateAuthorizationResponse(serverParams, cachedState);
// throw when there is no auth code in the response
if (!serverParams.code) {
throw createClientAuthError(
ClientAuthErrorCodes.authorizationCodeMissingFromServerResponse
);
}
return serverParams as AuthorizationCodePayload;
}
/**
* Function which validates server authorization code response.
* @param serverResponseHash
* @param requestState
*/
export function validateAuthorizationResponse(
serverResponse: AuthorizeResponse,
requestState: string
): void {
if (!serverResponse.state || !requestState) {
throw serverResponse.state
? createClientAuthError(
ClientAuthErrorCodes.stateNotFound,
"Cached State"
)
: createClientAuthError(
ClientAuthErrorCodes.stateNotFound,
"Server State"
);
}
let decodedServerResponseState: string;
let decodedRequestState: string;
try {
decodedServerResponseState = decodeURIComponent(serverResponse.state);
} catch (e) {
throw createClientAuthError(
ClientAuthErrorCodes.invalidState,
serverResponse.state
);
}
try {
decodedRequestState = decodeURIComponent(requestState);
} catch (e) {
throw createClientAuthError(
ClientAuthErrorCodes.invalidState,
serverResponse.state
);
}
if (decodedServerResponseState !== decodedRequestState) {
throw createClientAuthError(ClientAuthErrorCodes.stateMismatch);
}
// Check for error
if (
serverResponse.error ||
serverResponse.error_description ||
serverResponse.suberror
) {
const serverErrorNo = parseServerErrorNo(serverResponse);
if (
isInteractionRequiredError(
serverResponse.error,
serverResponse.error_description,
serverResponse.suberror
)
) {
throw new InteractionRequiredAuthError(
serverResponse.error || "",
serverResponse.error_description,
serverResponse.suberror,
serverResponse.timestamp || "",
serverResponse.trace_id || "",
serverResponse.correlation_id || "",
serverResponse.claims || "",
serverErrorNo
);
}
throw new ServerError(
serverResponse.error || "",
serverResponse.error_description,
serverResponse.suberror,
serverErrorNo
);
}
}
/**
* Get server error No from the error_uri
* @param serverResponse
* @returns
*/
function parseServerErrorNo(
serverResponse: AuthorizeResponse
): string | undefined {
const errorCodePrefix = "code=";
const errorCodePrefixIndex =
serverResponse.error_uri?.lastIndexOf(errorCodePrefix);
return errorCodePrefixIndex && errorCodePrefixIndex >= 0
? serverResponse.error_uri?.substring(
errorCodePrefixIndex + errorCodePrefix.length
)
: undefined;
}
/**
* Helper to get sid from account. Returns null if idTokenClaims are not present or sid is not present.
* @param account
*/
function extractAccountSid(account: AccountInfo): string | null {
return account.idTokenClaims?.sid || null;
}
function extractLoginHint(account: AccountInfo): string | null {
return account.loginHint || account.idTokenClaims?.login_hint || null;
}

View File

@ -0,0 +1,230 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { CcsCredential, CcsCredentialType } from "../account/CcsCredential.js";
import { buildClientInfoFromHomeAccountId } from "../account/ClientInfo.js";
import { Logger } from "../logger/Logger.js";
import { BaseAuthRequest } from "../request/BaseAuthRequest.js";
import { HeaderNames, URL_FORM_CONTENT_TYPE } from "../utils/Constants.js";
import * as RequestParameterBuilder from "../request/RequestParameterBuilder.js";
import * as UrlUtils from "../utils/UrlUtils.js";
import { IPerformanceClient } from "../exports-browser-only.js";
import { ServerAuthorizationTokenResponse } from "../response/ServerAuthorizationTokenResponse.js";
import { RequestThumbprint } from "../network/RequestThumbprint.js";
import {
INetworkModule,
NetworkRequestOptions,
} from "../network/INetworkModule.js";
import { NetworkResponse } from "../network/NetworkResponse.js";
import { ThrottlingUtils } from "../network/ThrottlingUtils.js";
import { NetworkError } from "../error/NetworkError.js";
import { AuthError } from "../error/AuthError.js";
import {
ClientAuthErrorCodes,
createClientAuthError,
} from "../error/ClientAuthError.js";
import { invokeAsync } from "../utils/FunctionWrappers.js";
import * as PerformanceEvents from "../telemetry/performance/PerformanceEvents.js";
import { CacheManager } from "../cache/CacheManager.js";
import { ServerTelemetryManager } from "../telemetry/server/ServerTelemetryManager.js";
/**
* Creates default headers for requests to token endpoint
*/
export function createTokenRequestHeaders(
logger: Logger,
preventCorsPreflight: boolean,
ccsCred?: CcsCredential
): Record<string, string> {
const headers: Record<string, string> = {};
headers[HeaderNames.CONTENT_TYPE] = URL_FORM_CONTENT_TYPE;
if (!preventCorsPreflight && ccsCred) {
switch (ccsCred.type) {
case CcsCredentialType.HOME_ACCOUNT_ID:
try {
const clientInfo = buildClientInfoFromHomeAccountId(
ccsCred.credential
);
headers[
HeaderNames.CCS_HEADER
] = `Oid:${clientInfo.uid}@${clientInfo.utid}`;
} catch (e) {
logger.verbose(
`Could not parse home account ID for CCS Header: '${e}'`,
""
);
}
break;
case CcsCredentialType.UPN:
headers[HeaderNames.CCS_HEADER] = `UPN: ${ccsCred.credential}`;
break;
}
}
return headers;
}
/**
* Creates query string for the /token request
* @param request
*/
export function createTokenQueryParameters(
request: BaseAuthRequest,
clientId: string,
redirectUri: string,
performanceClient: IPerformanceClient
): string {
const parameters = new Map<string, string>();
if (request.embeddedClientId) {
RequestParameterBuilder.addBrokerParameters(
parameters,
clientId,
redirectUri
);
}
if (request.extraQueryParameters) {
RequestParameterBuilder.addExtraParameters(
parameters,
request.extraQueryParameters
);
}
RequestParameterBuilder.addCorrelationId(parameters, request.correlationId);
RequestParameterBuilder.instrumentBrokerParams(
parameters,
request.correlationId,
performanceClient
);
return UrlUtils.mapToQueryString(parameters);
}
/**
* Http post to token endpoint
* @param tokenEndpoint
* @param queryString
* @param headers
* @param thumbprint
*/
export async function executePostToTokenEndpoint(
tokenEndpoint: string,
queryString: string,
headers: Record<string, string>,
thumbprint: RequestThumbprint,
correlationId: string,
cacheManager: CacheManager,
networkClient: INetworkModule,
logger: Logger,
performanceClient: IPerformanceClient,
serverTelemetryManager: ServerTelemetryManager | null
): Promise<NetworkResponse<ServerAuthorizationTokenResponse>> {
const response = await sendPostRequest<ServerAuthorizationTokenResponse>(
thumbprint,
tokenEndpoint,
{ body: queryString, headers: headers },
correlationId,
cacheManager,
networkClient,
logger,
performanceClient
);
if (
serverTelemetryManager &&
response.status < 500 &&
response.status !== 429
) {
// Telemetry data successfully logged by server, clear Telemetry cache
serverTelemetryManager.clearTelemetryCache();
}
return response;
}
/**
* Wraps sendPostRequestAsync with necessary preflight and postflight logic
* @param thumbprint - Request thumbprint for throttling
* @param tokenEndpoint - Endpoint to make the POST to
* @param options - Body and Headers to include on the POST request
* @param correlationId - CorrelationId for telemetry
* @param cacheManager - Cache manager instance
* @param networkClient - Network module instance
* @param logger - Logger instance
* @param performanceClient - Performance client instance
*/
export async function sendPostRequest<
T extends ServerAuthorizationTokenResponse
>(
thumbprint: RequestThumbprint,
tokenEndpoint: string,
options: NetworkRequestOptions,
correlationId: string,
cacheManager: CacheManager,
networkClient: INetworkModule,
logger: Logger,
performanceClient: IPerformanceClient
): Promise<NetworkResponse<T>> {
ThrottlingUtils.preProcess(cacheManager, thumbprint, correlationId);
let response;
try {
response = await invokeAsync(
networkClient.sendPostRequestAsync.bind(networkClient)<T>,
PerformanceEvents.NetworkClientSendPostRequestAsync,
logger,
performanceClient,
correlationId
)(tokenEndpoint, options);
const responseHeaders = response.headers || {};
performanceClient?.addFields(
{
refreshTokenSize: response.body.refresh_token?.length || 0,
httpVerToken:
responseHeaders[HeaderNames.X_MS_HTTP_VERSION] || "",
requestId: responseHeaders[HeaderNames.X_MS_REQUEST_ID] || "",
},
correlationId
);
} catch (e) {
if (e instanceof NetworkError) {
const responseHeaders = e.responseHeaders;
if (responseHeaders) {
performanceClient?.addFields(
{
httpVerToken:
responseHeaders[HeaderNames.X_MS_HTTP_VERSION] ||
"",
requestId:
responseHeaders[HeaderNames.X_MS_REQUEST_ID] || "",
contentTypeHeader:
responseHeaders[HeaderNames.CONTENT_TYPE] ||
undefined,
contentLengthHeader:
responseHeaders[HeaderNames.CONTENT_LENGTH] ||
undefined,
httpStatus: e.httpStatus,
},
correlationId
);
}
throw e.error;
}
if (e instanceof AuthError) {
throw e;
} else {
throw createClientAuthError(ClientAuthErrorCodes.networkError);
}
}
ThrottlingUtils.postProcess(
cacheManager,
thumbprint,
response,
correlationId
);
return response;
}