Authenticate using 3-legged OAuth with auth manager

If you want your agent to access external tools and services (for example, Jira tasks or GitHub repositories) on behalf of a specific user, you must configure a 3-legged OAuth auth provider in Agent Identity auth manager.

3-legged OAuth auth providers manage user redirection and tokens for you. This removes the need to write custom code to handle complex OAuth 2.0 flows.

3-legged OAuth workflow

3-legged OAuth auth providers require user consent because the agent accesses resources on behalf of the user.

  1. Prompt and redirection: The chat interface prompts the user to sign in and then redirects the user to the third-party application's consent page.
  2. Consent and storage: After the user grants permission, the resulting OAuth tokens are stored in a Google-managed credential vault.
  3. Injection: When you use the Agent Development Kit (ADK), the agent automatically retrieves the token from the auth provider and injects it into the tool invocation headers.

Before you begin

  1. [Verify that you have chosen the correct authentication method](/iam/docs/agent-identity-overview#auth-models).
  2. Enable the Agent Identity Connector API.

    Roles required to enable APIs

    To enable APIs, you need the Service Usage Admin IAM role (roles/serviceusage.serviceUsageAdmin), which contains the serviceusage.services.enable permission. Learn how to grant roles.

    Enable the API

  3. Create and deploy an agent.
  4. Ensure that you have a frontend application to handle user sign-in prompts and redirection to third-party consent pages.
  5. Verify that you have the roles required to complete this task.

Required roles

To get the permissions that you need to create and use a 3-legged auth provider, ask your administrator to grant you the following IAM roles on the project:

For more information about granting roles, see Manage access to projects, folders, and organizations.

These predefined roles contain the permissions required to create and use a 3-legged auth provider. To see the exact permissions that are required, expand the Required permissions section:

Required permissions

The following permissions are required to create and use a 3-legged auth provider:

  • To create auth providers: iamconnectors.connectors.create
  • To use auth providers:
    • iamconnectors.connectors.retrieveCredentials
    • aiplatform.endpoints.predict
    • aiplatform.sessions.create

You might also be able to get these permissions with custom roles or other predefined roles.

Create a 3-legged auth provider

Create an auth provider to define the configuration and credentials for third-party applications.

To create a 3-legged auth provider, use the Google Cloud console or the Google Cloud CLI.

Console

  1. In the Google Cloud console, go to the Agent Registry page.

    Go to Agent Registry

  2. Click the name of the agent that you want to create an auth provider for.
  3. Click Identity.
  4. In the Auth Providers section, click Add auth provider.
  5. In the Add auth provider pane, enter a name and description.

    The name can contain only lowercase letters, numbers, or hyphens, cannot end with a hyphen, and must start with a lowercase letter.

  6. From the OAuth Type list, select OAuth (3 legged) .
  7. Click Create and continue.
  8. To grant your agent identity permission to use the auth provider, click Grant access.

    This automatically assigns the Connector User (roles/iamconnectors.user) role to the agent identity on the auth provider resource.

  9. Copy the callback URL.
  10. In a separate tab, register the callback URL on your third-party application.
  11. From your third-party application, obtain the client ID, client secret, token URL, and authorization URL.
  12. In the Auth provider credentials section, enter the following information:
    • Client ID
    • Client Secret
    • Token URL
    • Authorization URL
  13. Click Add provider config.

The newly created auth provider appears in the Auth Providers list.

Google Cloud CLI

  1. Create the auth provider with authorization and token URLs:

    gcloud alpha agent-identity connectors create AUTH_PROVIDER_NAME \
        --location="LOCATION" \
        --three-legged-oauth-authorization-url="AUTHORIZATION_URL" \
        --three-legged-oauth-token-url="TOKEN_URL"
  2. Retrieve the redirect URI from the auth provider details:

    gcloud alpha agent-identity connectors describe AUTH_PROVIDER_NAME \
        --location="LOCATION"

    The command returns the redirect URI in the redirectUrl field.

  3. Register the redirect URI with your third-party application to obtain the client ID and client secret.

  4. Update the auth provider with the client ID and client secret:

    gcloud alpha agent-identity connectors update AUTH_PROVIDER_NAME \
        --location="LOCATION" \
        --three-legged-oauth-client-id="CLIENT_ID" \
        --three-legged-oauth-client-secret="CLIENT_SECRET"
  5. To grant your agent identity permission to use auth providers, update the IAM allow policy for either the project or the specific auth provider, and grant the Connector User (roles/iamconnectors.user) role to the agent principal.

    Agent Identity is based on the industry-standard SPIFFE ID format. In IAM allow policies, agent identities are referred to using principal identifiers.

    Project-level (gcloud)

    Granting the role at the project level allows the agent to use any auth provider in that project.

    • To grant a single agent access to auth providers in a project, run the following command:

      gcloud projects add-iam-policy-binding PROJECT_ID \
          --role='roles/iamconnectors.user' \
          --member="principal://agents.global.org-ORGANIZATION_ID.system.id.goog/resources/aiplatform/projects/PROJECT_NUMBER/locations/LOCATION/reasoningEngines/ENGINE_ID"
    • To grant all agents in a project access to auth providers, run the following command:

      gcloud projects add-iam-policy-binding PROJECT_ID \
          --role='roles/iamconnectors.user' \
          --member="principalSet://agents.global.org-ORGANIZATION_ID.system.id.goog/attribute.platformContainer/aiplatform/projects/PROJECT_NUMBER"

    Connector-level (curl)

    To grant a single agent access to a specific auth provider, use the setIamPolicy API. This command overwrites any existing allow policy on the resource.

    curl -X POST \
        -H "Authorization: Bearer $(gcloud auth print-access-token)" \
        -H "Content-Type: application/json" \
        -d '{
      "policy": {
        "bindings": [
          {
            "role": "roles/iamconnectors.user",
            "members": ["principal://agents.global.org-ORGANIZATION_ID.system.id.goog/resources/aiplatform/projects/PROJECT_NUMBER/locations/LOCATION/reasoningEngines/ENGINE_ID"]
          }
        ]
      }
    }' \
        "https://iamconnectors.googleapis.com/v1alpha/projects/PROJECT_ID/locations/LOCATION/connectors/AUTH_PROVIDER_NAME:setIamPolicy"

    Replace the following:

    • PROJECT_ID: Your Google Cloud project ID.
    • AUTH_PROVIDER_NAME: The name of the auth provider.
    • ORGANIZATION_ID: Your Google Cloud organization ID.
    • PROJECT_NUMBER: Your Google Cloud project number.
    • LOCATION: The location for your agent (for example, us-central1).
    • ENGINE_ID: The ID of your reasoning engine.

