[google-auth-library-java] OAuthException.createFromHttpResponseException throws ClassCastException on a Google API error body, losing the HTTP status and body

Abierto
#14,514 1 comentario 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
3/5
Tiempo estimado
1-2 días
Aptitud para principiantes
72/100
Tipo de issue
Error
Claridad
Bastante claro
Estado de actividad
Activo
Stack tecnológico
java

Línea de trabajo

Start with OAuthException.java:90 and run the deterministic createFromHttpResponseException reproduction using the supplied Google API error body. Trace its callers in StsRequestHandler.exchangeToken and ExternalAccountAuthorizedUserCredentials.java:204, then add regression coverage showing that the response status and body are retained without the ClassCastException, including a body without an error key.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

priority: p2 type: bug

Environment details

  1. API: auth, workload identity federation (AwsCredentials, STS token exchange during Cloud Storage calls)
  2. OS type and version: Linux (AWS Lambda and AWS ECS)
  3. Java version: 25
  4. Version(s): google-auth-library-oauth2-http 1.50.0 (libraries-bom 26.86.0), google-http-client 2.2.0. createFromHttpResponseException is unchanged in 1.53.0 and on main (05a998f).

Steps to reproduce

  1. Deterministic, no credentials needed: call OAuthException.createFromHttpResponseException with an HttpResponseException whose content is a Google API error body (snippet below). It throws ClassCastException.
  2. Against the real STS: an AwsCredentials whose token_url is https://sts.googleapis.com/v1/introspect gets HTTP 400 with {"error":{"code":400,"message":"Invalid JSON payload received. ...","status":"INVALID_ARGUMENT"}} and fails with the same ClassCastException. The misconfigured URL is only a way to get that body shape on demand; in production the same exception comes from https://sts.googleapis.com/v1/token.

Code example

package com.google.auth.oauth2;

import com.google.api.client.http.HttpHeaders;
import com.google.api.client.http.HttpResponseException;

class OAuthExceptionNestedErrorRepro {
  public static void main(String[] args) throws Exception {
    HttpResponseException e =
        new HttpResponseException.Builder(400, "Bad Request", new HttpHeaders())
            .setContent(
                "{\"error\":{\"code\":400,\"message\":\"Request contains an invalid argument.\",\"status\":\"INVALID_ARGUMENT\"}}")
            .build();
    OAuthException.createFromHttpResponseException(e); // java.lang.ClassCastException
  }
}

Stack trace

Production, workload identity federation from AWS, token exchange triggered by a Cloud Storage request:

java.lang.ClassCastException: class com.google.api.client.util.ArrayMap cannot be cast to class java.lang.String
  at com.google.auth.oauth2.OAuthException.createFromHttpResponseException(OAuthException.java:90)
  at com.google.auth.oauth2.StsRequestHandler.exchangeToken(StsRequestHandler.java:129)
  at com.google.auth.oauth2.ExternalAccountCredentials.exchangeExternalCredentialForAccessToken(ExternalAccountCredentials.java:567)
  at com.google.auth.oauth2.AwsCredentials.refreshAccessToken(AwsCredentials.java:132)
  at com.google.auth.oauth2.OAuth2Credentials.getRequestMetadata(OAuth2Credentials.java:189)

External references such as API reference guides

Any additional information below

  • OAuthException.java:90 casts error to String. The request-validation errors we could trigger on /v1/token come back in the OAuth form ({"error":"invalid_request","error_description":"..."}), but some production responses from /v1/token carry error as an object (Google's API error format), which makes the cast throw. We could not capture those bodies because the library drops them.
  • The ClassCastException is thrown from inside catch (HttpResponseException e) in StsRequestHandler.exchangeToken. It is a RuntimeException, so it escapes the IOException contract of refresh() / getRequestMetadata(), and it has no cause, so the HttpResponseException holding the status and body is lost. LoggingUtils logs the response only on success, so the body is not logged either. When this happened to us intermittently in production (a burst of failures across several independent GCP projects within minutes), we could not tell whether STS was rate limiting, unavailable, or refusing the credential.
  • Because the ClassCastException is not an IOException, google-cloud-storage classifies it as unretryable (Unretryable error (attempts: 1, maxAttempts: 6)), so a transient STS failure is not retried either.
  • The same method is also called from ExternalAccountAuthorizedUserCredentials.java:204.
  • Related: a body without an error key makes the constructor's checkNotNull(errorCode) throw NullPointerException.
Lenguaje dominante
Java
Estrellas
2.1k
Forks
1.2k
Merge medio
2 d 14 h
PR fusionados (30 d)
192

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de googleapis/google-cloud-java

Todos los issues de googleapis/google-cloud-java

Issues similares

Más issues de Java

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.