UNPKG

@azure/core-rest-pipeline

Version:

Isomorphic client library for making HTTP requests in node.js and browser.

218 lines (217 loc) 8.16 kB
var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var bearerTokenAuthenticationPolicy_exports = {}; __export(bearerTokenAuthenticationPolicy_exports, { bearerTokenAuthenticationPolicy: () => bearerTokenAuthenticationPolicy, bearerTokenAuthenticationPolicyName: () => bearerTokenAuthenticationPolicyName, parseChallenges: () => parseChallenges }); module.exports = __toCommonJS(bearerTokenAuthenticationPolicy_exports); var import_tokenCycler = require("../util/tokenCycler.js"); var import_log = require("../log.js"); var import_restError = require("../restError.js"); const bearerTokenAuthenticationPolicyName = "bearerTokenAuthenticationPolicy"; async function trySendRequest(request, next) { try { return [await next(request), void 0]; } catch (e) { if ((0, import_restError.isRestError)(e) && e.response) { return [e.response, e]; } else { throw e; } } } async function defaultAuthorizeRequest(options) { const { scopes, getAccessToken, request } = options; const getTokenOptions = { abortSignal: request.abortSignal, tracingOptions: request.tracingOptions, enableCae: true }; const accessToken = await getAccessToken(scopes, getTokenOptions); if (accessToken) { options.request.headers.set("Authorization", `Bearer ${accessToken.token}`); } } function isChallengeResponse(response) { return response.status === 401 && response.headers.has("WWW-Authenticate"); } async function authorizeRequestOnCaeChallenge(onChallengeOptions, caeClaims) { const { scopes } = onChallengeOptions; const accessToken = await onChallengeOptions.getAccessToken(scopes, { enableCae: true, claims: caeClaims }); if (!accessToken) { return false; } onChallengeOptions.request.headers.set( "Authorization", `${accessToken.tokenType ?? "Bearer"} ${accessToken.token}` ); return true; } function bearerTokenAuthenticationPolicy(options) { const { credential, scopes, challengeCallbacks } = options; const logger = options.logger || import_log.logger; const callbacks = { authorizeRequest: challengeCallbacks?.authorizeRequest?.bind(challengeCallbacks) ?? defaultAuthorizeRequest, authorizeRequestOnChallenge: challengeCallbacks?.authorizeRequestOnChallenge?.bind(challengeCallbacks) }; const getAccessToken = credential ? (0, import_tokenCycler.createTokenCycler)( credential /* , options */ ) : () => Promise.resolve(null); return { name: bearerTokenAuthenticationPolicyName, /** * If there's no challenge parameter: * - It will try to retrieve the token using the cache, or the credential's getToken. * - Then it will try the next policy with or without the retrieved token. * * It uses the challenge parameters to: * - Skip a first attempt to get the token from the credential if there's no cached token, * since it expects the token to be retrievable only after the challenge. * - Prepare the outgoing request if the `prepareRequest` method has been provided. * - Send an initial request to receive the challenge if it fails. * - Process a challenge if the response contains it. * - Retrieve a token with the challenge information, then re-send the request. */ async sendRequest(request, next) { if (!request.url.toLowerCase().startsWith("https://")) { throw new Error( "Bearer token authentication is not permitted for non-TLS protected (non-https) URLs." ); } await callbacks.authorizeRequest({ scopes: Array.isArray(scopes) ? scopes : [scopes], request, getAccessToken, logger }); let response; let error; let shouldSendRequest; [response, error] = await trySendRequest(request, next); if (isChallengeResponse(response)) { let claims = getCaeChallengeClaims(response.headers.get("WWW-Authenticate")); if (claims) { let parsedClaim; try { parsedClaim = atob(claims); } catch (e) { logger.warning( `The WWW-Authenticate header contains "claims" that cannot be parsed. Unable to perform the Continuous Access Evaluation authentication flow. Unparsable claims: ${claims}` ); return response; } shouldSendRequest = await authorizeRequestOnCaeChallenge( { scopes: Array.isArray(scopes) ? scopes : [scopes], response, request, getAccessToken, logger }, parsedClaim ); if (shouldSendRequest) { [response, error] = await trySendRequest(request, next); } } else if (callbacks.authorizeRequestOnChallenge) { shouldSendRequest = await callbacks.authorizeRequestOnChallenge({ scopes: Array.isArray(scopes) ? scopes : [scopes], request, response, getAccessToken, logger }); if (shouldSendRequest) { [response, error] = await trySendRequest(request, next); } if (isChallengeResponse(response)) { claims = getCaeChallengeClaims(response.headers.get("WWW-Authenticate") ?? ""); if (claims) { let parsedClaim; try { parsedClaim = atob(claims); } catch (e) { logger.warning( `The WWW-Authenticate header contains "claims" that cannot be parsed. Unable to perform the Continuous Access Evaluation authentication flow. Unparsable claims: ${claims}` ); return response; } shouldSendRequest = await authorizeRequestOnCaeChallenge( { scopes: Array.isArray(scopes) ? scopes : [scopes], response, request, getAccessToken, logger }, parsedClaim ); if (shouldSendRequest) { [response, error] = await trySendRequest(request, next); } } } } } if (error) { throw error; } else { return response; } } }; } function parseChallenges(challenges) { const challengeRegex = /(\w+)\s+((?:\w+=(?:"[^"]*"|[^,]*),?\s*)+)/g; const paramRegex = /(\w+)="([^"]*)"/g; const parsedChallenges = []; let match; while ((match = challengeRegex.exec(challenges)) !== null) { const scheme = match[1]; const paramsString = match[2]; const params = {}; let paramMatch; while ((paramMatch = paramRegex.exec(paramsString)) !== null) { params[paramMatch[1]] = paramMatch[2]; } parsedChallenges.push({ scheme, params }); } return parsedChallenges; } function getCaeChallengeClaims(challenges) { if (!challenges) { return; } const parsedChallenges = parseChallenges(challenges); return parsedChallenges.find( (x) => x.scheme === "Bearer" && x.params.claims && x.params.error === "insufficient_claims" )?.params.claims; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { bearerTokenAuthenticationPolicy, bearerTokenAuthenticationPolicyName, parseChallenges }); //# sourceMappingURL=bearerTokenAuthenticationPolicy.js.map