agentleFS
GoogleKnown publishergoogle/adk-samples/core/python/oauth-user-consent-flow/AGENTS.md

adk-samples / oauth-user-consent-flow

AGENTS.md11k starsChanged 44 days agoApache-2.0View on GitHub

A clone-and-study ADK agent that reads a user's Google Drive files on their behalf, gated by an OAuth 2.0 user-consent flow. The agent is deployed to Agent Runtime (Vertex AI Agent Engine) and registered with Gemini Enterprise. The interesting part is not the agent (a thin wrapper around one Drive tool); it's the OAuth consent plumbing — the negotiate_creds() three-stage credential resolution that makes the same code work in both local ADK Web UI dev and production Gemini Enterprise, plus…

  • Reads credentials

1 other project uses a version of this.

Trending

More talk lately.about the whole repository

Dec 2025: 1 mentionJan 2026: 1 mentionFeb 2026: 2 mentionsMar 2026: 0 mentionsApr 2026: 0 mentionsMay 2026: 1 mentionJun 2026: 1 mentionJul 2026: 2 mentionsAug 2026: 0 mentionsSep 2026: 4 mentionsOct 2026: 4 mentionsNov 2026: 0 mentions
Dec 2025Jun 2026Nov 2026
Mentions a month of the google/adk-samples repository that our weekly crawl found. There is no adoption data for this file yet.

Most recently taken up by

Who's talking about google/adk-samples

The biggest 5 of 32 mentions linking to the repository that our crawl found. Each line is their own headline.

See all 32

Talked about on GitHub, Bluesky, Hacker News and Stack Overflow (32 times).

On GitHub · 23

18 more on GitHub

On Bluesky · 4

The file

We split this AGENTS.md into the rules it gives an agent

