@azure/keyvault-common 2.0.0 → 2.0.1-alpha.20241112.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/keyVaultAuthenticationPolicy.d.ts +2 -2
- package/dist/browser/keyVaultAuthenticationPolicy.d.ts.map +1 -1
- package/dist/browser/keyVaultAuthenticationPolicy.js.map +1 -1
- package/dist/commonjs/keyVaultAuthenticationPolicy.d.ts +2 -2
- package/dist/commonjs/keyVaultAuthenticationPolicy.d.ts.map +1 -1
- package/dist/commonjs/keyVaultAuthenticationPolicy.js.map +1 -1
- package/dist/commonjs/tsdoc-metadata.json +1 -1
- package/dist/esm/keyVaultAuthenticationPolicy.d.ts +2 -2
- package/dist/esm/keyVaultAuthenticationPolicy.d.ts.map +1 -1
- package/dist/esm/keyVaultAuthenticationPolicy.js.map +1 -1
- package/dist/keyvault-common.d.ts +2 -2
- package/package.json +5 -9
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { PipelinePolicy } from "@azure/core-rest-pipeline";
|
|
2
|
-
import { TokenCredential } from "@azure/core-auth";
|
|
1
|
+
import type { PipelinePolicy } from "@azure/core-rest-pipeline";
|
|
2
|
+
import type { TokenCredential } from "@azure/core-auth";
|
|
3
3
|
/**
|
|
4
4
|
* Additional options for the challenge based authentication policy.
|
|
5
5
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyVaultAuthenticationPolicy.d.ts","sourceRoot":"","sources":["../../src/keyVaultAuthenticationPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"keyVaultAuthenticationPolicy.d.ts","sourceRoot":"","sources":["../../src/keyVaultAuthenticationPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,cAAc,EAKf,MAAM,2BAA2B,CAAC;AAInC,OAAO,KAAK,EAAmB,eAAe,EAAE,MAAM,kBAAkB,CAAC;AA8BzE;;GAEG;AACH,MAAM,WAAW,mCAAmC;IAClD;;;;OAIG;IACH,oCAAoC,CAAC,EAAE,OAAO,CAAC;CAChD;AAmBD;;GAEG;AACH,eAAO,MAAM,gCAAgC,iCAAiC,CAAC;AAE/E;;;;;;;;;;;;GAYG;AACH,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,eAAe,EAC3B,OAAO,GAAE,mCAAwC,GAChD,cAAc,CA2KhB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyVaultAuthenticationPolicy.js","sourceRoot":"","sources":["../../src/keyVaultAuthenticationPolicy.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n RequestBodyType,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\nimport { WWWAuthenticate, parseWWWAuthenticateHeader } from \"./parseWWWAuthenticate.js\";\n\nimport { GetTokenOptions, TokenCredential } from \"@azure/core-auth\";\nimport { createTokenCycler } from \"./tokenCycler.js\";\nimport { logger } from \"./logger.js\";\n\n/**\n * @internal\n * Holds the state of Challenge Auth.\n * When making the first request we force Key Vault to begin a challenge\n * by clearing out the request body and storing it locally.\n *\n * Later on, the authorizeRequestOnChallenge callback will process the\n * challenge and, if ready to resend the original request, reset the body\n * so that it may be sent again.\n *\n * Once a client has succeeded once, we can start skipping CAE.\n */\ntype ChallengeState =\n | {\n status: \"none\";\n }\n | {\n status: \"started\";\n originalBody?: RequestBodyType;\n }\n | {\n status: \"complete\";\n scopes: string[];\n tenantId?: string;\n };\n\n/**\n * Additional options for the challenge based authentication policy.\n */\nexport interface KeyVaultAuthenticationPolicyOptions {\n /**\n * Whether to disable verification that the challenge resource matches the Key Vault or Managed HSM domain.\n *\n * Defaults to false.\n */\n disableChallengeResourceVerification?: boolean;\n}\n\nfunction verifyChallengeResource(scope: string, request: PipelineRequest): void {\n let scopeAsUrl: URL;\n try {\n scopeAsUrl = new URL(scope);\n } catch (e) {\n throw new Error(`The challenge contains invalid scope '${scope}'`);\n }\n\n const requestUrl = new URL(request.url);\n\n if (!requestUrl.hostname.endsWith(`.${scopeAsUrl.hostname}`)) {\n throw new Error(\n `The challenge resource '${scopeAsUrl.hostname}' does not match the requested domain. Set disableChallengeResourceVerification to true in your client options to disable. See https://aka.ms/azsdk/blog/vault-uri for more information.`,\n );\n }\n}\n\n/**\n * Name of the Key Vault authentication policy.\n */\nexport const keyVaultAuthenticationPolicyName = \"keyVaultAuthenticationPolicy\";\n\n/**\n * A custom implementation of the bearer-token authentication policy that handles Key Vault and CAE challenges.\n *\n * Key Vault supports other authentication schemes, but we ensure challenge authentication\n * is used by first sending a copy of the request, without authorization or content.\n *\n * when the challenge is received, it will be authenticated and used to send the original\n * request with authorization.\n *\n * Following the first request of a client, follow-up requests will get the cached token\n * if possible.\n *\n */\nexport function keyVaultAuthenticationPolicy(\n credential: TokenCredential,\n options: KeyVaultAuthenticationPolicyOptions = {},\n): PipelinePolicy {\n const { disableChallengeResourceVerification } = options;\n let challengeState: ChallengeState = { status: \"none\" };\n const getAccessToken = createTokenCycler(credential);\n\n function requestToOptions(request: PipelineRequest): GetTokenOptions {\n return {\n abortSignal: request.abortSignal,\n requestOptions: {\n timeout: request.timeout > 0 ? request.timeout : undefined,\n },\n tracingOptions: request.tracingOptions,\n };\n }\n\n async function authorizeRequest(request: PipelineRequest): Promise<void> {\n const requestOptions: GetTokenOptions = requestToOptions(request);\n\n switch (challengeState.status) {\n case \"none\":\n challengeState = {\n status: \"started\",\n originalBody: request.body,\n };\n request.body = null;\n break;\n case \"started\":\n break; // Retry, we should not overwrite the original body\n case \"complete\": {\n const token = await getAccessToken(challengeState.scopes, {\n ...requestOptions,\n enableCae: true,\n tenantId: challengeState.tenantId,\n });\n if (token) {\n request.headers.set(\"authorization\", `Bearer ${token.token}`);\n }\n break;\n }\n }\n }\n\n async function handleChallenge(\n request: PipelineRequest,\n response: PipelineResponse,\n next: SendRequest,\n ): Promise<PipelineResponse> {\n // If status is not 401, this is a no-op\n if (response.status !== 401) {\n return response;\n }\n\n if (request.body === null && challengeState.status === \"started\") {\n // Reset the original body before doing anything else.\n // Note: If successful status will be \"complete\", otherwise \"none\" will\n // restart the process.\n request.body = challengeState.originalBody;\n }\n\n const getTokenOptions = requestToOptions(request);\n\n const challenge = response.headers.get(\"WWW-Authenticate\");\n if (!challenge) {\n logger.warning(\n \"keyVaultAuthentication policy encountered a 401 response without a corresponding WWW-Authenticate header. This is unexpected. Not handling the 401 response.\",\n );\n return response;\n }\n const parsedChallenge: WWWAuthenticate = parseWWWAuthenticateHeader(challenge);\n\n const scope = parsedChallenge.resource\n ? parsedChallenge.resource + \"/.default\"\n : parsedChallenge.scope;\n\n if (!scope) {\n // Cannot handle this kind of challenge here (if scope is not present, may be a CAE challenge)\n return response;\n }\n\n if (!disableChallengeResourceVerification) {\n verifyChallengeResource(scope, request);\n }\n\n const accessToken = await getAccessToken([scope], {\n ...getTokenOptions,\n enableCae: true,\n tenantId: parsedChallenge.tenantId,\n });\n\n if (!accessToken) {\n // No access token provided, treat as no-op\n return response;\n }\n\n request.headers.set(\"Authorization\", `Bearer ${accessToken.token}`);\n\n challengeState = {\n status: \"complete\",\n scopes: [scope],\n tenantId: parsedChallenge.tenantId,\n };\n\n // We have a token now, so try send the request again\n return next(request);\n }\n\n async function handleCaeChallenge(\n request: PipelineRequest,\n response: PipelineResponse,\n next: SendRequest,\n ): Promise<PipelineResponse> {\n // Cannot handle CAE challenge if a regular challenge has not been completed first\n if (challengeState.status !== \"complete\") {\n return response;\n }\n\n // If status is not 401, this is a no-op\n if (response.status !== 401) {\n return response;\n }\n\n const getTokenOptions = requestToOptions(request);\n\n const challenge = response.headers.get(\"WWW-Authenticate\");\n if (!challenge) {\n return response;\n }\n const { claims: base64EncodedClaims, error }: WWWAuthenticate =\n parseWWWAuthenticateHeader(challenge);\n\n if (error !== \"insufficient_claims\" || base64EncodedClaims === undefined) {\n return response;\n }\n\n const claims = atob(base64EncodedClaims);\n\n const accessToken = await getAccessToken(challengeState.scopes, {\n ...getTokenOptions,\n enableCae: true,\n tenantId: challengeState.tenantId,\n claims,\n });\n\n request.headers.set(\"Authorization\", `Bearer ${accessToken.token}`);\n\n return next(request);\n }\n\n async function sendRequest(\n request: PipelineRequest,\n next: SendRequest,\n ): Promise<PipelineResponse> {\n // Add token if possible\n await authorizeRequest(request);\n\n // Try send request (first attempt)\n let response = await next(request);\n\n // Handle standard challenge if present\n response = await handleChallenge(request, response, next);\n\n // Handle CAE challenge if present\n response = await handleCaeChallenge(request, response, next);\n\n return response;\n }\n\n return {\n name: keyVaultAuthenticationPolicyName,\n sendRequest,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"keyVaultAuthenticationPolicy.js","sourceRoot":"","sources":["../../src/keyVaultAuthenticationPolicy.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n RequestBodyType,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\nimport type { WWWAuthenticate } from \"./parseWWWAuthenticate.js\";\nimport { parseWWWAuthenticateHeader } from \"./parseWWWAuthenticate.js\";\n\nimport type { GetTokenOptions, TokenCredential } from \"@azure/core-auth\";\nimport { createTokenCycler } from \"./tokenCycler.js\";\nimport { logger } from \"./logger.js\";\n\n/**\n * @internal\n * Holds the state of Challenge Auth.\n * When making the first request we force Key Vault to begin a challenge\n * by clearing out the request body and storing it locally.\n *\n * Later on, the authorizeRequestOnChallenge callback will process the\n * challenge and, if ready to resend the original request, reset the body\n * so that it may be sent again.\n *\n * Once a client has succeeded once, we can start skipping CAE.\n */\ntype ChallengeState =\n | {\n status: \"none\";\n }\n | {\n status: \"started\";\n originalBody?: RequestBodyType;\n }\n | {\n status: \"complete\";\n scopes: string[];\n tenantId?: string;\n };\n\n/**\n * Additional options for the challenge based authentication policy.\n */\nexport interface KeyVaultAuthenticationPolicyOptions {\n /**\n * Whether to disable verification that the challenge resource matches the Key Vault or Managed HSM domain.\n *\n * Defaults to false.\n */\n disableChallengeResourceVerification?: boolean;\n}\n\nfunction verifyChallengeResource(scope: string, request: PipelineRequest): void {\n let scopeAsUrl: URL;\n try {\n scopeAsUrl = new URL(scope);\n } catch (e) {\n throw new Error(`The challenge contains invalid scope '${scope}'`);\n }\n\n const requestUrl = new URL(request.url);\n\n if (!requestUrl.hostname.endsWith(`.${scopeAsUrl.hostname}`)) {\n throw new Error(\n `The challenge resource '${scopeAsUrl.hostname}' does not match the requested domain. Set disableChallengeResourceVerification to true in your client options to disable. See https://aka.ms/azsdk/blog/vault-uri for more information.`,\n );\n }\n}\n\n/**\n * Name of the Key Vault authentication policy.\n */\nexport const keyVaultAuthenticationPolicyName = \"keyVaultAuthenticationPolicy\";\n\n/**\n * A custom implementation of the bearer-token authentication policy that handles Key Vault and CAE challenges.\n *\n * Key Vault supports other authentication schemes, but we ensure challenge authentication\n * is used by first sending a copy of the request, without authorization or content.\n *\n * when the challenge is received, it will be authenticated and used to send the original\n * request with authorization.\n *\n * Following the first request of a client, follow-up requests will get the cached token\n * if possible.\n *\n */\nexport function keyVaultAuthenticationPolicy(\n credential: TokenCredential,\n options: KeyVaultAuthenticationPolicyOptions = {},\n): PipelinePolicy {\n const { disableChallengeResourceVerification } = options;\n let challengeState: ChallengeState = { status: \"none\" };\n const getAccessToken = createTokenCycler(credential);\n\n function requestToOptions(request: PipelineRequest): GetTokenOptions {\n return {\n abortSignal: request.abortSignal,\n requestOptions: {\n timeout: request.timeout > 0 ? request.timeout : undefined,\n },\n tracingOptions: request.tracingOptions,\n };\n }\n\n async function authorizeRequest(request: PipelineRequest): Promise<void> {\n const requestOptions: GetTokenOptions = requestToOptions(request);\n\n switch (challengeState.status) {\n case \"none\":\n challengeState = {\n status: \"started\",\n originalBody: request.body,\n };\n request.body = null;\n break;\n case \"started\":\n break; // Retry, we should not overwrite the original body\n case \"complete\": {\n const token = await getAccessToken(challengeState.scopes, {\n ...requestOptions,\n enableCae: true,\n tenantId: challengeState.tenantId,\n });\n if (token) {\n request.headers.set(\"authorization\", `Bearer ${token.token}`);\n }\n break;\n }\n }\n }\n\n async function handleChallenge(\n request: PipelineRequest,\n response: PipelineResponse,\n next: SendRequest,\n ): Promise<PipelineResponse> {\n // If status is not 401, this is a no-op\n if (response.status !== 401) {\n return response;\n }\n\n if (request.body === null && challengeState.status === \"started\") {\n // Reset the original body before doing anything else.\n // Note: If successful status will be \"complete\", otherwise \"none\" will\n // restart the process.\n request.body = challengeState.originalBody;\n }\n\n const getTokenOptions = requestToOptions(request);\n\n const challenge = response.headers.get(\"WWW-Authenticate\");\n if (!challenge) {\n logger.warning(\n \"keyVaultAuthentication policy encountered a 401 response without a corresponding WWW-Authenticate header. This is unexpected. Not handling the 401 response.\",\n );\n return response;\n }\n const parsedChallenge: WWWAuthenticate = parseWWWAuthenticateHeader(challenge);\n\n const scope = parsedChallenge.resource\n ? parsedChallenge.resource + \"/.default\"\n : parsedChallenge.scope;\n\n if (!scope) {\n // Cannot handle this kind of challenge here (if scope is not present, may be a CAE challenge)\n return response;\n }\n\n if (!disableChallengeResourceVerification) {\n verifyChallengeResource(scope, request);\n }\n\n const accessToken = await getAccessToken([scope], {\n ...getTokenOptions,\n enableCae: true,\n tenantId: parsedChallenge.tenantId,\n });\n\n if (!accessToken) {\n // No access token provided, treat as no-op\n return response;\n }\n\n request.headers.set(\"Authorization\", `Bearer ${accessToken.token}`);\n\n challengeState = {\n status: \"complete\",\n scopes: [scope],\n tenantId: parsedChallenge.tenantId,\n };\n\n // We have a token now, so try send the request again\n return next(request);\n }\n\n async function handleCaeChallenge(\n request: PipelineRequest,\n response: PipelineResponse,\n next: SendRequest,\n ): Promise<PipelineResponse> {\n // Cannot handle CAE challenge if a regular challenge has not been completed first\n if (challengeState.status !== \"complete\") {\n return response;\n }\n\n // If status is not 401, this is a no-op\n if (response.status !== 401) {\n return response;\n }\n\n const getTokenOptions = requestToOptions(request);\n\n const challenge = response.headers.get(\"WWW-Authenticate\");\n if (!challenge) {\n return response;\n }\n const { claims: base64EncodedClaims, error }: WWWAuthenticate =\n parseWWWAuthenticateHeader(challenge);\n\n if (error !== \"insufficient_claims\" || base64EncodedClaims === undefined) {\n return response;\n }\n\n const claims = atob(base64EncodedClaims);\n\n const accessToken = await getAccessToken(challengeState.scopes, {\n ...getTokenOptions,\n enableCae: true,\n tenantId: challengeState.tenantId,\n claims,\n });\n\n request.headers.set(\"Authorization\", `Bearer ${accessToken.token}`);\n\n return next(request);\n }\n\n async function sendRequest(\n request: PipelineRequest,\n next: SendRequest,\n ): Promise<PipelineResponse> {\n // Add token if possible\n await authorizeRequest(request);\n\n // Try send request (first attempt)\n let response = await next(request);\n\n // Handle standard challenge if present\n response = await handleChallenge(request, response, next);\n\n // Handle CAE challenge if present\n response = await handleCaeChallenge(request, response, next);\n\n return response;\n }\n\n return {\n name: keyVaultAuthenticationPolicyName,\n sendRequest,\n };\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { PipelinePolicy } from "@azure/core-rest-pipeline";
|
|
2
|
-
import { TokenCredential } from "@azure/core-auth";
|
|
1
|
+
import type { PipelinePolicy } from "@azure/core-rest-pipeline";
|
|
2
|
+
import type { TokenCredential } from "@azure/core-auth";
|
|
3
3
|
/**
|
|
4
4
|
* Additional options for the challenge based authentication policy.
|
|
5
5
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyVaultAuthenticationPolicy.d.ts","sourceRoot":"","sources":["../../src/keyVaultAuthenticationPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"keyVaultAuthenticationPolicy.d.ts","sourceRoot":"","sources":["../../src/keyVaultAuthenticationPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,cAAc,EAKf,MAAM,2BAA2B,CAAC;AAInC,OAAO,KAAK,EAAmB,eAAe,EAAE,MAAM,kBAAkB,CAAC;AA8BzE;;GAEG;AACH,MAAM,WAAW,mCAAmC;IAClD;;;;OAIG;IACH,oCAAoC,CAAC,EAAE,OAAO,CAAC;CAChD;AAmBD;;GAEG;AACH,eAAO,MAAM,gCAAgC,iCAAiC,CAAC;AAE/E;;;;;;;;;;;;GAYG;AACH,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,eAAe,EAC3B,OAAO,GAAE,mCAAwC,GAChD,cAAc,CA2KhB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyVaultAuthenticationPolicy.js","sourceRoot":"","sources":["../../src/keyVaultAuthenticationPolicy.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n RequestBodyType,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\nimport { WWWAuthenticate, parseWWWAuthenticateHeader } from \"./parseWWWAuthenticate.js\";\n\nimport { GetTokenOptions, TokenCredential } from \"@azure/core-auth\";\nimport { createTokenCycler } from \"./tokenCycler.js\";\nimport { logger } from \"./logger.js\";\n\n/**\n * @internal\n * Holds the state of Challenge Auth.\n * When making the first request we force Key Vault to begin a challenge\n * by clearing out the request body and storing it locally.\n *\n * Later on, the authorizeRequestOnChallenge callback will process the\n * challenge and, if ready to resend the original request, reset the body\n * so that it may be sent again.\n *\n * Once a client has succeeded once, we can start skipping CAE.\n */\ntype ChallengeState =\n | {\n status: \"none\";\n }\n | {\n status: \"started\";\n originalBody?: RequestBodyType;\n }\n | {\n status: \"complete\";\n scopes: string[];\n tenantId?: string;\n };\n\n/**\n * Additional options for the challenge based authentication policy.\n */\nexport interface KeyVaultAuthenticationPolicyOptions {\n /**\n * Whether to disable verification that the challenge resource matches the Key Vault or Managed HSM domain.\n *\n * Defaults to false.\n */\n disableChallengeResourceVerification?: boolean;\n}\n\nfunction verifyChallengeResource(scope: string, request: PipelineRequest): void {\n let scopeAsUrl: URL;\n try {\n scopeAsUrl = new URL(scope);\n } catch (e) {\n throw new Error(`The challenge contains invalid scope '${scope}'`);\n }\n\n const requestUrl = new URL(request.url);\n\n if (!requestUrl.hostname.endsWith(`.${scopeAsUrl.hostname}`)) {\n throw new Error(\n `The challenge resource '${scopeAsUrl.hostname}' does not match the requested domain. Set disableChallengeResourceVerification to true in your client options to disable. See https://aka.ms/azsdk/blog/vault-uri for more information.`,\n );\n }\n}\n\n/**\n * Name of the Key Vault authentication policy.\n */\nexport const keyVaultAuthenticationPolicyName = \"keyVaultAuthenticationPolicy\";\n\n/**\n * A custom implementation of the bearer-token authentication policy that handles Key Vault and CAE challenges.\n *\n * Key Vault supports other authentication schemes, but we ensure challenge authentication\n * is used by first sending a copy of the request, without authorization or content.\n *\n * when the challenge is received, it will be authenticated and used to send the original\n * request with authorization.\n *\n * Following the first request of a client, follow-up requests will get the cached token\n * if possible.\n *\n */\nexport function keyVaultAuthenticationPolicy(\n credential: TokenCredential,\n options: KeyVaultAuthenticationPolicyOptions = {},\n): PipelinePolicy {\n const { disableChallengeResourceVerification } = options;\n let challengeState: ChallengeState = { status: \"none\" };\n const getAccessToken = createTokenCycler(credential);\n\n function requestToOptions(request: PipelineRequest): GetTokenOptions {\n return {\n abortSignal: request.abortSignal,\n requestOptions: {\n timeout: request.timeout > 0 ? request.timeout : undefined,\n },\n tracingOptions: request.tracingOptions,\n };\n }\n\n async function authorizeRequest(request: PipelineRequest): Promise<void> {\n const requestOptions: GetTokenOptions = requestToOptions(request);\n\n switch (challengeState.status) {\n case \"none\":\n challengeState = {\n status: \"started\",\n originalBody: request.body,\n };\n request.body = null;\n break;\n case \"started\":\n break; // Retry, we should not overwrite the original body\n case \"complete\": {\n const token = await getAccessToken(challengeState.scopes, {\n ...requestOptions,\n enableCae: true,\n tenantId: challengeState.tenantId,\n });\n if (token) {\n request.headers.set(\"authorization\", `Bearer ${token.token}`);\n }\n break;\n }\n }\n }\n\n async function handleChallenge(\n request: PipelineRequest,\n response: PipelineResponse,\n next: SendRequest,\n ): Promise<PipelineResponse> {\n // If status is not 401, this is a no-op\n if (response.status !== 401) {\n return response;\n }\n\n if (request.body === null && challengeState.status === \"started\") {\n // Reset the original body before doing anything else.\n // Note: If successful status will be \"complete\", otherwise \"none\" will\n // restart the process.\n request.body = challengeState.originalBody;\n }\n\n const getTokenOptions = requestToOptions(request);\n\n const challenge = response.headers.get(\"WWW-Authenticate\");\n if (!challenge) {\n logger.warning(\n \"keyVaultAuthentication policy encountered a 401 response without a corresponding WWW-Authenticate header. This is unexpected. Not handling the 401 response.\",\n );\n return response;\n }\n const parsedChallenge: WWWAuthenticate = parseWWWAuthenticateHeader(challenge);\n\n const scope = parsedChallenge.resource\n ? parsedChallenge.resource + \"/.default\"\n : parsedChallenge.scope;\n\n if (!scope) {\n // Cannot handle this kind of challenge here (if scope is not present, may be a CAE challenge)\n return response;\n }\n\n if (!disableChallengeResourceVerification) {\n verifyChallengeResource(scope, request);\n }\n\n const accessToken = await getAccessToken([scope], {\n ...getTokenOptions,\n enableCae: true,\n tenantId: parsedChallenge.tenantId,\n });\n\n if (!accessToken) {\n // No access token provided, treat as no-op\n return response;\n }\n\n request.headers.set(\"Authorization\", `Bearer ${accessToken.token}`);\n\n challengeState = {\n status: \"complete\",\n scopes: [scope],\n tenantId: parsedChallenge.tenantId,\n };\n\n // We have a token now, so try send the request again\n return next(request);\n }\n\n async function handleCaeChallenge(\n request: PipelineRequest,\n response: PipelineResponse,\n next: SendRequest,\n ): Promise<PipelineResponse> {\n // Cannot handle CAE challenge if a regular challenge has not been completed first\n if (challengeState.status !== \"complete\") {\n return response;\n }\n\n // If status is not 401, this is a no-op\n if (response.status !== 401) {\n return response;\n }\n\n const getTokenOptions = requestToOptions(request);\n\n const challenge = response.headers.get(\"WWW-Authenticate\");\n if (!challenge) {\n return response;\n }\n const { claims: base64EncodedClaims, error }: WWWAuthenticate =\n parseWWWAuthenticateHeader(challenge);\n\n if (error !== \"insufficient_claims\" || base64EncodedClaims === undefined) {\n return response;\n }\n\n const claims = atob(base64EncodedClaims);\n\n const accessToken = await getAccessToken(challengeState.scopes, {\n ...getTokenOptions,\n enableCae: true,\n tenantId: challengeState.tenantId,\n claims,\n });\n\n request.headers.set(\"Authorization\", `Bearer ${accessToken.token}`);\n\n return next(request);\n }\n\n async function sendRequest(\n request: PipelineRequest,\n next: SendRequest,\n ): Promise<PipelineResponse> {\n // Add token if possible\n await authorizeRequest(request);\n\n // Try send request (first attempt)\n let response = await next(request);\n\n // Handle standard challenge if present\n response = await handleChallenge(request, response, next);\n\n // Handle CAE challenge if present\n response = await handleCaeChallenge(request, response, next);\n\n return response;\n }\n\n return {\n name: keyVaultAuthenticationPolicyName,\n sendRequest,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"keyVaultAuthenticationPolicy.js","sourceRoot":"","sources":["../../src/keyVaultAuthenticationPolicy.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n RequestBodyType,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\nimport type { WWWAuthenticate } from \"./parseWWWAuthenticate.js\";\nimport { parseWWWAuthenticateHeader } from \"./parseWWWAuthenticate.js\";\n\nimport type { GetTokenOptions, TokenCredential } from \"@azure/core-auth\";\nimport { createTokenCycler } from \"./tokenCycler.js\";\nimport { logger } from \"./logger.js\";\n\n/**\n * @internal\n * Holds the state of Challenge Auth.\n * When making the first request we force Key Vault to begin a challenge\n * by clearing out the request body and storing it locally.\n *\n * Later on, the authorizeRequestOnChallenge callback will process the\n * challenge and, if ready to resend the original request, reset the body\n * so that it may be sent again.\n *\n * Once a client has succeeded once, we can start skipping CAE.\n */\ntype ChallengeState =\n | {\n status: \"none\";\n }\n | {\n status: \"started\";\n originalBody?: RequestBodyType;\n }\n | {\n status: \"complete\";\n scopes: string[];\n tenantId?: string;\n };\n\n/**\n * Additional options for the challenge based authentication policy.\n */\nexport interface KeyVaultAuthenticationPolicyOptions {\n /**\n * Whether to disable verification that the challenge resource matches the Key Vault or Managed HSM domain.\n *\n * Defaults to false.\n */\n disableChallengeResourceVerification?: boolean;\n}\n\nfunction verifyChallengeResource(scope: string, request: PipelineRequest): void {\n let scopeAsUrl: URL;\n try {\n scopeAsUrl = new URL(scope);\n } catch (e) {\n throw new Error(`The challenge contains invalid scope '${scope}'`);\n }\n\n const requestUrl = new URL(request.url);\n\n if (!requestUrl.hostname.endsWith(`.${scopeAsUrl.hostname}`)) {\n throw new Error(\n `The challenge resource '${scopeAsUrl.hostname}' does not match the requested domain. Set disableChallengeResourceVerification to true in your client options to disable. See https://aka.ms/azsdk/blog/vault-uri for more information.`,\n );\n }\n}\n\n/**\n * Name of the Key Vault authentication policy.\n */\nexport const keyVaultAuthenticationPolicyName = \"keyVaultAuthenticationPolicy\";\n\n/**\n * A custom implementation of the bearer-token authentication policy that handles Key Vault and CAE challenges.\n *\n * Key Vault supports other authentication schemes, but we ensure challenge authentication\n * is used by first sending a copy of the request, without authorization or content.\n *\n * when the challenge is received, it will be authenticated and used to send the original\n * request with authorization.\n *\n * Following the first request of a client, follow-up requests will get the cached token\n * if possible.\n *\n */\nexport function keyVaultAuthenticationPolicy(\n credential: TokenCredential,\n options: KeyVaultAuthenticationPolicyOptions = {},\n): PipelinePolicy {\n const { disableChallengeResourceVerification } = options;\n let challengeState: ChallengeState = { status: \"none\" };\n const getAccessToken = createTokenCycler(credential);\n\n function requestToOptions(request: PipelineRequest): GetTokenOptions {\n return {\n abortSignal: request.abortSignal,\n requestOptions: {\n timeout: request.timeout > 0 ? request.timeout : undefined,\n },\n tracingOptions: request.tracingOptions,\n };\n }\n\n async function authorizeRequest(request: PipelineRequest): Promise<void> {\n const requestOptions: GetTokenOptions = requestToOptions(request);\n\n switch (challengeState.status) {\n case \"none\":\n challengeState = {\n status: \"started\",\n originalBody: request.body,\n };\n request.body = null;\n break;\n case \"started\":\n break; // Retry, we should not overwrite the original body\n case \"complete\": {\n const token = await getAccessToken(challengeState.scopes, {\n ...requestOptions,\n enableCae: true,\n tenantId: challengeState.tenantId,\n });\n if (token) {\n request.headers.set(\"authorization\", `Bearer ${token.token}`);\n }\n break;\n }\n }\n }\n\n async function handleChallenge(\n request: PipelineRequest,\n response: PipelineResponse,\n next: SendRequest,\n ): Promise<PipelineResponse> {\n // If status is not 401, this is a no-op\n if (response.status !== 401) {\n return response;\n }\n\n if (request.body === null && challengeState.status === \"started\") {\n // Reset the original body before doing anything else.\n // Note: If successful status will be \"complete\", otherwise \"none\" will\n // restart the process.\n request.body = challengeState.originalBody;\n }\n\n const getTokenOptions = requestToOptions(request);\n\n const challenge = response.headers.get(\"WWW-Authenticate\");\n if (!challenge) {\n logger.warning(\n \"keyVaultAuthentication policy encountered a 401 response without a corresponding WWW-Authenticate header. This is unexpected. Not handling the 401 response.\",\n );\n return response;\n }\n const parsedChallenge: WWWAuthenticate = parseWWWAuthenticateHeader(challenge);\n\n const scope = parsedChallenge.resource\n ? parsedChallenge.resource + \"/.default\"\n : parsedChallenge.scope;\n\n if (!scope) {\n // Cannot handle this kind of challenge here (if scope is not present, may be a CAE challenge)\n return response;\n }\n\n if (!disableChallengeResourceVerification) {\n verifyChallengeResource(scope, request);\n }\n\n const accessToken = await getAccessToken([scope], {\n ...getTokenOptions,\n enableCae: true,\n tenantId: parsedChallenge.tenantId,\n });\n\n if (!accessToken) {\n // No access token provided, treat as no-op\n return response;\n }\n\n request.headers.set(\"Authorization\", `Bearer ${accessToken.token}`);\n\n challengeState = {\n status: \"complete\",\n scopes: [scope],\n tenantId: parsedChallenge.tenantId,\n };\n\n // We have a token now, so try send the request again\n return next(request);\n }\n\n async function handleCaeChallenge(\n request: PipelineRequest,\n response: PipelineResponse,\n next: SendRequest,\n ): Promise<PipelineResponse> {\n // Cannot handle CAE challenge if a regular challenge has not been completed first\n if (challengeState.status !== \"complete\") {\n return response;\n }\n\n // If status is not 401, this is a no-op\n if (response.status !== 401) {\n return response;\n }\n\n const getTokenOptions = requestToOptions(request);\n\n const challenge = response.headers.get(\"WWW-Authenticate\");\n if (!challenge) {\n return response;\n }\n const { claims: base64EncodedClaims, error }: WWWAuthenticate =\n parseWWWAuthenticateHeader(challenge);\n\n if (error !== \"insufficient_claims\" || base64EncodedClaims === undefined) {\n return response;\n }\n\n const claims = atob(base64EncodedClaims);\n\n const accessToken = await getAccessToken(challengeState.scopes, {\n ...getTokenOptions,\n enableCae: true,\n tenantId: challengeState.tenantId,\n claims,\n });\n\n request.headers.set(\"Authorization\", `Bearer ${accessToken.token}`);\n\n return next(request);\n }\n\n async function sendRequest(\n request: PipelineRequest,\n next: SendRequest,\n ): Promise<PipelineResponse> {\n // Add token if possible\n await authorizeRequest(request);\n\n // Try send request (first attempt)\n let response = await next(request);\n\n // Handle standard challenge if present\n response = await handleChallenge(request, response, next);\n\n // Handle CAE challenge if present\n response = await handleCaeChallenge(request, response, next);\n\n return response;\n }\n\n return {\n name: keyVaultAuthenticationPolicyName,\n sendRequest,\n };\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { PipelinePolicy } from "@azure/core-rest-pipeline";
|
|
2
|
-
import { TokenCredential } from "@azure/core-auth";
|
|
1
|
+
import type { PipelinePolicy } from "@azure/core-rest-pipeline";
|
|
2
|
+
import type { TokenCredential } from "@azure/core-auth";
|
|
3
3
|
/**
|
|
4
4
|
* Additional options for the challenge based authentication policy.
|
|
5
5
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyVaultAuthenticationPolicy.d.ts","sourceRoot":"","sources":["../../src/keyVaultAuthenticationPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,
|
|
1
|
+
{"version":3,"file":"keyVaultAuthenticationPolicy.d.ts","sourceRoot":"","sources":["../../src/keyVaultAuthenticationPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,cAAc,EAKf,MAAM,2BAA2B,CAAC;AAInC,OAAO,KAAK,EAAmB,eAAe,EAAE,MAAM,kBAAkB,CAAC;AA8BzE;;GAEG;AACH,MAAM,WAAW,mCAAmC;IAClD;;;;OAIG;IACH,oCAAoC,CAAC,EAAE,OAAO,CAAC;CAChD;AAmBD;;GAEG;AACH,eAAO,MAAM,gCAAgC,iCAAiC,CAAC;AAE/E;;;;;;;;;;;;GAYG;AACH,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,eAAe,EAC3B,OAAO,GAAE,mCAAwC,GAChD,cAAc,CA2KhB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyVaultAuthenticationPolicy.js","sourceRoot":"","sources":["../../src/keyVaultAuthenticationPolicy.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n RequestBodyType,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\nimport { WWWAuthenticate, parseWWWAuthenticateHeader } from \"./parseWWWAuthenticate.js\";\n\nimport { GetTokenOptions, TokenCredential } from \"@azure/core-auth\";\nimport { createTokenCycler } from \"./tokenCycler.js\";\nimport { logger } from \"./logger.js\";\n\n/**\n * @internal\n * Holds the state of Challenge Auth.\n * When making the first request we force Key Vault to begin a challenge\n * by clearing out the request body and storing it locally.\n *\n * Later on, the authorizeRequestOnChallenge callback will process the\n * challenge and, if ready to resend the original request, reset the body\n * so that it may be sent again.\n *\n * Once a client has succeeded once, we can start skipping CAE.\n */\ntype ChallengeState =\n | {\n status: \"none\";\n }\n | {\n status: \"started\";\n originalBody?: RequestBodyType;\n }\n | {\n status: \"complete\";\n scopes: string[];\n tenantId?: string;\n };\n\n/**\n * Additional options for the challenge based authentication policy.\n */\nexport interface KeyVaultAuthenticationPolicyOptions {\n /**\n * Whether to disable verification that the challenge resource matches the Key Vault or Managed HSM domain.\n *\n * Defaults to false.\n */\n disableChallengeResourceVerification?: boolean;\n}\n\nfunction verifyChallengeResource(scope: string, request: PipelineRequest): void {\n let scopeAsUrl: URL;\n try {\n scopeAsUrl = new URL(scope);\n } catch (e) {\n throw new Error(`The challenge contains invalid scope '${scope}'`);\n }\n\n const requestUrl = new URL(request.url);\n\n if (!requestUrl.hostname.endsWith(`.${scopeAsUrl.hostname}`)) {\n throw new Error(\n `The challenge resource '${scopeAsUrl.hostname}' does not match the requested domain. Set disableChallengeResourceVerification to true in your client options to disable. See https://aka.ms/azsdk/blog/vault-uri for more information.`,\n );\n }\n}\n\n/**\n * Name of the Key Vault authentication policy.\n */\nexport const keyVaultAuthenticationPolicyName = \"keyVaultAuthenticationPolicy\";\n\n/**\n * A custom implementation of the bearer-token authentication policy that handles Key Vault and CAE challenges.\n *\n * Key Vault supports other authentication schemes, but we ensure challenge authentication\n * is used by first sending a copy of the request, without authorization or content.\n *\n * when the challenge is received, it will be authenticated and used to send the original\n * request with authorization.\n *\n * Following the first request of a client, follow-up requests will get the cached token\n * if possible.\n *\n */\nexport function keyVaultAuthenticationPolicy(\n credential: TokenCredential,\n options: KeyVaultAuthenticationPolicyOptions = {},\n): PipelinePolicy {\n const { disableChallengeResourceVerification } = options;\n let challengeState: ChallengeState = { status: \"none\" };\n const getAccessToken = createTokenCycler(credential);\n\n function requestToOptions(request: PipelineRequest): GetTokenOptions {\n return {\n abortSignal: request.abortSignal,\n requestOptions: {\n timeout: request.timeout > 0 ? request.timeout : undefined,\n },\n tracingOptions: request.tracingOptions,\n };\n }\n\n async function authorizeRequest(request: PipelineRequest): Promise<void> {\n const requestOptions: GetTokenOptions = requestToOptions(request);\n\n switch (challengeState.status) {\n case \"none\":\n challengeState = {\n status: \"started\",\n originalBody: request.body,\n };\n request.body = null;\n break;\n case \"started\":\n break; // Retry, we should not overwrite the original body\n case \"complete\": {\n const token = await getAccessToken(challengeState.scopes, {\n ...requestOptions,\n enableCae: true,\n tenantId: challengeState.tenantId,\n });\n if (token) {\n request.headers.set(\"authorization\", `Bearer ${token.token}`);\n }\n break;\n }\n }\n }\n\n async function handleChallenge(\n request: PipelineRequest,\n response: PipelineResponse,\n next: SendRequest,\n ): Promise<PipelineResponse> {\n // If status is not 401, this is a no-op\n if (response.status !== 401) {\n return response;\n }\n\n if (request.body === null && challengeState.status === \"started\") {\n // Reset the original body before doing anything else.\n // Note: If successful status will be \"complete\", otherwise \"none\" will\n // restart the process.\n request.body = challengeState.originalBody;\n }\n\n const getTokenOptions = requestToOptions(request);\n\n const challenge = response.headers.get(\"WWW-Authenticate\");\n if (!challenge) {\n logger.warning(\n \"keyVaultAuthentication policy encountered a 401 response without a corresponding WWW-Authenticate header. This is unexpected. Not handling the 401 response.\",\n );\n return response;\n }\n const parsedChallenge: WWWAuthenticate = parseWWWAuthenticateHeader(challenge);\n\n const scope = parsedChallenge.resource\n ? parsedChallenge.resource + \"/.default\"\n : parsedChallenge.scope;\n\n if (!scope) {\n // Cannot handle this kind of challenge here (if scope is not present, may be a CAE challenge)\n return response;\n }\n\n if (!disableChallengeResourceVerification) {\n verifyChallengeResource(scope, request);\n }\n\n const accessToken = await getAccessToken([scope], {\n ...getTokenOptions,\n enableCae: true,\n tenantId: parsedChallenge.tenantId,\n });\n\n if (!accessToken) {\n // No access token provided, treat as no-op\n return response;\n }\n\n request.headers.set(\"Authorization\", `Bearer ${accessToken.token}`);\n\n challengeState = {\n status: \"complete\",\n scopes: [scope],\n tenantId: parsedChallenge.tenantId,\n };\n\n // We have a token now, so try send the request again\n return next(request);\n }\n\n async function handleCaeChallenge(\n request: PipelineRequest,\n response: PipelineResponse,\n next: SendRequest,\n ): Promise<PipelineResponse> {\n // Cannot handle CAE challenge if a regular challenge has not been completed first\n if (challengeState.status !== \"complete\") {\n return response;\n }\n\n // If status is not 401, this is a no-op\n if (response.status !== 401) {\n return response;\n }\n\n const getTokenOptions = requestToOptions(request);\n\n const challenge = response.headers.get(\"WWW-Authenticate\");\n if (!challenge) {\n return response;\n }\n const { claims: base64EncodedClaims, error }: WWWAuthenticate =\n parseWWWAuthenticateHeader(challenge);\n\n if (error !== \"insufficient_claims\" || base64EncodedClaims === undefined) {\n return response;\n }\n\n const claims = atob(base64EncodedClaims);\n\n const accessToken = await getAccessToken(challengeState.scopes, {\n ...getTokenOptions,\n enableCae: true,\n tenantId: challengeState.tenantId,\n claims,\n });\n\n request.headers.set(\"Authorization\", `Bearer ${accessToken.token}`);\n\n return next(request);\n }\n\n async function sendRequest(\n request: PipelineRequest,\n next: SendRequest,\n ): Promise<PipelineResponse> {\n // Add token if possible\n await authorizeRequest(request);\n\n // Try send request (first attempt)\n let response = await next(request);\n\n // Handle standard challenge if present\n response = await handleChallenge(request, response, next);\n\n // Handle CAE challenge if present\n response = await handleCaeChallenge(request, response, next);\n\n return response;\n }\n\n return {\n name: keyVaultAuthenticationPolicyName,\n sendRequest,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"keyVaultAuthenticationPolicy.js","sourceRoot":"","sources":["../../src/keyVaultAuthenticationPolicy.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n PipelinePolicy,\n PipelineRequest,\n PipelineResponse,\n RequestBodyType,\n SendRequest,\n} from \"@azure/core-rest-pipeline\";\nimport type { WWWAuthenticate } from \"./parseWWWAuthenticate.js\";\nimport { parseWWWAuthenticateHeader } from \"./parseWWWAuthenticate.js\";\n\nimport type { GetTokenOptions, TokenCredential } from \"@azure/core-auth\";\nimport { createTokenCycler } from \"./tokenCycler.js\";\nimport { logger } from \"./logger.js\";\n\n/**\n * @internal\n * Holds the state of Challenge Auth.\n * When making the first request we force Key Vault to begin a challenge\n * by clearing out the request body and storing it locally.\n *\n * Later on, the authorizeRequestOnChallenge callback will process the\n * challenge and, if ready to resend the original request, reset the body\n * so that it may be sent again.\n *\n * Once a client has succeeded once, we can start skipping CAE.\n */\ntype ChallengeState =\n | {\n status: \"none\";\n }\n | {\n status: \"started\";\n originalBody?: RequestBodyType;\n }\n | {\n status: \"complete\";\n scopes: string[];\n tenantId?: string;\n };\n\n/**\n * Additional options for the challenge based authentication policy.\n */\nexport interface KeyVaultAuthenticationPolicyOptions {\n /**\n * Whether to disable verification that the challenge resource matches the Key Vault or Managed HSM domain.\n *\n * Defaults to false.\n */\n disableChallengeResourceVerification?: boolean;\n}\n\nfunction verifyChallengeResource(scope: string, request: PipelineRequest): void {\n let scopeAsUrl: URL;\n try {\n scopeAsUrl = new URL(scope);\n } catch (e) {\n throw new Error(`The challenge contains invalid scope '${scope}'`);\n }\n\n const requestUrl = new URL(request.url);\n\n if (!requestUrl.hostname.endsWith(`.${scopeAsUrl.hostname}`)) {\n throw new Error(\n `The challenge resource '${scopeAsUrl.hostname}' does not match the requested domain. Set disableChallengeResourceVerification to true in your client options to disable. See https://aka.ms/azsdk/blog/vault-uri for more information.`,\n );\n }\n}\n\n/**\n * Name of the Key Vault authentication policy.\n */\nexport const keyVaultAuthenticationPolicyName = \"keyVaultAuthenticationPolicy\";\n\n/**\n * A custom implementation of the bearer-token authentication policy that handles Key Vault and CAE challenges.\n *\n * Key Vault supports other authentication schemes, but we ensure challenge authentication\n * is used by first sending a copy of the request, without authorization or content.\n *\n * when the challenge is received, it will be authenticated and used to send the original\n * request with authorization.\n *\n * Following the first request of a client, follow-up requests will get the cached token\n * if possible.\n *\n */\nexport function keyVaultAuthenticationPolicy(\n credential: TokenCredential,\n options: KeyVaultAuthenticationPolicyOptions = {},\n): PipelinePolicy {\n const { disableChallengeResourceVerification } = options;\n let challengeState: ChallengeState = { status: \"none\" };\n const getAccessToken = createTokenCycler(credential);\n\n function requestToOptions(request: PipelineRequest): GetTokenOptions {\n return {\n abortSignal: request.abortSignal,\n requestOptions: {\n timeout: request.timeout > 0 ? request.timeout : undefined,\n },\n tracingOptions: request.tracingOptions,\n };\n }\n\n async function authorizeRequest(request: PipelineRequest): Promise<void> {\n const requestOptions: GetTokenOptions = requestToOptions(request);\n\n switch (challengeState.status) {\n case \"none\":\n challengeState = {\n status: \"started\",\n originalBody: request.body,\n };\n request.body = null;\n break;\n case \"started\":\n break; // Retry, we should not overwrite the original body\n case \"complete\": {\n const token = await getAccessToken(challengeState.scopes, {\n ...requestOptions,\n enableCae: true,\n tenantId: challengeState.tenantId,\n });\n if (token) {\n request.headers.set(\"authorization\", `Bearer ${token.token}`);\n }\n break;\n }\n }\n }\n\n async function handleChallenge(\n request: PipelineRequest,\n response: PipelineResponse,\n next: SendRequest,\n ): Promise<PipelineResponse> {\n // If status is not 401, this is a no-op\n if (response.status !== 401) {\n return response;\n }\n\n if (request.body === null && challengeState.status === \"started\") {\n // Reset the original body before doing anything else.\n // Note: If successful status will be \"complete\", otherwise \"none\" will\n // restart the process.\n request.body = challengeState.originalBody;\n }\n\n const getTokenOptions = requestToOptions(request);\n\n const challenge = response.headers.get(\"WWW-Authenticate\");\n if (!challenge) {\n logger.warning(\n \"keyVaultAuthentication policy encountered a 401 response without a corresponding WWW-Authenticate header. This is unexpected. Not handling the 401 response.\",\n );\n return response;\n }\n const parsedChallenge: WWWAuthenticate = parseWWWAuthenticateHeader(challenge);\n\n const scope = parsedChallenge.resource\n ? parsedChallenge.resource + \"/.default\"\n : parsedChallenge.scope;\n\n if (!scope) {\n // Cannot handle this kind of challenge here (if scope is not present, may be a CAE challenge)\n return response;\n }\n\n if (!disableChallengeResourceVerification) {\n verifyChallengeResource(scope, request);\n }\n\n const accessToken = await getAccessToken([scope], {\n ...getTokenOptions,\n enableCae: true,\n tenantId: parsedChallenge.tenantId,\n });\n\n if (!accessToken) {\n // No access token provided, treat as no-op\n return response;\n }\n\n request.headers.set(\"Authorization\", `Bearer ${accessToken.token}`);\n\n challengeState = {\n status: \"complete\",\n scopes: [scope],\n tenantId: parsedChallenge.tenantId,\n };\n\n // We have a token now, so try send the request again\n return next(request);\n }\n\n async function handleCaeChallenge(\n request: PipelineRequest,\n response: PipelineResponse,\n next: SendRequest,\n ): Promise<PipelineResponse> {\n // Cannot handle CAE challenge if a regular challenge has not been completed first\n if (challengeState.status !== \"complete\") {\n return response;\n }\n\n // If status is not 401, this is a no-op\n if (response.status !== 401) {\n return response;\n }\n\n const getTokenOptions = requestToOptions(request);\n\n const challenge = response.headers.get(\"WWW-Authenticate\");\n if (!challenge) {\n return response;\n }\n const { claims: base64EncodedClaims, error }: WWWAuthenticate =\n parseWWWAuthenticateHeader(challenge);\n\n if (error !== \"insufficient_claims\" || base64EncodedClaims === undefined) {\n return response;\n }\n\n const claims = atob(base64EncodedClaims);\n\n const accessToken = await getAccessToken(challengeState.scopes, {\n ...getTokenOptions,\n enableCae: true,\n tenantId: challengeState.tenantId,\n claims,\n });\n\n request.headers.set(\"Authorization\", `Bearer ${accessToken.token}`);\n\n return next(request);\n }\n\n async function sendRequest(\n request: PipelineRequest,\n next: SendRequest,\n ): Promise<PipelineResponse> {\n // Add token if possible\n await authorizeRequest(request);\n\n // Try send request (first attempt)\n let response = await next(request);\n\n // Handle standard challenge if present\n response = await handleChallenge(request, response, next);\n\n // Handle CAE challenge if present\n response = await handleCaeChallenge(request, response, next);\n\n return response;\n }\n\n return {\n name: keyVaultAuthenticationPolicyName,\n sendRequest,\n };\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { PipelinePolicy } from '@azure/core-rest-pipeline';
|
|
2
|
-
import { TokenCredential } from '@azure/core-auth';
|
|
1
|
+
import type { PipelinePolicy } from '@azure/core-rest-pipeline';
|
|
2
|
+
import type { TokenCredential } from '@azure/core-auth';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* A custom implementation of the bearer-token authentication policy that handles Key Vault and CAE challenges.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@azure/keyvault-common",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1-alpha.20241112.1",
|
|
4
4
|
"description": "Common internal functionality for all of the Azure Key Vault clients in the Azure SDK for JavaScript",
|
|
5
5
|
"sdk-type": "client",
|
|
6
6
|
"author": "Microsoft Corporation",
|
|
@@ -10,12 +10,11 @@
|
|
|
10
10
|
"module": "./dist/esm/index.js",
|
|
11
11
|
"types": "./dist/commonjs/index.d.ts",
|
|
12
12
|
"scripts": {
|
|
13
|
-
"audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit",
|
|
14
13
|
"build": "npm run clean && dev-tool run build-package && dev-tool run extract-api",
|
|
15
14
|
"build:samples": "echo skipped",
|
|
16
15
|
"build:test": "npm run clean && dev-tool run build-package",
|
|
17
16
|
"check-format": "dev-tool run vendored prettier --list-different --config ../../../.prettierrc.json --ignore-path ../../../.prettierignore \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"",
|
|
18
|
-
"clean": "rimraf --glob dist dist-* temp types *.tgz *.log",
|
|
17
|
+
"clean": "dev-tool run vendored rimraf --glob dist dist-* temp types *.tgz *.log",
|
|
19
18
|
"execute:samples": "dev-tool samples run samples-dev",
|
|
20
19
|
"extract-api": "dev-tool run build-package && dev-tool run extract-api",
|
|
21
20
|
"format": "dev-tool run vendored prettier --write --config ../../../.prettierrc.json --ignore-path ../../../.prettierignore \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"",
|
|
@@ -64,17 +63,14 @@
|
|
|
64
63
|
"@azure/core-util": "^1.10.0"
|
|
65
64
|
},
|
|
66
65
|
"devDependencies": {
|
|
67
|
-
"@azure-tools/test-utils-vitest": "
|
|
68
|
-
"@azure/dev-tool": "
|
|
69
|
-
"@azure/eslint-plugin-azure-sdk": "
|
|
70
|
-
"@microsoft/api-extractor": "^7.31.1",
|
|
66
|
+
"@azure-tools/test-utils-vitest": ">=1.0.0-alpha <1.0.0-alphb",
|
|
67
|
+
"@azure/dev-tool": ">=1.0.0-alpha <1.0.0-alphb",
|
|
68
|
+
"@azure/eslint-plugin-azure-sdk": ">=3.0.0-alpha <3.0.0-alphb",
|
|
71
69
|
"@types/node": "^18.0.0",
|
|
72
70
|
"@vitest/browser": "^2.0.5",
|
|
73
71
|
"@vitest/coverage-istanbul": "^2.0.5",
|
|
74
|
-
"cross-env": "^7.0.2",
|
|
75
72
|
"eslint": "^9.9.0",
|
|
76
73
|
"playwright": "^1.46.0",
|
|
77
|
-
"rimraf": "^5.0.5",
|
|
78
74
|
"typescript": "~5.6.2",
|
|
79
75
|
"vitest": "^2.0.5"
|
|
80
76
|
},
|