Skip to content

fix(google-auth-library): redact tokens in logged response bodies - #9794

Open
aelmanaa wants to merge 2 commits into
googleapis:mainfrom
aelmanaa:fix/auth-redact-token-logs
Open

aelmanaa wants to merge 2 commits into
googleapis:mainfrom
aelmanaa:fix/auth-redact-token-logs

Conversation

@aelmanaa

@aelmanaa aelmanaa commented Nov 2, 2026

Copy link
Copy Markdown

Refs #7690

With GOOGLE_SDK_NODE_LOGGING=auth (or *, all), the AuthClient response and error interceptors log the response body of every request. Token refreshes go through the same transporter, so access_token, id_token and refresh_token are printed to stderr. The repro is in #7690 (comment).

This change logs a copy of the body with access_token, id_token, refresh_token, accessToken and token replaced by ***REDACTED***, as gdchclient.ts already does in toJSON. The response the caller gets is unchanged. Other fields are still logged.

It does not cover gcp-metadata's instance metadata is %s log from the original report.

Tests: a new case in test/test.authclient.ts checks both log paths and that the caller's body is unchanged. It fails without the change. The package's tests pass (957 passing), and eslint is clean on both changed files.

With GOOGLE_SDK_NODE_LOGGING=auth (or * or all), the AuthClient response
interceptor logs each response body. Token refreshes go through the same
transporter, so access_token, id_token and refresh_token were printed to
stderr.

Replace access_token, id_token, refresh_token, accessToken and token in a
copy of the body before logging it. The response the caller gets is
unchanged.

Refs googleapis#7690
@aelmanaa
aelmanaa requested review from a team as code owners November 2, 2026 15:17

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces credential redaction for logged response and error bodies in the AuthClient class to prevent sensitive tokens from being exposed in logs. It defines a list of sensitive fields and a redactForLog helper method, which is integrated into the default response and error interceptors, along with corresponding unit tests. The review feedback correctly points out a potential issue where passing non-plain objects like Buffers or Streams to redactForLog could lead to performance degradation or circular reference errors during object spreading, and suggests a robust check to only process plain objects.

Comment on lines +438 to +449
static redactForLog(data: unknown): unknown {
if (data === null || typeof data !== 'object' || Array.isArray(data)) {
return data;
}
const copy: Record<string, unknown> = {...data};
for (const field of AuthClient.REDACTED_LOG_FIELDS) {
if (copy[field] !== undefined) {
copy[field] = '***REDACTED***';
}
}
return copy;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

If data is a Buffer or a Stream (which can happen when downloading files or handling binary payloads), typeof data === 'object' is true and Array.isArray(data) is false. Spreading a Buffer or Stream using {...data} will:

  1. For Buffer, create a massive object with numeric keys for every single byte, causing severe performance/memory issues and rendering the log output useless.
  2. For Stream, copy internal stream properties, which can lead to circular reference errors or verbose internal state logs.

To prevent this, we should ensure we only attempt to copy and redact plain objects (e.g., those parsed from JSON). We can achieve this robustly by checking that the prototype of data is either Object.prototype or null.

  static redactForLog(data: unknown): unknown {
    if (data === null || typeof data !== 'object') {
      return data;
    }
    const proto = Object.getPrototypeOf(data);
    if (proto !== Object.prototype && proto !== null) {
      return data;
    }
    const copy: Record<string, unknown> = {...(data as Record<string, unknown>)};
    for (const field of AuthClient.REDACTED_LOG_FIELDS) {
      if (copy[field] !== undefined) {
        copy[field] = '***REDACTED***';
      }
    }
    return copy;
  }
References
  1. Always validate and reject missing or falsy arguments upfront before performing property checks, to prevent TypeErrors.
  2. Do not use value?.toString() === '[object Object]' to verify if a variable is a plain object. Use robust checks instead.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed: only plain objects are copied now; Buffers, streams and other bodies are logged as before. Added a test.

This branch has not been deployed

No deployments
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.

1 participant