From 6cf9c139b3d3492da6828f50e4581cb4f736d960 Mon Sep 17 00:00:00 2001 From: Mary Gao Date: Mon, 24 Jan 2022 13:49:27 +0800 Subject: [PATCH 01/15] Set up CI with Azure Pipelines [skip ci] --- azure-pipelines.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 azure-pipelines.yml diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 000000000000..01442dd61aa0 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,21 @@ +# Node.js +# Build a general Node.js project with npm. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript + +trigger: +- main + +pool: + vmImage: ubuntu-latest + +steps: +- task: NodeTool@0 + inputs: + versionSpec: '10.x' + displayName: 'Install Node.js' + +- script: | + npm install + npm run build + displayName: 'npm install and build' From 6d6718963f27771e7ede9e229759de22aaeef2ee Mon Sep 17 00:00:00 2001 From: Mary Gao Date: Tue, 21 Jun 2022 19:27:00 +0800 Subject: [PATCH 02/15] UPdate changes --- sdk/core/core-auth/src/tokenCredential.ts | 5 +++++ .../src/policies/bearerTokenAuthenticationPolicy.ts | 4 +++- sdk/core/core-rest-pipeline/src/util/tokenCycler.ts | 5 +++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/sdk/core/core-auth/src/tokenCredential.ts b/sdk/core/core-auth/src/tokenCredential.ts index 2d8416b9cc1f..d55c2ea7a9da 100644 --- a/sdk/core/core-auth/src/tokenCredential.ts +++ b/sdk/core/core-auth/src/tokenCredential.ts @@ -52,6 +52,11 @@ export interface GetTokenOptions { * Allows specifying a tenantId. Useful to handle challenges that provide tenant Id hints. */ tenantId?: string; + + /** + * Detailed claims to perform the Continuous Access Evaluation authentication flow + */ + claims?: string; } /** diff --git a/sdk/core/core-rest-pipeline/src/policies/bearerTokenAuthenticationPolicy.ts b/sdk/core/core-rest-pipeline/src/policies/bearerTokenAuthenticationPolicy.ts index ca008f1679c8..c8b374a9e801 100644 --- a/sdk/core/core-rest-pipeline/src/policies/bearerTokenAuthenticationPolicy.ts +++ b/sdk/core/core-rest-pipeline/src/policies/bearerTokenAuthenticationPolicy.ts @@ -6,6 +6,7 @@ import { AzureLogger } from "@azure/logger"; import { PipelineRequest, PipelineResponse, SendRequest } from "../interfaces"; import { PipelinePolicy } from "../pipeline"; import { createTokenCycler } from "../util/tokenCycler"; +import { logger as coreLogger } from "../log"; /** * The programmatic identifier of the bearerTokenAuthenticationPolicy. @@ -136,7 +137,8 @@ function getChallenge(response: PipelineResponse): string | undefined { export function bearerTokenAuthenticationPolicy( options: BearerTokenAuthenticationPolicyOptions ): PipelinePolicy { - const { credential, scopes, challengeCallbacks, logger } = options; + const { credential, scopes, challengeCallbacks } = options; + const logger = options.logger || coreLogger; const callbacks = { authorizeRequest: challengeCallbacks?.authorizeRequest ?? defaultAuthorizeRequest, authorizeRequestOnChallenge: challengeCallbacks?.authorizeRequestOnChallenge, diff --git a/sdk/core/core-rest-pipeline/src/util/tokenCycler.ts b/sdk/core/core-rest-pipeline/src/util/tokenCycler.ts index 72f3c7d1b13d..fb932f1fd1ba 100644 --- a/sdk/core/core-rest-pipeline/src/util/tokenCycler.ts +++ b/sdk/core/core-rest-pipeline/src/util/tokenCycler.ts @@ -201,9 +201,10 @@ export function createTokenCycler( // step 1. // - // IF the tenantId passed in token options is different to the one we have, we need to + // IF the tenantId passed in token options is different to the one we have + // IF we are in CAE claim process, we need to // refresh the token with the new tenantId. - const mustRefresh = tenantId !== tokenOptions.tenantId || cycler.mustRefresh; + const mustRefresh = tenantId !== tokenOptions.tenantId || !!tokenOptions.claims || cycler.mustRefresh; if (mustRefresh) return refresh(scopes, tokenOptions); From 85b4f605714c253ed0f25815b3b585bb0c1b0a9d Mon Sep 17 00:00:00 2001 From: Mary Gao Date: Wed, 22 Jun 2022 10:37:48 +0800 Subject: [PATCH 03/15] Bugfix --- sdk/core/core-auth/review/core-auth.api.md | 1 + sdk/core/core-auth/src/tokenCredential.ts | 2 +- sdk/core/core-rest-pipeline/src/util/tokenCycler.ts | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sdk/core/core-auth/review/core-auth.api.md b/sdk/core/core-auth/review/core-auth.api.md index 6f6c12e825f9..690a6b4a0a2f 100644 --- a/sdk/core/core-auth/review/core-auth.api.md +++ b/sdk/core/core-auth/review/core-auth.api.md @@ -37,6 +37,7 @@ export class AzureSASCredential implements SASCredential { // @public export interface GetTokenOptions { abortSignal?: AbortSignalLike; + claims?: string; requestOptions?: { timeout?: number; }; diff --git a/sdk/core/core-auth/src/tokenCredential.ts b/sdk/core/core-auth/src/tokenCredential.ts index d55c2ea7a9da..0440f35f1772 100644 --- a/sdk/core/core-auth/src/tokenCredential.ts +++ b/sdk/core/core-auth/src/tokenCredential.ts @@ -54,7 +54,7 @@ export interface GetTokenOptions { tenantId?: string; /** - * Detailed claims to perform the Continuous Access Evaluation authentication flow + * Claim details to perform the Continuous Access Evaluation authentication flow */ claims?: string; } diff --git a/sdk/core/core-rest-pipeline/src/util/tokenCycler.ts b/sdk/core/core-rest-pipeline/src/util/tokenCycler.ts index fb932f1fd1ba..2365b5a0c986 100644 --- a/sdk/core/core-rest-pipeline/src/util/tokenCycler.ts +++ b/sdk/core/core-rest-pipeline/src/util/tokenCycler.ts @@ -201,9 +201,9 @@ export function createTokenCycler( // step 1. // - // IF the tenantId passed in token options is different to the one we have - // IF we are in CAE claim process, we need to - // refresh the token with the new tenantId. + // If the tenantId passed in token options is different to the one we have + // Or if we are in claim challenge and the token was rejected and a new access token need to be issued, we need to + // refresh the token with the new tenantId or token. const mustRefresh = tenantId !== tokenOptions.tenantId || !!tokenOptions.claims || cycler.mustRefresh; if (mustRefresh) return refresh(scopes, tokenOptions); From dff166368ded9ac4dbce246416a69a5b652dbc90 Mon Sep 17 00:00:00 2001 From: Mary Gao Date: Mon, 4 Jul 2022 15:22:48 +0800 Subject: [PATCH 04/15] Delete useless file --- azure-pipelines.yml | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 azure-pipelines.yml diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index 01442dd61aa0..000000000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,21 +0,0 @@ -# Node.js -# Build a general Node.js project with npm. -# Add steps that analyze code, save build artifacts, deploy, and more: -# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript - -trigger: -- main - -pool: - vmImage: ubuntu-latest - -steps: -- task: NodeTool@0 - inputs: - versionSpec: '10.x' - displayName: 'Install Node.js' - -- script: | - npm install - npm run build - displayName: 'npm install and build' From 4bc68c227f99115a21ba7c7c86e9a639e3729408 Mon Sep 17 00:00:00 2001 From: Mary Gao Date: Mon, 4 Jul 2022 20:38:34 +0800 Subject: [PATCH 05/15] Update the test cases --- .../src/util/tokenCycler.ts | 2 +- ...TokenAuthenticationPolicyChallenge.spec.ts | 31 ++++++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/sdk/core/core-rest-pipeline/src/util/tokenCycler.ts b/sdk/core/core-rest-pipeline/src/util/tokenCycler.ts index 2365b5a0c986..f139603644f8 100644 --- a/sdk/core/core-rest-pipeline/src/util/tokenCycler.ts +++ b/sdk/core/core-rest-pipeline/src/util/tokenCycler.ts @@ -204,7 +204,7 @@ export function createTokenCycler( // If the tenantId passed in token options is different to the one we have // Or if we are in claim challenge and the token was rejected and a new access token need to be issued, we need to // refresh the token with the new tenantId or token. - const mustRefresh = tenantId !== tokenOptions.tenantId || !!tokenOptions.claims || cycler.mustRefresh; + const mustRefresh = tenantId !== tokenOptions.tenantId || Boolean(tokenOptions.claims) || cycler.mustRefresh; if (mustRefresh) return refresh(scopes, tokenOptions); diff --git a/sdk/core/core-rest-pipeline/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts b/sdk/core/core-rest-pipeline/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts index 04eb8b7d0767..7ddd42403ce6 100644 --- a/sdk/core/core-rest-pipeline/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts +++ b/sdk/core/core-rest-pipeline/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts @@ -21,6 +21,7 @@ export interface TestChallenge { } let cachedChallenge: string | undefined; +let cachedPreviousToken: AccessToken | null; /** * Converts a uint8Array to a string. @@ -68,6 +69,7 @@ function parseCAEChallenge(challenges: string): any[] { async function authorizeRequestOnChallenge( options: AuthorizeRequestOnChallengeOptions ): Promise { + const { scopes } = options; const challenge = options.response.headers.get("WWW-Authenticate"); @@ -95,6 +97,9 @@ async function authorizeRequestOnChallenge( if (!accessToken) { return false; } + if (!!cachedPreviousToken) { + cachedPreviousToken = accessToken; + } options.request.headers.set("Authorization", `Bearer ${accessToken.token}`); return true; @@ -259,17 +264,22 @@ describe("bearerTokenAuthenticationPolicy with challenge", function () { request: pipelineRequest, status: 200, }, + { + headers: createHttpHeaders(), + request: pipelineRequest, + status: 200, + }, ]; const getTokenResponses = [ { token: "mock-token", expiresOnTimestamp: Date.now() + 5000 }, - { token: "mock-token2", expiresOnTimestamp: Date.now() + 10000 }, + { token: "mock-token2", expiresOnTimestamp: Date.now() + 100000 }, + { token: "mock-token3", expiresOnTimestamp: Date.now() + 100000 }, ]; const credential = new MockRefreshAzureCredential([...getTokenResponses]); const pipeline = createEmptyPipeline(); let firstRequest: boolean = true; - let previousToken: AccessToken | null; const bearerPolicy = bearerTokenAuthenticationPolicy({ // Intentionally left empty, as it should be replaced by the challenge. scopes: [], @@ -280,13 +290,13 @@ describe("bearerTokenAuthenticationPolicy with challenge", function () { firstRequest = false; // send first request without the Authorization header } else { - if (!previousToken) { - previousToken = await getAccessToken([], {}); - if (!previousToken) { + if (!cachedPreviousToken) { + cachedPreviousToken = await getAccessToken([], {}); + if (!cachedPreviousToken) { throw new Error("Failed to retrieve an access token"); } } - request.headers.set("Authorization", `Bearer ${previousToken.token}`); + request.headers.set("Authorization", `Bearer ${cachedPreviousToken.token}`); } }, authorizeRequestOnChallenge, @@ -308,8 +318,14 @@ describe("bearerTokenAuthenticationPolicy with challenge", function () { }, }; + // Will refresh token once as the first time token is empty await pipeline.sendRequest(testHttpsClient, pipelineRequest); clock.tick(5000); + // Will refresh token twice + // - 1st refreshing because the token is epxired + // - 2nd refreshing because the response with old token has 401 error with claim details so we need refresh token again + await pipeline.sendRequest(testHttpsClient, pipelineRequest); + // Token is still valid and no need to refresh it await pipeline.sendRequest(testHttpsClient, pipelineRequest); // Our goal is to test that: @@ -334,7 +350,8 @@ describe("bearerTokenAuthenticationPolicy with challenge", function () { undefined, `Bearer ${getTokenResponses[0].token}`, `Bearer ${getTokenResponses[1].token}`, - `Bearer ${getTokenResponses[1].token}`, + `Bearer ${getTokenResponses[2].token}`, + `Bearer ${getTokenResponses[2].token}`, ]); }); From a05c61f9eb34f0212622c62821dda79865a0e05a Mon Sep 17 00:00:00 2001 From: Mary Gao Date: Mon, 4 Jul 2022 23:00:27 +0800 Subject: [PATCH 06/15] Format code --- sdk/core/core-rest-pipeline/src/util/tokenCycler.ts | 3 ++- .../test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sdk/core/core-rest-pipeline/src/util/tokenCycler.ts b/sdk/core/core-rest-pipeline/src/util/tokenCycler.ts index f139603644f8..3a7c159ccea7 100644 --- a/sdk/core/core-rest-pipeline/src/util/tokenCycler.ts +++ b/sdk/core/core-rest-pipeline/src/util/tokenCycler.ts @@ -204,7 +204,8 @@ export function createTokenCycler( // If the tenantId passed in token options is different to the one we have // Or if we are in claim challenge and the token was rejected and a new access token need to be issued, we need to // refresh the token with the new tenantId or token. - const mustRefresh = tenantId !== tokenOptions.tenantId || Boolean(tokenOptions.claims) || cycler.mustRefresh; + const mustRefresh = + tenantId !== tokenOptions.tenantId || Boolean(tokenOptions.claims) || cycler.mustRefresh; if (mustRefresh) return refresh(scopes, tokenOptions); diff --git a/sdk/core/core-rest-pipeline/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts b/sdk/core/core-rest-pipeline/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts index 7ddd42403ce6..4f42bc32442c 100644 --- a/sdk/core/core-rest-pipeline/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts +++ b/sdk/core/core-rest-pipeline/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts @@ -69,7 +69,6 @@ function parseCAEChallenge(challenges: string): any[] { async function authorizeRequestOnChallenge( options: AuthorizeRequestOnChallengeOptions ): Promise { - const { scopes } = options; const challenge = options.response.headers.get("WWW-Authenticate"); @@ -318,7 +317,7 @@ describe("bearerTokenAuthenticationPolicy with challenge", function () { }, }; - // Will refresh token once as the first time token is empty + // Will refresh token once as the first time token is empty await pipeline.sendRequest(testHttpsClient, pipelineRequest); clock.tick(5000); // Will refresh token twice From c2087ab4f701621d8786fe2655556c13e2df7642 Mon Sep 17 00:00:00 2001 From: Mary Gao Date: Mon, 4 Jul 2022 23:26:05 +0800 Subject: [PATCH 07/15] Update lint error --- .../test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/core/core-rest-pipeline/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts b/sdk/core/core-rest-pipeline/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts index 4f42bc32442c..4b396c3d19e4 100644 --- a/sdk/core/core-rest-pipeline/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts +++ b/sdk/core/core-rest-pipeline/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts @@ -96,7 +96,7 @@ async function authorizeRequestOnChallenge( if (!accessToken) { return false; } - if (!!cachedPreviousToken) { + if (cachedPreviousToken) { cachedPreviousToken = accessToken; } From 0fdfc78b9da9cdd4a7c2a728a745decebc373712 Mon Sep 17 00:00:00 2001 From: Mary Gao Date: Tue, 5 Jul 2022 12:32:59 +0800 Subject: [PATCH 08/15] bump a newer version --- sdk/core/core-rest-pipeline/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/core/core-rest-pipeline/package.json b/sdk/core/core-rest-pipeline/package.json index 6d2d69e9dc11..47c4106071e1 100644 --- a/sdk/core/core-rest-pipeline/package.json +++ b/sdk/core/core-rest-pipeline/package.json @@ -1,6 +1,6 @@ { "name": "@azure/core-rest-pipeline", - "version": "1.9.1", + "version": "1.9.2", "description": "Isomorphic client library for making HTTP requests in node.js and browser.", "sdk-type": "client", "main": "dist/index.js", From 93ecd56103e03f94598be79e7fb2db8c7ec895ca Mon Sep 17 00:00:00 2001 From: Mary Gao Date: Wed, 6 Jul 2022 21:11:17 +0800 Subject: [PATCH 09/15] Remove useless renaming --- sdk/core/core-client/src/authorizeRequestOnClaimChallenge.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/core/core-client/src/authorizeRequestOnClaimChallenge.ts b/sdk/core/core-client/src/authorizeRequestOnClaimChallenge.ts index 5c5ffb3da139..0ef43a84e83c 100644 --- a/sdk/core/core-client/src/authorizeRequestOnClaimChallenge.ts +++ b/sdk/core/core-client/src/authorizeRequestOnClaimChallenge.ts @@ -85,7 +85,7 @@ export async function authorizeRequestOnClaimChallenge( parsedChallenge.scope ? [parsedChallenge.scope] : scopes, { claims: decodeStringToString(parsedChallenge.claims), - } as GetTokenOptions + } ); if (!accessToken) { From 832c5d941d50bcdb6ded7c955e554e59f3893c2c Mon Sep 17 00:00:00 2001 From: Mary Gao Date: Wed, 6 Jul 2022 21:15:55 +0800 Subject: [PATCH 10/15] Bump patch version --- sdk/core/core-auth/package.json | 2 +- sdk/core/core-client/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/core/core-auth/package.json b/sdk/core/core-auth/package.json index 4cc0a97fb3c3..d82cd78b74dc 100644 --- a/sdk/core/core-auth/package.json +++ b/sdk/core/core-auth/package.json @@ -1,6 +1,6 @@ { "name": "@azure/core-auth", - "version": "1.3.3", + "version": "1.3.4", "description": "Provides low-level interfaces and helper methods for authentication in Azure SDK", "sdk-type": "client", "main": "dist/index.js", diff --git a/sdk/core/core-client/package.json b/sdk/core/core-client/package.json index 23dc45db2399..8a585c53625e 100644 --- a/sdk/core/core-client/package.json +++ b/sdk/core/core-client/package.json @@ -1,6 +1,6 @@ { "name": "@azure/core-client", - "version": "1.6.1", + "version": "1.6.2", "description": "Core library for interfacing with AutoRest generated code", "sdk-type": "client", "main": "dist/index.js", From 93fd3780525be6699f0c982b4eebac662577961c Mon Sep 17 00:00:00 2001 From: Mary Gao Date: Wed, 6 Jul 2022 21:54:56 +0800 Subject: [PATCH 11/15] Update changelog --- sdk/core/core-auth/package.json | 2 +- sdk/core/core-client/package.json | 2 +- sdk/core/core-rest-pipeline/CHANGELOG.md | 8 ++------ sdk/core/core-rest-pipeline/package.json | 2 +- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/sdk/core/core-auth/package.json b/sdk/core/core-auth/package.json index d82cd78b74dc..4cc0a97fb3c3 100644 --- a/sdk/core/core-auth/package.json +++ b/sdk/core/core-auth/package.json @@ -1,6 +1,6 @@ { "name": "@azure/core-auth", - "version": "1.3.4", + "version": "1.3.3", "description": "Provides low-level interfaces and helper methods for authentication in Azure SDK", "sdk-type": "client", "main": "dist/index.js", diff --git a/sdk/core/core-client/package.json b/sdk/core/core-client/package.json index 8a585c53625e..23dc45db2399 100644 --- a/sdk/core/core-client/package.json +++ b/sdk/core/core-client/package.json @@ -1,6 +1,6 @@ { "name": "@azure/core-client", - "version": "1.6.2", + "version": "1.6.1", "description": "Core library for interfacing with AutoRest generated code", "sdk-type": "client", "main": "dist/index.js", diff --git a/sdk/core/core-rest-pipeline/CHANGELOG.md b/sdk/core/core-rest-pipeline/CHANGELOG.md index e05ce1e16b38..359a728e4314 100644 --- a/sdk/core/core-rest-pipeline/CHANGELOG.md +++ b/sdk/core/core-rest-pipeline/CHANGELOG.md @@ -1,14 +1,10 @@ # Release History -## 1.9.1 (Unreleased) - -### Features Added - -### Breaking Changes +## 1.9.1 (2022-07-06) ### Bugs Fixed -### Other Changes +- Fixed a bug in claim challenge we failed to refresh our token. [#22324](https://github.com/Azure/azure-sdk-for-js/pull/22324) ## 1.9.0 (2022-06-03) diff --git a/sdk/core/core-rest-pipeline/package.json b/sdk/core/core-rest-pipeline/package.json index 47c4106071e1..6d2d69e9dc11 100644 --- a/sdk/core/core-rest-pipeline/package.json +++ b/sdk/core/core-rest-pipeline/package.json @@ -1,6 +1,6 @@ { "name": "@azure/core-rest-pipeline", - "version": "1.9.2", + "version": "1.9.1", "description": "Isomorphic client library for making HTTP requests in node.js and browser.", "sdk-type": "client", "main": "dist/index.js", From fcbe942b78250286094b624803a363dddd4d5dcc Mon Sep 17 00:00:00 2001 From: Mary Gao Date: Wed, 6 Jul 2022 21:58:34 +0800 Subject: [PATCH 12/15] Update changelog --- sdk/core/core-auth/CHANGELOG.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/sdk/core/core-auth/CHANGELOG.md b/sdk/core/core-auth/CHANGELOG.md index a7405c6ce6c6..76b6a0788251 100644 --- a/sdk/core/core-auth/CHANGELOG.md +++ b/sdk/core/core-auth/CHANGELOG.md @@ -1,14 +1,8 @@ # Release History -## 1.3.3 (Unreleased) +## 1.3.3 (2022-07-06) -### Features Added - -### Breaking Changes - -### Key Bugs Fixed - -### Fixed +- Added `claims` optional property to the `GetTokenOptions` interface. If `claims` is set, it indicates that we are in challenge process and force to refresh the token. ## 1.3.2 (2021-07-01) From 35f36e7625f91d0f2f816ccf825c7aa346c59a6f Mon Sep 17 00:00:00 2001 From: Mary Gao Date: Wed, 6 Jul 2022 22:21:02 +0800 Subject: [PATCH 13/15] Update a test case --- ...TokenAuthenticationPolicyChallenge.spec.ts | 102 +++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/sdk/core/core-rest-pipeline/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts b/sdk/core/core-rest-pipeline/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts index 4b396c3d19e4..eb10f92a396c 100644 --- a/sdk/core/core-rest-pipeline/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts +++ b/sdk/core/core-rest-pipeline/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts @@ -322,7 +322,7 @@ describe("bearerTokenAuthenticationPolicy with challenge", function () { clock.tick(5000); // Will refresh token twice // - 1st refreshing because the token is epxired - // - 2nd refreshing because the response with old token has 401 error with claim details so we need refresh token again + // - 2nd refreshing because the response with old token has 401 error and claim details so we need refresh token again await pipeline.sendRequest(testHttpsClient, pipelineRequest); // Token is still valid and no need to refresh it await pipeline.sendRequest(testHttpsClient, pipelineRequest); @@ -354,6 +354,106 @@ describe("bearerTokenAuthenticationPolicy with challenge", function () { ]); }); + it("tests that once the challenge is processed we won't refresh the token again and again", async function () { + const expected = [ + { + scope: ["http://localhost/.default"], + challengeClaims: JSON.stringify({ + access_token: { foo: "bar" }, + }), + } + ]; + + const pipelineRequest = createPipelineRequest({ url: "https://example.com" }); + const responses: PipelineResponse[] = [ + { + headers: createHttpHeaders({ + "WWW-Authenticate": `Bearer scope="${expected[0].scope[0]}", claims="${encodeString( + expected[0].challengeClaims + )}"`, + }), + request: pipelineRequest, + status: 401, + }, + { + headers: createHttpHeaders(), + request: pipelineRequest, + status: 200, + }, + { + headers: createHttpHeaders(), + request: pipelineRequest, + status: 200, + }, + { + headers: createHttpHeaders(), + request: pipelineRequest, + status: 200, + }, + ]; + + const getTokenResponses = [ + { token: "mock-token-initialzation", expiresOnTimestamp: Date.now() + 180000 }, + // ensure the token will not expire + { token: "mock-token-challenge", expiresOnTimestamp: Date.now() + 180000 }, + ]; + const credential = new MockRefreshAzureCredential([...getTokenResponses]); + + const pipeline = createEmptyPipeline(); + const bearerPolicy = bearerTokenAuthenticationPolicy({ + scopes: [], + credential, + challengeCallbacks: { + authorizeRequestOnChallenge, + }, + }); + pipeline.addPolicy(bearerPolicy); + + const finalSendRequestHeaders: (string | undefined)[] = []; + + const testHttpsClient: HttpClient = { + sendRequest: async (req) => { + finalSendRequestHeaders.push(req.headers.get("Authorization")); + if (responses.length) { + const response = responses.shift()!; + response.request = req; + return response; + } + throw new Error("No responses found"); + }, + }; + + // Will refresh token twice + // - 1st refreshing to initialize the token + // - 2nd refreshing to handle challenge process + await pipeline.sendRequest(testHttpsClient, pipelineRequest); + assert.equal(credential.authCount, 2); + clock.tick(5000); + // Will not refresh the token because the previous one is still valid + await pipeline.sendRequest(testHttpsClient, pipelineRequest); + await pipeline.sendRequest(testHttpsClient, pipelineRequest); + + // Our goal is to test that: + // - After a challenge is received and processed and once the token is valid, we'll use it in future calls + assert.equal(credential.authCount, 2); + assert.deepEqual(credential.scopesAndClaims, [ + { + scope: [], + challengeClaims: undefined, + }, + { + scope: expected[0].scope, + challengeClaims: expected[0].challengeClaims, + }, + ]); + assert.deepEqual(finalSendRequestHeaders, [ + `Bearer ${getTokenResponses[0].token}`, + `Bearer ${getTokenResponses[1].token}`, + `Bearer ${getTokenResponses[1].token}`, + `Bearer ${getTokenResponses[1].token}`, + ]); + }); + it("service errors without challenges should bubble up", async function () { const pipelineRequest = createPipelineRequest({ url: "https://example.com" }); const credential = new MockRefreshAzureCredential([]); From 15b4d31775f9906b04de5c831c9d4eb31c576a7b Mon Sep 17 00:00:00 2001 From: Mary Gao Date: Wed, 6 Jul 2022 22:22:26 +0800 Subject: [PATCH 14/15] Update ci failure --- sdk/core/core-client/src/authorizeRequestOnClaimChallenge.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/core/core-client/src/authorizeRequestOnClaimChallenge.ts b/sdk/core/core-client/src/authorizeRequestOnClaimChallenge.ts index 0ef43a84e83c..bf5e978923c8 100644 --- a/sdk/core/core-client/src/authorizeRequestOnClaimChallenge.ts +++ b/sdk/core/core-client/src/authorizeRequestOnClaimChallenge.ts @@ -2,7 +2,6 @@ // Licensed under the MIT license. import { AuthorizeRequestOnChallengeOptions } from "@azure/core-rest-pipeline"; -import { GetTokenOptions } from "@azure/core-auth"; import { logger as coreClientLogger } from "./log"; import { decodeStringToString } from "./base64"; From 1f89098aaf1212973d83a40c8d7a1d78e2f22c66 Mon Sep 17 00:00:00 2001 From: Mary Gao Date: Wed, 6 Jul 2022 22:23:51 +0800 Subject: [PATCH 15/15] Update format --- .../node/bearerTokenAuthenticationPolicyChallenge.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/core/core-rest-pipeline/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts b/sdk/core/core-rest-pipeline/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts index eb10f92a396c..0084f8d6ed78 100644 --- a/sdk/core/core-rest-pipeline/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts +++ b/sdk/core/core-rest-pipeline/test/node/bearerTokenAuthenticationPolicyChallenge.spec.ts @@ -361,7 +361,7 @@ describe("bearerTokenAuthenticationPolicy with challenge", function () { challengeClaims: JSON.stringify({ access_token: { foo: "bar" }, }), - } + }, ]; const pipelineRequest = createPipelineRequest({ url: "https://example.com" }); @@ -434,7 +434,7 @@ describe("bearerTokenAuthenticationPolicy with challenge", function () { await pipeline.sendRequest(testHttpsClient, pipelineRequest); // Our goal is to test that: - // - After a challenge is received and processed and once the token is valid, we'll use it in future calls + // - After a challenge is received and processed and once the token is valid, we'll use it in future calls assert.equal(credential.authCount, 2); assert.deepEqual(credential.scopesAndClaims, [ {