Skip to main content
English
Saviynt

Blog Post

How to Use Saviynt Runtime Authorization to Strengthen AI Agent Flows on GCP

9 min read

AI Agent Authorization on Google Cloud

If your AI agents run on Google Cloud, you probably already have a gateway routing traffic from agent to anywhere. What you may not have is an authorization engine that tells it what is allowed and what is not.

So far in this series, Zuma has supplied both halves. In Post 1, we laid out a five-layer defense-in-depth model for AI agents. In Post 2, we watched Zuma's gateways stop an authorized agent three times, by policy, intent and anomaly. In Post 3, we pointed a pro-code agent on Amazon Bedrock AgentCore at those same gateways and got the same results.

But many teams aren't looking for another gateway. They've standardized on their cloud provider's. They want better decisions, not a second network path.

This post is for them. We take Google's managed Agent Gateway exactly as it ships and plug Saviynt's Runtime Decision Point Service (RDPS) into it. Google enforces. Saviynt decides. Every tool call an agent makes is checked against policy, intent and behavior before it reaches the backend, and nothing about your agent stack has to change.

 


Enforcing and deciding are two different jobs

Every authorization system does two things. It enforces: sitting in the traffic path and blocking or forwarding each request. And it decides: looking at the request and answering with allow or deny. The two don’t have to be bundled.

Google's Agent Gateway is built to enforce. It's the managed network entry and exit point for agents on Gemini Enterprise Agent Platform (formerly Vertex AI). When an agent is bound to a gateway in egress mode, every outbound tool call is routed through it transparently. Google runs the connectivity (Private Service Connect, private DNS) and the in-line enforcement, so you don't build or operate any of it. What the gateway needs is an authority to ask, on every call: should this go through?

RDPS is built to decide. It evaluates each tool call against the same layers you've seen throughout this series: policy-based access control (PBAC), intent deviation, and behavioral baselines. And because Zuma's services are modular, RDPS runs on its own. No Saviynt gateway required.

Put them together and each side performs a specific function:

 

Google Agent Gateway

Saviynt RDPS

Role

Enforcement point

Decision point

Sits in the traffic path

Yes

Yes, called on by every request by the gateway

Owns

Connectivity, routing, block or forward

Policy, intent and behavioral evaluation

You operate

Configuration only (managed)

Boundary and policy setup

 


RDPS as a standalone decision point

RDPS doesn't sit in the network path as a proxy. Anything that can make an HTTPS request can ask it for a decision through a single contract:

POST {RDPS_BASEURL}/boundaries/{boundaryId}/decision

Authorization: Bearer {token}

 

{

"principal": { ... }, // who is behind the call

"agent": { ... }, // the agent's identity, purpose, constraints

"action": { ... }, // the tool name + arguments being invoked

"resource": { ... }, // the target (MCP server, object type)

"context": { ... } // gateway id, session, timestamp

}

 

The response includes the overall decision and per-category results:

 

{

"decision": "ALLOW",

"categoryResults": [

{ "category": "PBAC", "decision": "ALLOW", "reasons": [] },

{ "category": "INTENT_DEVIATION", "decision": "ALLOW", "riskLevel": "LOW" }

],

"evaluationId": "..."

}

 

Every decision call carries an OAuth 2.0 bearer token. RDPS validates it against the authentication providers configured on the boundary level before it evaluates anything. That's how your gateway proves to RDPS that it's allowed to ask.

Because the contract is plain HTTP, the same RDPS instance and authorization boundary can serve any number of gateway interceptors or custom modules. That's one decision authority and one set of policies, enforced at as many points as you need.

 


The bridge: a thin ext_proc

google-cloud-project-diagram

The tool call never leaves Google's path. Only the question "allow or deny?" travels to RDPS, and, in this instance, Salesforce, although it could be any target system sees the call only on ALLOW. The link between Google's gateway and RDPS is a small ext_proc (Envoy ExternalProcessor)- a gRPC service that the gateway streams each request to. The gateway sends headers and the full request body and waits for a verdict. The ext_proc:

  1. Parses the body as MCP JSON-RPC.
  2. Identifies tools/call messages (everything else — handshakes, tools/list, notifications — passes through).
  3. Authenticates to an OAuth2 provider and builds an RDPS decision request with the tool name, arguments, destination, identity, and the bearer token.
  4. Calls RDPS and maps the result: ALLOW releases the buffered body so the gateway forwards the call; DENY returns and unauthorized response.

