---
title: Authorizing Gemini Agent2Agent (A2A) chains with Ping Identity solutions
description: "Authorize a Gemini Agent2Agent chain that delegates the user's token throughout the chain."
component: identity-for-ai
page_id: identity-for-ai:integrations:idai-authorizing-gemini-agent2agent-with-ping
canonical_url: https://developer.pingidentity.com/identity-for-ai/integrations/idai-authorizing-gemini-agent2agent-with-ping.html
llms_txt: https://developer.pingidentity.com/identity-for-ai/llms.txt
docs_for_agents: https://developer.pingidentity.com/build-with-ai/docs-for-agents.md
revdate: 2026-10-14T19:08:24Z
keywords: ["Identity for AI", "ai identity", "ai security", "Google Cloud Gemini Enterprise", "Gemini Enterprise Agent Platform", "PingOne Authorize", "authentication", "authorization"]
section_ids:
  how-this-works: How this works
  components: Components
  token-chain: Token chain
  benefits-of-this-approach: Benefits of this approach
  goals: Goals
  what-youll-do: What you'll do
  before-you-begin: Before you begin
  tasks: Tasks
  idai-gemini-a2a-mcp-server: Deploy the Order Status MCP Server
  idai-gemini-a2a-policies: Create authorization policies in PingOne Authorize
  idai-gemini-a2a-extension-service: Deploy the Agent Gateway Service Extension
  idai-gemini-a2a-agent-gateway: Create the Agent Gateway
  idai-gemini-a2a-order-status-agent: Deploy the Order Status Agent
  idai-gemini-a2a-support-agent: Deploy the Support Agent
  idai-gemini-a2a-agent-bridge: Deploy the Agent Bridge
  idai-gemini-a2a-chat-ui: Deploy the Chat UI
  validation: Validation
  troubleshooting: Troubleshooting
  whats-next: What's next
---

# Authorizing Gemini Agent2Agent (A2A) chains with Ping Identity solutions

This use case secures multiple agents in an A2A chain to reach a downstream tool, requiring authorization at each step in the chain.

After the user authenticates through a Chat UI, each agent uses [RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) token exchange to generate a token scoped for the next destination in the chain. The Google Cloud Agent Gateway Service Extension intercepts these requests, validates tokens, and executes subsequent token exchanges for every agent interaction. A token for a specific agent isn't usable anywhere else in the chain. The downstream tool eventually receives a narrowly scoped, short-lived token with a nested `act` claim recording the delegation chain from the original user to the downstream tool.

This use case extends the core [Integrating Gemini Enterprise Agent Platform with Ping Identity solutions](idai-integrating-gemini-agent-platform-with-ping.html) architecture by introducing a multi-agent delegation chain.

## How this works

Consider a scenario where a user-facing Support Agent answers questions about orders by delegating tasks to a specialized Order Status Agent, which in turn queries an Order Status MCP Server.

![Diagram showing an Agent2Agent flow.](_images/idai-gemini-a2a-flow.png)

This multi-agent architecture follows a sequence of validation, policy enforcement, and token exchange:

1. The user authenticates through the Chat UI.

2. The Support Agent exchanges the user's token for a delegated token licensed for the Agent Gateway, then initiates an A2A request to the Order Status Agent. The Agent Gateway intercepts this outbound call.

3. The Agent Gateway Service Extension validates the token and requests an authorization decision from PingOne Authorize. Upon a `PERMIT` decision, the service extension performs a token exchange to generate a new token scoped for the Order Status Agent, then forwards the request.

4. The Order Status Agent validates the received token. To call the downstream tool, it performs its own token exchange targeting the Agent Gateway.

5. The Agent Gateway intercepts the Order Status Agent's outbound MCP request. The service extension requests another authorization decision and performs the final token exchange to generate a token scoped for the Order Status MCP Server.

6. The request reaches the MCP server. The server validates the token, which contains a nested `act` claim recording the complete delegation history (service extension to Order Status Agent, to service extension, to Support Agent). The MCP server executes the action.

If PingOne Authorize returns a `DENY` decision, the service extension blocks the request and returns an error, preventing unauthorized agent access to downstream resources.

### Components

