Skip to content

fix: Google OAuth — correct scope + device code flow for SSH - #56

Merged
assapir merged 5 commits into
mainfrom
fix/google-oauth-scope
Mar 2, 2026
Merged

assapir merged 5 commits into
mainfrom
fix/google-oauth-scope

Conversation

@assapir

@assapir assapir commented Mar 2, 2026 •

Copy link
Copy Markdown
Owner

Problem

Google OAuth login fails with:

Error 400: invalid_scope
Some requested scopes cannot be shown: [https://www.googleapis.com/auth/generative-language]

Additionally, the loopback redirect flow doesn't work over SSH (browser can't reach 127.0.0.1 on the remote machine).

Root Cause

  1. Invalid scope — generative-language is not a valid Google OAuth scope. It doesn't appear in any Google API discovery document.
  2. Over-privileged scope — cloud-platform (used by Google's Gemini CLI) grants full read/write to ALL Google Cloud resources. The Gemini API's generateContent has no scope requirements per the discovery doc — any valid OAuth token works.
  3. SSH/headless broken — loopback redirect binds a TCP listener on the remote machine's localhost, unreachable from a local browser.

Fix

Minimal OAuth scope

Changed scope to openid email for both flows — minimum needed for a valid OAuth token. No over-privileged access.

Device code flow for SSH

Added a second OAuth client (TV/Limited Input type) that supports device code flow:

  • Auto-detects headless environment (SSH_CONNECTION, SSH_TTY, missing DISPLAY)
  • Shows a URL + code — user approves on any device with a browser
  • Polls Google's token endpoint with backoff until approved

Token refresh

Added client_hint field to OAuthCredentials (backward-compatible via serde(default)) so refresh uses the correct OAuth client for each flow.

Auth flows

Environment Flow Client type How it works
Desktop/GUI Loopback redirect Desktop app Browser redirects to localhost, TCP listener catches code
SSH/headless Device code TV/Limited Input User visits URL, enters code, agent polls for approval
Any API key N/A GEMINI_API_KEY env var (unchanged)

Files changed

  • src/auth/google_oauth.rs — scope fix, device code flow, per-client refresh
  • src/auth/oauth.rs — client_hint field on OAuthCredentials
  • src/auth/storage.rs — pass client_hint to Google refresh
  • src/provider.rs — auto-detect headless, dispatch to correct flow
  • tests/auth_test.rs — updated for new field

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Fixes Google (Gemini) OAuth login failures caused by an invalid OAuth scope by switching to a valid Google scope that works with the Gemini API.

Changes:

  • Update Google OAuth scope from generative-language to cloud-platform.
  • Update the URL-encoding unit test to match the new scope string.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@assapir assapir changed the title fix: use cloud-platform OAuth scope for Google Gemini fix: Google OAuth — correct scope + device code flow for SSH Mar 2, 2026
assapir added 4 commits March 2, 2026 15:20
The scope 'generative-language' is not a valid Google OAuth scope and
causes a 400 invalid_scope error during login. The Gemini API's
generateContent endpoint has no required scopes in Google's discovery
document — the correct scope is 'cloud-platform', which is what
Google's own Gemini CLI uses.
Auto-detect headless/SSH environment and use Google's device code flow
instead of loopback redirect. This lets users authenticate over SSH by
visiting a URL and entering a code on any device.

Implementation:
- New TV/Limited Input OAuth client for device code flow
- is_headless() detects SSH_CONNECTION, SSH_TTY, missing DISPLAY
- Device code polling with backoff per Google spec
- client_hint field on OAuthCredentials so refresh uses correct client
- Loopback flow uses cloud-platform scope (browser flow)
- Device flow uses openid+email scope (Gemini API has no scope requirements)

Both flows store credentials identically — existing loopback auth and
API key auth continue to work unchanged.
The Gemini API's generateContent has no scope requirements (confirmed
via Google's discovery doc). Using cloud-platform was massively
over-privileged — it grants full read/write to all Google Cloud
resources. Both flows now use 'openid email' (minimum for auth).

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/auth/oauth.rs Outdated
Comment thread src/auth/google_oauth.rs Outdated
- Make client_hint doc comment provider-agnostic (OAuthCredentials is shared)
- Use neutral example URL in urlencoded test (no stale scope reference)
@assapir
assapir merged commit 87f0431 into main Mar 2, 2026
4 checks passed
@assapir
assapir deleted the fix/google-oauth-scope branch March 2, 2026 13:40
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.

2 participants