The ext_proc is intentionally thin. All the policy logic, intent analysis, and anomaly detection live in RDPS.

 


Part 1: Set up the authorization (once, in RDPS)

The decision side comes first. It takes three steps, done once, independent of GCP.

  1. Create the authorization boundary. Enable the categories to evaluate. The boundary returns an id the ext_proc uses on every decision call (RDPS_BOUNDARY_ID).
  2. Configure the OAuth2 authentication provider. Register the trusted issuer RDPS accepts tokens from. Each decision request's bearer token is validated against it before any policy runs. Because RDPS trusts the issuer, the same provider works for any enforcement point that can present a token from it.
  3. Create the policy. Attach a Rego policy scoped to the boundary. Here's a simple one that denies the write and delete tools, allowing everything else (read):

import rego.v1

 

denied_actions := {

"createSobjectRecord",

"updateSobjectRecord",

"updateRelatedRecord",

"deleteSobjectRecord",

"deleteRelatedRecord"

}

 

deny if {

input.action.name in denied_actions

}

 

 

With the boundary, provider, and policy in place, RDPS is ready to answer decisions. Everything from here is on the GCP side: standing up the enforcement path that calls it.

 


Part 2: Build the enforcement path on Google Cloud

The example uses an ADK agent on Agent Runtime whose tool is the Salesforce Platform MCP server (sobject-all), bound to an egress Agent Gateway that delegates decisions to the ext_proc. Resource names use a gcp-rdps-demo- prefix.

Prerequisites

  • A GCP project with the agent platform, Cloud Run, networking, DNS, and registry APIs enabled.
  • Access to create resources across the services this build touches: Vertex AI (Agent Engine), Cloud Run, Compute (VPC, PSC, and especially load balancing), Cloud DNS, the Agent Registry, Artifact Registry, and Cloud Build.
  • A reachable Zuma RDPS data-plane URL and a boundary with authentication provider configured.

Step 1 — Private networking

