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!: Auth Metrics #1860

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 49 additions & 2 deletions src/auth/authclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,13 @@ export abstract class AuthClient
forceRefreshOnFailure = false;
universeDomain = DEFAULT_UNIVERSE;

/**
* The type of credential.
*
* @see {@link AuthClient.CREDENTIAL_TYPES}
*/
readonly credentialType?: keyof typeof AuthClient.CREDENTIAL_TYPES;

constructor(opts: AuthClientOptions = {}) {
super();

Expand Down Expand Up @@ -234,9 +241,33 @@ export abstract class AuthClient
}

/**
* Provides an alternative Gaxios request implementation with auth credentials
* The public request API in which credentials may be added to the request.
*
* @param options options for `gaxios`
*/
abstract request<T>(options: GaxiosOptions): GaxiosPromise<T>;

/**
* The internal request handler. At this stage the credentials have been created, but
* the standard headers have not been added until this call.
*
* @param options options for `gaxios`
*/
abstract request<T>(opts: GaxiosOptions): GaxiosPromise<T>;
protected _request<T>(options: GaxiosOptions): GaxiosPromise<T> {
if (!options.headers?.['x-goog-api-client']) {
options.headers = options.headers || {};
const nodeVersion = process.version.replace(/^v/, '');
options.headers['x-goog-api-client'] = `gl-node/${nodeVersion}`;
}

if (this.credentialType) {
options.headers['x-goog-api-client'] =
options.headers['x-goog-api-client'] +
` cred-type/${this.credentialType}`;
}

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

/**
* The main authentication interface. It takes an optional url which when
Expand Down Expand Up @@ -286,6 +317,22 @@ export abstract class AuthClient
return headers;
}

/**
* An enum of the known credential types
*/
protected static CREDENTIAL_TYPES = {
/** for gcloud user credential (auth code flow and refresh flow) */
u: 'u',
/** service account credential with assertion token flow */
sa: 'sa',
/** service account credential with self signed jwt token flow */
jwt: 'jwt',
/** service account credential attached to metadata server, i.e. VM credential */
mds: 'mds',
/** impersonated credential */
imp: 'imp',
} as const;

/**
* Retry config for Auth-related requests.
*
Expand Down
7 changes: 3 additions & 4 deletions src/auth/baseexternalclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ export abstract class BaseExternalAccountClient extends AuthClient {
} else if (projectNumber) {
// Preferable not to use request() to avoid retrial policies.
const headers = await this.getRequestHeaders();
const response = await this.transporter.request<ProjectInfo>({
const response = await this._request<ProjectInfo>({
...BaseExternalAccountClient.RETRY_CONFIG,
headers,
url: `${this.cloudResourceManagerURL.toString()}${projectNumber}`,
Expand Down Expand Up @@ -512,7 +512,7 @@ export abstract class BaseExternalAccountClient extends AuthClient {
if (requestHeaders && requestHeaders.Authorization) {
opts.headers.Authorization = requestHeaders.Authorization;
}
response = await this.transporter.request<T>(opts);
response = await this._request<T>(opts);
} catch (e) {
const res = (e as GaxiosError).response;
if (res) {
Expand Down Expand Up @@ -679,8 +679,7 @@ export abstract class BaseExternalAccountClient extends AuthClient {
},
responseType: 'json',
};
const response =
await this.transporter.request<IamGenerateAccessTokenResponse>(opts);
const response = await this._request<IamGenerateAccessTokenResponse>(opts);
const successResponse = response.data;
return {
access_token: successResponse.accessToken,
Expand Down
2 changes: 2 additions & 0 deletions src/auth/computeclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export interface ComputeOptions extends OAuth2ClientOptions {
}

export class Compute extends OAuth2Client {
credentialType = Compute.CREDENTIAL_TYPES.mds;

readonly serviceAccountEmail: string;
scopes: string[];

Expand Down
2 changes: 1 addition & 1 deletion src/auth/downscopedclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ export class DownscopedClient extends AuthClient {
if (requestHeaders && requestHeaders.Authorization) {
opts.headers.Authorization = requestHeaders.Authorization;
}
response = await this.transporter.request<T>(opts);
response = await this._request<T>(opts);
} catch (e) {
const res = (e as GaxiosError).response;
if (res) {
Expand Down
2 changes: 1 addition & 1 deletion src/auth/externalAccountAuthorizedUserClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ export class ExternalAccountAuthorizedUserClient extends AuthClient {
if (requestHeaders && requestHeaders.Authorization) {
opts.headers.Authorization = requestHeaders.Authorization;
}
response = await this.transporter.request<T>(opts);
response = await this._request<T>(opts);
} catch (e) {
const res = (e as GaxiosError).response;
if (res) {
Expand Down
1 change: 1 addition & 0 deletions src/auth/impersonated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export interface FetchIdTokenResponse {
}

export class Impersonated extends OAuth2Client implements IdTokenProvider {
credentialType = Impersonated.CREDENTIAL_TYPES.imp;
private sourceClient: AuthClient;
private targetPrincipal: string;
private targetScopes: string[];
Expand Down
31 changes: 24 additions & 7 deletions src/auth/jwtclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export interface JWTOptions extends OAuth2ClientOptions {
}

export class JWT extends OAuth2Client implements IdTokenProvider {
credentialType!: (typeof JWT.CREDENTIAL_TYPES)['jwt' | 'sa'];

email?: string;
keyFile?: string;
key?: string;
Expand Down Expand Up @@ -95,6 +97,9 @@ export class JWT extends OAuth2Client implements IdTokenProvider {
// Start with an expired refresh token, which will automatically be
// refreshed before the first API call is made.
this.credentials = {refresh_token: 'jwt-placeholder', expiry_date: 1};

// Using to set the `credentialType`
this.#useSelfSigned();
}

/**
Expand All @@ -109,6 +114,24 @@ export class JWT extends OAuth2Client implements IdTokenProvider {
return jwt;
}

#useSelfSigned(url?: string | null): boolean {
url = this.defaultServicePath ? `https://${this.defaultServicePath}/` : url;

const useSelfSignedFlow =
!this.apiKey &&
!!(
(!this.hasUserScopes() && url) ||
(this.useJWTAccessWithScope && this.hasAnyScopes()) ||
this.universeDomain !== DEFAULT_UNIVERSE
);

this.credentialType = useSelfSignedFlow
? JWT.CREDENTIAL_TYPES.jwt
: JWT.CREDENTIAL_TYPES.sa;

return useSelfSignedFlow;
}

/**
* Obtains the metadata to be sent with the request.
*
Expand All @@ -117,19 +140,13 @@ export class JWT extends OAuth2Client implements IdTokenProvider {
protected async getRequestMetadataAsync(
url?: string | null
): Promise<RequestMetadataResponse> {
url = this.defaultServicePath ? `https://${this.defaultServicePath}/` : url;
const useSelfSignedJWT =
(!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.#useSelfSigned(url)) {
if (
this.additionalClaims &&
(
Expand Down
21 changes: 11 additions & 10 deletions src/auth/oauth2client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,7 @@ export class OAuth2Client extends AuthClient {
if (this.clientAuthentication === ClientAuthentication.ClientSecretPost) {
values.client_secret = this._clientSecret;
}
const res = await this.transporter.request<CredentialRequest>({
const res = await this._request<CredentialRequest>({
...OAuth2Client.RETRY_CONFIG,
method: 'POST',
url,
Expand Down Expand Up @@ -773,7 +773,7 @@ export class OAuth2Client extends AuthClient {

try {
// request for new token
res = await this.transporter.request<CredentialRequest>({
res = await this._request<CredentialRequest>({
...OAuth2Client.RETRY_CONFIG,
method: 'POST',
url,
Expand Down Expand Up @@ -1003,11 +1003,12 @@ export class OAuth2Client extends AuthClient {
method: 'POST',
};
if (callback) {
this.transporter
.request<RevokeCredentialsResult>(opts)
.then(r => callback(null, r), callback);
this._request<RevokeCredentialsResult>(opts).then(
r => callback(null, r),
callback
);
} else {
return this.transporter.request<RevokeCredentialsResult>(opts);
return this._request<RevokeCredentialsResult>(opts);
}
}

Expand Down Expand Up @@ -1082,7 +1083,7 @@ export class OAuth2Client extends AuthClient {
if (this.apiKey) {
opts.headers['X-Goog-Api-Key'] = this.apiKey;
}
r2 = await this.transporter.request<T>(opts);
r2 = await this._request<T>(opts);
} catch (e) {
const res = (e as GaxiosError).response;
if (res) {
Expand Down Expand Up @@ -1204,7 +1205,7 @@ export class OAuth2Client extends AuthClient {
* user info.
*/
async getTokenInfo(accessToken: string): Promise<TokenInfo> {
const {data} = await this.transporter.request<TokenInfoRequest>({
const {data} = await this._request<TokenInfoRequest>({
...OAuth2Client.RETRY_CONFIG,
method: 'POST',
headers: {
Expand Down Expand Up @@ -1271,7 +1272,7 @@ export class OAuth2Client extends AuthClient {
throw new Error(`Unsupported certificate format ${format}`);
}
try {
res = await this.transporter.request({
res = await this._request({
...OAuth2Client.RETRY_CONFIG,
url,
});
Expand Down Expand Up @@ -1342,7 +1343,7 @@ export class OAuth2Client extends AuthClient {
const url = this.endpoints.oauth2IapPublicKeyUrl.toString();

try {
res = await this.transporter.request({
res = await this._request({
...OAuth2Client.RETRY_CONFIG,
url,
});
Expand Down
2 changes: 1 addition & 1 deletion src/auth/passthrough.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class PassThroughClient extends AuthClient {
* @returns The response of the request.
*/
async request<T>(opts: GaxiosOptions) {
return this.transporter.request<T>(opts);
return this._request<T>(opts);
}

/**
Expand Down
4 changes: 3 additions & 1 deletion src/auth/refreshclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ export class UserRefreshClient extends OAuth2Client {
// This is also a hard one because `this.refreshToken` is a function.
_refreshToken?: string | null;

credentialType = UserRefreshClient.CREDENTIAL_TYPES.u;

/**
* User Refresh Token credentials.
*
Expand Down Expand Up @@ -80,7 +82,7 @@ export class UserRefreshClient extends OAuth2Client {
}

async fetchIdToken(targetAudience: string): Promise<string> {
const res = await this.transporter.request<CredentialRequest>({
const res = await this._request<CredentialRequest>({
...UserRefreshClient.RETRY_CONFIG,
url: this.endpoints.oauth2TokenUrl,
headers: {
Expand Down
2 changes: 1 addition & 1 deletion src/transporters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export class DefaultTransporter implements Transporter {
opts.headers['User-Agent'] =
`${uaValue} ${DefaultTransporter.USER_AGENT}`;
}
// track google-auth-library-nodejs version:

if (!opts.headers['x-goog-api-client']) {
const nodeVersion = process.version.replace(/^v/, '');
opts.headers['x-goog-api-client'] = `gl-node/${nodeVersion}`;
Expand Down
Loading