From 4548992046388cefc29851c23d4ace3e3363ac79 Mon Sep 17 00:00:00 2001 From: Daniel Bankhead Date: Fri, 5 Jan 2024 18:13:11 -0800 Subject: [PATCH 1/3] feat: Use self-signed JWTs when non-default Universe Domains --- src/auth/jwtclient.ts | 4 +++- test/test.jwt.ts | 47 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/auth/jwtclient.ts b/src/auth/jwtclient.ts index d25f4146..85bfa886 100644 --- a/src/auth/jwtclient.ts +++ b/src/auth/jwtclient.ts @@ -24,6 +24,7 @@ import { OAuth2ClientOptions, RequestMetadataResponse, } from './oauth2client'; +import {DEFAULT_UNIVERSE} from './authclient'; export interface JWTOptions extends OAuth2ClientOptions { email?: string; @@ -119,7 +120,8 @@ export class JWT extends OAuth2Client implements IdTokenProvider { url = this.defaultServicePath ? `https://${this.defaultServicePath}/` : url; const useSelfSignedJWT = (!this.hasUserScopes() && url) || - (this.useJWTAccessWithScope && this.hasAnyScopes()); + (this.useJWTAccessWithScope && this.hasAnyScopes()) || + this.universeDomain !== DEFAULT_UNIVERSE; if (!this.apiKey && useSelfSignedJWT) { if ( this.additionalClaims && diff --git a/test/test.jwt.ts b/test/test.jwt.ts index f20fcbe5..1d8ee8d9 100644 --- a/test/test.jwt.ts +++ b/test/test.jwt.ts @@ -1007,6 +1007,53 @@ describe('jwt', () => { ); }); + it('signs JWT with audience if: user scope = true, default scope = true, audience = truthy, universeDomain = not default universe', async () => { + const stubGetRequestHeaders = sandbox.stub().returns({}); + const stubJWTAccess = sandbox.stub(jwtaccess, 'JWTAccess').returns({ + getRequestHeaders: stubGetRequestHeaders, + }); + const jwt = new JWT({ + email: 'foo@serviceaccount.com', + key: fs.readFileSync(PEM_PATH, 'utf8'), + scopes: ['scope1', 'scope2'], + subject: 'bar@subjectaccount.com', + universeDomain: 'my-universe.com', + }); + jwt.defaultScopes = ['scope1', 'scope2']; + await jwt.getRequestHeaders('https//beepboop.googleapis.com'); + sandbox.assert.calledOnce(stubJWTAccess); + sandbox.assert.calledWith( + stubGetRequestHeaders, + 'https//beepboop.googleapis.com', + undefined, + undefined + ); + }); + + it('signs JWT with audience if: user scope = true, default scope = true, audience = truthy, useJWTAccessWithScope = true, universeDomain = not default universe', async () => { + const stubGetRequestHeaders = sandbox.stub().returns({}); + const stubJWTAccess = sandbox.stub(jwtaccess, 'JWTAccess').returns({ + getRequestHeaders: stubGetRequestHeaders, + }); + const jwt = new JWT({ + email: 'foo@serviceaccount.com', + key: fs.readFileSync(PEM_PATH, 'utf8'), + scopes: ['scope1', 'scope2'], + subject: 'bar@subjectaccount.com', + universeDomain: 'my-universe.com', + }); + jwt.useJWTAccessWithScope = true; + jwt.defaultScopes = ['scope1', 'scope2']; + await jwt.getRequestHeaders('https//beepboop.googleapis.com'); + sandbox.assert.calledOnce(stubJWTAccess); + sandbox.assert.calledWith( + stubGetRequestHeaders, + 'https//beepboop.googleapis.com', + undefined, + ['scope1', 'scope2'] + ); + }); + it('does not use self signed JWT if target_audience provided', async () => { const JWTAccess = sandbox.stub(jwtaccess, 'JWTAccess').returns({ getRequestHeaders: sinon.stub().returns({}), From a4351cadc2b60c1e4e442d77d100584335b1d6a8 Mon Sep 17 00:00:00 2001 From: Daniel Bankhead Date: Fri, 5 Jan 2024 18:32:20 -0800 Subject: [PATCH 2/3] feat: AL-9 --- src/auth/jwtclient.ts | 7 +++++++ test/test.jwt.ts | 23 +++++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/auth/jwtclient.ts b/src/auth/jwtclient.ts index 85bfa886..9e12b23c 100644 --- a/src/auth/jwtclient.ts +++ b/src/auth/jwtclient.ts @@ -122,6 +122,13 @@ export class JWT extends OAuth2Client implements IdTokenProvider { (!this.hasUserScopes() && url) || (this.useJWTAccessWithScope && this.hasAnyScopes()) || this.universeDomain !== DEFAULT_UNIVERSE; + + if (this.subject && this.universeDomain !== DEFAULT_UNIVERSE) { + throw new RangeError( + `Service Account user is configured for the credential. Domain-wide delegation is not supported in universes other than ${DEFAULT_UNIVERSE}` + ); + } + if (!this.apiKey && useSelfSignedJWT) { if ( this.additionalClaims && diff --git a/test/test.jwt.ts b/test/test.jwt.ts index 1d8ee8d9..48c63f58 100644 --- a/test/test.jwt.ts +++ b/test/test.jwt.ts @@ -1016,7 +1016,6 @@ describe('jwt', () => { email: 'foo@serviceaccount.com', key: fs.readFileSync(PEM_PATH, 'utf8'), scopes: ['scope1', 'scope2'], - subject: 'bar@subjectaccount.com', universeDomain: 'my-universe.com', }); jwt.defaultScopes = ['scope1', 'scope2']; @@ -1039,7 +1038,6 @@ describe('jwt', () => { email: 'foo@serviceaccount.com', key: fs.readFileSync(PEM_PATH, 'utf8'), scopes: ['scope1', 'scope2'], - subject: 'bar@subjectaccount.com', universeDomain: 'my-universe.com', }); jwt.useJWTAccessWithScope = true; @@ -1054,6 +1052,27 @@ describe('jwt', () => { ); }); + it('throws on domain-wide delegation on non-default universe', async () => { + const stubGetRequestHeaders = sandbox.stub().returns({}); + const stubJWTAccess = sandbox.stub(jwtaccess, 'JWTAccess').returns({ + getRequestHeaders: stubGetRequestHeaders, + }); + const jwt = new JWT({ + email: 'foo@serviceaccount.com', + key: fs.readFileSync(PEM_PATH, 'utf8'), + scopes: ['scope1', 'scope2'], + subject: 'bar@subjectaccount.com', + universeDomain: 'my-universe.com', + }); + jwt.useJWTAccessWithScope = true; + jwt.defaultScopes = ['scope1', 'scope2']; + + await assert.rejects( + () => jwt.getRequestHeaders('https//beepboop.googleapis.com'), + /Domain-wide delegation is not supported in universes other than/ + ); + }); + it('does not use self signed JWT if target_audience provided', async () => { const JWTAccess = sandbox.stub(jwtaccess, 'JWTAccess').returns({ getRequestHeaders: sinon.stub().returns({}), From 1a7ba87d42661e5b51c6751ccbbc66e226d9e830 Mon Sep 17 00:00:00 2001 From: Daniel Bankhead Date: Mon, 22 Jan 2024 10:16:03 -0800 Subject: [PATCH 3/3] fix: remove unused --- test/test.jwt.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.jwt.ts b/test/test.jwt.ts index 48c63f58..fc11bd02 100644 --- a/test/test.jwt.ts +++ b/test/test.jwt.ts @@ -1054,7 +1054,7 @@ describe('jwt', () => { it('throws on domain-wide delegation on non-default universe', async () => { const stubGetRequestHeaders = sandbox.stub().returns({}); - const stubJWTAccess = sandbox.stub(jwtaccess, 'JWTAccess').returns({ + sandbox.stub(jwtaccess, 'JWTAccess').returns({ getRequestHeaders: stubGetRequestHeaders, }); const jwt = new JWT({