Skip to content

fix(api_server): add optional ADK_API_TOKEN Bearer auth middleware#5980

Closed
ghost wants to merge 1 commit into
mainfrom
unknown repository
Closed

fix(api_server): add optional ADK_API_TOKEN Bearer auth middleware#5980
ghost wants to merge 1 commit into
mainfrom
unknown repository

Conversation

@ghost
Copy link
Copy Markdown

@ghost ghost commented Jun 5, 2026

Summary

Adds an opt-in Bearer-token authentication middleware to ApiServer. The middleware is a no-op when ADK_API_TOKEN is unset (current behavior preserved for every existing deployment) and requires a matching Authorization: Bearer <token> header on every non-public request when ADK_API_TOKEN is set.

/health and /version remain open so liveness probes do not need credentials.

Why

ApiServer registers the following routes without per-route authentication:

  • POST /run, POST /run_sse — invoke the agent runner, which can reach in-process Python code execution via UnsafeLocalCodeExecutor.execute_code if the loaded agent wires that executor.
  • POST/GET/PUT/DELETE /apps/{app_name}/users/{user_id}/sessions/{session_id} — per-user session state CRUD, where the user_id is taken from the URL with no caller-identity binding.
  • All other routes registered by _register_production_endpoints and the dev-server endpoints in dev_server.py.

The only middleware in front today is CORSMiddleware + _OriginCheckMiddleware + _DefaultAppRewriteMiddleware — none of which authenticate the caller. When an operator binds the server to a network-reachable address (--host 0.0.0.0, container host, LAN demo, etc.) and the deployment has no upstream auth layer, any network-reachable caller hits those routes unauthenticated.

This PR provides the smallest opt-in mitigation that turns the existing unauth surface into authenticated endpoints without breaking any current deployment.

How

New _BearerAuthMiddleware in src/google/adk/cli/api_server.py mirrors the shape of the existing _OriginCheckMiddleware and _DefaultAppRewriteMiddleware ASGI middlewares in the same file.

Behavior matrix:

ADK_API_TOKEN env Path Authorization header Result
unset any any passes through (current behavior)
set /health or /version any passes through
set other absent 401
set other Bearer <wrong-token> 401
set other Bearer <correct-token> passes through
set non-HTTP (websocket, lifespan) any passes through

The middleware is wired in ApiServer.get_fast_api_app() right after _DefaultAppRewriteMiddleware. No CLI, public-API, or config-schema changes.

Tests

tests/unittests/cli/test_bearer_auth_middleware.py covers each row of the matrix above by exercising the middleware directly through the ASGI protocol (no FastAPI test client needed). Token-unset, token-set-no-header, token-set-wrong-bearer, token-set-correct-bearer, token-set-public-paths (parameterised over /health + /version), non-HTTP scopes, and session-route guard.

Backward compatibility

ADK_API_TOKEN is unset by default, in which case the middleware is a pass-through. Every existing deployment continues to behave exactly as before. Adopting the new behavior is a one-line environment variable change at deploy time.

Reference

Filed as a patch against Google bug-tracker issue 509219988 (the reviewing team there asked for a public patch alongside the report).

ApiServer registers /run, /run_sse, and the session CRUD routes without
per-route authentication. When the server is bound to a network-reachable
address with no upstream auth layer, those routes reach in-process Python
code execution (UnsafeLocalCodeExecutor.execute_code) and per-user session
state without any caller identity check.

This change adds an ASGI middleware that is a no-op when ADK_API_TOKEN is
unset (preserving the current behavior for deployments that gate access
upstream) and that requires a matching Authorization: Bearer <token>
header on every non-public request when ADK_API_TOKEN is set. /health and
/version remain open so liveness probes do not need credentials.

The middleware mirrors the shape of the existing _OriginCheckMiddleware
and _DefaultAppRewriteMiddleware in the same file. The fix is the smallest
opt-in path that turns the existing unauth surface into authenticated
endpoints without changing any user-facing API or breaking deployments
that already wire their own auth.

Refs: Google bug-tracker issue 509219988 (reporter request to provide a
patch alongside the report).
@google-cla
Copy link
Copy Markdown

google-cla Bot commented Jun 5, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@adk-bot
Copy link
Copy Markdown
Collaborator

adk-bot commented Jun 5, 2026

Response from ADK Triaging Agent

Hello @Cross-Trade-XT, thank you for creating this PR!

It looks like this PR is not fully following the contribution guidelines:

  1. Contributor License Agreement (CLA): The CLA check has failed. Please sign the CLA at https://cla.developers.google.com/ to proceed.
  2. Logs or Screenshots: For features or bug fixes, please provide logs or screenshots after the fix is applied to help reviewers understand the changes.
  3. Testing Plan & E2E Evidence: Please include a dedicated testing plan section in your PR description. Since this is an API server change (Runner), please provide the testing setup (e.g., how you tested it manually), commands used, and the console output/logs showing successful requests with and without the token.

This information will help reviewers to review your PR more efficiently. Thanks!

@f4x0rz
Copy link
Copy Markdown

f4x0rz commented Jun 5, 2026

Closing in favor of #5981 which is opened from my CLA-linked account. Same commit content, with a Testing Plan section and live integration evidence added per @adk-bot's request. Sorry for the noise.

@ghost
Copy link
Copy Markdown
Author

ghost commented Jun 5, 2026

Closing in favor of #5981, opened from my CLA-linked account. Same commit content, plus a Testing Plan section and live integration evidence added per @adk-bot's request. Sorry for the noise.

@ghost ghost closed this Jun 5, 2026
This pull request was closed.
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