Authenticate in your agent code

To authenticate your agent, you can use the ADK or call the Agent Identity API directly.

ADK

Reference the auth provider in your agent's code using the MCP toolset in the ADK.

from google.adk.agents.llm_agent import LlmAgent
from google.adk.auth.credential_manager import CredentialManager
from google.adk.integrations.agent_identity import GcpAuthProvider, GcpAuthProviderScheme
from google.adk.tools.mcp_tool.mcp_session_manager import StreamableHTTPConnectionParams
from google.adk.tools.mcp_tool.mcp_toolset import McpToolset
from google.adk.auth.auth_tool import AuthConfig

# Register the Google Cloud Auth Provider so the CredentialManager can use it.
CredentialManager.register_auth_provider(GcpAuthProvider())

# The URI to redirect the user to after consent is granted and the
# callback is received by the auth provider.
CONTINUE_URI = "https://YOUR_FRONTEND_URL/validateUserId"

# Create the Auth Provider scheme using the auth provider's full resource name.
auth_scheme = GcpAuthProviderScheme(
    name="projects/PROJECT_ID/locations/LOCATION/connectors/AUTH_PROVIDER_NAME",
    continue_uri=CONTINUE_URI
)

# Configure an MCP tool with the authentication scheme.
toolset = McpToolset(
    connection_params=StreamableHTTPConnectionParams(url="https://YOUR_MCP_SERVER_URL"),
    auth_scheme=auth_scheme,
)

# Initialize the agent with the authenticated tools.
agent = LlmAgent(
    name="YOUR_AGENT_NAME",
    model="gemini-2.0-flash",
    instruction="YOUR_AGENT_INSTRUCTIONS",
    tools=[toolset],
)

ADK

Reference the auth provider in your agent's code using an authenticated function tool in the ADK.

import httpx
from google.adk.agents.llm_agent import LlmAgent
from google.adk.auth.credential_manager import CredentialManager
from google.adk.integrations.agent_identity import GcpAuthProvider
from google.adk.integrations.agent_identity import GcpAuthProviderScheme
from google.adk.apps import App
from google.adk.auth.auth_credential import AuthCredential
from google.adk.auth.auth_tool import AuthConfig
from google.adk.tools.authenticated_function_tool import AuthenticatedFunctionTool
from vertexai import agent_engines

