Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Adds support for STS response not returning expires_in field. #1216

Merged
merged 23 commits into from
Aug 4, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f0895ef
Adds support for stsResponse doesnt return expiry time.
xil222 Jul 21, 2021
b245f0e
Adds support for STS response not returning expires_in field.
xil222 Jul 21, 2021
77ab907
Merge branch 'expireTime' of https://github.com/xil222/google-auth-li…
xil222 Jul 21, 2021
2277ed4
Merge branch 'master' into expireTime
bcoe Jul 22, 2021
bb2a430
meta: add nodejs-auth as codeowner (#1217)
bcoe Jul 23, 2021
b1f1094
samples: update TODO section (#1218)
averikitsch Jul 26, 2021
59f6017
feat(impersonated): add impersonated credentials auth (#1207)
bcoe Jul 29, 2021
ec4de59
chore: release 7.4.0 (#1220)
release-please[bot] Jul 29, 2021
549e518
fix(downscoped-client): bug fixes for downscoped client implementatio…
xil222 Jul 29, 2021
049c8c0
chore: release 7.4.1 (#1222)
release-please[bot] Jul 29, 2021
682a859
sample: update region tag (#1223)
averikitsch Jul 29, 2021
84d434b
Adds support for STS response not returning expires_in field.
xil222 Jul 21, 2021
241be27
Adds support for stsResponse doesnt return expiry time.
xil222 Jul 21, 2021
1c4b296
Remove extra tests in baseExternalClient.
xil222 Jul 29, 2021
22afcec
merge origin/expireTime
xil222 Jul 29, 2021
a7aa1aa
Merge branch 'master' into expireTime
xil222 Jul 29, 2021
91a4927
change parsing expire_in logic in baseexternalclient.
xil222 Jul 30, 2021
c87fa70
Merge branch 'expireTime' of https://github.com/xil222/google-auth-li…
xil222 Jul 30, 2021
cc0e1d6
Merge branch 'master' into expireTime
xil222 Jul 30, 2021
26d194f
Add testAuthClient setter and tweak some tests.
xil222 Aug 3, 2021
5233611
Merge branch 'expireTime' of https://github.com/xil222/google-auth-li…
xil222 Aug 3, 2021
af59d70
add emit events for some tests.
xil222 Aug 4, 2021
d314a21
Merge branch 'master' into expireTime
bcoe Aug 4, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/auth/baseexternalclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -378,10 +378,13 @@ export abstract class BaseExternalAccountClient extends AuthClient {
stsResponse.access_token
);
} else {
xil222 marked this conversation as resolved.
Show resolved Hide resolved
const expireDate = stsResponse.expires_in
? new Date().getTime() + stsResponse.expires_in * 1000
: null;
// Save response in cached access token.
this.cachedAccessToken = {
access_token: stsResponse.access_token,
expiry_date: new Date().getTime() + stsResponse.expires_in * 1000,
expiry_date: expireDate,
res: stsResponse.res,
};
}
Expand Down
7 changes: 6 additions & 1 deletion src/auth/downscopedclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,15 @@ export class DownscopedClient extends AuthClient {
this.credentialAccessBoundary
);

const sourceCredExpireDate =
xil222 marked this conversation as resolved.
Show resolved Hide resolved
this.authClient.credentials?.expiry_date || null;
const expiryDate = stsResponse.expires_in
? new Date().getTime() + stsResponse.expires_in * 1000
: sourceCredExpireDate;
// Save response in cached access token.
this.cachedDownscopedAccessToken = {
access_token: stsResponse.access_token,
expiry_date: new Date().getTime() + stsResponse.expires_in * 1000,
expiry_date: expiryDate,
res: stsResponse.res,
};

Expand Down
2 changes: 1 addition & 1 deletion src/auth/stscredentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export interface StsSuccessfulResponse {
access_token: string;
issued_token_type: string;
token_type: string;
expires_in: number;
expires_in?: number;
refresh_token?: string;
scope: string;
res?: GaxiosResponse | null;
Expand Down
68 changes: 68 additions & 0 deletions test/test.baseexternalclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,74 @@ describe('BaseExternalAccountClient', () => {
scopes.forEach(scope => scope.done());
});

it('should return credential with no expiry date if STS response does not return one', async () => {
xil222 marked this conversation as resolved.
Show resolved Hide resolved
clock = sinon.useFakeTimers(0);
const now = new Date().getTime();
const customThresh = 10 * 1000;
const stsSuccessfulResponse2 = Object.assign({}, stsSuccessfulResponse);
delete stsSuccessfulResponse2.expires_in;
const credentials = {
access_token: 'ACCESS_TOKEN',
expiry_date: now + ONE_HOUR_IN_SECS * 1000,
};

const scope = mockStsTokenExchange([
{
statusCode: 200,
response: stsSuccessfulResponse2,
request: {
grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange',
audience,
scope: 'https://www.googleapis.com/auth/cloud-platform',
requested_token_type:
'urn:ietf:params:oauth:token-type:access_token',
subject_token: 'subject_token_0',
subject_token_type: 'urn:ietf:params:oauth:token-type:jwt',
},
},
]);

const client = new TestExternalAccountClient(
externalAccountOptionsWithCreds,
{
// Override 5min threshold with 10 second threshold.
eagerRefreshThresholdMillis: customThresh,
}
);
client.setCredentials(credentials);
const actualResponse = await client.getAccessToken();

delete actualResponse.res;
assert.deepStrictEqual(actualResponse, {
token: credentials.access_token,
});

// Cached credential should be returned.
clock.tick(ONE_HOUR_IN_SECS * 1000 - customThresh - 1);
const actualCachedResponse = await client.getAccessToken();

delete actualCachedResponse.res;
assert.deepStrictEqual(actualCachedResponse, {
token: credentials.access_token,
});

// Simulate credential is expired.
// As current time is equal to expirationTime - customThresh,
// refresh should be triggered.
clock.tick(1);
const actualNewCredResponse = await client.getAccessToken();

// Confirm raw GaxiosResponse appended to response.
assertGaxiosResponsePresent(actualNewCredResponse);
delete actualNewCredResponse.res;
assert.deepStrictEqual(actualNewCredResponse, {
token: stsSuccessfulResponse2.access_token,
});
assert.deepStrictEqual(client.credentials.expiry_date, null);

scope.done();
});

it('should apply basic auth when credentials are provided', async () => {
const scopes: nock.Scope[] = [];
scopes.push(
Expand Down
120 changes: 120 additions & 0 deletions test/test.downscopedclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,126 @@ describe('DownscopedClient', () => {
);
scope.done();
});

it('should copy source cred expiry time if STS response does not return expiry time', async () => {
xil222 marked this conversation as resolved.
Show resolved Hide resolved
const now = new Date().getTime();
clock = sinon.useFakeTimers(now);
const sourceCredentials = client.credentials;
const downscopedCredentials = {
access_token: 'DOWNSCOPED_CLIENT_ACCESS_TOKEN',
expiry_date: now + ONE_HOUR_IN_SECS * 1000,
};
const stsSuccessfulResponseWithoutExpireInField = Object.assign(
stsSuccessfulResponse,
{}
);
xil222 marked this conversation as resolved.
Show resolved Hide resolved
delete stsSuccessfulResponseWithoutExpireInField.expires_in;
const scope = mockStsBetaTokenExchange([
{
statusCode: 200,
response: stsSuccessfulResponseWithoutExpireInField,
request: {
grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange',
requested_token_type:
'urn:ietf:params:oauth:token-type:access_token',
subject_token: 'subject_token',
subject_token_type: 'urn:ietf:params:oauth:token-type:access_token',
options:
testClientAccessBoundary &&
xil222 marked this conversation as resolved.
Show resolved Hide resolved
JSON.stringify(testClientAccessBoundary),
},
},
]);
client.setCredentials(sourceCredentials);
const downscopedClient = new DownscopedClient(
client,
testClientAccessBoundary
);
assert.strictEqual(client.credentials.expiry_date, 1);
downscopedClient.setCredentials(downscopedCredentials);

clock.tick(ONE_HOUR_IN_SECS * 1000 - EXPIRATION_TIME_OFFSET - 1);
const tokenResponse = await downscopedClient.getAccessToken();
assert.deepStrictEqual(
tokenResponse.token,
xil222 marked this conversation as resolved.
Show resolved Hide resolved
downscopedCredentials.access_token
);

clock.tick(1);
const refreshedTokenResponse = await downscopedClient.getAccessToken();
assert.deepStrictEqual(
refreshedTokenResponse.token,
stsSuccessfulResponseWithoutExpireInField.access_token
);
assert.strictEqual(
downscopedClient.credentials.expiry_date,
sourceCredentials.expiry_date
);
scope.done();
});

it('should has no expiry date if source cred has no expiry time and STS response does not return one', async () => {
xil222 marked this conversation as resolved.
Show resolved Hide resolved
const now = new Date().getTime();
clock = sinon.useFakeTimers(now);
const downscopedCredentials = {
access_token: 'DOWNSCOPED_CLIENT_ACCESS_TOKEN',
expiry_date: now + ONE_HOUR_IN_SECS * 1000,
};
const credentials = {
access_token: 'ACCESS_TOKEN',
};
xil222 marked this conversation as resolved.
Show resolved Hide resolved
const stsSuccessfulResponseWithoutExpireInField = Object.assign(
stsSuccessfulResponse,
{}
xil222 marked this conversation as resolved.
Show resolved Hide resolved
);
delete stsSuccessfulResponseWithoutExpireInField.expires_in;
const scope = mockStsBetaTokenExchange([
{
statusCode: 200,
response: stsSuccessfulResponseWithoutExpireInField,
request: {
grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange',
requested_token_type:
'urn:ietf:params:oauth:token-type:access_token',
subject_token: 'subject_token',
subject_token_type: 'urn:ietf:params:oauth:token-type:access_token',
options:
testClientAccessBoundary &&
xil222 marked this conversation as resolved.
Show resolved Hide resolved
JSON.stringify(testClientAccessBoundary),
},
},
]);

assert.strictEqual(client.credentials.expiry_date, 1);
client.setCredentials(credentials);
assert.strictEqual(client.credentials.expiry_date, undefined);

const downscopedClient = new DownscopedClient(
client,
testClientAccessBoundary
);
downscopedClient.setCredentials(downscopedCredentials);

clock.tick(ONE_HOUR_IN_SECS * 1000 - EXPIRATION_TIME_OFFSET - 1);
const tokenResponse = await downscopedClient.getAccessToken();
assert.deepStrictEqual(
tokenResponse.token,
downscopedCredentials.access_token
);

clock.tick(1);
const refreshedTokenResponse = await downscopedClient.getAccessToken();
assert.deepStrictEqual(
refreshedTokenResponse.token,
stsSuccessfulResponseWithoutExpireInField.access_token
);
assert.strictEqual(
downscopedClient.credentials.access_token,
stsSuccessfulResponseWithoutExpireInField.access_token
);
assert.strictEqual(downscopedClient.credentials.expiry_date, null);
scope.done();
});
});

describe('getRequestHeader()', () => {
Expand Down