@azure/core-client 1.3.2-alpha.20211001.1 → 1.4.0-alpha.20211123.3

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,15 +1,24 @@
1
1
  # Release History
2
2
 
3
- ## 1.3.2 (Unreleased)
3
+ ## 1.4.0 (Unreleased)
4
4
 
5
5
  ### Features Added
6
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
+
7
10
  ### Breaking Changes
8
11
 
9
12
  ### Bugs Fixed
10
13
 
11
14
  ### Other Changes
12
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
+
13
22
  ## 1.3.1 (2021-09-30)
14
23
 
15
24
  ### Other Changes
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Azure Core Service client library for JavaScript (Experimental)
1
+ # Azure Core Service client library for JavaScript
2
2
 
3
3
  This library is primarily intended to be used in code generated by [AutoRest](https://github.com/Azure/Autorest) and [`autorest.typescript`](https://github.com/Azure/autorest.typescript).
4
4
 
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);
@@ -1337,12 +1354,15 @@ function appendQueryParams(url, queryParams, sequenceParams) {
1337
1354
  if (typeof value === "string") {
1338
1355
  searchPieces.push(`${name}=${value}`);
1339
1356
  }
1340
- else {
1357
+ else if (Array.isArray(value)) {
1341
1358
  // QUIRK: If we get an array of values, include multiple key/value pairs
1342
1359
  for (const subValue of value) {
1343
1360
  searchPieces.push(`${name}=${subValue}`);
1344
1361
  }
1345
1362
  }
1363
+ else {
1364
+ searchPieces.push(`${name}=${value}`);
1365
+ }
1346
1366
  }
1347
1367
  // QUIRK: we have to set search manually as searchParams will encode comma when it shouldn't.
1348
1368
  parsedUrl.search = searchPieces.length ? `?${searchPieces.join("&")}` : "";
@@ -1874,10 +1894,77 @@ function getCredentialScopes(options) {
1874
1894
  return undefined;
1875
1895
  }
1876
1896
 
1897
+ // Copyright (c) Microsoft Corporation.
1898
+ const logger = logger$1.createClientLogger("authorizeRequestOnClaimChallenge");
1899
+ /**
1900
+ * Converts: `Bearer a="b", c="d", Bearer d="e", f="g"`.
1901
+ * Into: `[ { a: 'b', c: 'd' }, { d: 'e', f: 'g' } ]`.
1902
+ *
1903
+ * @internal
1904
+ */
1905
+ function parseCAEChallenge(challenges) {
1906
+ const bearerChallenges = `, ${challenges.trim()}`.split(", Bearer ").filter((x) => x);
1907
+ return bearerChallenges.map((challenge) => {
1908
+ const challengeParts = `${challenge.trim()}, `.split('", ').filter((x) => x);
1909
+ const keyValuePairs = challengeParts.map((keyValue) => (([key, value]) => ({ [key]: value }))(keyValue.trim().split('="')));
1910
+ // Key-value pairs to plain object:
1911
+ return keyValuePairs.reduce((a, b) => (Object.assign(Object.assign({}, a), b)), {});
1912
+ });
1913
+ }
1914
+ /**
1915
+ * This function can be used as a callback for the `bearerTokenAuthenticationPolicy` of `@azure/core-rest-pipeline`, to support CAE challenges:
1916
+ * [Continuous Access Evaluation](https://docs.microsoft.com/azure/active-directory/conditional-access/concept-continuous-access-evaluation).
1917
+ *
1918
+ * Call the `bearerTokenAuthenticationPolicy` with the following options:
1919
+ *
1920
+ * ```ts
1921
+ * import { bearerTokenAuthenticationPolicy } from "@azure/core-rest-pipeline";
1922
+ * import { authorizeRequestOnClaimChallenge } from "@azure/core-client";
1923
+ *
1924
+ * const bearerTokenAuthenticationPolicy = bearerTokenAuthenticationPolicy({
1925
+ * authorizeRequestOnChallenge: authorizeRequestOnClaimChallenge
1926
+ * });
1927
+ * ```
1928
+ *
1929
+ * Once provided, the `bearerTokenAuthenticationPolicy` policy will internally handle Continuous Access Evaluation (CAE) challenges.
1930
+ * When it can't complete a challenge it will return the 401 (unauthorized) response from ARM.
1931
+ *
1932
+ * Example challenge with claims:
1933
+ *
1934
+ * ```
1935
+ * Bearer authorization_uri="https://login.windows-ppe.net/", error="invalid_token",
1936
+ * error_description="User session has been revoked",
1937
+ * claims="eyJhY2Nlc3NfdG9rZW4iOnsibmJmIjp7ImVzc2VudGlhbCI6dHJ1ZSwgInZhbHVlIjoiMTYwMzc0MjgwMCJ9fX0="
1938
+ * ```
1939
+ */
1940
+ async function authorizeRequestOnClaimChallenge(onChallengeOptions) {
1941
+ const { scopes, response } = onChallengeOptions;
1942
+ const challenge = response.headers.get("WWW-Authenticate");
1943
+ if (!challenge) {
1944
+ logger.info(`The WWW-Authenticate header was missing. Failed to perform the Continuous Access Evaluation authentication flow.`);
1945
+ return false;
1946
+ }
1947
+ const challenges = parseCAEChallenge(challenge) || [];
1948
+ const parsedChallenge = challenges.find((x) => x.claims);
1949
+ if (!parsedChallenge) {
1950
+ logger.info(`The WWW-Authenticate header was missing the necessary "claims" to perform the Continuous Access Evaluation authentication flow.`);
1951
+ return false;
1952
+ }
1953
+ const accessToken = await onChallengeOptions.getAccessToken(parsedChallenge.scope ? [parsedChallenge.scope] : scopes, {
1954
+ claims: decodeStringToString(parsedChallenge.claims)
1955
+ });
1956
+ if (!accessToken) {
1957
+ return false;
1958
+ }
1959
+ onChallengeOptions.request.headers.set("Authorization", `Bearer ${accessToken.token}`);
1960
+ return true;
1961
+ }
1962
+
1877
1963
  exports.MapperTypeNames = MapperTypeNames;
1878
1964
  exports.ServiceClient = ServiceClient;
1879
1965
  exports.XML_ATTRKEY = XML_ATTRKEY;
1880
1966
  exports.XML_CHARKEY = XML_CHARKEY;
1967
+ exports.authorizeRequestOnClaimChallenge = authorizeRequestOnClaimChallenge;
1881
1968
  exports.createClientPipeline = createClientPipeline;
1882
1969
  exports.createSerializer = createSerializer;
1883
1970
  exports.deserializationPolicy = deserializationPolicy;