@azure/core-client 1.3.1 → 1.3.3-alpha.20211101.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Release History
2
2
 
3
+ ## 1.3.3 (Unreleased)
4
+
5
+ ### Features Added
6
+
7
+ - Added a new function `authorizeRequestOnClaimChallenge`, that can be used with the `@azure/core-rest-pipeline`'s `bearerTokenAuthenticationPolicy` to support [Continuous Access Evaluation (CAE) challenges](https://docs.microsoft.com/azure/active-directory/conditional-access/concept-continuous-access-evaluation).
8
+ - Call the `bearerTokenAuthenticationPolicy` with the following options: `bearerTokenAuthenticationPolicy({ authorizeRequestOnChallenge: authorizeRequestOnClaimChallenge })`. Once provided, the `bearerTokenAuthenticationPolicy` policy will internally handle Continuous Access Evaluation (CAE) challenges. When it can't complete a challenge it will return the 401 (unauthorized) response from ARM.
9
+
10
+ ### Breaking Changes
11
+
12
+ ### Bugs Fixed
13
+
14
+ ### Other Changes
15
+
16
+ ## 1.3.2 (2021-10-25)
17
+
18
+ ### Bugs Fixed
19
+
20
+ - Skip query parameter replacement for absolute URLs. [PR #18310](https://github.com/Azure/azure-sdk-for-js/pull/18310)
21
+
3
22
  ## 1.3.1 (2021-09-30)
4
23
 
5
24
  ### Other Changes
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var coreRestPipeline = require('@azure/core-rest-pipeline');
6
+ var logger$1 = require('@azure/logger');
6
7
  require('@azure/core-asynciterator-polyfill');
7
8
 
8
9
  // Copyright (c) Microsoft Corporation.
@@ -140,6 +141,13 @@ function encodeByteArray(value) {
140
141
  function decodeString(value) {
141
142
  return Buffer.from(value, "base64");
142
143
  }
144
+ /**
145
+ * Decodes a base64 string into a string.
146
+ * @param value - the base64 string to decode
147
+ */
148
+ function decodeStringToString(value) {
149
+ return Buffer.from(value, "base64").toString();
150
+ }
143
151
 
144
152
  // Copyright (c) Microsoft Corporation.
145
153
  // Licensed under the MIT license.
@@ -1145,6 +1153,7 @@ const CollectionFormatToDelimiterMap = {
1145
1153
  };
1146
1154
  function getRequestUrl(baseUri, operationSpec, operationArguments, fallbackObject) {
1147
1155
  const urlReplacements = calculateUrlReplacements(operationSpec, operationArguments, fallbackObject);
1156
+ let isAbsolutePath = false;
1148
1157
  let requestUrl = replaceAll(baseUri, urlReplacements);
1149
1158
  if (operationSpec.path) {
1150
1159
  const path = replaceAll(operationSpec.path, urlReplacements);
@@ -1153,13 +1162,20 @@ function getRequestUrl(baseUri, operationSpec, operationArguments, fallbackObjec
1153
1162
  // ignore the baseUri.
1154
1163
  if (isAbsoluteUrl(path)) {
1155
1164
  requestUrl = path;
1165
+ isAbsolutePath = true;
1156
1166
  }
1157
1167
  else {
1158
1168
  requestUrl = appendPath(requestUrl, path);
1159
1169
  }
1160
1170
  }
1161
1171
  const { queryParams, sequenceParams } = calculateQueryParameters(operationSpec, operationArguments, fallbackObject);
1162
- requestUrl = appendQueryParams(requestUrl, queryParams, sequenceParams);
1172
+ /**
1173
+ * Notice that this call sets the `noOverwrite` parameter to true if the `requestUrl`
1174
+ * is an absolute path. This ensures that existing query parameter values in `requestUrl`
1175
+ * do not get overwritten. On the other hand when `requestUrl` is not absolute path, it
1176
+ * is still being built so there is nothing to overwrite.
1177
+ */
1178
+ requestUrl = appendQueryParams(requestUrl, queryParams, sequenceParams, isAbsolutePath);
1163
1179
  return requestUrl;
1164
1180
  }
1165
1181
  function replaceAll(input, replacements) {
@@ -1297,7 +1313,7 @@ function simpleParseQueryParams(queryString) {
1297
1313
  return result;
1298
1314
  }
1299
1315
  /** @internal */
1300
- function appendQueryParams(url, queryParams, sequenceParams) {
1316
+ function appendQueryParams(url, queryParams, sequenceParams, noOverwrite = false) {
1301
1317
  if (queryParams.size === 0) {
1302
1318
  return url;
1303
1319
  }
@@ -1319,14 +1335,15 @@ function appendQueryParams(url, queryParams, sequenceParams) {
1319
1335
  }
1320
1336
  }
1321
1337
  else if (existingValue) {
1322
- let newValue = value;
1323
1338
  if (Array.isArray(value)) {
1324
1339
  value.unshift(existingValue);
1325
1340
  }
1326
1341
  else if (sequenceParams.has(name)) {
1327
- newValue = [existingValue, value];
1342
+ combinedParams.set(name, [existingValue, value]);
1343
+ }
1344
+ if (!noOverwrite) {
1345
+ combinedParams.set(name, value);
1328
1346
  }
1329
- combinedParams.set(name, newValue);
1330
1347
  }
1331
1348
  else {
1332
1349
  combinedParams.set(name, value);
@@ -1874,10 +1891,77 @@ function getCredentialScopes(options) {
1874
1891
  return undefined;
1875
1892
  }
1876
1893
 
1894
+ // Copyright (c) Microsoft Corporation.
1895
+ const logger = logger$1.createClientLogger("authorizeRequestOnClaimChallenge");
1896
+ /**
1897
+ * Converts: `Bearer a="b", c="d", Bearer d="e", f="g"`.
1898
+ * Into: `[ { a: 'b', c: 'd' }, { d: 'e', f: 'g' } ]`.
1899
+ *
1900
+ * @internal
1901
+ */
1902
+ function parseCAEChallenge(challenges) {
1903
+ const bearerChallenges = `, ${challenges.trim()}`.split(", Bearer ").filter((x) => x);
1904
+ return bearerChallenges.map((challenge) => {
1905
+ const challengeParts = `${challenge.trim()}, `.split('", ').filter((x) => x);
1906
+ const keyValuePairs = challengeParts.map((keyValue) => (([key, value]) => ({ [key]: value }))(keyValue.trim().split('="')));
1907
+ // Key-value pairs to plain object:
1908
+ return keyValuePairs.reduce((a, b) => (Object.assign(Object.assign({}, a), b)), {});
1909
+ });
1910
+ }
1911
+ /**
1912
+ * This function can be used as a callback for the `bearerTokenAuthenticationPolicy` of `@azure/core-rest-pipeline`, to support CAE challenges:
1913
+ * [Continuous Access Evaluation](https://docs.microsoft.com/azure/active-directory/conditional-access/concept-continuous-access-evaluation).
1914
+ *
1915
+ * Call the `bearerTokenAuthenticationPolicy` with the following options:
1916
+ *
1917
+ * ```ts
1918
+ * import { bearerTokenAuthenticationPolicy } from "@azure/core-rest-pipeline";
1919
+ * import { authorizeRequestOnClaimChallenge } from "@azure/core-client";
1920
+ *
1921
+ * const bearerTokenAuthenticationPolicy = bearerTokenAuthenticationPolicy({
1922
+ * authorizeRequestOnChallenge: authorizeRequestOnClaimChallenge
1923
+ * });
1924
+ * ```
1925
+ *
1926
+ * Once provided, the `bearerTokenAuthenticationPolicy` policy will internally handle Continuous Access Evaluation (CAE) challenges.
1927
+ * When it can't complete a challenge it will return the 401 (unauthorized) response from ARM.
1928
+ *
1929
+ * Example challenge with claims:
1930
+ *
1931
+ * ```
1932
+ * Bearer authorization_uri="https://login.windows-ppe.net/", error="invalid_token",
1933
+ * error_description="User session has been revoked",
1934
+ * claims="eyJhY2Nlc3NfdG9rZW4iOnsibmJmIjp7ImVzc2VudGlhbCI6dHJ1ZSwgInZhbHVlIjoiMTYwMzc0MjgwMCJ9fX0="
1935
+ * ```
1936
+ */
1937
+ async function authorizeRequestOnClaimChallenge(onChallengeOptions) {
1938
+ const { scopes, response } = onChallengeOptions;
1939
+ const challenge = response.headers.get("WWW-Authenticate");
1940
+ if (!challenge) {
1941
+ logger.info(`The WWW-Authenticate header was missing. Failed to perform the Continuous Access Evaluation authentication flow.`);
1942
+ return false;
1943
+ }
1944
+ const challenges = parseCAEChallenge(challenge) || [];
1945
+ const parsedChallenge = challenges.find((x) => x.claims);
1946
+ if (!parsedChallenge) {
1947
+ logger.info(`The WWW-Authenticate header was missing the necessary "claims" to perform the Continuous Access Evaluation authentication flow.`);
1948
+ return false;
1949
+ }
1950
+ const accessToken = await onChallengeOptions.getAccessToken(parsedChallenge.scope ? [parsedChallenge.scope] : scopes, {
1951
+ claims: decodeStringToString(parsedChallenge.claims)
1952
+ });
1953
+ if (!accessToken) {
1954
+ return false;
1955
+ }
1956
+ onChallengeOptions.request.headers.set("Authorization", `Bearer ${accessToken.token}`);
1957
+ return true;
1958
+ }
1959
+
1877
1960
  exports.MapperTypeNames = MapperTypeNames;
1878
1961
  exports.ServiceClient = ServiceClient;
1879
1962
  exports.XML_ATTRKEY = XML_ATTRKEY;
1880
1963
  exports.XML_CHARKEY = XML_CHARKEY;
1964
+ exports.authorizeRequestOnClaimChallenge = authorizeRequestOnClaimChallenge;
1881
1965
  exports.createClientPipeline = createClientPipeline;
1882
1966
  exports.createSerializer = createSerializer;
1883
1967
  exports.deserializationPolicy = deserializationPolicy;