Skip to content

Commit

Permalink
refactor: Use Gaxios Interceptors for default Auth headers
Browse files Browse the repository at this point in the history
  • Loading branch information
d-goog committed Feb 6, 2025
1 parent 8bdeec9 commit b6852df
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 31 deletions.
77 changes: 46 additions & 31 deletions src/auth/authclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ export interface AuthClientOptions

/**
* The {@link Gaxios `Gaxios`} instance used for making requests.
*
* @see {@link AuthClientOptions.useAuthRequestParameters}
*/
gaxios?: Gaxios;

Expand All @@ -106,6 +108,19 @@ export interface AuthClientOptions
* on the expiry_date.
*/
forceRefreshOnFailure?: boolean;

/**
* Enables/disables the adding of the AuthClient's default interceptor.
*
* @see {@link AuthClientOptions.gaxios}
*
* @remarks
*
* Disabling is useful for debugging and experimentation.
*
* @default true
*/
useAuthRequestParameters?: boolean;
}

/**
Expand Down Expand Up @@ -210,6 +225,12 @@ export abstract class AuthClient
// Shared client options
this.transporter = opts.gaxios ?? new Gaxios(opts.transporterOptions);

if (options.get('useAuthRequestParameters') !== false) {
this.transporter.interceptors.request.add(
AuthClient.DEFAULT_REQUEST_INTERCEPTOR
);
}

if (opts.eagerRefreshThresholdMillis) {
this.eagerRefreshThresholdMillis = opts.eagerRefreshThresholdMillis;
}
Expand All @@ -224,37 +245,6 @@ export abstract class AuthClient
*/
abstract request<T>(options: GaxiosOptions): GaxiosPromise<T>;

/**
* The internal request handler.
*
* @privateRemarks
*
* At this stage the credentials have been already prepared - passing it
* here adds the standard headers to the request, if not previously configured.
*
* @param options options for `gaxios`
*/
protected _request<T>(options: GaxiosOptions): GaxiosPromise<T> {
const headers = options.headers || {};

// Set `x-goog-api-client`, if not already set
if (!headers['x-goog-api-client']) {
const nodeVersion = process.version.replace(/^v/, '');
headers['x-goog-api-client'] = `gl-node/${nodeVersion}`;
}

// Set `User-Agent`
if (!headers['User-Agent']) {
headers['User-Agent'] = USER_AGENT;
} else if (!headers['User-Agent'].includes(`${PRODUCT_NAME}/`)) {
headers['User-Agent'] = `${headers['User-Agent']} ${USER_AGENT}`;
}

options.headers = headers;

return this.transporter.request<T>(options);
}

/**
* The main authentication interface. It takes an optional url which when
* present is the endpoint being accessed, and returns a Promise which
Expand Down Expand Up @@ -303,6 +293,31 @@ export abstract class AuthClient
return headers;
}

static readonly DEFAULT_REQUEST_INTERCEPTOR: Parameters<
Gaxios['interceptors']['request']['add']
>[0] = {
resolved: async config => {
const headers = config.headers || {};

// Set `x-goog-api-client`, if not already set
if (!headers['x-goog-api-client']) {
const nodeVersion = process.version.replace(/^v/, '');
headers['x-goog-api-client'] = `gl-node/${nodeVersion}`;
}

// Set `User-Agent`
if (!headers['User-Agent']) {
headers['User-Agent'] = USER_AGENT;
} else if (!headers['User-Agent'].includes(`${PRODUCT_NAME}/`)) {
headers['User-Agent'] = `${headers['User-Agent']} ${USER_AGENT}`;
}

config.headers = headers;

return config;
},
};

/**
* Retry config for Auth-related requests.
*
Expand Down
28 changes: 28 additions & 0 deletions test/test.authclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {strict as assert} from 'assert';

import {PassThroughClient} from '../src';
import {snakeToCamel} from '../src/util';
import {Gaxios} from 'gaxios';

describe('AuthClient', () => {
it('should accept and normalize snake case options to camel case', () => {
Expand All @@ -38,4 +39,31 @@ describe('AuthClient', () => {
assert.equal(authClient[camelCased], value);
}
});

it('should allow disabling of the default interceptor', () => {
const gaxios = new Gaxios();
const originalInterceptorCount = gaxios.interceptors.request.size;

const authClient = new PassThroughClient({
gaxios,
useAuthRequestParameters: false,
});

assert.equal(authClient.transporter, gaxios);
assert.equal(
authClient.transporter.interceptors.request.size,
originalInterceptorCount
);
});

it('should add the default interceptor exactly once between instances', () => {
const gaxios = new Gaxios();
const originalInterceptorCount = gaxios.interceptors.request.size;
const expectedInterceptorCount = originalInterceptorCount + 1;

new PassThroughClient({gaxios});
new PassThroughClient({gaxios});

assert.equal(gaxios.interceptors.request.size, expectedInterceptorCount);
});
});

0 comments on commit b6852df

Please sign in to comment.