The gateway reaches the ext_proc over a Private Service Connect path into your VPC. Create a VPC, a small subnet with Private Google Access, a PSC network attachment (the gateway's egress interface), and a PSC endpoint to all Google APIs. Add private DNS zones for run.app., googleapis.com., and p.googleapis.com. pointing at the PSC address

private-networking-interior-pic

Step 2 — Deploy the ext_proc behind an internal load balancer

Deploy the ext_proc to Cloud Run with HTTP/2 enabled and the RDPS environment variables set (RDPS_DATAPLANE_URL, RDPS_BOUNDARY_ID, RDPS_TENANT_ID). The gateway can't reach a raw *.run.app host over PSC, so front it with a regional internal Application Load Balancer: a serverless NEG, an INTERNAL_MANAGED HTTP/2 backend, a URL map, a self-signed cert, an HTTPS proxy, and a forwarding rule on :443. Add a DNS A record for the ext_proc FQDN pointing at the load balancer's internal IP.

Watch out: this step needs load-balancing permissions (serverless NEGs, backend services, forwarding rules) on top of Cloud Run access. Check them before you start, or the build stops here.

Step 3 — Register the MCP tool server in the Agent Registry

Register the Salesforce MCP server as an mcpServer or service in the global Agent Registry with its interface URL and protocolBinding=JSONRPC. Note the resulting resource name (projects/<proj>/locations/global/mcpServers/<id>) — you'll need it in Step 5.

This is what makes the gateway treat api.salesforce.com as a governed destination. Without the registry entry, the agent's tool-call traffic goes directly to Salesforce and bypasses the gateway - which means RDPS never sees the call.

Step 4 — Create the gateway with two authorization policies

Create the AGENT_TO_ANYWHERE Agent Gateway (protocols [MCP], referencing the global registry), with network config pointing at the PSC attachment and DNS peering for run.app. into your VPC.

Then attach two authorization policies. Keeping them separate matters:

  • An IAP policy (REQUEST_AUTHZ, DRY_RUN, failOpen=true) for the agent's own foundational egress — session creation, model calls, token endpoints.
  • An RDPS policy (CONTENT_AUTHZ, pointing at the ext_proc, failOpen=false) for tool calls. Fail-closed means a denied tool call is truly blocked.

Step 5 — Deploy the agent with the registry-backed toolset

Deploy the ADK agent to Agent Engine, bound to the gateway in egress mode with AGENT_IDENTITY. The critical wiring: build the toolset from the Agent Registry mcpServer resource, not a raw URL.

from google.adk.integrations.agent_registry import AgentRegistry

 

registry = AgentRegistry(

project_id=PROJECT, location="global",

header_provider=get_auth_headers,

)

toolset = registry.get_mcp_toolset(

"projects/<proj>/locations/global/mcpServers/<id>"

)

 


Inside the ext_proc: calling RDPS and acting on the decision

We saw the policy earlier. Here's the other half — how the ext_proc turns an intercepted tool call into an RDPS decision and enforces the result. (Snippets are condensed for illustration)

First, build the decision request from the intercepted MCP tools/call and POST it to the boundary's decision endpoint, carrying the OAuth2 bearer token:

endpoint = f"{RDPS_DATAPLANE_URL}/boundaries/{RDPS_BOUNDARY_ID}/decision"

 

headers = {

"Content-Type": "application/json",

"Authorization": f"Bearer {RDPS_TOKEN}", # token from the OAuth2 provider

}

 

req = urllib.request.Request(endpoint, data=json.dumps(payload).encode(),

headers=headers, method="POST")

with urllib.request.urlopen(req, timeout=RDPS_TIMEOUT_SECS) as resp:

result = json.loads(resp.read().decode()) # { "decision": "...", "categoryResults": [...] }

 

Then evaluate the decision. Only tools/call messages are sent to RDPS; handshakes and tools/list pass through. A DENY from any category blocks the call; if RDPS can't be reached, the configured fail mode decides:

 

# non-tool-call traffic is never evaluated — let it through

if not tool_calls:

return True, "no tools/call in body — passthrough", None

 

for jsonrpc in tool_calls:

result = _call_rdps(_build_rdps_payload(jsonrpc, dest_host, agent_id))

 

if result is None: # RDPS unreachable

if RDPS_FAIL_MODE == "closed":

return False, "RDPS unavailable (fail-closed)", jsonrpc.get("id")

return True, "RDPS unavailable (fail-open)", None

 

if result.get("decision", "").upper() == "DENY":

reasons = _rdps_deny_reasons(result) # pull reasons from DENY categories

return False, f"RDPS DENY: {'; '.join(reasons)}", jsonrpc.get("id")

 

return True, "RDPS ALLOW", None

 

Finally, the gateway side acts on that verdict. On ALLOW, the gateway forwards the call; on DENY the ext_proc returns unauthorized error.

Same agent, same credentials, same gateway. The only thing that changed is the action, and that's exactly what runtime authorization is meant to catch.

Decide once, enforce anywhere

Four posts in, the pattern is clear. The agent can change: low-code in Post 2, pro-code in Post 3. The enforcement point can change too: Zuma's gateways in Posts 2 and 3, Google's Agent Gateway here. What stays constant is the decision. The same boundary, the same policies and the same intent and behavioral checks govern every call.

That matters because no enterprise will run a single agent stack. Some teams will standardize on a cloud provider's gateway, some on Zuma's, some on an API gateway they already own. If every enforcement point carries its own policy logic, you write the same rules several times and the gaps between them become the attack surface.

Separating the decision from the enforcement removes that problem. Google's Agent Gateway supplies managed network enforcement and an extension point for delegated authorization. RDPS supplies content-aware, intent-aware runtime decisions. A thin ext_proc connects them. Each component does what it's built for, and the defense-in-depth model from Post 1 now runs on Google's agent platform without a second gateway.

With Zuma, you discover every agent (Insights), govern its lifecycle (Governance) and authorize every action at runtime (Access), wherever that action is enforced.

Learn more about Zuma.

Share this story

Subscribe to Our Newsletter

Get the latest insights on identity governance, security trends, and customer success stories delivered to your inbox.