使用代理的自有身份向外部服务进行身份验证

借助智能体身份,您可以在 Google Cloud 上的智能体使用自己的 身份向外部服务以及 Cloud Run 或 Google Kubernetes Engine (GKE) 上托管的工作负载进行身份验证。通过使用 OpenID Connect (OIDC) ID 令牌,您可以将智能体连接到第三方云平台(例如 Amazon Web Services (AWS) 和 Microsoft Azure)、自定义 API、API Gateway 和本地后端。

当 Agent Identity 自行行事时,Agent Identity 会发出 OIDC ID 令牌,该令牌是 JSON Web 令牌 (JWT),用于断言智能体的 SPIFFE 身份,并由智能体的信任域(托管式工作负载身份池)的颁发者密钥签名。外部系统可以通过 Google Cloud Security Token Service 托管的公共端点验证这些 ID 令牌,而无需 Google Cloud 凭据或 SDK:

  • OpenID Connect Discovery 1.0 端点 (/.well-known/openid-configuration),用于发布 OpenID 提供方元数据和公钥端点 (jwks_uri)。
  • JSON Web 密钥集 (JWKS) 端点 (/openid/jwks),用于提供用于验证智能体 ID 令牌上的签名的有效公钥。

如需让智能体使用自己的身份向 Google Cloud API 和资源进行身份验证,请参阅 让智能体使用自己的身份进行身份验证 Google Cloud 。 如需代表最终用户访问服务,请参阅 使用三方模式 OAuth 和身份验证管理器进行身份验证。

