spring-projects / spring-projects/spring-ai

GoogleGenAiChatAutoConfiguration builds Vertex AI credentials unscoped → invalid_scope on token refresh

Open Beginner friendly
#6,595 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: waiting-for-triage
Dominant language
Java
Stars
9.5k
Forks
2.9k
Avg merge
1d 7h
Merged PRs (30d)
6

Description

Bug description

In Vertex AI mode, the google-genai starter applies the service-account credentials loaded from spring.ai.google.genai.credentials-uri without an OAuth scope, so the access-token refresh is rejected by Google with invalid_scope.

GoogleGenAiChatAutoConfiguration#configureVertexAi(...) builds credentials via GoogleCredentials.fromStream(is) and passes them straight to the client builder:

private void configureVertexAi(Client.Builder builder, GoogleGenAiConnectionProperties props) throws IOException {
    ...
    builder.project(props.getProjectId()).location(props.getLocation()).vertexAI(true);
    if (props.getCredentialsUri() != null) {
        try (var is = props.getCredentialsUri().getInputStream()) {
            builder.credentials(GoogleCredentials.fromStream(is));   // <-- not scoped
        }
    }
}

GoogleCredentials.fromStream(...) on a service-account key returns a scopeless ServiceAccountCredentials. Vertex AI requires the https://www.googleapis.com/auth/cloud-platform scope, so token refresh fails:

com.google.auth.oauth2.GoogleAuthException: Error getting access token for service account: 400 Bad Request
POST https://oauth2.googleapis.com/token
{"error":"invalid_scope","error_description":"Invalid OAuth scope or ID token audience provided."}
iss: <service-account>@<project>.iam.gserviceaccount.com

This only affects the explicit credentials-uri path (the ADC path is scoped elsewhere), and there is currently no way to fix it via configuration: GoogleGenAiConnectionProperties exposes no scope property, and the googleGenAiClient bean offers no Client.Builder customizer hook (it is only @ConditionalOnMissingBean), so the sole workaround is to replace the entire Client bean.

Related: #5242 (credentials-uri not applied to the client in 1.1.2). In 2.0.0 the credentials are applied — but unscoped — so this is the follow-on defect.

Environment
  • Spring AI: 2.0.0 (spring-ai-starter-model-google-genai / spring-ai-autoconfigure-model-google-genai:2.0.0)
  • google-genai SDK: com.google.genai:google-genai:1.58.0
  • Spring Boot: 4.1.x
  • Java: 21
  • Mode: Vertex AI (spring.ai.google.genai.vertex-ai=true), credentials via spring.ai.google.genai.credentials-uri
  • No vector store involved
Steps to reproduce
  1. Configure Vertex AI mode with an explicit service-account key (not ADC):
    spring.ai.google.genai.vertex-ai=true
    spring.ai.google.genai.project-id=<project>
    spring.ai.google.genai.location=us-central1
    spring.ai.google.genai.credentials-uri=file:/path/to/service-account.json
    spring.ai.model.chat=google-genai
    
  2. Autowire ChatModel (or ChatClient.Builder) and make any call, e.g. chatClient.prompt("hi").call().content().
  3. The call fails: GoogleAuthException: Error getting access token for service account: 400 Bad Request / {"error":"invalid_scope"}.
Expected behavior

Service-account credentials supplied via spring.ai.google.genai.credentials-uri should authenticate Vertex AI calls successfully. The autoconfiguration should scope them to cloud-platform (as the legacy vertex-ai-gemini starter did), e.g.:

builder.credentials(GoogleCredentials.fromStream(is)
        .createScoped(java.util.List.of("https://www.googleapis.com/auth/cloud-platform")));

Ideally also expose a way to configure this without replacing the client — a scope property and/or an ObjectProvider<Consumer<Client.Builder>> customizer hook on the googleGenAiClient bean.

Minimal Complete Reproducible example

Workaround that confirms the root cause — supplying a scoped Client (the starter's bean is @ConditionalOnMissingBean, so it backs off) makes the same call succeed:

@TestConfiguration
class ScopedGoogleGenAiClientConfig {

    @Bean
    Client googleGenAiClient(Environment env) throws IOException {
        var credentials = GoogleCredentials
                .fromStream(new FileInputStream(env.getProperty("spring.ai.google.genai.credentials-uri-path")))
                .createScoped(List.of("https://www.googleapis.com/auth/cloud-platform"));  // <-- the missing step
        return Client.builder()
                .project(env.getProperty("spring.ai.google.genai.project-id"))
                .location(env.getProperty("spring.ai.google.genai.location"))
                .vertexAI(true)
                .credentials(credentials)
                .build();
    }
}

With the stock autoconfiguration (no custom Client), the same setup throws invalid_scope; adding .createScoped(cloud-platform) is the only difference that makes it pass.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at GoogleGenAiChatAutoConfiguration#configureVertexAi(...) and trace the credentials-uri branch from GoogleCredentials.fromStream(is) into the Client.Builder. Verify the Vertex AI service-account credentials use the cloud-platform scope, then add or update coverage for the explicit credentials-uri path and confirm the invalid_scope scenario is resolved without changing the ADC path.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
ai, authentication, backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.