# First, register Google Cloud auth provider
CredentialManager.register_auth_provider(GcpAuthProvider())

# The URI to redirect the user to after consent is completed.
CONTINUE_URI = "WEB_APP_VALIDATE_USER_URI"

# Create Auth Config
spotify_auth_config = AuthConfig(
    auth_scheme=GcpAuthProviderScheme(
        name="projects/PROJECT_ID/locations/LOCATION/connectors/AUTH_PROVIDER_NAME",
        continue_uri=CONTINUE_URI
    )
)

# Use the Auth Config in Authenticated Function Tool
spotify_search_track_tool = AuthenticatedFunctionTool(
    func=spotify_search_track, auth_config=spotify_auth_config
)

# Sample function tool
async def spotify_search_track(credential: AuthCredential, query: str) -> str | list:
    token = None
    if credential.http and credential.http.credentials:
        token = credential.http.credentials.token

    if not token:
        return "Error: No authentication token available."

    async with httpx.AsyncClient() as client:
        response = await client.get(
            "https://api.spotify.com/v1/search",
            headers={"Authorization": f"Bearer {token}"},
            params={"q": query, "type": "track", "limit": 1},
        )
        # Add your own logic here

agent = LlmAgent(
    name="YOUR_AGENT_NAME",
    model="YOUR_MODEL_NAME",
    instruction="YOUR_AGENT_INSTRUCTIONS",
    tools=[spotify_search_track_tool],
)

app = App(
    name="YOUR_APP_NAME",
    root_agent=agent,
)

vertex_app = agent_engines.AdkApp(app_name=app)

ADK

Reference the auth provider in your agent's code using the Agent Registry MCP toolset in the ADK.

from google.adk.agents.llm_agent import LlmAgent
from google.adk.auth.credential_manager import CredentialManager
from google.adk.integrations.agent_identity import GcpAuthProvider
from google.adk.integrations.agent_identity import GcpAuthProviderScheme
from google.adk.tools.mcp_tool.mcp_session_manager import StreamableHTTPConnectionParams
from google.adk.tools.mcp_tool.mcp_toolset import McpToolset
from google.adk.auth.auth_tool import AuthConfig
from google.adk.integrations.agent_registry import AgentRegistry

# First, register Google Cloud auth provider
CredentialManager.register_auth_provider(GcpAuthProvider())

# The URI to redirect the user to after consent is completed.
CONTINUE_URI="WEB_APP_VALIDATE_USER_URI"

# Create Google Cloud auth provider by providing auth provider full resource name
auth_scheme=GcpAuthProviderScheme(
name="projects/GOOGLE_PROJECT/locations/LOCATION/connectors/AUTH_PROVIDER_NAME",
continue_uri=CONTINUE_URI)

# Set Agent Registry
registry = AgentRegistry(project_id="GOOGLE_PROJECT", location="global")

toolset = registry.get_mcp_toolset(mcp_server_name="projects/GOOGLE_PROJECT/locations/global/mcpServers/agentregistry-00000000-0000-0000-0000-000000000000", auth_scheme=auth_scheme )

# Example MCP tool
toolset = McpToolset(
    connection_params=StreamableHTTPConnectionParams(url="MCP_URL"),
    auth_scheme=auth_scheme,
)

agent = LlmAgent(
    name="YOUR_AGENT_NAME",
    model="YOUR_MODEL_NAME",
    instruction="YOUR_AGENT_INSTRUCTIONS",
    tools=[toolset],
)

Call the API directly

If you aren't using the ADK, your agent must call the iamconnectorcredentials.retrieveCredentials API to get the token.

Because this is a multi-step OAuth flow, the API returns a Long Running Operation (LRO). Your agent must handle the lifecycle of the operation:

  1. Initial request: The agent calls retrieveCredentials.
  2. Consent required: If the user hasn't granted consent, the API returns an LRO where the metadata contains the auth_uri and a consent_nonce.
  3. Frontend redirection: Your application must redirect the user to the auth_uri.
  4. Completion: After the user grants consent, call FinalizeCredential using the consent_nonce to complete the flow and obtain the token.

Update your client-side application

To handle user sign-in and redirection for 3-legged OAuth, your client-side application must implement the following steps to manage user consent and resume the conversation:

For a full implementation example, see the ValidateUserId frontend sample.

Handle the authorization trigger

When an agent needs user consent, it returns an adk_request_credential function call. Your application must intercept this call to initiate a user authorization dialog or redirect.

