Skip to content

Comments

feat: Regional Access Boundary Changes#891

Open
vverman wants to merge 12 commits intogoogleapis:trust_boundaryfrom
vverman:trust-boundary-upto-speed
Open

feat: Regional Access Boundary Changes#891
vverman wants to merge 12 commits intogoogleapis:trust_boundaryfrom
vverman:trust-boundary-upto-speed

Conversation

@vverman
Copy link
Contributor

@vverman vverman commented Jan 17, 2026

Contains changes for the feature Regional Access Boundary (Previously Called Trust Boundaries).

The following are salient changes:

  1. Calls to refresh RAB are now all async and in a separate thread.
  2. Logic for refreshing RAB now exists in its own class for cleaner maintenance.
  3. Self-signed jwts are within scope.
  4. Changes to how we trigger RAB refresh and deal with refresh errors.

@vverman vverman requested a review from a team as a code owner January 17, 2026 01:31
* @throws {Error} If the URL cannot be constructed for a compatible client.
*/
protected async getTrustBoundaryUrl(): Promise<string | null> {
public async getRegionalAccessBoundaryUrl(): Promise<string | null> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this method became public?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made it so for ease of testing the getRegionalAccessBoundaryUrl logic. For ex: in the base external client we have scenarios where we want to test if the right url is returned or error is thrown in case of workload, workforce or a null audience.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd go ahead and put an @private on it, as that will cause it to be omitted from docs, but it will still show up in the transpiled class. Anyone digging in the source can see the @private to know they shouldn't use it.

Actually, it looks like @internal omits it from any exported .d.ts, so that may be enough. I'd double-check the output though, to see what it makes.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vverman +1 to using @internal if you can verify that it's omitted from docs and transpiled classes.

@vverman vverman force-pushed the trust-boundary-upto-speed branch from 238aed8 to f40a3e1 Compare January 29, 2026 02:12
…e requestAsync level. RAB urls now have googleapis as universe domain.
@vverman vverman force-pushed the trust-boundary-upto-speed branch from e3e00da to 3427c0e Compare January 29, 2026 19:14
@vverman vverman requested a review from nbayati January 30, 2026 21:00
Copy link
Contributor

@feywind feywind left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see anything too weird in this, I guess let me know when you've got one that merges against main?

if (res && res.status === 400) {
const data = res.data as {error?: {message?: string}; message?: string};
const message =
data?.error?.message || data?.message || error.message || '';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?? might be more idiomatic now, unless e.g. data?.message could be empty but not null/undefined.

* @throws {Error} If the URL cannot be constructed for a compatible client.
*/
protected async getTrustBoundaryUrl(): Promise<string | null> {
public async getRegionalAccessBoundaryUrl(): Promise<string | null> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd go ahead and put an @private on it, as that will cause it to be omitted from docs, but it will still show up in the transpiled class. Anyone digging in the source can see the @private to know they shouldn't use it.

Actually, it looks like @internal omits it from any exported .d.ts, so that may be enough. I'd double-check the output though, to see what it makes.

}

protected async getTrustBoundaryUrl(): Promise<string> {
public async getRegionalAccessBoundaryUrl(): Promise<string> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment re protected -> public.

}

protected async getTrustBoundaryUrl(): Promise<string> {
public async getRegionalAccessBoundaryUrl(): Promise<string> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto protected -> public.

* Triggers an asynchronous regional access boundary refresh if needed.
* @param accessToken The access token to use for the lookup.
*/
private maybeTriggerRegionalAccessBoundaryRefresh(accessToken: string) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Absolutely nitpicky, but I like to try to put an explicit return type on void methods just to make sure that you notice if something changes internal to it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, thanks for the suggestion!

@miguelvelezsa miguelvelezsa removed their assignment Feb 5, 2026
Comment on lines 145 to 146
'{universe_domain}',
this.universeDomain,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SERVICE_ACCOUNT_LOOKUP_ENDPOINT no longer has a placeholder for universedomain.
The tests such as test/test.authclient.ts and test/test.compute.ts should be updated to remove the unnecessary replace too.

* @param url The URL to check.
*/
private isGlobalEndpoint(url: string | URL): boolean {
const hostname = url instanceof URL ? url.hostname : new URL(url).hostname;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new URL(url) will throws a TypeError if the url string is a relative URL, or a malformed one.
We need to gracefully handle those cases by assuming they are global.

/**
* Maximum cooldown period for RAB lookup failures.
*/
const RAB_MAX_COOLDOWN_MILLIS = 24 * 60 * 60 * 1000;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's update this to be 6 hours.

* returned response.
* @param opts The HTTP request options.
* @param reAuthRetried Whether the current attempt is a retry after a failed attempt due to an auth failure.
* @param retryWithoutRAB Whether the current attempt is a retry after a failed attempt due to a stale regional access boundary.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we no longer have the reactive refresh in this PR, this parameter should be removed too.

* returned response.
* @param opts The HTTP request options.
* @param reAuthRetried Whether the current attempt is a retry after a failed attempt due to an auth failure.
* @param retryWithoutRAB Whether the current attempt is a retry after a failed attempt due to a stale regional access boundary.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused parameter: Since we no longer have the reactive refresh in this PR, this parameter should be removed too.

* @throws {Error} If the URL cannot be constructed for a compatible client.
*/
protected async getTrustBoundaryUrl(): Promise<string | null> {
public async getRegionalAccessBoundaryUrl(): Promise<string | null> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vverman +1 to using @internal if you can verify that it's omitted from docs and transpiled classes.

async getRequestHeaders(url?: string | URL): Promise<Headers> {
const headers = (await this.getRequestMetadataAsync(url)).headers;
const {headers, isIDToken} = await this.getRequestMetadataAsync(url);
if (!isIDToken) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idtoken client overrides getRequestMetadataAsync is not returning isIDToken=true.
IIUC, IdTokenClient inherits the default getRegionalAccessBoundaryUrl (which returns null), so RAB lookup happen for idtoken and we won't attach the header anyway, but I think setting this flag explicitly ensures we skip the RAB logic entirely and avoids unnecessary checks and future-proofs against changes in the base classes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants