CtrlK
BlogDocsLog inGet started
Tessl Logo

deporvillage/gemini-a2a-tile

Gemini Enterprise A2A configuration and rules.

73

Quality

73%

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

Gemini Enterprise A2A Authentication Guide

When configuring an A2A agent to receive user identity signals (user_id, email) from Gemini Enterprise, you must configure an OAuth 2.0 Security Scheme. This document outlines the required production setup and common pitfalls.

The "Infinite Auth Loop" Pitfall

Do not use Mock Identity Providers (IdPs) with unsigned JWTs.

If you attempt to mock the OAuth token exchange (e.g., returning a JWT with alg: none), Gemini Enterprise's backend (Vertex AI Agent Builder) will silently reject the token due to strict cryptographic signature validation. Because the token is rejected, Gemini assumes the login failed and immediately redirects the user back to the /oauth/authorize endpoint, creating an infinite redirect loop.

The Production Standard: Google Workspace / GCP OAuth

Since Gemini Enterprise runs within the Google ecosystem, the most seamless and secure way to handle A2A authentication (especially if your organization uses Google Workspace) is to use Google as your Identity Provider.

Step 1: Configure Google Cloud Platform (GCP)

  1. Go to the GCP Credentials Page.
  2. Click + CREATE CREDENTIALS -> OAuth client ID.
  3. Select Web application as the application type.
  4. Under Authorized redirect URIs, you MUST add the Gemini Enterprise/Vertex AI redirect URIs:
    • https://vertexaisearch.cloud.google.com/oauth-redirect
    • https://vertexaisearch.cloud.google.com/static/oauth/oauth.html
  5. Click Create and safely store your Client ID and Client Secret.

Step 2: Configure Gemini Enterprise Agent

When registering your Agent Card in the Gemini Enterprise UI, fill out the OAuth details as follows:

  • Client ID: (Your GCP Client ID)
  • Client Secret: (Your GCP Client Secret)
  • Authorization URL: https://accounts.google.com/o/oauth2/v2/auth?access_type=offline (The access_type=offline parameter is MANDATORY for Gemini to receive a refresh token)
  • Token URL: https://oauth2.googleapis.com/token
  • Scopes: openid profile email

Step 3: Server-Side Validation & Token Introspection (AWS AgentCore)

When Gemini invokes your agent at /v1/message:send, it will inject an opaque Google Access Token into the headers (NOT a JWT, and NOT in the JSON payload): Authorization: Bearer ya29...

Because it's opaque, the agent cannot cryptographically decode it locally to find the user's email or ID. To resolve the identity, the agent (or an API Gateway) must perform Token Introspection by making a direct HTTP call to Google's UserInfo endpoint, passing the opaque token. Google then validates the token and responds with the verified user profile.

Your A2A server (or API Gateway) MUST:

  1. Intercept the Authorization header.
  2. Call Google's Token Introspection endpoint (GET https://www.googleapis.com/oauth2/v3/userinfo) with the opaque token.
  3. Extract the email, sub (User ID), and hd (Hosted Domain) claims from the response.
    • Example: hd: "deporvillage.com" confirms the user belongs to your organization.
  4. Inject these verified claims into the request context so the AgentExecutor (e.g., Snowflake queries) can use them safely for RBAC.

Token Introspection Code Example (Python / httpx):

import httpx
import logging

async def resolve_google_identity(access_token: str) -> dict:
    """
    Calls Google's UserInfo endpoint to resolve the opaque access token.
    Returns a dictionary with user profile data (email, sub, name, hd).
    """
    try:
        async with httpx.AsyncClient() as client:
            response = await client.get(
                "https://www.googleapis.com/oauth2/v3/userinfo",
                headers={"Authorization": f"Bearer {access_token}"}
            )
            
            if response.status_code == 200:
                # Success! We have the user's identity
                return response.json()
            else:
                logging.error(f"Token introspection failed: {response.status_code} - {response.text}")
                return {"error": "Invalid or expired token"}
                
    except Exception as e:
        logging.error(f"Network error reaching Google API: {e}")
        return {"error": "Network failure"}

Resolved Identity Payload Example:

{
  "sub": "114760933146628843083",
  "name": "Elvin Gomez",
  "given_name": "Elvin",
  "family_name": "Gomez",
  "email": "elvin.gomez@deporvillage.com",
  "email_verified": true,
  "hd": "deporvillage.com"
}
Workspace
deporvillage
Visibility
Public
Created
Last updated
Publish Source
CLI
Badge
deporvillage/gemini-a2a-tile badge