Manage the session context by recording the consent_nonce provided by the auth provider. This nonce is required to verify the user during the validation step. Save the auth_config and auth_request_function_call_id within the session to facilitate resuming the flow after consent is granted.

if (auth_request_function_call := get_auth_request_function_call(event_data)):
    print("--> Authentication required by agent.")
    try:
        auth_config = get_auth_config(auth_request_function_call)
        auth_uri, consent_nonce = handle_adk_request_credential(
            auth_config, auth_provider_name, request.user_id
        )
        if auth_uri:
            event_data['popup_auth_uri'] = auth_uri
            fc_id = auth_request_function_call.get('id') if isinstance(auth_request_function_call, dict) else getattr(auth_request_function_call, 'id', None)
            event_data['auth_request_function_call_id'] = fc_id
            event_data['auth_config'] = auth_config.model_dump()

            # Store session state
            if session_id:
                consent_sessions[session_id] = {
                    "user_id": request.user_id,
                    "consent_nonce": consent_nonce
                }
    except Exception as e:
        print(f"Error handling adk_request_credential: {e}")
        # Optionally, add logic to inform the user about the error.

def handle_adk_request_credential(auth_config, auth_provider_name, user_id):
    if auth_config.exchanged_auth_credential and auth_config.exchanged_auth_credential.oauth2:
        oauth2 = auth_config.exchanged_auth_credential.oauth2
        return oauth2.auth_uri, oauth2.nonce
    return None, None

Implement a user validation endpoint

Implement a validation endpoint on your web server (the same URI provided as continue_uri during configuration). This endpoint must do the following:

  1. Receive user_id_validation_state and auth_provider_name as query parameters.
  2. Retrieve the user_id and consent_nonce from the session context.
  3. Call the auth provider's FinalizeCredentials API with these parameters.
  4. Close the authorization window upon receiving a success response.
@app.api_route("/validateUserId", methods=["GET"])
async def validate_user(request: Request):
  auth_provider_name = request.query_params.get("auth_provider_name")
  session_id = request.cookies.get("session_id")
  session = consent_sessions.get(session_id, {})

  payload = {
      "userId": session.get("user_id"),
      "userIdValidationState": request.query_params.get(
          "user_id_validation_state"
      ),
      "consentNonce": session.get("consent_nonce"),
  }

  finalize_url = f"https://iamconnectorcredentials.googleapis.com/v1alpha/{auth_provider_name}/credentials:finalize"

  try:
    async with httpx.AsyncClient(timeout=30.0) as client:
      resp = await client.post(finalize_url, json=payload)
      resp.raise_for_status()
  except httpx.HTTPError as e:
    err_text = e.response.text if hasattr(e, "response") else str(e)
    status = e.response.status_code if hasattr(e, "response") else 500
    return HTMLResponse(err_text, status_code=status)

  return HTMLResponse("""
      <script>
          window.close();
      </script>
      <p>Success. You can close this window.</p>
  """)

Resume the agent conversation

After the user grants consent and the authorization window closes, retrieve the auth_config and auth_request_function_call_id from your session data. To continue the conversation, include these details in a new request to the agent as a function_response.

if request.is_auth_resume and session.auth_request_function_call_id and session.auth_config:
    auth_content = types.Content(
        role='user',
        parts=[
            types.Part(
                function_response=types.FunctionResponse(
                    id=session.auth_request_function_call_id,
                    name='adk_request_credential',
                    response=session.auth_config
                )
            )
        ],
    )
    # Send message to agent
    async for event in agent.async_stream_query(
        user_id=request.user_id,
        message=auth_content,
        session_id=session_id,
    ):
        # ...

Deploy the agent

When you deploy your agent to Google Cloud, ensure that Agent Identity is enabled.

If you're deploying to Agent Runtime, use the identity_type=AGENT_IDENTITY flag:

import vertexai
from vertexai import types
from vertexai.agent_engines import AdkApp

# Initialize the Vertex AI client with v1beta1 API for Agent Identity support
client = vertexai.Client(
    project="PROJECT_ID",
    location="LOCATION",
    http_options=dict(api_version="v1beta1")
)

# Use the proper wrapper class for your Agent Framework (e.g., AdkApp)
app = AdkApp(agent=agent)

# Deploy the agent with Agent Identity enabled
remote_app = client.agent_engines.create(
    agent=app,
    config={
        "identity_type": types.IdentityType.AGENT_IDENTITY,
        "requirements": ["google-cloud-aiplatform[agent_engines,adk]", "google-adk[agent-identity]"],
    },
)

What's next