| Component                                                                                                                                                                                  | Role                                                                                                                                      |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
| [Chat UI](https://github.com/pingidentity/gemini-enterprise-agent-platform-integrations/tree/main/pingone/agent-chaining/services/chat-ui)                                                 | User chat interface with PingOne PKCE sign-on.                                                                                            |
| [Agent Bridge](https://github.com/pingidentity/gemini-enterprise-agent-platform-integrations/tree/main/pingone/agent-chaining/services/agent-bridge)                                       | Google Cloud entry point. Validates the user token and stores it in the agent session state.                                              |
| [Support Agent](https://github.com/pingidentity/gemini-enterprise-agent-platform-integrations/tree/main/pingone/agent-chaining/services/support-agent)                                     | User-facing agent. Exchanges the user's token and delegates to the Order Status Agent over A2A.                                           |
| [Order Status Agent](https://github.com/pingidentity/gemini-enterprise-agent-platform-integrations/tree/main/pingone/agent-chaining/services/order-status-agent)                           | Specialized agent that owns order lookup. Validates the inbound delegation token, then does a token exchange and calls the MCP server.    |
| [Agent Gateway](https://github.com/pingidentity/gemini-enterprise-agent-platform-integrations/tree/main/pingone/agent-chaining/services/agent-gateway)                                     | Google-managed policy enforcement point that intercepts both agent egress exchanges and forwards each to the service extension over gRPC. |
| [Agent Gateway Service Extension](https://github.com/pingidentity/gemini-enterprise-agent-platform-integrations/tree/main/pingone/agent-chaining/services/agent-gateway-extension-service) | Service extension that sends requests to PingOne Authorize, exchanges tokens, and injects resulting tokens into requests.                 |
| [Order Status MCP Server](https://github.com/pingidentity/gemini-enterprise-agent-platform-integrations/tree/main/pingone/agent-chaining/services/order-status-mcp-server)                 | Downstream resource that the agent chain invokes to check order status. It validates the final delegated token.                           |
| [PingOne](https://docs.pingidentity.com/pingone/use_cases/p1_oauth_2_token_exchange_delegation.html)                                                                                       | Identity provider (IdP) that issues the user token and every delegated token in the chain.                                                |
| [PingOne Authorize](https://docs.pingidentity.com/pingone/authorization_using_pingone_authorize/p1az_introduction.html)                                                                    | External policy decision point that evaluates authorization requests.                                                                     |

### Token chain

The token flow begins when the user authenticates through the Chat UI. The Support Agent presents the user's token to PingOne as the subject of an RFC 8693 token exchange, alongside its own actor token (obtained using the Client Credentials grant). PingOne evaluates the user token's `may_act` claim against the acting client before issuing the delegated token, preserving the user as the subject.

The Agent Gateway Service Extension then exchanges the delegated token, generating the token that the Order Status Agent consumes. The Order Status Agent repeats this pattern for its outbound request to the MCP server. During each exchange, PingOne nests the acting client in the `act` claim, recording the full delegation history.

For simplicity, the individual actor tokens requested by the agents and the gateway aren't included here.

* User token: Chat UI's initial sign-on token

  ```json
  {
    "iss": "https://auth.pingone.<region>/<env>/as",
    "sub": "<alice-user-id>",
    "client_id": "<chat-ui-client-id>",
    "aud": "support-agent",
    "scope": "openid support-agent:invoke",
    "grant_type": "authorization_code",
    "may_act": { "sub": "<support-agent-client-id>" }
  }
  ```

  * `sub`: Represents the user.

  * `client_id`: Generated by the Chat UI.

  * `aud`: Scoped for use on the Support Agent resource.

  * `scope`: Grants the ability to invoke the Support Agent.

  * `may_act`: Licenses the Support Agent as the sole authorized next actor.

* Delegated token 1: Support agent's outbound A2A token

  ```json
  {
    "iss": "https://auth.pingone.<region>/<env>/as",
    "sub": "<alice-user-id>",
    "client_id": "<support-agent-client-id>",
    "aud": "google-agent-gateway",
    "scope": "order-status:invoke",
    "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
    "act": { "sub": "<support-agent-client-id>", "act": null },
    "may_act": { "sub": "<agent-gw-extension-client-id>" }
  }
  ```

  * `sub`: Represents the user (the agent acts on behalf of the user, not itself).

  * `client_id`: Generated by the Support Agent.

  * `aud`: Scoped for use on the intermediate Agent Gateway resource.

  * `scope`: Grants the ability to invoke the Order Status Agent.

  * `act`: Records the Support Agent as the first delegate (the original sign-on token had no actor, resulting in an explicit null).

  * `may_act`: Licenses the Agent Gateway Service Extension as the sole authorized next actor.

* Delegated token 2: Agent Gateway's inbound A2A token for the Order Status Agent

  ```json
  {
    "iss": "https://auth.pingone.<region>/<env>/as",
    "sub": "<alice-user-id>",
    "client_id": "<agent-gw-extension-client-id>",
    "aud": "order-status-agent",
    "scope": "order-status:invoke",
    "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
    "act": {
      "sub": "<agent-gw-extension-client-id>", "act": {
        "sub": "<support-agent-client-id>", "act": null
      }
    },
    "may_act": { "sub": "<order-status-agent-client-id>" }
  }
  ```

  * `sub`: Represents the user (identity is preserved end-to-end).

  * `client_id`: Generated by the Agent Gateway Service Extension.

  * `aud`: Scoped for use on the Order Status Agent.

  * `scope`: Grants the ability to invoke the Order Status Agent.

  * `act`: Records the service extension as the next delegate, on behalf of the Support Agent.

  * `may_act`: Licenses the Order Status Agent as the sole authorized next actor.

* Delegated token 3: Order Status Agent's outbound MCP token

  ```json
  {
    "iss": "https://auth.pingone.<region>/<env>/as",
    "sub": "<alice-user-id>",
    "client_id": "<order-status-agent-client-id>",
    "aud": "google-agent-gateway",
    "scope": "order:read",
    "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
    "act": {
      "sub": "<order-status-agent-client-id>", "act": {
        "sub": "<agent-gw-extension-client-id>", "act": {
          "sub": "<support-agent-client-id>", "act": null
        }
      }
    },
    "may_act": { "sub": "<agent-gw-extension-client-id>" }
  }
  ```

  * `sub`: Represents the user (identity is preserved end-to-end).

  * `client_id`: Generated by the Order Status Agent.

  * `aud`: Scoped for use on the intermediate Agent Gateway resource.

  * `scope`: Grants the ability to read orders from the downstream MCP server.

  * `act`: Records the Order Status Agent as the next delegate, on behalf of the previous delegation chain.

  * `may_act`: Licenses the Agent Gateway Service Extension as the sole authorized next actor.

* MCP tool token: Agent Gateway's final token for the MCP server

  ```json
  {
    "iss": "https://auth.pingone.<region>/<env>/as",
    "sub": "<alice-user-id>",
    "client_id": "<agent-gw-extension-client-id>",
    "aud": "order-status-mcp-server",
    "scope": "order:read",
    "grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
    "act": {
      "sub": "<agent-gw-extension-client-id>", "act": {
        "sub": "<order-status-agent-client-id>", "act": {
          "sub": "<agent-gw-extension-client-id>", "act": {
            "sub": "<support-agent-client-id>", "act": null
          }
        }
      }
    }
  }
  ```

  * `sub`: Represents the user (identity is preserved end-to-end).

  * `client_id`: Generated by the Agent Gateway Service Extension.

  * `aud`: Scoped for use on the Order Status MCP Server.

  * `scope`: Grants the ability to read orders.

  * `act`: Contains the complete delegation chain (service extension, on behalf of Order Status Agent, on behalf of service extension, on behalf of Support Agent).

  * `may_act`: Because this is the final token, it doesn't include this claim. There are no further downstream token exchanges.

### Benefits of this approach

* Strictly scoped credentials

  Agents possess short-lived tokens that aren't broadly applicable. Through continuous RFC 8693 token exchanges, each stage of the delegation chain generates a new token scoped exclusively for its immediate destination. A token generated for one agent is invalid anywhere else in the chain, ensuring that least privilege compounds across every interaction.

* Dual-layer trust boundary

  A2A interaction requires both a Google credential, to satisfy Google Cloud identity and access management (IAM) at the Agent Runtime endpoint, and a PingOne delegated token to provide verifiable user identity and delegation context. This defense-in-depth approach ensures that compromising one trust layer doesn't grant access to the other.

* Continuous centralized authorization

  The Agent Gateway and service extension enforce authorization decisions at every stage of the chain (both A2A and MCP). Because policies are centralized in PingOne Authorize, you can update access rules dynamically without modifying or redeploying agents, the gateway, or the MCP server.

* End-to-end delegation record

  The final token delivered to the downstream tool contains a nested `act` claim that records every actor in the sequence. Because PingOne strictly enforces the `may_act` claim during each exchange, the downstream tool receives proof of who acted on behalf of the user, from the initial Chat UI sign-on to the final server invocation.

## Goals

* **Propagate user identity through a multi-agent chain**: Authenticate users at the Chat UI and configure PingOne to preserve the complete delegation history (using the `act` claim) across all A2A interactions.

* **Centralize policy enforcement for agent actions**: Deploy the Agent Gateway and service extension to consult PingOne Authorize for authorization decisions at runtime, restricting agent and tool invocations based on group membership and business hours.

* **Scope downstream access with token exchange**: Use RFC 8693 token exchanges at each stage of the delegation chain to translate inbound credentials into narrowly scoped tokens for the next authorized actor.

## What you'll do

1. [Deploy the Order Status MCP Server](#idai-gemini-a2a-mcp-server) to Google Cloud Run and register it in Google Agent Registry.

2. [Create authorization policies](#idai-gemini-a2a-policies) that limit agent invocations to business hours and to members of the support group.

3. [Deploy the Agent Gateway Service Extension](#idai-gemini-a2a-extension-service) to Cloud Run and configure it to request authorization decisions on every tool call.

4. [Deploy the Agent Gateway](#idai-gemini-a2a-agent-gateway) and attach the service extension to it.

5. [Deploy the Order Status Agent](#idai-gemini-a2a-order-status-agent) and create its PingOne resource.

6. [Deploy the Support Agent](#idai-gemini-a2a-support-agent) and create its PingOne resource.

7. [Deploy the Agent Bridge](#idai-gemini-a2a-agent-bridge) to Cloud Run and configure it to validate the user's token and pass it to the Support Agent.

8. [Deploy the Chat UI](#idai-gemini-a2a-chat-ui) and configure it to pass the user's token to the Support Agent through the Agent Bridge.

## Before you begin

Ensure you meet the prerequisites in the [Gemini Enterprise Agent Platform](idai-integrating-gemini-agent-platform-with-ping.html#idai-gemini-prereqs) integration overview.

## Tasks

You can find implementation files and code samples for this use case in the [Agent Chaining](https://github.com/pingidentity/gemini-enterprise-agent-platform-integrations/tree/main/pingone/agent-chaining) GitHub repository.

### Deploy the Order Status MCP Server

The [Order Status MCP Server](https://github.com/pingidentity/gemini-enterprise-agent-platform-integrations/tree/main/pingone/agent-chaining/services/order-status-mcp-server) exposes a `get_order_status` tool. This service holds the order data and is the final enforcement point for the agent chaining delegation model. To access the tool, the agent's request is routed through the Agent Gateway and the Agent Gateway Service Extension. This MCP server verifies the OAuth token injected by the gateway.

You'll deploy the supply chain MCP server to Cloud Run, register it in Agent Registry as an approved egress destination, and configure it with a matching resource in PingOne to ensure every token issued for tool access carries claims that prove delegation.

> **Collapse: Create the Order Status MCP Server resource in PingOne**
>
> Add a custom resource in PingOne that represents the Order Status MCP Server the agent is allowed to access.
>
> |   |                                                                                                                                                                            |
> | - | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
> |   | You can find more details about [adding custom resources](https://docs.pingidentity.com/pingone/applications/p1_adding_custom_resource.html) in the PingOne documentation. |
>
> You'll configure the following custom attributes to govern how tokens are generated during both token exchanges and standard client credential requests:
>
> * `sub`: Determines the subject of the issued token based on the request type. During a token exchange, it copies the `sub` claim from the subject token to preserve the user's identity across the delegation chain. During a `client_credentials` request (used by the gateway extension to fetch its own actor token), it outputs the literal string `no-subject` because no user context exists.
>
> * `act`: Manages the token delegation chain. During a `client_credentials` request, it outputs the constant value `noActor`. During a token exchange, it verifies that the subject token's `may_act` claim matches the actor token's `client_id`. If they match, it nests the actor's identity in the subject token's existing `act` claim. If they don't match, the expression returns a null value. Because this is the only attribute where you must select the **Required** checkbox, a null return explicitly fails the unauthorized exchange.
>
> * `grant_type` (Optional): This attribute can enrich the final token by adding the authorization grant type (such as `urn:ietf:params:oauth:grant-type:token-exchange`) as a first-class claim.
>
> |   |                                                                                                                                                                                                      |
> | - | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
> |   | Because the Order Status MCP Server resource is terminal, a `may_act` attribute isn't necessary. The MCP server validates the token, then the chain ends without further downstream token exchanges. |
>
> 1. In the PingOne admin console, go to **Applications > Resources** and click the **[icon: plus, set=fa]**icon.
>
> 2. Define the resource:
>
>    1. **Resource Name**: `AC Order Status MCP Server`
>
>    2. **Audience**: `order-status-mcp-server`
>
> 3. Click **Next**.
>
> 4. Define attributes for the resource:
>
>    Detailed steps follow the table.
>
>    | Attribute    | Required | Expression                                                                                                                                                                                                                                                                                                |
>    | ------------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
>    | `sub`        | No       | ```spel
>    (#root.context.requestData.grantType == "client_credentials") ? "no-subject" : #root.context.requestData.subjectToken.sub
>    ```                                                                                                                                                                     |
>    | `act`        | Yes      | ```spel
>    (#root.context.requestData.grantType == "client_credentials")?"noActor":((#root.context.requestData.subjectToken.may_act.sub == #root.context.requestData.actorToken.client_id)?{"sub":#root.context.requestData.actorToken.client_id,"act":#root.context.requestData.subjectToken.act}:null)
>    ``` |
>    | `grant_type` | No       | ```spel
>    #root.context.requestData.grantType
>    ```                                                                                                                                                                                                                                                           |
>
>    1. Click the **Gear** icon ([icon: gear, set=fa]) next to the `sub` attribute, enter the expression, and click **Save**.
>
>       |   |                                                                                                                               |
>       | - | ----------------------------------------------------------------------------------------------------------------------------- |
>       |   | Leave the **Required** checkbox cleared for this attribute to prevent validation errors during `client_credentials` requests. |
>
>       Learn more about expressions in [Using the expression builder](https://docs.pingidentity.com/pingone/pingone_expression_language/p1_use_expression_builder.html) in the PingOne documentation.
>
>    2. Click **+ Add** and name the attribute `act`.
>
>    3. Click [icon: gear, set=fa], enter the expression, and click **Save**.
>
>    4. Select the **Required** checkbox for the `act` attribute.
>
>       This ensures that any token exchange that fails to set `act` is rejected.
>
>    5. (Optional) Click **+ Add**, name the attribute `grant_type`, click [icon: gear, set=fa], enter the expression, and click **Save**.
>
> 5. Click **Next**, then click **+ Add Scope**.
>
> 6. Name the scope `order:read`.
>
> 7. Click **Save**.

> **Collapse: Create a support team group in PingOne**
>
> Create a group in PingOne to manage user access to the Support Order MCP Server.
>
> |   |                                                                                                                                                       |
> | - | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
> |   | You can find more details about [creating groups](https://docs.pingidentity.com/pingone/directory/p1_create_group.html) in the PingOne documentation. |
>
> 1. In the PingOne admin console, go to **Directory > Groups**.
>
> 2. Click the **[icon: plus, set=fa]**icon.
>
> 3. Name the group `support_team` and click **Save**.
>
> 4. Add any PingOne users authorized to invoke the Support Order MCP Server:
>
>    1. On the **Users** tab, click **Add Individually**.
>
>    2. In the **All Users** list, select checkboxes for the users you want to add.
>
> 5. Click **Save**.

> **Collapse: Deploy the Order Status MCP Server to Cloud Run**
>
> Build the MCP server with configured environment variables and deploy it as a Google Cloud Run service.
>
> 1. Set environment variables for the MCP server:
>
>    1. In the cloned Gemini Enterprise Agent Platform Integrations repository, go to the `pingone/agent-chaining/services/order-status-mcp-server` directory.
>
>    2. Make a copy of the `.env.sample` file and name it `.env`:
>
>       ```bash
>       cp .env.sample .env
>       ```
>
>    3. Set environment variables in the `.env` file:
>
>       | Variable                    | Value                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
>       | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
>       | `GC_REGION`                 | Your Google Cloud Platform region. For example: `us-central1`.                                                                                                                                                                                                                                                                                                                                                                                                                                    |
>       | `GC_CLOUD_RUN_SERVICE_NAME` | The Cloud Run service name: `ac-order-status-mcp-server`.                                                                                                                                                                                                                                                                                                                                                                                                                                         |
>       | `IDP_ISSUER`                | The PingOne issuer URL: `https://auth.pingone.<region>/<env-id>/as`.Set the placeholders:- \<region>: The country code top-level domain (ccTLD), such as `.com`, `.eu`, `.asia`. You can find this in your PingOne admin console URL. For example, `https://console.pingone.com/index.html?env=12345678-1234-1234-1234-123456789012` has a ccTLD of `.com`.
>
>       - \<env-id>: The PingOne environment ID. In the example PingOne admin console URL, the ID is `12345678-1234-1234-1234-123456789012`. |
>       | `IDP_REQUIRED_AUDIENCE`     | The expected `aud` claim. Enter the audience you gave the Order Status MCP Server resource in PingOne: `order-status-mcp-server`.                                                                                                                                                                                                                                                                                                                                                                 |
>       | `IDP_REQUIRED_SCOPE`        | The scope the inbound token must carry. Enter the scope you gave the AC Order Status MCP Server resource in PingOne: `order:read`.                                                                                                                                                                                                                                                                                                                                                                |
>
> 2. In the `pingone/agent-chaining/services/order-status-mcp-server` directory, run the following command:
>
>    ```bash
>    make deploy
>    ```
>
>    This builds the Docker image, pushes it to Google Container Registry, and deploys it to Cloud Run.

> **Collapse: Register the Order Status MCP Server in Agent Registry**
>
> Register the MCP server in Agent Registry to ensure the Agent Gateway recognizes it as an approved egress destination.
>
> |   |                                                                                                                                                                                                                               |
> | - | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
> |   | No additional IAM configuration is required after registration. The registry-wide `roles/iap.egressor` binding, which is applied automatically by each agent's `deploy.py` script, provides all necessary egress enforcement. |
>
> 1. In Google Cloud, go to **Agent Platform > Govern > Agent Registry** and click **Add MCP Server**.
>
> 2. Enter the following values:
>
>    * **Name**: `ac-order-status-mcp-server`
>
>    * **Description**: `Order status MCP server for the Agent Chaining demo`
>
> 3. Select your **Region** (for example, `us-central1`).
>
> 4. Enter the MCP server URL:
>
>    1. To find the URL, duplicate the browser tab and go to **Cloud Run > Services**. Click the **order-status-mcp-server**.
>
>    2. Copy the URL at the top of the page. For example: `https://agent-chain-order-status-mcp-server-909399354423.us-central1.run.app`.
>
>    3. In Agent Registry, paste the URL in the **MCP server URL** field, and then add `/mcp` to the end of the URL.
>
>       |   |                                                                                                                                                                                                                                                                                                                                 |
>       | - | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
>       |   | The registered URL must match the host URL the agent calls exactly (defined by the `MCP_ORDER_STATUS_SERVER_URL` variable in the agent's `.env` file). Identity-Aware Proxy (IAP) resolves egress destinations by host. If the host doesn't match, IAP denies the request before it ever reaches the gateway service extension. |
>
> 5. Enter the MCP specification JSON, which defines the MCP server's endpoints and expected request and response formats:
>
>    1. In the cloned repository, go to the `pingone/agent-chaining/services/order-status-mcp-server` directory and copy the contents of the `tool-spec.json` file.
>
>    2. In Agent Registry, paste the copied contents into the **JSON** field.
>
>    3. Click **Next**.
>
> 6. Click **Save** to complete the registration.

### Create authorization policies in PingOne Authorize

Create policies that decide whether the service extension permits the agent's request to the MCP server. These policies limit access to members of the support group during business hours.

> **Collapse: Create attributes in the Trust Framework**
>
> Create attributes in PingOne Authorize that extract information from service extension decision requests. These attributes are used in authorization policies to determine whether the agent is allowed to access the MCP server.
>
> |   |                                                                                                                                                                                                                     |
> | - | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
> |   | You can find more details about [adding authorization attributes](https://docs.pingidentity.com/pingone/authorization_using_pingone_authorize/p1_az_adding_attributes.html) in the PingOne Authorize documentation. |
>
> | Name           | Type       | Extension parameter key |
> | -------------- | ---------- | ----------------------- |
> | `User Sub`     | **String** | `User Sub`              |
> | `Request Hour` | **Number** | `Request Hour`          |
>
> 1. In the PingOne admin console, go to **Authorization > Trust Framework**.
>
> 2. On the **Attributes** tab, click the **[icon: plus, set=fa]**icon.
>
> 3. Name the attribute `User Sub`.
>
>    The attribute name must match the parameter key in the decision request. It's case-sensitive.
>
> 4. If the attribute has a **Parent**, click [icon: trash, set=fa]to clear the parent value, ensuring the attribute isn't nested under another attribute.
>
> 5. In the **Resolvers** section, click **[icon: plus, set=fa]Add Resolver** and keep the default **Request Parameter** resolver.
>
> 6. (Optional) Name the resolver `user_sub` to match the parameter name in the agent's request.
>
>    Request Parameter resolvers extract values from decision requests and pass them to PingOne Authorize for evaluation. The resolver name is a label only and isn't matched to the parameter name in the request.
>
> 7. For the Value Settings **Type**, keep the default value of **String**.
>
> 8. Click **Save Changes**.
>
> 9. If you don't already have the `Request Hour` attribute, repeat the steps above to add it, with a resolver parameter optionally named `request_hour` and the value settings type **Number**.

> **Collapse: Create the AC Agent Gateway Policies policy set**
>
> Create a policy set in PingOne Authorize that contains the policies governing the agent's access to the MCP server.
>
> |   |                                                                                                                                                                                                                 |
> | - | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
> |   | You can find more details about [adding policy sets](https://docs.pingidentity.com/pingone/authorization_using_pingone_authorize/p1az_adding_policy_or_policy_set.html) in the PingOne Authorize documentation. |
>
> 1. In the PingOne admin console, go to **Authorization > Policies**.
>
> 2. Click the root **Policies** policy set, then click the **[icon: plus, set=fa]**icon and select **Add Policy Set**.
>
> 3. Name the policy set `AC Agent Gateway Policies`.
>
> 4. For the **Combining Algorithm**, select **Unless one decision is deny, the decision will be permit**.
>
> 5. Click **Save changes**.

> **Collapse: Create the Only Permit During Business Hours policy**
>
> Create a policy that permits access to the MCP server only during business hours. PingOne Authorize denies access if the request hour is before 8:00 AM or after 6:00 PM.
>
> The service extension converts from UTC before sending a decision request to ensure permitted hours are in local time. For example, a request at 3:00 PM UTC is converted to 8:00 AM Pacific and is permitted, while a request at 3:00 AM UTC is converted to 8:00 PM Pacific and is denied.
>
> |   |                                                                                                                                                                                                              |
> | - | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
> |   | You can find more details about [adding policies](https://docs.pingidentity.com/pingone/authorization_using_pingone_authorize/p1az_adding_policy_or_policy_set.html) in the PingOne Authorize documentation. |
>
> 1. In **Authorization > Policies**, select the **AC Agent Gateway Policies** policy set.
>
> 2. Click the **[icon: plus, set=fa]**icon and select **Add Policy**.
>
> 3. Name the policy `Only Permit Within Business Hours`.
>
> 4. In **Applies When**:
>
>    1. Click **+ Comparison**, then select **Any**.
>
>    2. Select the **Request Hour** attribute.
>
>    3. Select the **Greater Than** operator.
>
>    4. For the value, enter `18`.
>
>    5. Click **+ Comparison** and select the **Request Hour** attribute.
>
>    6. Select the **Less Than** operator.
>
>    7. For the value, enter `8`.
>
> 5. For the **Combining Algorithm**, select **Unless one decision is deny, the decision will be permit**.
>
> 6. Click **+ Add Rule**, name the rule `Deny Outside Business Hours`, then for **Effect**, select **Deny**.
>
> 7. Click **Save changes**.

> **Collapse: Create the Only Permit Users in Support Group policy**
>
> Create a policy that permits only members of the support group to access the MCP server. Agents are denied access when `user_sub` isn't a member of the support team.
>
> |   |                                                                                                                                                                                                              |
> | - | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
> |   | You can find more details about [adding policies](https://docs.pingidentity.com/pingone/authorization_using_pingone_authorize/p1az_adding_policy_or_policy_set.html) in the PingOne Authorize documentation. |
>
> 1. In **Authorization > Policies**, select the **AC Agent Gateway Policies** policy set.
>
> 2. Click the **[icon: plus, set=fa]**icon and select **Add Policy**.
>
> 3. Name the policy `Only Permit Users in Support Group`.
>
> 4. In **Applies When**:
>
>    1. Click **+ Comparison** and select the **User Sub** attribute.
>
>    2. Select the **Is Not Member Of** operator.
>
>    3. For the value, select the **support_team** attribute.
>
> 5. For the **Combining Algorithm**, select **Unless one decision is deny, the decision will be permit**.
>
> 6. Click **+ Add Rule**, name the rule `Deny Non-Members`, then for **Effect**, select **Deny**.
>
> 7. Click **Save changes**.

> **Collapse: Publish an authorization policy version**
>
> Publish a policy version to deploy your policies to a decision endpoint and make them available in decision evaluations.
>
> |   |                                                                                                                                                                                                                 |
> | - | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
> |   | You can find more details about [publishing policy versions](https://docs.pingidentity.com/pingone/authorization_using_pingone_authorize/p1az_publishing_versions.html) in the PingOne Authorize documentation. |
>
> 1. In the PingOne admin console, go to **Authorization > Version History**.
>
> 2. Click the **More Options (⋮)** icon next to the **Latest Version** and select **Publish Version**.
>
> 3. In the **Publish to** list, select a decision endpoint.
>
> 4. For the version **Name**, enter `AC`.
>
> 5. Click **Publish**.
>
>    The version name and the endpoint the version is published to are displayed in **Version History**.
>
> 6. []()Go to **Authorization > Decision Endpoints** and expand the decision endpoint where you published the policy version. Copy the decision endpoint URL and save it somewhere convenient. You'll need it later when you [deploy the service extension](#idai-gemini-a2a-authz-dec-endpoint).

### Deploy the Agent Gateway Service Extension

The [Agent Gateway Service Extension](https://github.com/pingidentity/gemini-enterprise-agent-platform-integrations/tree/main/pingone/agent-chaining/services/agent-gateway-extension-service) is an Envoy `ext_proc` gRPC handler that the Agent Gateway calls to request authorization decisions and perform token exchanges.

The service extension governs two interaction paths:

* **A2A:** The Support Agent calls the Order Status Agent.

* **Agent to tool (MCP):** The Order Status Agent calls the Order Status MCP Server.

For requests matching either target, the service extension performs the following operations:

* Validates the agent's delegated token, specifically checking the `iss`, `aud`, and `scope` claims.

* Calls PingOne Authorize on A2A `message:send` or MCP `tools/call` requests to evaluate the user's identity and the time of the request.

* Upon a deny decision, error, or unknown request body, blocks the request and returns a `403 Forbidden` error.

* Upon a permit decision, performs a token exchange to generate a token scoped to the target audience. The injection method depends on the destination:

  * For A2A requests, injects the new token into the body metadata. The `Authorization` header is reserved for the Google credential required to pass the receiving endpoint's IAM check.

  * For MCP requests, injects the new token into the `Authorization: Bearer` header.

> **Collapse: Create a PingOne worker application**
>
> Create a PingOne worker application that the service extension uses to call PingOne Authorize for authorization decisions.
>
> |   |                                                                                                                                                                               |
> | - | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
> |   | You can find more details about [adding applications](https://docs.pingidentity.com/pingone/applications/p1_applications_add_applications.html) in the PingOne documentation. |
>
> 1. In the PingOne admin console, go to **Applications > Applications**.
>
> 2. Click the **[icon: plus, set=fa]**icon and create the application profile:
>
>    1. Name the application `AC PingOne Authorize Worker App`.
>
>    2. For the **Application Type**, click **Worker**.
>
>    3. Click **Save**.
>
> 3. To enable the application, click the toggle next to the application name in the details panel.
>
> 4. On the **Configuration** tab, click the Pencil icon ([icon: pencil, set=fa]), make sure **Client Credentials** is the **Grant Type**, then click **Cancel**.
>
> 5. On the **Roles** tab, click **Grant Roles**.
>
> 6. Expand the **Environment Admin** role and select your environment.
>
> 7. Expand the **Identity Data Read Only** role and select your environment.
>
> 8. Click **Save**.

> **Collapse: Create a PingOne application for token exchange**
>
> Create a PingOne OIDC application that the service extension uses to perform token exchanges for the MCP server.
>
> |   |                                                                                                                                                                               |
> | - | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
> |   | You can find more details about [adding applications](https://docs.pingidentity.com/pingone/applications/p1_applications_add_applications.html) in the PingOne documentation. |
>
> 1. In the PingOne admin console, go to **Applications > Applications**.
>
> 2. Click the **[icon: plus, set=fa]**icon and create the application profile:
>
>    1. Name the application `AC Agent Gateway Extension`.
>
>    2. For the **Application Type**, click **OIDC Web App**.
>
>    3. Click **Save**.
>
> 3. To enable the application, click the toggle next to the application name in the details panel.
>
> 4. On the **Configuration** tab, click [icon: pencil, set=fa]:
>
>    1. For **Grant Type**, select **Client Credentials** and **Token Exchange**.
>
>    2. Clear the **Code** and **Authorization Code** checkboxes.
>
>    3. Click **Save**
>
> 5. On the **Resources** tab, click [icon: pencil, set=fa], then select the **order:read** scope on the AC Order Status MCP Server.
>
> 6. []()Select the **order_status:invoke** scope on the AC Order Status Agent.
>
>    This scope isn't available yet. Skip this step for now and complete it after you [deploy the Order Status Agent](#idai-gemini-a2a-order-status-deployed-scope).
>
> 7. Click **Save**.
>
> 8. []()On the **Overview** tab, copy the **Client ID** for this application and save it somewhere convenient. You'll need it later when you [configure attributes](#idai-gemini-a2a-gateway-attr) for the Agent Gateway resource in PingOne.

> **Collapse: Deploy the Agent Gateway Service Extension to Cloud Run**
>
> Build the service extension with configured environment variables and deploy it as a Google Cloud Run service.
>
> 1. Set environment variables for the service extension:
>
>    1. In the cloned repository, go to the `pingone/agent-chaining/services/agent-gateway-extension-service` directory.
>
>    2. Make a copy of the `.env.sample` file and name it `.env`:
>
>       ```bash
>       cp .env.sample .env
>       ```
>
>    3. Set environment variables in the `.env` file:
>
>       | Variable                      | Value                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
>       | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
>       | `GC_REGION`                   | Your Google Cloud Platform region. For example: `us-central1`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
>       | `GC_CLOUD_RUN_SERVICE_NAME`   | The Cloud Run service name: `ac-agent-gateway-extension-service`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
>       | `GC_SERVICE_EXTENSION_NAME`   | The deployed service extension's name (created when the service extension was registered): `ac-agent-gateway-extension`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
>       | `IDP_ISSUER`                  | The PingOne issuer URL: `https://auth.pingone.<region>/<env-id>/as`.To find this endpoint, go to the AC Agent Gateway Extension application in PingOne. Copy the **Issuer ID** under **Connection Details** on the **Overview** tab.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
>       | `EXCHANGE_CLIENT_ID`          | The AC Agent Gateway Extension application's **Client ID**. This is located above **Connection Details** on the **Overview** tab.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
>       | `EXCHANGE_CLIENT_SECRET`      | The AC Agent Gateway Extension application's **Client Secret**. This is located above **Connection Details** on the **Overview** tab.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
>       | `AUTHZ_CLIENT_ID`             | The AC PingOne Authorize Worker App's **Client ID**. This is located above **Connection Details** on the **Overview** tab for this application.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
>       | `AUTHZ_CLIENT_SECRET`         | The AC PingOne Authorize Worker App's **Client Secret**. This is located above **Connection Details** on the **Overview** tab.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
>       | []()`AUTHZ_DECISION_ENDPOINT` | The PingOne Authorize decision endpoint URL where you [published the policy version](#idai-gemini-a2a-decision-endpoint) earlier.To find the URL, go to **Authorization > Decision Endpoints** and expand the decision endpoint where you published the policy version. Copy the decision endpoint URL.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
>       | `IDP_REQUIRED_AUDIENCE`       | The intermediate audience targeted by the initial token exchanges for both the Support Agent and the Order Status Agent. This is `ac-google-cloud-agent-gateway`.No downstream service validates this audience directly. Instead, the gateway uses this intermediate audience as the source to generate the final token for the target destination (defined by either `AGENT_AUDIENCE` or `TOOL_AUDIENCE`). This ensures the final PingOne resources remain terminal, requiring only `sub` and `act` attributes rather than processing multiple chained exchanges.                                                                                                                                                                                                                                              |
>       | `IDP_REQUIRED_SCOPE_AGENT`    | The scope the inbound token must carry to invoke the Order Status Agent. This is `order_status:invoke`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
>       | `IDP_REQUIRED_SCOPE_TOOL`     | The scope the inbound token must carry to invoke the Order Status MCP Server. This is `order:read`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
>       | []()`AGENT_ENGINE_ID`         | The Reasoning Engine's identifier for the Order Status Agent. In this example, the ID follows `reasoningEngines:` in the Order Status Agent's Uniform Resource Name (URN): `urn:agent:projects-909399354423:projects:909399354423:locations:us-central1:aiplatform:reasoningEngines:7390548000881246208`.You'll populate this placeholder after you [deploy the Order Status Agent](#idai-gemini-a2a-order-status-deployed-engine-id) in a later task. To find the ID at that point, go to the `ac-order-status-agent` service in **Agent Registry**. After you populate this variable, redeploy the service extension.&#xA;&#xA;The Reasoning Engine ID changes each time you redeploy the Order Status Agent. If you redeploy the agent, update this environment variable and redeploy the service extension. |
>       | `AGENT_SCOPE`                 | The scope the service extension requests during token exchange to authorize access to the downstream Order Status Agent: `order-status:invoke`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
>       | `AGENT_AUDIENCE`              | The final target audience generated during the A2A request token exchange. This is the audience you'll give the agent resource in PingOne: `order-status-agent`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
>       | `TOOL_URL`                    | The Order Status MCP Server's Cloud Run base URL: `https://ac-order-status-mcp-server-<your-project-number>.<your-deployment-region>.run.app`.To find this URL, go to **Cloud Run > Services** and click the **order-status-mcp-server** service. Copy the URL at the top of the page (for example, `https://ac-order-status-mcp-server-f447x3emfq-uc.a.run.app`).                                                                                                                                                                                                                                                                                                                                                                                                                                              |
>       | `TOOL_SCOPE`                  | The scope the service extension requests during token exchange to authorize access to the downstream MCP server: `order:read`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
>       | `TOOL_AUDIENCE`               | The final target audience generated during the MCP request token exchange. This is the audience you gave the AC Order Status MCP Server resource in PingOne: `order-status-mcp-server`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
>
> 2. In the `pingone/agent-chaining/services/agent-gateway-extension-service` directory, run the following commands:
>
>    ```bash
>    make deploy
>    ```
>
>    This builds the Docker image, pushes it to Google Container Registry, and deploys it to Cloud Run.
>
>    ```bash
>    make register
>    ```
>
>    This command registers your deployed Cloud Run service as an available service extension in Google Cloud. Behind the scenes, it renders the `service-extension.tmpl.yaml` template with the live Cloud Run URL and imports the configuration, preparing the extension for use by the Agent Gateway.

### Create the Agent Gateway

The [Agent Gateway](https://github.com/pingidentity/gemini-enterprise-agent-platform-integrations/tree/main/pingone/agent-chaining/services/agent-gateway) is a Google-managed resource that acts as the policy enforcement point for all agent egress traffic. It intercepts all outbound traffic bound for downstream tools and routes it to the service extension.

Because the Support Agent and the Order Status Agent operate concurrently, this gateway is configured to govern both the A2A interaction path (between the agents) and the MCP interaction path (to the downstream server).

You'll configure the gateway to call the service extension, using [Envoy external processing](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/ext_proc_filter) (`ext_proc`), on every request to the Order Status MCP Server. This includes attaching the service extension to the gateway, registering the egress destinations it can reach, and creating the PingOne resource that licenses the service extension as the only actor allowed to exchange tokens bound for the gateway.

> **Collapse: Configure Agent Gateway environment variables**
>
> Configure the environment variables required to deploy the Agent Gateway and link it to the service extension.
>
> 1. Set environment variables for the Agent Gateway:
>
>    1. In the cloned repository, go to the `pingone/agent-chaining/services/agent-gateway` directory.
>
>    2. Make a copy of the `.env.sample` file and name it `.env`:
>
>       ```bash
>       cp .env.sample .env
>       ```
>
>    3. Check the environment variables in the `.env` file. Most are already defined.
>
>       | Variable                    | Value                                                                                                                    |
>       | --------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
>       | `GC_REGION`                 | Your Google Cloud Platform region. For example: `us-central1`.                                                           |
>       | `GC_GATEWAY_NAME`           | The Agent Gateway's name: `ac-agent-gateway`.                                                                            |
>       | `GC_SERVICE_EXTENSION_NAME` | The deployed service extension's name (created when the service extension was registered): `ac-agent-gateway-extension`. |
>       | `GC_AUTHZ_POLICY_NAME`      | The name of the Google Cloud authorization policy resource that binds the extension to the gateway: `ac-authz-policy`.   |

> **Collapse: Create the Agent Gateway in Google Cloud**
>
> Use the Google Cloud Console to deploy the Agent Gateway. This creates the service that intercepts agent egress and hands requests to the service extension.
>
> 1. In Google Cloud, go to **Agent Platform > Govern > Gateways** and click **Add gateway**.
>
> 2. Enter the following values:
>
>    * **Name**: `ac-agent-gateway`.
>
>    * **Region**: Your Google Cloud Platform region. For example: `us-central1`.
>
>    * **Deployment mode**: Keep the default **Google-managed**.
>
>    * **Agent registries**: Keep the default **Regional registry**.
>
>    * **Governed Access Path**: Keep the default **Agent-to-Anywhere (egress)**.
>
>    * **Access Authorization**: Select **Enforce policies**.
>
>    * **Policy Model**: Select **Allow Policy**.
>
> 3. Click **Add gateway**.

> **Collapse: Attach the Agent Gateway Service Extension to the Agent Gateway**
>
> To enforce your access policies, you must connect the service extension to the Agent Gateway. The `make attach` command populates `authz-policy.tmpl.yaml` with your project, region, and gateway name, and then imports it with `gcloud`. This creates an authorization policy that binds the extension to the gateway as a `CONTENT_AUTHZ` policy scoped to both the Reasoning Engine A2A path (`/v1beta1/projects/…/reasoningEngines/`) and the `/mcp` path.
>
> 1. In the `pingone/baseline-autonomous-agent-to-tool/services/agent-gateway` directory, run the following command:
>
>    ```bash
>    make attach
>    ```
>
>    |   |                                                                                  |
>    | - | -------------------------------------------------------------------------------- |
>    |   | You can run `make render` alone to inspect the generated YAML without importing. |
>
> There are now two complementary authorization extensions attached to the Agent Gateway:
>
> | Extension                             | Service role    | Target                      | Description                                                                                                                                                                                                                                                          |
> | ------------------------------------- | --------------- | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
> | `ac-agent-gateway-iap-authzextension` | `REQUEST_AUTHZ` | `iap.googleapis.com`        | A Google-managed extension automatically created with the gateway. It enforces the IAP identity and egress check (`iap.egressor`), which appears on the gateway as the **Auth provider: Google Cloud Identity-Aware Proxy**.                                         |
> | `ac-agent-gateway-extension`          | `CONTENT_AUTHZ` | Cloud Run service extension | The extension created in the previous step. It routes traffic to your Cloud Run host and requests an authorization decision. Upon permit, the extension performs the PingOne token exchange and injects the resulting delegated token in the `Authorization` header. |

> **Collapse: Register egress destinations for the Agent Gateway**
>
> Because the Agent Gateway governs all agent egress, every host the agent communicates with must be registered as a destination in Agent Registry. Your environment currently includes the following registered destinations:
>
> * **Order Status MCP Server:** This was registered as an MCP server (rather than a standard endpoint) when you deployed it.
>
>   |   |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
>   | - | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
>   |   | The registered URL must match the host URL invoked by the agent exactly. Because IAP resolves egress destinations by host name, any mismatch causes IAP to block the request before it ever reaches the service extension. For example, a failure occurs if the registry holds the stable Cloud Run URL (`…​-<project-number>.<region>.run.app`), but the agent calls a direct revision URL (`…​-<hash>-<region>.a.run.app`). If this happens, you'll see a `DENIED` entry in the gateway logs with no `agentRegistryResource`. |
>
> * **Google APIs:** Endpoints for `aiplatform`, `iamcredentials`, and telemetry (`*.mtls.googleapis.com`) were automatically generated during the gateway deployment to support the runtime's internal egress. Don't modify these endpoints.
>
> You'll register the PingOne host as the final required egress endpoint.
>
> |   |                                                                                                                                                                                                                                                                                                                  |
> | - | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
> |   | If you completed the [authorizing autonomous agents](idai-authorizing-gemini-autonomous-agents-with-ping.html) and [authorizing agents on behalf of users](idai-authorizing-gemini-on-behalf-of-users-with-ping.html) use cases, you already registered the PingOne host as an endpoint. You can skip this task. |
>
> 1. In Google Cloud, go to **Agent Platform > Govern > Agent Registry** and click **Add Endpoint**.
>
> 2. Enter the following values:
>
>    * **Name**: `PingOne`.
>
>    * **Region**: Your Google Cloud Platform region. For example: `us-central1`.
>
>    * **Destination URL**: Your PingOne host. For example: `https://auth.pingone.com`.
>
> 3. Click **Save**.

> **Collapse: Create the Agent Gateway resource in PingOne**
>
> The Agent Gateway resource acts as an intermediate target for both agents' initial token exchanges. The gateway service extension uses the output of these initial exchanges to perform subsequent exchanges targeting the next actor.
>
> Custom attributes secure this delegation chain. While a basic token exchange can function without them, omitting these attributes means an actor token could be accepted at face value. By defining them, PingOne explicitly attests to the actor's identity and refuses to generate a token if the subject token didn't authorize the exchanging actor.
>
> You'll configure the following attributes:
>
> * `sub`: Determines the subject of the token based on the grant type. During a token exchange, it copies the subject token's `sub` claim to carry the user's identity through the transaction. During a `client_credentials` request (used when the agents fetch their own actor tokens), it outputs the string `no-subject`.
>
> * `act`: Preserves the complete delegation history. During an exchange, it verifies that the incoming token's `may_act` claim matches the exchanging agent's `client_id`. If authorized, it nests the incoming token's `act` claim inside the new one, ensuring the entire chain survives the transaction rather than just the previous actor.
>
> * `may_act`: Explicitly licenses the gateway service extension as the sole authorized next actor on every token minted for this resource. For both token exchanges, the extension is the only client whose client ID matches, ensuring the downstream resource's required act check passes for it and fails closed for anyone else.
>
> * `grant_type` (Optional): Enriches the token by exposing the authorization grant type (such as `urn:ietf:params:oauth:grant-type:token-exchange`) as a first-class claim.
>
> |   |                                                                                                                                                                            |
> | - | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
> |   | You can find more details about [adding custom resources](https://docs.pingidentity.com/pingone/applications/p1_adding_custom_resource.html) in the PingOne documentation. |
>
> 1. In the PingOne admin console, go to **Applications > Resources** and click the **[icon: plus, set=fa]**icon.
>
> 2. Define the resource:
>
>    1. **Resource Name**: `AC Google Cloud Agent Gateway`
>
>    2. **Audience**: `ac-google-cloud-agent-gateway`
>
> 3. Click **Next**.
>
> 4. Define attributes for the resource:
>
>    Detailed steps follow the table.
>
>    | Attribute    | Required | Expression                                                                                                                                                                                                                                                                                                |
>    | ------------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
>    | `sub`        | No       | ```spel
>    (#root.context.requestData.grantType == "client_credentials") ? "no-subject" : #root.context.requestData.subjectToken.sub
>    ```                                                                                                                                                                     |
>    | `act`        | Yes      | ```spel
>    (#root.context.requestData.grantType == "client_credentials")?"noActor":((#root.context.requestData.subjectToken.may_act.sub == #root.context.requestData.actorToken.client_id)?{"sub":#root.context.requestData.actorToken.client_id,"act":#root.context.requestData.subjectToken.act}:null)
>    ``` |
>    | `may_act`    | No       | ```spel
>    {"sub":"<extension-client-id>"}
>    ```                                                                                                                                                                                                                                                               |
>    | `grant_type` | No       | ```spel
>    #root.context.requestData.grantType
>    ```                                                                                                                                                                                                                                                           |
>
>    1. Click the **Gear** icon ([icon: gear, set=fa]) next to the `sub` attribute, enter the expression, and click **Save**.
>
>       |   |                                                                                                                                                                           |
>       | - | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
>       |   | Leave the **Required** checkbox cleared. If marked required, standard `client_credentials` requests are rejected by PingOne before the attribute expression is evaluated. |
>
>       Learn more about expressions in [Using the expression builder](https://docs.pingidentity.com/pingone/pingone_expression_language/p1_use_expression_builder.html) in the PingOne documentation.
>
>    2. Click **+ Add** and name the attribute `act`.
>
>    3. Click [icon: gear, set=fa], enter the expression, and click **Save**.
>
>    4. Select the **Required** checkbox for the `act` attribute.
>
>       This ensures that any token exchange that fails to set `act` is rejected. Unauthorized requests evaluate to null and explicitly fail.
>
>    5. Click **+ Add** and name the attribute `may_act`.
>
>    6. []()Click [icon: gear, set=fa], enter the expression, replace the `<extension-client-id>` placeholder with the AC Agent Gateway Extension application's **Client ID** you saved [earlier](#idai-gemini-a2a-ext-client-id), and click **Save**.
>
>    7. (Optional) Click **+ Add**, name the attribute `grant_type`, click [icon: gear, set=fa], enter the expression, and click **Save**.
>
> 5. Click **Next**, then click **+ Add Scope**.
>
> 6. Name the scope `order-status:invoke`.
>
>    The Support Agent requests this scope when it exchanges the user's sign-on token during the first exchange.
>
> 7. Click **+ Add Scope** and name it `order:read`.
>
>    The Order Status Agent requests this scope when it exchanges the Support Agent's delegated token during the third exchange.
>
> 8. Click **Save**.

### Deploy the Order Status Agent

The [Order Status Agent](https://github.com/pingidentity/gemini-enterprise-agent-platform-integrations/tree/main/pingone/agent-chaining/services/order-status-agent) acts as the specialized downstream agent in the agent delegation chain. In the A2A path, it receives `get_order_status` requests from the Support Agent and serves as the only client authorized to call the Order Status MCP Server.

When the gateway extension routes an A2A message to this agent, it delivers a delegated token with the `metadata.delegatedAuthorization` property. The agent independently validates this token, verifying its signature, issuer, the `order-status-agent` audience, and the `order-status:invoke` scope. The agent then executes its own token exchange (using the validated token as the subject and its own PingOne client credentials as the actor), targeting the shared intermediate `ac-google-cloud-agent-gateway` audience.

As the agent initiates the outbound call to the MCP server, the Agent Runtime routes the egress traffic through the Agent Gateway. The service extension intercepts this request and performs the final token exchange, generating a token scoped to the `order-status-mcp-server` audience.

You'll create an identity for the agent in PingOne and deploy the agent to the Agent Runtime, binding it to the Agent Gateway.

> **Collapse: Create the Order Status Agent in PingOne**
>
> Create an AI agent in PingOne to establish a dedicated identity for the Order Status Agent. This identity enables the agent to authenticate and acquire access tokens during delegation exchanges.
>
> |   |                                                                                                                                                              |
> | - | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
> |   | You can find more details about [adding AI agents](https://docs.pingidentity.com/pingone/ai_agents/p1_managing_ai_agents.html) in the PingOne documentation. |
>
> 1. In the PingOne admin console, go to **AI Agents** and click the **[icon: plus, set=fa]**icon.
>
> 2. Name the agent `AC Order Status Agent`.
>
> 3. Click **Save**.
>
> 4. To enable the agent, click the toggle next to the agent name in the details panel.
>
> 5. On the **Configuration** tab, click [icon: pencil, set=fa]:
>
>    1. In **Grant Type**, select **Client Credentials** and **Token Exchange**.
>
>    2. Clear the **Code** and **Authorization Code** checkboxes.
>
>    3. Click **Save**
>
> 6. On the **Resources** tab, click [icon: pencil, set=fa], then select the **order:read** scope on the AC Google Cloud Agent Gateway resource.
>
> 7. Click **Save**.
>
> 8. []()On the **Overview** tab, copy the **Client ID** for this agent and save it somewhere convenient. You'll need it later when you [configure attributes](#idai-gemini-a2a-osa-attr) for the Order Status Agent resource in PingOne.

> **Collapse: Create the Order Status Agent resource in PingOne**
>
> The Order Status Agent resource acts as the primary destination target for the A2A invocation. Only the gateway service extension generates tokens against this resource, using either an RFC 8693 token exchange (for the A2A request) or a `client_credentials` grant (to fetch its own actor token for the A2A scope). Because no tokens are generated here with a direct user sign-on (`authorization_code` grant), you must configure the following custom attributes to securely manage the delegation chain:
>
> * `sub`: Determines the subject of the token based on the grant type. During a token exchange, it copies the incoming token's `sub` claim to carry the user's identity through the A2A interaction. During a `client_credentials` request, it outputs the string `no-subject`.
>
>   |   |                                                                                                                                                                                                                                                                                                                                       |
>   | - | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
>   |   | Configure this expression using only the `subjectToken.sub` and `no-subject` branches. Do not include a `#root.user.id` branch. Because no user context exists during a `client_credentials` grant, referencing the user object causes the entire expression evaluation to fail, which drops the `sub` claim from the token entirely. |
>
> * `act`: Preserves the delegation history across the A2A interaction. During an exchange, it verifies that the incoming token's `may_act` claim identifies the exchanging service extension. If authorized, it nests the subject token's `act` claim one level deeper in the new token. If the original sign-on token lacked an `act` claim, it flows through the nesting as an explicit null at the innermost position.
>
> * `may_act`: Licenses the Order Status Agent as the sole authorized next actor on every token minted for this resource. By assigning a constant value containing this agent's PingOne client ID, this attribute ensures the agent's subsequent token exchange (which targets the shared gateway audience), carries a valid `act` claim and passes the gateway resource's delegation check, failing closed for anyone else.
>
> * `grant_type` (Optional): Enriches the token by exposing the authorization grant type (such as `urn:ietf:params:oauth:grant-type:token-exchange`) as a first-class claim.
>
> |   |                                                                                                                                                                            |
> | - | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
> |   | You can find more details about [adding custom resources](https://docs.pingidentity.com/pingone/applications/p1_adding_custom_resource.html) in the PingOne documentation. |
>
> 1. In the PingOne admin console, go to **Applications > Resources** and click the **[icon: plus, set=fa]**icon.
>
> 2. Define the resource:
>
>    1. **Resource Name**: `AC Order Status Agent`
>
>    2. **Audience**: `order-status-agent`
>
> 3. Click **Next**.
>
> 4. Define attributes for the resource:
>
>    Detailed steps follow the table.
>
>    | Attribute    | Required | Expression                                                                                                                                                                                                                                                                                                |
>    | ------------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
>    | `sub`        | No       | ```spel
>    (#root.context.requestData.grantType == "client_credentials") ? "no-subject" : #root.context.requestData.subjectToken.sub
>    ```                                                                                                                                                                     |
>    | `act`        | Yes      | ```spel
>    (#root.context.requestData.grantType == "client_credentials")?"noActor":((#root.context.requestData.subjectToken.may_act.sub == #root.context.requestData.actorToken.client_id)?{"sub":#root.context.requestData.actorToken.client_id,"act":#root.context.requestData.subjectToken.act}:null)
>    ``` |
>    | `may_act`    | No       | ```spel
>    {"sub":"<ORDER-STATUS-AGENT-CLIENT-ID>"}
>    ```                                                                                                                                                                                                                                                      |
>    | `grant_type` | No       | ```spel
>    #root.context.requestData.grantType
>    ```                                                                                                                                                                                                                                                           |
>
>    1. Click the **Gear** icon ([icon: gear, set=fa]) next to the `sub` attribute, enter the expression, and click **Save**.
>
>       |   |                                                                                                                                                                           |
>       | - | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
>       |   | Leave the **Required** checkbox cleared. If marked required, standard `client_credentials` requests are rejected by PingOne before the attribute expression is evaluated. |
>
>       Learn more about expressions in [Using the expression builder](https://docs.pingidentity.com/pingone/pingone_expression_language/p1_use_expression_builder.html) in the PingOne documentation.
>
>    2. Click **+ Add** and name the attribute `act`.
>
>    3. Click [icon: gear, set=fa], enter the expression, and click **Save**.
>
>    4. Select the **Required** checkbox for the `act` attribute.
>
>       This ensures that any token exchange that fails to set `act` is rejected. Unauthorized requests evaluate to null and explicitly fail.
>
>    5. Click **+ Add** and name the attribute `may_act`.
>
>    6. []()Click [icon: gear, set=fa], enter the expression, replace the `<ORDER-STATUS-AGENT-CLIENT-ID>` placeholder with the AC Order Status Agent's **Client ID** you saved [earlier](#idai-gemini-a2a-osa-client-id), and click **Save**.
>
>    7. (Optional) Click **+ Add**, name the attribute `grant_type`, click [icon: gear, set=fa], enter the expression, and click **Save**.
>
> 5. Click **Next**, then click **+ Add Scope**.
>
> 6. Name the scope `order-status:invoke`.
>
>    The Support Agent requests this scope when it exchanges the user's sign-on token during the first exchange.
>
> 7. Click **Save**.

> **Collapse: Deploy and register the Order Status Agent in Agent Registry**
>
> To finish Order Status Agent setup, deploy it to the Google Cloud Agent Runtime. This deployment automatically registers the agent in Agent Registry and routes all of its egress traffic through the Agent Gateway.
>
> 1. Set environment variables for the agent:
>
>    1. In the cloned repository, go to the `pingone/agent-chaining/services/order-status-agent` directory.
>
>    2. Make a copy of the `.env.sample` file and name it `.env`:
>
>       ```bash
>       cp .env.sample .env
>       ```
>
>    3. Set environment variables in the `.env` file:
>
>       | Variable                | Value                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
>       | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
>       | `GC_REGION`             | Your Google Cloud Platform region. For example: `us-central1`.                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
>       | `AGENT_DISPLAY_NAME`    | Display name for the Reasoning Engine: `ac-order-status-agent`.                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
>       | `IDP_ISSUER`            | The PingOne issuer URL: `https://auth.pingone.<region>/<env-id>/as`.To find this endpoint, go to the AC Order Status Agent in PingOne. Copy the **Issuer ID** under **Connection Details** on the **Overview** tab.                                                                                                                                                                                                                                                                                                       |
>       | `GC_AGENT_GATEWAY`      | The gateway name: `ac-agent-gateway`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
>       | `AGENT_CLIENT_ID`       | The AC Order Status Agent's **Client ID**. This is located above **Connection Details** on the agent's **Overview** tab in PingOne.                                                                                                                                                                                                                                                                                                                                                                                       |
>       | `AGENT_CLIENT_SECRET`   | The AC Order Status Agent's **Client Secret**. This is located above **Connection Details** on the agent's **Overview** tab.                                                                                                                                                                                                                                                                                                                                                                                              |
>       | `IDP_REQUIRED_AUDIENCE` | The audience accepted on the inbound A2A token exchange: `order-status-agent`.The gateway service extension validates this audience before generating the outbound token for the MCP server.                                                                                                                                                                                                                                                                                                                              |
>       | `IDP_REQUIRED_SCOPE`    | The scope accepted on the inbound A2A token exchange: `order-status:invoke`.The gateway service extension validates this scope before generating the outbound token for the MCP server.                                                                                                                                                                                                                                                                                                                                   |
>       | `TOOL_URL`              | The Order Status MCP Server's Cloud Run base URL: `https://ac-order-status-mcp-server-<your-project-number>.<your-deployment-region>.run.app/mcp`.To find this URL, go to **Cloud Run > Services** and click the **order-status-mcp-server**. Copy the URL at the top of the page. Paste the URL in the `.env` file, making sure `/mcp` is at the end of the URL.                                                                                                                                                         |
>       | `TOOL_SCOPE`            | The scope the agent requests during token exchange to authorize access to the downstream MCP server: `order:read`.                                                                                                                                                                                                                                                                                                                                                                                                        |
>       | `GATEWAY_AUDIENCE`      | The audience the agent requests during token exchange to authorize access to the Agent Gateway: `ac-google-cloud-agent-gateway`.The agent doesn't target the final `order-status-mcp-server` audience directly. Instead, it exchanges its token for this intermediate audience. The gateway service extension then intercepts the outbound request and performs the final token exchange to the MCP server. This value must exactly match the `AGENT_GATEWAY_AUDIENCE` value configured in the gateway service extension. |
>
> 2. In the `pingone/agent-chaining/services/order-status-agent` directory, run the following command:
>
>    ```bash
>    make deploy
>    ```
>
>    The `deploy.py` script provisions the Reasoning Engine using `identity_type = AGENT_IDENTITY`. The script also binds the engine to the gateway and grants it the `roles/iap.egressor` role on Agent Registry, authorizing the agent to securely reach all registered downstream endpoints.
>
> 3. []()Complete the Order Status Agent's `AGENT_ENGINE_ID` [placeholder](#idai-gemini-a2a-order-status-engine-id) in the service extension's `.env` file, then redeploy the service extension.
>
> 4. []()In PingOne, go to the AC Agent Gateway Extension application and [assign](#idai-gemini-a2a-order-status-scope) the **order_status:invoke** scope on the AC Order Status Agent.

### Deploy the Support Agent

The [Support Agent](https://github.com/pingidentity/gemini-enterprise-agent-platform-integrations/tree/main/pingone/agent-chaining/services/support-agent) serves as the user-facing entry point in the delegation chain. Instead of interacting with the downstream Order Status MCP Server directly, it delegates order status requests to the Order Status Agent over A2A.

The Agent Bridge stores the user's PingOne sign-on token in ADK session state. This token is initially scoped to the agent's own `support-agent` audience. The agent manages the token through this sequence:

* The agent independently verifies the token's signature, issuer, audience, and the `support-agent:invoke` scope before using it.

* The agent performs a token exchange using the user's token as the subject and its own PingOne client credentials as the actor. This targets the intermediate `ac-google-cloud-agent-gateway` audience.

* As the agent initiates the outbound A2A request, the Agent Runtime routes egress traffic through the Agent Gateway. The service extension intercepts this request and performs the final token exchange, generating a token scoped to the `order-status-agent` audience.

You'll create the agent's PingOne application and deploy the agent to the Agent Runtime, binding it to the Agent Gateway.

> **Collapse: Create the Support Agent in PingOne**
>
> Create an AI agent in PingOne to establish a dedicated identity for the Support Agent. This identity enables the agent to authenticate and acquire access tokens during delegation exchanges.
>
> |   |                                                                                                                                                              |
> | - | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
> |   | You can find more details about [adding AI agents](https://docs.pingidentity.com/pingone/ai_agents/p1_managing_ai_agents.html) in the PingOne documentation. |
>
> 1. In the PingOne admin console, go to **AI Agents** and click the **[icon: plus, set=fa]**icon.
>
> 2. Name the agent `AC Support Agent`.
>
> 3. Click **Save**.
>
> 4. To enable the agent, click the toggle next to the agent name in the details panel.
>
> 5. On the **Configuration** tab, click [icon: pencil, set=fa]:
>
>    1. In **Grant Type**, select **Client Credentials** and **Token Exchange**.
>
>    2. Clear the **Code** and **Authorization Code** checkboxes.
>
>    3. Click **Save**.
>
> 6. On the **Resources** tab, click [icon: pencil, set=fa], then select the **order-status:invoke** scope on the AC Google Cloud Agent Gateway resource.
>
> 7. Click **Save**.
>
> 8. []()On the **Overview** tab, copy the **Client ID** for this agent and save it somewhere convenient. You'll need it later when you [configure attributes](#idai-gemini-a2a-sa-attr) for the Support Agent resource in PingOne.

> **Collapse: Create the Support Agent resource in PingOne**
>
> The Support Agent resource generates the Chat UI's sign-on token, establishing the user identity that enters the delegation chain.
>
> Because this resource exclusively processes direct user sign-ons with the `authorization_code` grant and doesn't process `client_credentials` requests, the default User ID mapping for the `sub` claim is sufficient. This mirrors the AOBOU Financial Agent resource in the [authorizing agents on behalf of users](idai-authorizing-gemini-on-behalf-of-users-with-ping.html) use case.
>
> You don't need to configure an `act` attribute. Because no actor exists during a direct user sign-on, the initial sign-on token doesn't include an `act` claim. During subsequent token exchanges, the gateway resource's nesting logic translates this absence into a null at the innermost position of every downstream token.
>
> To initialize the delegation chain, you'll configure the following custom attributes:
>
> * `may_act`: Licenses the Support Agent as the only authorized next actor on every token minted for this resource. Assigning a constant value (the agent's PingOne client ID) to this attribute ensures the Support Agent's subsequent token exchange, which targets the shared gateway audience, carries a valid `act` claim. This allows the exchange to pass the gateway resource's delegation check while failing closed for anyone else.
>
> * `grant_type` (Optional): Enriches the token by exposing the authorization grant type (such as `authorization_code`) as a first-class claim.
>
> |   |                                                                                                                                                                            |
> | - | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
> |   | You can find more details about [adding custom resources](https://docs.pingidentity.com/pingone/applications/p1_adding_custom_resource.html) in the PingOne documentation. |
>
> 1. In the PingOne admin console, go to **Applications > Resources** and click the **[icon: plus, set=fa]**icon.
>
> 2. Define the resource:
>
>    1. **Resource Name**: `AC Support Agent`
>
>    2. **Audience**: `support-agent`
>
> 3. Click **Next**.
>
> 4. Define attributes for the resource:
>
>    Detailed steps follow the table.
>
>    | Attribute    | Required | Expression                                      |
>    | ------------ | -------- | ----------------------------------------------- |
>    | `may_act`    | No       | ```spel
>    {"sub":"<SUPPORT-AGENT-CLIENT-ID>"}
>    ``` |
>    | `grant_type` | No       | ```spel
>    #root.context.requestData.grantType
>    ``` |
>
>    1. Click **+ Add** and name the attribute `may_act`.
>
>    2. []()Click [icon: gear, set=fa], enter the expression, replace the `<SUPPORT-AGENT-CLIENT-ID>` placeholder with the AC Support Agent's **Client ID** you saved [earlier](#idai-gemini-a2a-sa-client-id), and click **Save**.
>
>       Learn more about expressions in [Using the expression builder](https://docs.pingidentity.com/pingone/pingone_expression_language/p1_use_expression_builder.html) in the PingOne documentation.
>
>    3. (Optional) Click **+ Add**, name the attribute `grant_type`, click [icon: gear, set=fa], enter the expression, and click **Save**.
>
> 5. Click **Next**, then click **+ Add Scope**.
>
> 6. Name the scope `support-agent:invoke`.
>
>    The Order Status Agent requests this scope when it exchanges the Support Agent's delegated token during the second exchange.
>
> 7. Click **Save**.

> **Collapse: Deploy and register the Support Agent in Agent Registry**
>
> To finish Support Agent setup, deploy it to the Google Cloud Agent Runtime. This deployment automatically registers the agent in Agent Registry and routes all of its egress traffic through the Agent Gateway.
>
> 1. Set environment variables for the agent:
>
>    1. In the cloned repository, go to the `pingone/agent-chaining/services/support-agent` directory.
>
>    2. Make a copy of the `.env.sample` file and name it `.env`:
>
>       ```bash
>       cp .env.sample .env
>       ```
>
>    3. Set environment variables in the `.env` file:
>
>       | Variable                | Value                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
>       | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
>       | `GC_REGION`             | Your Google Cloud Platform region. For example: `us-central1`.                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
>       | `AGENT_DISPLAY_NAME`    | Display name for the Reasoning Engine: `ac_support_agent`.                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
>       | `IDP_ISSUER`            | The PingOne issuer URL: `https://auth.pingone.<region>/<env-id>/as`.To find this endpoint, go to the AC Support Agent in PingOne. Copy the **Issuer ID** under **Connection Details** on the **Overview** tab.                                                                                                                                                                                                                                                                                                   |
>       | `GC_AGENT_GATEWAY`      | The gateway name: `ac-agent-gateway`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
>       | `AGENT_CLIENT_ID`       | The AC Support Agent's **Client ID**. This is located above **Connection Details** on the agent's **Overview** tab in PingOne.                                                                                                                                                                                                                                                                                                                                                                                   |
>       | `AGENT_CLIENT_SECRET`   | The AC Support Agent's **Client Secret**. This is located above **Connection Details** on the agent's **Overview** tab.                                                                                                                                                                                                                                                                                                                                                                                          |
>       | `IDP_REQUIRED_AUDIENCE` | The expected audience used to validate the user's incoming PKCE sign-on token provided by the Agent Bridge: `support-agent`.The gateway service extension validates the token against this audience before using it as the subject token in the outbound token exchange. The validation process is independent of the agent's own client credentials.                                                                                                                                                            |
>       | `IDP_REQUIRED_SCOPE`    | The expected scope on the inbound browser token: `support-agent:invoke`.                                                                                                                                                                                                                                                                                                                                                                                                                                         |
>       | `AGENT_ENGINE_ID`       | The Reasoning Engine's identifier for the Order Status Agent. In this example, the ID follows `reasoningEngines:` in the Order Status Agent's URN: `urn:agent:projects-909399354423:projects:909399354423:locations:us-central1:aiplatform:reasoningEngines:7390548000881246208`.To find this ID, go to the `ac-order-status-agent` service in **Agent Registry**.                                                                                                                                               |
>       | `AGENT_SCOPE`           | The scope requested on the delegated A2A token: `order-status:invoke`. The gateway service extension validates this scope before generating the outbound token for the Order Status Agent.                                                                                                                                                                                                                                                                                                                       |
>       | `GATEWAY_AUDIENCE`      | The audience the agent requests during token exchange to authorize access to the Agent Gateway: `ac-google-cloud-agent-gateway`.The agent doesn't target the `order-status-agent` audience directly. Instead, it exchanges its token for this intermediate audience. The gateway service extension then intercepts the outbound request and performs the token exchange to the Order Status Agent. This value must exactly match the `AGENT_GATEWAY_AUDIENCE` value configured in the gateway service extension. |
>
> 2. In the `pingone/agent-chaining/services/support-agent` directory, run the following command:
>
>    ```bash
>    make deploy
>    ```
>
>    The `deploy.py` script provisions the Reasoning Engine using `identity_type = AGENT_IDENTITY`. The script also binds the engine to the gateway and grants it the `roles/iap.egressor` role on Agent Registry, authorizing the agent to securely reach all registered downstream endpoints.

### Deploy the Agent Bridge

The [Agent Bridge](https://github.com/pingidentity/gemini-enterprise-agent-platform-integrations/tree/main/pingone/agent-chaining/services/agent-bridge) acts as the secure integration layer between the Chat UI and the Support Agent. This server-side bridge mediates the connection between the browser, which holds only a PingOne identity, and the agent running on the Google Cloud Agent Runtime. When it receives a chat request, the bridge validates the user's PingOne token against the PingOne JSON Web Key Set (JWKS), verifying the signature, issuer, `support-agent` audience, and `sub` claim. The bridge then resolves the agent session and stores the token in the session state for the Support Agent to consume.

> **Collapse: Deploy the Agent Bridge to Cloud Run**
>
> Build the Agent Bridge with configured environment variables and deploy it as a Google Cloud Run service.
>
> 1. Set environment variables for the Agent Bridge:
>
>    1. In the cloned repository, go to the `pingone/agent-chaining/services/agent-bridge` directory.
>
>    2. Make a copy of the `.env.sample` file and name it `.env`:
>
>       ```bash
>       cp .env.sample .env
>       ```
>
>    3. Set environment variables in the `.env` file:
>
>       | Variable                    | Value                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
>       | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
>       | `GC_REGION`                 | Your Google Cloud Platform region. For example: `us-central1`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
>       | `GC_CLOUD_RUN_SERVICE_NAME` | The Cloud Run service name: `ac-agent-bridge`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
>       | `IDP_ISSUER`                | The PingOne issuer URL: `https://auth.pingone.<region>/<env-id>/as`.To find this endpoint, go to the AC Support Agent in PingOne. Copy the **Issuer ID** under **Connection Details** on the **Overview** tab.                                                                                                                                                                                                                                                                                                                    |
>       | []()`CORS_ORIGIN`           | The Chat UI Cloud Run URL. For example: `https://ac-chat-ui-<project-id>.<region>.run.app`.This is a placeholder. After you [deploy the Chat UI](#idai-gemini-a2a-chat-ui-deployed-cors-origin) in a later task, update this value with the actual URL, then redeploy the Agent Bridge.                                                                                                                                                                                                                                           |
>       | `IDP_REQUIRED_AUDIENCE`     | The expected audience for the browser's PKCE sign-on token: `support-agent`.This is the Support Agent's PingOne resource audience. The Agent Bridge validates the token against this audience before storing it in session state for the Support Agent to consume.                                                                                                                                                                                                                                                                |
>       | `AGENT_ENGINE_ID`           | The Reasoning Engine's identifier for the Support Agent. In this example, the ID follows `reasoningEngines:` in the Support Agent's URN: `urn:agent:projects-909399354423:projects:909399354423:locations:us-central1:aiplatform:reasoningEngines:5246834578252890112`.To find this ID, go to the `ac_support-agent` service in **Agent Registry**.&#xA;&#xA;The Reasoning Engine ID changes each time you redeploy the Support Agent. If you redeploy the agent, update this environment variable and redeploy the Agent Bridge. |
>
> 2. In the `pingone/agent-chaining/services/agent-bridge` directory, run the following command:
>
>    ```bash
>    make deploy
>    ```
>
>    This builds the Docker image, pushes it to Google Container Registry, and deploys it to Cloud Run.

### Deploy the Chat UI

The [Chat UI](https://github.com/pingidentity/gemini-enterprise-agent-platform-integrations/tree/main/pingone/agent-chaining/services/chat-ui) is a React and Vite single-page application (SPA) that serves as the frontend interface for users. It handles user authentication directly against PingOne using an OAuth 2.0 Authorization Code grant with PKCE. After a user authenticates, the application routes chat requests and the resulting sign-on token to the Support Agent through the Agent Bridge.

> **Collapse: Create the Chat UI application in PingOne**
>
> Create a SPA in PingOne that represents the Chat UI. This application manages initial user authentication using PKCE. It acquires the user's baseline sign-on token and passes it to the Agent Bridge, which uses the token to initialize the downstream delegation chain.
>
> 1. In the PingOne admin console, go to **Applications > Applications**.
>
> 2. Click the **[icon: plus, set=fa]**icon and create the application profile:
>
>    1. Name the application `Agent Chaining Chat UI`.
>
>    2. For the **Application Type**, click **Single-Page**.
>
>    3. Click **Save**.
>
> 3. To enable the application, click the toggle next to the application name in the details panel.
>
> 4. On the **Configuration** tab, click [icon: pencil, set=fa]:
>
>    1. Make sure **Authorization Code** with **S256_REQUIRED** is the **Grant Type**.
>
>    2. []()In the **Redirect URIs** and **Signoff URLs** fields, enter the Chat UI URL from Cloud Run. For example: `https://ac-chat-ui-<project-id>.<region>.run.app`.
>
>       These are placeholders. After you [deploy the Chat UI](#idai-gemini-a2a-chat-deployed-redirect-uri) in a later task, update these values with the actual URL.
>
> 5. Click **Save**.
>
> 6. On the **Resources** tab, click [icon: pencil, set=fa], then select scopes:
>
>    * **email**
>
>    * **openid** (selected by default)
>
>    * **profile**
>
>    * **support-agent:invoke** scope on the AC Support Agent resource (at the bottom of the scopes list)
>
> 7. Click **Save**.

> **Collapse: Deploy the Chat UI to Cloud Run**
>
> Configure environment variables and deploy the Chat UI to Cloud Run. The deployment process compiles the Vite application, packages the static web assets into an NGINX Docker image, and deploys the container to connect to the Agent Bridge.
>
> 1. Set environment variables for the Chat UI:
>
>    1. In the cloned repository, go to the `pingone/agent-chaining/services/chat-ui` directory.
>
>    2. Make a copy of the `.env.sample` file and name it `.env`:
>
>       ```bash
>       cp .env.sample .env
>       ```
>
>    3. Set environment variables in the `.env` file:
>
>       | Variable                    | Value                                                                                                                                                                                                                                                                                                                                                                                                                         |
>       | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
>       | `GC_REGION`                 | Your Google Cloud Platform region. For example: `us-central1`.                                                                                                                                                                                                                                                                                                                                                                |
>       | `GC_CLOUD_RUN_SERVICE_NAME` | The Chat UI's Cloud Run service name: `ac-chat-ui`.                                                                                                                                                                                                                                                                                                                                                                           |
>       | `VITE_IDP_ISSUER`           | The PingOne issuer URL: `https://auth.pingone.<region>/<env-id>/as`.To find this endpoint, go to the Agent Chaining Chat UI application in PingOne. Copy the **Issuer ID** under **Connection Details** on the **Overview** tab.                                                                                                                                                                                              |
>       | `VITE_CLIENT_ID`            | The Agent Chaining Chat UI application's **Client ID**. This is located above **Connection Details** on the **Overview** tab.                                                                                                                                                                                                                                                                                                 |
>       | []()`VITE_REDIRECT_URI`     | The Chat UI Cloud Run URL: `https://ac-chat-ui-<project-id>.<region>.run.app`.After you deploy the Chat UI to Cloud Run, update this value with the actual URL. To find the URL, go to **Cloud Run > Services** and click the **ac-chat-ui** service. Copy the URL at the top of the page.&#xA;&#xA;This redirect URL must exactly match the Redirect URIs and Signoff URLs configured in the Chat UI application in PingOne. |
>       | `VITE_AGENT_BRIDGE_URL`     | Your Agent Bridge Cloud Run URL. For example: `https://ac-agent-chain-bridge-<project-id>.<region>.run.app`.To find this URL, go to **Cloud Run > Services** and click the **ac-agent-bridge** service. Copy the URL at the top of the page.                                                                                                                                                                                  |
>       | `VITE_SCOPES`               | The OAuth 2.0 scopes the Chat UI requests from PingOne during the user authentication flow: `openid profile email support-agent:invoke`.This includes standard OpenID Connect scopes (such as `openid` and `profile`) along with the specific scope required to authorize the request to the Support Agent (`support-agent:invoke`).                                                                                          |
>
> 2. In the `pingone/agent-chaining/services/chat-ui` directory, run the following command:
>
>    ```bash
>    make deploy
>    ```
>
>    This builds the Vite app, packages it in an NGINX Docker image, and deploys it to Cloud Run.
>
>    Vite statically embeds all `VITE_*` environment variables (such as `VITE_SCOPES`) directly into the JavaScript bundle during the build phase. Updating variables on a running Cloud Run container has no effect. You must run `make deploy` to rebuild the application and apply your changes.
>
>    Docker can silently reuse a cached build layer even after you modify your local `.env` file. If a variable change doesn't appear in the deployed application (which you can verify by checking if the served `index-*.js` filename remains identical to the previous build), force a clean build using the `--no-cache` flag. Always rule out a stale Docker cache before troubleshooting your PingOne or backend configurations.
>
> 3. []()Complete the Chat UI's `CORS_ORIGIN` [placeholder](#idai-gemini-a2a-chat-ui-cors-origin) in the Agent Bridge's `.env` file, then redeploy the Agent Bridge.
>
> 4. Complete the Chat UI's `VITE_REDIRECT_URI` [placeholder](#idai-gemini-a2a-chat-redirect-uri) in the Chat UI's `.env` file, then redeploy the Chat UI.
>
> 5. []()In the [Chat UI application](#idai-gemini-a2a-p1-redirect-uri) in PingOne, enter the deployed `ac-chat-ui` service's [Cloud Run URL](#idai-gemini-a2a-chat-redirect-uri) in the **Redirect URIs** and **Signoff URLs** placeholders.
>
>    This ensures that the Chat UI can successfully redirect users back to the application after authentication.

## Validation

To validate your configuration, sign on to the Chat UI as a PingOne user belonging to the `support_team` group and query the status of an order. This initiates a request that the Support Agent delegates to the Order Status Agent, which calls the Order Status MCP Server to retrieve the order status. Because the delegation chain is recorded at every stage of the interaction, you can verify that the correct tokens are exchanged and validated.

1. Go to the Chat UI URL (`https://ac-chat-ui-<project-id>.<region>.run.app`) and click **Sign In**.

2. Authenticate as a PingOne user assigned to the `support_team` group.

3. Ask the agent: `What is the status of order ORD-123?`

   The agent should reply with the correct order status (for example, `ORD-123` returns as shipped, and `ORD-456` returns as processing).

You can observe the full delegation sequence by monitoring the Cloud Run logs for the service extension and the Order Status MCP Server, along with the Reasoning Engine logs for the Order Status Agent.

While the chat runs, the logs should display the following progression:

```text
# service extension (A2A hop): the Support Agent's delegated token is validated
# against the shared gateway audience, reminted for the Order Status Agent, and
# injected alongside the Google credential the A2A endpoint's IAM check requires.
[ExtSvc] onHeaders authority="us-central1-aiplatform.mtls.googleapis.com" path="<redacted>/reasoningEngines/<order-status-agent-id>/a2a/message:send" matched=true
[ExtSvc] delegated token minted target=A2A ttl=59m30s
[ExtSvc] validated delegated token — sub=<user-sub> aud=ac-google-cloud-agent-gateway scope="order-status:invoke"
[ExtSvc] injecting Google credential + reminted token (aud=order-status-agent scope=order-status:invoke) for A2A

# service extension: {p1-paz} is consulted on every governed action
# (A2A message:send and MCP tools/call alike).
[ExtSvc] authorize user=<user-sub> action=get_order_status hour=10
[ExtSvc] {p1-paz} PERMIT user=<user-sub>
[ExtSvc] PERMIT target=A2A action=get_order_status order=ORD-123 (authz=pingone-authorize)

# Order Status Agent: the gateway-reminted A2A token is validated independently
# (signature, issuer, audience, and scope) before use. The log shows who the call
# is for (sub, the user throughout), which audience it was minted for (aud),
# who acted for it (act, the delegation chain so far), and the granted scope.
[OrderStatusAgent] Delegated token verified — sub=<user-sub> aud=['order-status-agent'] act=<ext-svc-client-id> -> <support-agent-client-id> scope="order-status:invoke"

# Order Status Agent: its own exchange mints the MCP-scoped token, then the
# MCP call runs only after the PERMIT above.
[OrderStatusAgent] get_order_status start order_id=ORD-123
[OrderStatusAgent] exchanged for MCP token (aud=order-status-mcp-server), calling MCP server order_id=ORD-123

# service extension (MCP hop): the Order Status Agent's token is validated and
# reminted for the MCP server; Authorization now carries the PingOne token
# directly (no Google credential needed on this hop).
[ExtSvc] onHeaders authority="<order-status-mcp-server-url>" path="/mcp" matched=true
[ExtSvc] delegated token minted target=MCP ttl=59m30s
[ExtSvc] validated delegated token — sub=<user-sub> aud=ac-google-cloud-agent-gateway scope="order:read"
[ExtSvc] injecting reminted token (aud=order-status-mcp-server scope=order:read) for MCP
[ExtSvc] authorize user=<user-sub> action=get_order_status hour=10
[ExtSvc] {p1-paz} PERMIT user=<user-sub>
[ExtSvc] PERMIT target=MCP action=get_order_status order=ORD-123 (authz=pingone-authorize)

# Order Status MCP server: every request's token is verified (signature,
# issuer, audience, and scope) before handling. The log shows who the call is
# for (sub, the user throughout), which audience it was minted for (aud), who
# acted for it (act.sub — the full chain: extension -> Order Status Agent ->
# extension -> Support Agent, the delegation).
[OrderStatusMCP] Token verified — sub=<user-sub> aud=[order-status-mcp-server] act.sub=<ext-svc-client-id> -> <order-status-agent-client-id> -> <ext-svc-client-id> -> <support-agent-client-id> scope="order:read"

# Order Status MCP server: the lookup itself.
[OrderStatusMCP] tool=get_order_status — caller=<user-sub> order=ORD-123 status=shipped
```

Outside the governed execution paths, the service extension also logs the Reasoning Engine's internal Agent Runtime session calls (`matched=false`). These internal calls pass through untouched. Only the `matched=true` lines above represent governed interactions. If PingOne Authorize issues a deny decision (for example, if the request occurs outside business hours or the user isn't on the support team), the logs surface an `[ExtSvc] DENY target=…​` entry followed by an immediate 403 Forbidden error, and the agent notifies the user that the request was denied.

## Troubleshooting

If you encounter issues while deploying or testing the A2A chain, use the following information to resolve common problems:

* Chat UI authentication fails

  Verify that the Agent Chaining Chat UI application in PingOne is configured with the Authorization Code grant type with PKCE. Additionally, check that the configured redirect URI exactly matches the URL of your deployed Cloud Run service.

* Chat UI configuration changes aren't applied after deployment

  If you update a `VITE_*` environment variable (such as `VITE_SCOPES`) but the deployed UI doesn't reflect the change, the Docker build cache probably reused a stale compilation layer. Rebuild and deploy the application using the `--no-cache` flag to force Vite to statically embed the new variables in the JavaScript bundle.

* Support Agent doesn't receive the user token

  Verify that the Agent Bridge is correctly deployed to Cloud Run and that the Chat UI is successfully sending the token to the bridge payload during the initial chat request.

* Requests are blocked before they reach the service extension

  If the Agent Gateway returns a 403 Forbidden error or logs a `DENIED` message with no `agentRegistryResource`, the request is failing Google Cloud's IAP egress check.

  * Make sure the Reasoning Engine identity has the `roles/iap.egressor` role applied.

  * Verify that the downstream URL registered in Agent Registry exactly matches the host URL the agent is configured to call. IAP resolves egress destinations by host name, and a mismatch causes an immediate denial.

* Order Status Agent or MCP server doesn't receive the delegated token

  Confirm that the Agent Gateway Service Extension is correctly deployed and attached to the Agent Gateway. Check the service extension logs in Cloud Run to verify that it's successfully intercepting the request and performing the RFC 8693 token exchange for the target audience.

  If the Order Status MCP Server fails to receive the delegated token, check that the Agent Gateway Service Extension is correctly reminting the token for the MCP server and that the MCP server is correctly validating the token.

* Token validation or token exchange fails

  Verify that your PingOne resources are configured with the correct custom attributes.

  * Ensure the `may_act` claims are set to the client ID of the next permitted actor in the delegation chain.

  * If a token exchange fails with a 400 error during a `client_credentials` grant, verify that the **Required** checkbox is cleared for the `sub` attribute on the target PingOne resource.

* PingOne Authorize policies aren't enforced

  Make sure the PingOne Authorize decision endpoint URL is correctly configured in your service extension environment variables. In PingOne Authorize **Version History**, verify that your policies are published to the correct version.

## What's next

A2A delegation is a powerful pattern for enabling secure, governed interactions between autonomous agents. Consider extending this architecture to support additional agents, tools, and services in your environment.

|   |                                                                                                                                                                                                                                                                              |
| - | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | When you're finished working with the A2A use case, you can clean up the resources you deployed in this environment. From the `pingone/agent-chaining` directory, run the `make destroy` command to safely remove the Google Cloud infrastructure specific to this use case. |