1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈0

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Scope: read-only Drive (https://www.googleapis.com/auth/drive.readonly), defined once in auths.py SCOPES and defaulted again in …

    line 126

≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
AGENTS.md

OAuth User-Consent Flow — Agent Runtime + Gemini Enterprise

Intent

A clone-and-study ADK agent that reads a user's Google Drive files on their behalf, gated by an OAuth 2.0 user-consent flow. The agent is deployed to Agent Runtime (Vertex AI Agent Engine) and registered with Gemini Enterprise. The interesting part is not the agent (a thin wrapper around one Drive tool); it's the OAuth consent plumbing — the negotiate_creds() three-stage credential resolution that makes the same code work in both local ADK Web UI dev and production Gemini Enterprise, plus the register-oauth step that tells Gemini Enterprise which credentials to prompt with.

When To Use

  • The user needs an agent that calls a user-scoped Google API (Drive, Gmail, Calendar, …) and must obtain explicit user consent via OAuth 2.0.
  • The user wants one codebase that works both locally (ADK Web UI drives the consent screen) and in production (Gemini Enterprise injects the token).
  • The user wants a worked example of registering an OAuth authorization resource with the Discovery Engine API and linking it to a deployed agent.

Eval

  • Scenarios Path: tests/eval/evalsets/ (config: tests/eval/eval_config.json)
  • Minimum Score: rubric_based_final_response_quality_v1 threshold 0.8 (LLM judge gemini-3.5-flash, rubrics: relevance, helpfulness)

Cases in basic.evalset.json cover a greeting and a Drive-read request; they exercise the agent's response quality, not a live OAuth exchange. Run with make eval (single evalset) or make eval-all (every *.evalset.json).

End-to-end flow

The negotiate_creds() three-stage pattern makes one tool work in two environments:

LOCAL (ADK Web UI)                     PRODUCTION (Gemini Enterprise)
------------------                     ------------------------------
user asks to read a Drive file         user asks to read a Drive file
   -> negotiate_creds() Stage 3:          -> Gemini Enterprise sees the agent's
      request_credential() -> consent        authorizationConfig, prompts consent
      screen (OAUTH_CLIENT_ID/SECRET          using the registered auth resource
      from auths.py)                       -> token injected into
   -> user grants drive.readonly            tool_context.state["temp:<AUTH_ID>"]
   -> Stage 2: get_auth_response()        -> negotiate_creds() Stage 1 finds it
      exchanges code -> Credentials          immediately (never hits 2 or 3)
   -> cached in tool_context.state        -> read_drive_file() calls Drive API
      (Stage 1 on next call)
  1. Register OAuth creds once (make register-oauth) → an authorization resource in the Discovery Engine API.
  2. Deploy the agent (make deploy) → Agent Runtime, ID saved to deployment_metadata.json.
  3. Link the deployed agent to Gemini Enterprise with the auth resource (make register-gemini-enterprise AUTH_ID_RESOURCE=... GE_APP_ID=...).
  4. At runtime read_drive_file() calls negotiate_creds(), gets a token, builds a drive v3 client, and returns file content (Docs/Sheets/Slides via export, everything else via get_media).

Most interesting files to study (in order)

OAuth consent flow (the heart of this recipe)

  1. app/tools.py — the core of the recipe. negotiate_creds() implements the three-stage credential resolution: Stage 1 reads a cached/injected token from tool_context.state (checks both TOKEN_CACHE_KEY and the temp:<AUTH_ID> key Gemini Enterprise injects; handles both a raw access-token str and a full credential dict, refreshing when expired); Stage 2 picks up a completed ADK exchange via tool_context.get_auth_response() and builds Credentials (caching them); Stage 3 calls tool_context.request_credential() and returns {"pending": True, …} to trigger the consent screen. read_drive_file() short-circuits on a pending dict, else builds a drive v3 client and exports/downloads by MIME type.
  2. app/auths.py — the OAuth config. Defines AUTH_SCHEME (OAuth2/authorizationCode with Google's authorize + token URLs and the SCOPES dict), AUTH_CREDENTIAL (client id/secret from OAUTH_CLIENT_ID / OAUTH_CLIENT_SECRET), and AUTH_CONFIG. TOKEN_CACHE_KEY = AUTH_ID (default google-drive-auth) is the seam: it must match the id registered with Gemini Enterprise so Stage 1 finds the injected temp:<AUTH_ID> token. In production the client id/secret here are never used — only TOKEN_CACHE_KEY and SCOPES matter.
  3. tools/register_oauth.py — the standalone register-oauth step (not deployed with the agent). Interactively (or from env) gathers project / location / AUTH_ID / client id+secret / scopes, then POSTs a serverSideOauth2 authorization resource to {endpoint}-discoveryengine.googleapis.com/v1alpha/.../authorizations. Note it builds an authorizationUri with access_type=offline and prompt=consent (required — a bare URL is rejected), and handles a 409 by delete-and-recreate. This resource is what Gemini Enterprise uses to prompt the user; its full resource name feeds register-gemini-enterprise.

Deployment & Gemini Enterprise wiring

  1. app/agent_engine_app.py — wraps app.agent.app in an AdkApp subclass (AgentEngineApp) for Agent Runtime: set_up() initialises vertexai + telemetry + Cloud Logging, and register_feedback / register_operations expose a feedback op. The module-level agent_engine object is the deploy entrypoint. Artifact service is GCS (LOGS_BUCKET_NAME) or in-memory.
  2. app/app_utils/deploy.py — the Click CLI behind make deploy. Imports the entrypoint object, generates class_methods from register_operations, and calls client.agent_engines.create/update on Vertex AI (create-or-update by display name), then writes deployment_metadata.json. Also supports --agent-identity (per-agent IAM, Preview) and --set-secrets.
  3. Makefile — the register-gemini-enterprise (via uvx agent-starter-pack register-gemini-enterprise, passing --authorization-id) and unregister-gemini-enterprise (a DELETE curl against Discovery Engine) targets are the second half of the OAuth story — read them alongside register_oauth.py.

Agent (thin layer — read last)

  1. app/agent.py — the runtime. A single Agent (root_agent) on Gemini (MODEL_NAME env var) with one tool, read_drive_file, and an instruction telling it to ask for a file id and to explain the "pending" auth state. App(root_agent=…, name="app") is exported for both adk web and deployment.

Data handling

  • No datastore. The only "data" is whatever Drive file the user names; there is no ingestion, indexing, or embedding.
  • Token handling is the real data path. Credentials live in tool_context.state keyed by TOKEN_CACHE_KEY/AUTH_ID. Locally the full credential dict is cached (and refreshed via the refresh token when expired); in production Gemini Enterprise injects a raw access-token string under temp:<AUTH_ID>. Nothing is persisted outside session state.
  • Scope: read-only Drive (https://www.googleapis.com/auth/drive.readonly), defined once in auths.py SCOPES and defaulted again in register_oauth.py.
  • File reads: Google Docs → text/plain, Sheets → text/csv, Slides → text/plain (all via files().export); everything else via files().get_media. Bytes are decoded as UTF-8.

Gotchas / things to know

  • AUTH_ID must match everywhere. TOKEN_CACHE_KEY in auths.py, the AUTH_ID you pass to make register-oauth, and the authorization id inside AUTH_ID_RESOURCE for make register-gemini-enterprise must all agree, or Stage 1 will never find the injected temp:<AUTH_ID> token in production.
  • register-gemini-enterprise must be given AUTH_ID_RESOURCE — the full resource name from register-oauth output (projects/<num>/locations/<loc>/authorizations/<id>), not the bare id. Omit it and the agent registers without authorizationConfig and production gets No OAuth token available. Verify with the curl in the README.
  • The Makefile deploy and eval targets reference an adk_ae_oauth package (e.g. --source-packages=./adk_ae_oauth, adk eval ./adk_ae_oauth), but the agent code actually lives under app/ (see pyproject.toml module-name = "app" and deploy.py's own ./app defaults). Adjust those targets to app if they fail — this is a leftover from the template's project name.
  • Local dev needs a real OAuth client. OAUTH_CLIENT_ID / OAUTH_CLIENT_SECRET must be set (in app/.env) and http://localhost:8501/dev-ui/ must be an authorized redirect URI, or the ADK Web UI consent screen (Stage 3) won't work.
  • GOOGLE_CLOUD_LOCATION=global. agent.py forces the location to global at import and resolves the project from google.auth.default(), so imports need ADC. Tests import lazily / patch google.auth.default to avoid this.
  • Integration tests need INTEGRATION_TEST. conftest.py ignores tests/integration/* unless INTEGRATION_TEST is set (they import the agent package at module level and hit live GCP). make test runs tests/unit then tests/integration.
  • First-time Discovery Engine setup. register-oauth fails with a TenantProject … global does not exist (404) until Vertex AI Search / Conversation has been initialised once in the project (create any dummy Search or Chat app).

Where to run things

Makefile targets:

  • make install — uv sync (installs deps, bootstraps uv if missing).
  • make playground — local ADK Web UI on port 8501 (adk web . --reload_agents).
  • make deploy (alias make backend) — deploy to Agent Runtime via app_utils/deploy.py; writes deployment_metadata.json.
  • make test — uv run pytest tests/unit then tests/integration.
  • make eval / make eval-all — adk eval against tests/eval/evalsets/*.evalset.json with tests/eval/eval_config.json.
  • make lint — codespell + ruff check + ruff format --check + ty check.
  • make register-oauth — register the OAuth authorization resource (tools/register_oauth.py).
  • make register-gemini-enterprise / make unregister-gemini-enterprise — link / unlink the deployed agent in Gemini Enterprise (pass AUTH_ID_RESOURCE, GE_APP_ID, and for unregister AGENT_NAME).

Reuse (copy as-is)

  • app/auths.py + app/tools.py are the reusable OAuth core — copy both into another ADK tool to get the negotiate_creds() three-stage pattern. Swap SCOPES and the Google API client in read_drive_file() for whatever user-scoped API you need; the credential negotiation is API-agnostic.
  • tools/register_oauth.py is a self-contained script for registering an OAuth authorization resource with the Discovery Engine API — run it standalone (env vars or interactive prompts) for any Gemini Enterprise agent, not just this one.
  • app/app_utils/ (deploy.py, telemetry.py, typing.py) + app/agent_engine_app.py are generic Agent Runtime scaffolding — copy them to deploy any ADK App to Vertex AI Agent Engine.
  • There is no coupling to a datastore or infra: the only external contract is the OAuth AUTH_ID (shared between auths.py and the two registration steps) and the standard GOOGLE_CLOUD_PROJECT / MODEL_NAME env vars.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.