准备工作

  1. 验证您是否选择了正确的身份验证方法。 查看智能体身份概览,了解 SPIFFE 身份、信任网域、 以及智能体凭据 的工作原理。
  2. 创建并部署智能体,并启用智能体 身份。
  3. 确保外部服务或身份提供方满足以下要求:

  4. 确定智能体和目标外部服务的以下配置值:

    • 颁发者网址 (iss 声明): 您的组织 (https://sts.googleapis.com/v1/organizations/ORGANIZATION_ID/locations/global/workloadIdentityPools/TRUST_DOMAIN) 或项目 (https://sts.googleapis.com/v1/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/TRUST_DOMAIN) 的工作负载身份池颁发者网址。
    • 允许的目标设备(aud 声明) :外部服务或身份提供方在验证 ID 令牌时预期的目标设备 URI。
  5. 验证您是否拥有完成 此任务所需的角色。

所需角色

如需获得部署启用了智能体身份的智能体所需的权限,请让管理员向您授予项目的以下 IAM 角色:

  • 将智能体部署到 Gemini Enterprise Agent Platform 上的 Agent Runtime: Vertex AI User (roles/aiplatform.user)
  • 将智能体服务部署到 Cloud Run: Cloud Run Admin (roles/run.admin)

如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限。

这些预定义角色包含 部署启用了智能体身份的智能体所需的权限。如需查看所需的确切权限,请展开所需权限部分:

所需权限

如需部署启用了智能体身份的智能体,您需要具有以下权限:

  • 将智能体部署到 Gemini Enterprise Agent Platform 上的 Agent Runtime:
    • aiplatform.reasoningEngines.create
    • aiplatform.reasoningEngines.update
  • 将智能体服务部署到 Cloud Run:
    • run.services.create
    • run.services.update

您也可以使用自定义角色或其他预定义角色来获取这些权限。

获取智能体的 OIDC ID 令牌

如需将智能体配置为获取 OIDC ID 令牌并将其发送到外部服务,请完成以下任务:

  1. 使用智能体身份配置智能体
  2. 在应用代码中请求 OIDC ID 令牌

使用智能体身份配置智能体

部署智能体时启用智能体身份:

  • 如果您将智能体部署到 Gemini Enterprise Agent Platform 上的 Agent Runtime ,请将 identity_type 设置为 AGENT_IDENTITY:

    remote_app = client.agent_engines.create(
        agent=app,
        config={
            "identity_type": types.IdentityType.AGENT_IDENTITY,
            "requirements": ["google-cloud-aiplatform[agent_engines,adk]"],
        },
    )
    
  • 如果您将容器化的智能体服务部署到 Cloud Run,请传递 --identity-type=agent-identity 标志:

    gcloud run deploy SERVICE_NAME \
        --image=IMAGE_URL \
        --identity-type=agent-identity \
        --no-allow-unauthenticated

    替换以下内容:

    • SERVICE_NAME:Cloud Run 服务的名称。
    • IMAGE_URL:智能体的容器映像网址。

在应用代码中请求 OIDC ID 令牌

在智能体的应用代码中,使用 Google Auth 客户端库为目标外部受众群体请求 OIDC ID 令牌。客户端库负责处理令牌生成、本地缓存和元数据服务器的自动续订。

默认情况下,受支持的 Google Auth 客户端库通过在令牌的 cnf (x5t#S256) 声明中添加证书指纹,以加密方式将 OIDC ID 令牌绑定到智能体的唯一 X.509 证书。接收服务可以选择根据在双向 TLS (mTLS) 握手期间提供的客户端证书验证此声明,或者如果它们不强制执行证书绑定,则忽略该声明。

当 ID 令牌中存在 cnf 声明时,Cloud Run 会自动强制执行证书绑定。如果智能体使用绑定的 ID 令牌调用 Cloud Run 服务,则必须通过 mTLS 连接调用其 mTLS 端点 (*.mtls.run.app)。如需继续调用标准 *.run.app 网址,请选择针对每个请求停用证书绑定(例如,在 Python 中设置 bind_id_token=False),或使用 GOOGLE_API_ENABLE_RUNTIME_BOUND_TOKEN=false 在整个运行时环境中停用证书绑定。如需查看 受支持的库版本和示例,请参阅停用证书绑定 令牌。

调用通过 mTLS 强制执行证书绑定的端点时,请将客户端会话配置为在 mTLS 握手期间提供智能体的 X.509 证书(例如,通过在 Python 中调用 authed_session.configure_mtls_channel())。

以下示例使用 google-auth 库请求证书绑定的 OIDC ID 令牌,选择性地配置 mTLS 通道,并将令牌作为出站请求中的 Bearer 令牌附加:

Python

from google.auth.transport.requests import AuthorizedSession
from google.oauth2 import id_token

# 1. Specify the audience expected by the external receiver
# (for example, AWS Bedrock AgentCore or your external service URL).
target_audience = "https://EXTERNAL_SERVICE_AUDIENCE"

# 2. Create ID token credentials and an AuthorizedSession, which handles
# local token caching, automatic renewal before expiry, and the
# Bearer header.
credentials = id_token.fetch_id_token_credentials(audience=target_audience)
authed_session = AuthorizedSession(credentials)

# 3. Send the authenticated request to the external service.
response = authed_session.post(
    "https://EXTERNAL_SERVICE_ENDPOINT",
    json={"prompt": "Hello from Agent"},
)

替换以下内容:

  • EXTERNAL_SERVICE_AUDIENCE:接收服务预期的目标设备 URI(例如 bedrock.us-east-1.amazonaws.com 或 api.example.com)。
  • EXTERNAL_SERVICE_ENDPOINT:智能体调用的外部 API 或后端终结点的网址。

Python (mTLS)

from google.auth.transport.requests import AuthorizedSession
from google.oauth2 import id_token

# 1. Specify the audience expected by the receiving service
# (for example, a regional Cloud Run mTLS URL or an on-premises mTLS endpoint).
target_audience = "https://EXTERNAL_SERVICE_AUDIENCE"

# 2. Create certificate-bound ID token credentials and configure the mTLS
# channel to present the agent's X.509 certificate.
credentials = id_token.fetch_id_token_credentials(audience=target_audience)
authed_session = AuthorizedSession(credentials)
authed_session.configure_mtls_channel()

# 3. Send the authenticated request over mTLS.
response = authed_session.post(
    "https://EXTERNAL_SERVICE_ENDPOINT",
    json={"prompt": "Hello from Agent"},
)

替换以下内容:

  • EXTERNAL_SERVICE_AUDIENCE:接收服务预期的目标设备 URI(例如区域 Cloud Run mTLS 网址或 api.example.com)。
  • EXTERNAL_SERVICE_ENDPOINT:智能体调用的 mTLS API 或后端端点的网址。

如需查看其他语言(例如 Go、 Node.js 和 Java)的客户端库说明和示例,请参阅 获取 ID 令牌。这些语言的客户端库支持 --identity-type=agent-identity。支持证书绑定 ID 令牌的库默认绑定令牌,并允许您针对单个请求停用绑定。

验证智能体身份 ID 令牌

当外部服务从智能体收到 OIDC ID 令牌时,请根据目标服务使用以下任一方法验证令牌:

使用内置的工作负载身份联合

如果接收服务在支持内置 IAM 身份验证或 OIDC 工作负载身份联合的云平台上运行,则无需编写自定义令牌验证代码:

  • Cloud Run:如果接收服务在 Cloud Run 上运行并具有经过身份验证的入站流量 (--no-allow-unauthenticated),则 Cloud Run 会在入站流量层验证传入的 智能体身份令牌。 当令牌包含 cnf 声明时,请将调用智能体配置为 以接收服务的区域 mTLS 网址 (https://SERVICE_NAME-PROJECT_NUMBER.REGION.mtls.run.app) 为目标,以便 Cloud Run 可以在 mTLS 握手期间验证客户端证书。 向调用智能体授予 接收服务的 Cloud Run Invoker (roles/run.invoker) 角色。 如需了解详情,请参阅 向 Cloud Run 上的 MCP 服务器进行身份验证。

    如果您的服务允许未经身份验证的入站流量并在 应用代码中验证令牌,请参阅 以编程方式验证令牌。

  • Amazon Bedrock:通过指定 Google Cloud Security Token Service 服务发现网址或颁发者网址以及 预期目标设备来配置入站 JWT 身份验证。如需查看相关说明,请参阅 AWS 文档中的配置入站 JWT 授权方 。

  • Microsoft Entra ID:使用 其他颁发者方案配置联合身份凭据。指定 Google Cloud Security Token Service 颁发者网址、预期目标设备和主体标识符(sub 声明)。 如需查看相关说明,请参阅 在应用和外部身份提供方之间创建信任关系 在 Microsoft Learn 文档中。

以编程方式验证令牌

如果智能体向自定义 API、微服务、API Gateway 或本地工作负载发送请求,则接收服务必须先验证传入的 OIDC ID 令牌,然后才能授予访问权限。智能体通常会在 Authorization: Bearer TOKEN HTTP 标头中传递此令牌。

如需以编程方式验证传入的 ID 令牌,请完成以下任务:

  1. 提取并验证令牌颁发者网址
  2. 发现并缓存公共签名密钥
  3. 验证令牌签名和声明
  4. 可选:验证证书绑定(cnf 声明)
  5. 授权智能体的 SPIFFE 身份

提取并验证令牌颁发者网址

当传入的请求到达时,读取未经验证的 JWT 载荷以提取 iss(颁发者)声明。此声明包含 智能体的 信任域的工作负载身份池的网址。此网址用作发现文档和公共签名密钥的基础网址。

在发出任何出站网络请求之前,请验证 iss 声明是否与 组织或项目的预期 Google Cloud Security Token Service 工作负载身份池网址匹配:

  • 组织级信任域:

    https://sts.googleapis.com/v1/organizations/ORGANIZATION_ID/locations/global/workloadIdentityPools/TRUST_DOMAIN

    例如,对于 ID 为 123456789012 的组织, TRUST_DOMAIN 为 agents.global.org-123456789012.system.id.goog。

  • 项目级信任域 (适用于没有组织的项目):

    https://sts.googleapis.com/v1/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/TRUST_DOMAIN

    例如,对于编号为 9876543210 的项目, TRUST_DOMAIN 为 agents.global.proj-9876543210.system.id.goog。

发现并缓存公共签名密钥

验证颁发者网址后,从 Google Cloud Security Token Service 检索并缓存公共签名密钥 :

  1. 查询 OpenID Connect Discovery 端点:将 /.well-known/openid-configuration 附加到基础颁发者网址,然后 发送未经身份验证的 HTTP GET 请求:

    在使用任何请求数据之前, 请先进行以下替换:

    • ORGANIZATION_ID:您的 Google Cloud 组织 ID。对于没有组织的项目,请将 organizations/ORGANIZATION_ID 替换为 projects/PROJECT_NUMBER。
    • TRUST_DOMAIN:智能体的信任域的工作负载身份池 ID(例如 agents.global.org-123456789012.system.id.goog)。

    HTTP 方法和网址:

    GET https://sts.googleapis.com/v1/organizations/ORGANIZATION_ID/locations/global/workloadIdentityPools/TRUST_DOMAIN/.well-known/openid-configuration

    如需发送您的请求,请展开以下选项之一:

    成功的请求会返回 HTTP 200 OK 状态和一个包含 OpenID 提供方元数据的 JSON 对象,其中包括 jwks_uri 字段:

    {
      "issuer": "https://sts.googleapis.com/v1/organizations/123456789012/locations/global/workloadIdentityPools/agents.global.org-123456789012.system.id.goog",
      "jwks_uri": "https://sts.googleapis.com/v1/organizations/123456789012/locations/global/workloadIdentityPools/agents.global.org-123456789012.system.id.goog/openid/jwks",
      "authorization_endpoint": "https://sts.googleapis.com/v1/organizations/123456789012/locations/global/workloadIdentityPools/agents.global.org-123456789012.system.id.goog/authorize",
      "token_endpoint": "https://sts.googleapis.com/v1/organizations/123456789012/locations/global/workloadIdentityPools/agents.global.org-123456789012.system.id.goog/token",
      "response_types_supported": [
        "id_token"
      ],
      "subject_types_supported": [
        "public"
      ],
      "id_token_signing_alg_values_supported": [
        "RS256"
      ]
    }
    
  2. 查询 JSON Web 密钥集 (JWKS) 端点:向 OpenID 提供方元数据中返回的 jwks_uri 网址发送 未经身份验证的 HTTP GET 请求:

    在使用任何请求数据之前, 请先进行以下替换:

    • ORGANIZATION_ID:您的 Google Cloud 组织 ID。对于没有组织的项目,请将 organizations/ORGANIZATION_ID 替换为 projects/PROJECT_NUMBER。
    • TRUST_DOMAIN:智能体的信任域的工作负载身份池 ID(例如 agents.global.org-123456789012.system.id.goog)。

    HTTP 方法和网址:

    GET https://sts.googleapis.com/v1/organizations/ORGANIZATION_ID/locations/global/workloadIdentityPools/TRUST_DOMAIN/openid/jwks

    如需发送您的请求,请展开以下选项之一:

    成功的请求会返回 HTTP 200 OK 状态和一个包含根据 RFC 7517 格式化的公钥数组的 JSON 对象:

    {
      "keys": [
        {
          "kty": "RSA",
          "use": "sig",
          "alg": "RS256",
          "kid": "4d1933f8e6c4e0b512c140989f6655c68997...",
          "n": "uQn4zN_1mQ0VpGv82-Wp3w...",
          "e": "AQAB"
        }
      ]
    }
    
  3. 缓存发现文档和密钥:来自 OpenID Connect Discovery 端点和 JWKS 端点的响应都包含以下 HTTP 缓存标头:

    Cache-Control: public, max-age=86400, must-revalidate
    

    将发现文档和 JWKS 缓存最多 24 小时 (86400 秒),以提高验证性能并避免 速率限制。

    Google Cloud 会定期轮替工作负载身份池的私有签名密钥和公共签名 密钥。如果验证程序收到包含 令牌kid(密钥 ID)的传入令牌,但该 ID 不在其本地密钥缓存中, 请先从 /openid/jwks 端点提取新的 JWKS,然后再 拒绝该令牌。

    如果您在查询发现端点或 JWKS 端点时遇到 HTTP 错误,请参阅 排查智能体身份身份验证问题。

验证令牌签名和声明

如需以加密方式验证令牌签名并验证 JWT 声明,请使用 标准 OIDC 或 JWT 验证库(例如 Google Tink),并执行以下操作:

  1. 签名:在缓存的 JWKS 中查找与 JWT 标头中的 kid(密钥 ID)匹配的公钥。使用 alg 字段 (RS256) 中指定的算法验证签名。为了实现向前兼容,请动态检查 JWKS 中的 alg 和 kty 字段,而不是对算法类型进行硬编码。
  2. 颁发者 (iss):确认 iss 声明与信任域的受信任 Google Cloud 工作负载身份池颁发者网址匹配。
  3. 目标设备 (aud) :确认 aud 声明与服务配置的目标设备标识符匹配。
  4. 颁发时间 (iat) 和到期时间 (exp) :验证 iat 声明是否在过去,以及当前时间是否早于 exp 声明(允许少量时钟偏差容差,例如 1 到 2 分钟)。

以下示例使用 Google Tink (tink.jwt) 根据 JWKS JSON 载荷验证智能体身份 ID 令牌:

Python

import tink
from tink import jwt

# Initialize Tink JWT signature primitives (call once at application startup).
jwt.register_jwt_signature()


def verify_agent_identity_token(
    token: str,
    jwks_json: str,
    expected_issuer: str,
    expected_audience: str,
) -> jwt.VerifiedJwt:
    """Verifies an Agent Identity JWT against a JWKS JSON string using Tink.

    Args:
        token: The compact serialized JWT string.
        jwks_json: The JWKS JSON string fetched from the STS pool endpoint.
        expected_issuer: The expected token issuer ('iss' claim).
        expected_audience: The expected token audience ('aud' claim).

    Returns:
        jwt.VerifiedJwt: The verified JWT claims object.

    Raises:
        tink.TinkError: If the JWKS cannot be parsed, the key is not found,
            or token validation (signature, issuer, audience, expiration) fails.
    """
    # 1. Convert the JWKS JSON into a Tink public KeysetHandle.
    keyset_handle = jwt.jwk_set_to_public_keyset_handle(jwks_json)

    # 2. Instantiate the Tink JwtPublicKeyVerify primitive.
    jwt_verifier = keyset_handle.primitive(jwt.JwtPublicKeyVerify)

    # 3. Configure expected validation rules (issuer, audience, expiration).
    # Google Cloud STS sets 'typ': 'JWT' in the header, so
    # expected_type_header="JWT" is required.
    validator = jwt.new_validator(
        expected_issuer=expected_issuer,
        expected_audience=expected_audience,
        expected_type_header="JWT",
        allow_missing_expiration=False,
    )

    # 4. Cryptographically verify the signature and standard OIDC claims.
    return jwt_verifier.verify_and_decode(token, validator)

可选:验证证书绑定(cnf 声明)

如果接收服务通过 mTLS 强制执行证书绑定,请根据在 mTLS 握手期间提供的客户端证书验证 cnf (x5t#S256) 声明:

  1. 终止双向 TLS (mTLS):将服务器或入站流量代理配置为 根据 TLS 1.3 (RFC 9846)在 TLS 握手期间 请求并验证 智能体的 X.509 证书。
  2. 计算证书指纹 (x5t#S256) :从 mTLS 连接中提取叶 X.509 客户端证书。计算其 DER 编码字节的 SHA-256 哈希,并对摘要进行 base64url 编码,不使用 = 填充。
  3. 验证 cnf (x5t#S256) 声明:检查经过验证的 JWT 载荷 中的 cnf 对象 ({"cnf": {"x5t#S256": "FINGERPRINT"}}),并验证 x5t#S256 值是否与客户端证书指纹匹配 (根据 RFC 8705 第 6.1 节)。 如果调用方未通过 mTLS 提供客户端证书,或者指纹不匹配,请使用 HTTP 401 Unauthorized 响应拒绝请求。

授权智能体的 SPIFFE 身份

验证令牌的签名和标准声明后,检查经过验证的 sub(主体)声明,以授权请求并在审核日志中记录调用智能体。

`sub` 声明包含智能体的唯一 SPIFFE ID,例如:

  spiffe://agents.global.org-123456789012.system.id.goog/resources/aiplatform/projects/9876543210/locations/us-central1/reasoningEngines/my-test-agent

在服务的授权逻辑中,将经过验证的 sub 声明与受信任智能体 SPIFFE ID(或信任域前缀)的许可清单进行比较,然后再授予对受保护资源的访问权限。

后续步骤