From 4ee02da4f6934eeb9550b8b659d1abda85194e6b Mon Sep 17 00:00:00 2001 From: Daniel Bankhead Date: Thu, 31 Aug 2023 12:29:50 -0700 Subject: [PATCH] feat: extend `universe_domain` support (#1633) * feat: extend `universe_domain` support * style: lint * chore: minor fix * chore: lint * chore: lint * run lint --------- Co-authored-by: Sofia Leon --- src/auth/baseexternalclient.ts | 29 +++++++++++++----- .../externalAccountAuthorizedUserClient.ts | 30 +++++++++++-------- test/test.baseexternalclient.ts | 22 +++++++++++++- ...est.externalaccountauthorizeduserclient.ts | 25 +++++++++++++++- 4 files changed, 85 insertions(+), 21 deletions(-) diff --git a/src/auth/baseexternalclient.ts b/src/auth/baseexternalclient.ts index f29c2787..8598cd69 100644 --- a/src/auth/baseexternalclient.ts +++ b/src/auth/baseexternalclient.ts @@ -62,24 +62,36 @@ const WORKFORCE_AUDIENCE_PATTERN = // eslint-disable-next-line @typescript-eslint/no-var-requires const pkg = require('../../../package.json'); +/** + * The default cloud universe + */ +export const DEFAULT_UNIVERSE = 'googleapis.com'; + +export interface SharedExternalAccountClientOptions { + audience: string; + token_url: string; + quota_project_id?: string; + /** + * universe domain is the default service domain for a given Cloud universe + */ + universe_domain?: string; +} + /** * Base external account credentials json interface. */ -export interface BaseExternalAccountClientOptions { +export interface BaseExternalAccountClientOptions + extends SharedExternalAccountClientOptions { type: string; - audience: string; subject_token_type: string; service_account_impersonation_url?: string; service_account_impersonation?: { token_lifetime_seconds?: number; }; - token_url: string; token_info_url?: string; client_id?: string; client_secret?: string; - quota_project_id?: string; workforce_pool_user_project?: string; - universe_domain?: string; } /** @@ -139,7 +151,7 @@ export abstract class BaseExternalAccountClient extends AuthClient { private readonly stsCredential: sts.StsCredentials; private readonly clientAuth?: ClientAuthentication; private readonly workforcePoolUserProject?: string; - private universeDomain?: string; + public universeDomain = DEFAULT_UNIVERSE; public projectId: string | null; public projectNumber: string | null; public readonly eagerRefreshThresholdMillis: number; @@ -217,7 +229,10 @@ export abstract class BaseExternalAccountClient extends AuthClient { this.forceRefreshOnFailure = !!additionalOptions?.forceRefreshOnFailure; this.projectId = null; this.projectNumber = this.getProjectNumber(this.audience); - this.universeDomain = options.universe_domain; + + if (options.universe_domain) { + this.universeDomain = options.universe_domain; + } } /** The service account email to be impersonated, if available. */ diff --git a/src/auth/externalAccountAuthorizedUserClient.ts b/src/auth/externalAccountAuthorizedUserClient.ts index ca267bda..3c05cc56 100644 --- a/src/auth/externalAccountAuthorizedUserClient.ts +++ b/src/auth/externalAccountAuthorizedUserClient.ts @@ -29,29 +29,30 @@ import { } from 'gaxios'; import {Credentials} from './credentials'; import * as stream from 'stream'; -import {EXPIRATION_TIME_OFFSET} from './baseexternalclient'; +import { + DEFAULT_UNIVERSE, + EXPIRATION_TIME_OFFSET, + SharedExternalAccountClientOptions, +} from './baseexternalclient'; + +/** + * The credentials JSON file type for external account authorized user clients. + */ +export const EXTERNAL_ACCOUNT_AUTHORIZED_USER_TYPE = + 'external_account_authorized_user'; /** * External Account Authorized User Credentials JSON interface. */ -export interface ExternalAccountAuthorizedUserClientOptions { +export interface ExternalAccountAuthorizedUserClientOptions + extends SharedExternalAccountClientOptions { type: typeof EXTERNAL_ACCOUNT_AUTHORIZED_USER_TYPE; - audience: string; client_id: string; client_secret: string; refresh_token: string; - token_url: string; token_info_url: string; revoke_url?: string; - quota_project_id?: string; } - -/** - * The credentials JSON file type for external account authorized user clients. - */ -export const EXTERNAL_ACCOUNT_AUTHORIZED_USER_TYPE = - 'external_account_authorized_user'; - /** * Internal interface for tracking the access token expiration time. */ @@ -155,6 +156,7 @@ export class ExternalAccountAuthorizedUserClient extends AuthClient { private cachedAccessToken: CredentialsWithResponse | null; private readonly externalAccountAuthorizedUserHandler: ExternalAccountAuthorizedUserHandler; private refreshToken: string; + public universeDomain = DEFAULT_UNIVERSE; /** * Instantiates an ExternalAccountAuthorizedUserClient instances using the @@ -197,6 +199,10 @@ export class ExternalAccountAuthorizedUserClient extends AuthClient { .eagerRefreshThresholdMillis as number; } this.forceRefreshOnFailure = !!additionalOptions?.forceRefreshOnFailure; + + if (options.universe_domain) { + this.universeDomain = options.universe_domain; + } } async getAccessToken(): Promise<{ diff --git a/test/test.baseexternalclient.ts b/test/test.baseexternalclient.ts index 4aab6ec3..826326a8 100644 --- a/test/test.baseexternalclient.ts +++ b/test/test.baseexternalclient.ts @@ -23,6 +23,7 @@ import { EXPIRATION_TIME_OFFSET, BaseExternalAccountClient, BaseExternalAccountClientOptions, + DEFAULT_UNIVERSE, } from '../src/auth/baseexternalclient'; import { OAuthErrorResponse, @@ -80,7 +81,6 @@ describe('BaseExternalAccountClient', () => { credential_source: { file: '/var/run/secrets/goog.id/token', }, - universe_domain: 'universe.domain.com', }; const externalAccountOptionsWithCreds = { type: 'external_account', @@ -294,6 +294,26 @@ describe('BaseExternalAccountClient', () => { }); }); + describe('universeDomain', () => { + it('should be the default universe if not set', () => { + const client = new TestExternalAccountClient(externalAccountOptions); + + assert.equal(client.universeDomain, DEFAULT_UNIVERSE); + }); + + it('should be set if provided', () => { + const universeDomain = 'my-universe.domain.com'; + const options: BaseExternalAccountClientOptions = { + ...externalAccountOptions, + universe_domain: universeDomain, + }; + + const client = new TestExternalAccountClient(options); + + assert.equal(client.universeDomain, universeDomain); + }); + }); + describe('getServiceAccountEmail()', () => { it('should return the service account email when impersonation is used', () => { const saEmail = 'service-1234@service-name.iam.gserviceaccount.com'; diff --git a/test/test.externalaccountauthorizeduserclient.ts b/test/test.externalaccountauthorizeduserclient.ts index f9dcc45e..c97e4b03 100644 --- a/test/test.externalaccountauthorizeduserclient.ts +++ b/test/test.externalaccountauthorizeduserclient.ts @@ -23,7 +23,10 @@ import { ExternalAccountAuthorizedUserClient, ExternalAccountAuthorizedUserClientOptions, } from '../src/auth/externalAccountAuthorizedUserClient'; -import {EXPIRATION_TIME_OFFSET} from '../src/auth/baseexternalclient'; +import { + DEFAULT_UNIVERSE, + EXPIRATION_TIME_OFFSET, +} from '../src/auth/baseexternalclient'; import {GaxiosError, GaxiosResponse} from 'gaxios'; import { getErrorFromOAuthErrorResponse, @@ -149,6 +152,26 @@ describe('ExternalAccountAuthorizedUserClient', () => { refreshOptions.eagerRefreshThresholdMillis ); }); + + describe('universeDomain', () => { + it('should be the default universe if not set', () => { + const client = new ExternalAccountAuthorizedUserClient( + externalAccountAuthorizedUserCredentialOptions + ); + + assert.equal(client.universeDomain, DEFAULT_UNIVERSE); + }); + + it('should be set if provided', () => { + const universeDomain = 'my-universe.domain.com'; + const client = new ExternalAccountAuthorizedUserClient({ + ...externalAccountAuthorizedUserCredentialOptions, + universe_domain: universeDomain, + }); + + assert.equal(client.universeDomain, universeDomain); + }); + }); }); describe('getAccessToken()', () => {