程序化身份验证

本文档介绍了如何从用户账号或服务账号向受 IAP 保护的资源进行身份验证。

程序化访问是指从非浏览器客户端调用受 IAP 保护的应用的场景。这包括命令行工具、服务到服务调用和移动应用。根据您的使用场景,您可能需要使用用户凭据或服务凭据向 IAP 进行身份验证。

  • 用户账号属于个别用户。当您的应用需要代表用户访问受 IAP 保护的资源,则对用户账号进行身份验证。如需了解详情,请参阅用户账号

  • 服务账号代表应用,而不是个别用户。如果要允许应用访问受 IAP 保护的资源,则对服务账号进行身份验证。如需了解详情,请参阅服务账号

IAP 支持以下类型的凭据,以便进行程序化访问:

  • OAuth 2.0 ID 令牌 - Google 为人类用户或服务账号签发的令牌,其目标对象声明设置为 IAP 应用的资源 ID。
  • 服务账号已签署的 JWT - 服务账号的自签名或 Google 签发的 JWT 令牌。

AuthorizationProxy-Authorization HTTP 标头中将这些凭据传递给 IAP。

准备工作

在开始之前,您需要一个受 IAP 保护的应用,您将使用开发者账号、服务账号或移动应用凭据以编程方式连接该应用。

对用户账号进行身份验证

您可以允许用户从桌面应用或移动应用访问您的应用,从而支持程序与受 IAP 保护的资源进行交互。

从移动应用进行身份验证

  1. 为您的移动应用创建或使用现有的 OAuth 2.0 客户端 ID。如需使用现有的 OAuth 2.0 客户端 ID,请按照如何共享 OAuth 客户端中的步骤操作。 将 OAuth 客户端 ID 添加到应用的程序化访问许可名单中。
  2. 为受 IAP 保护的客户端 ID 获取一个 ID 令牌。
  3. Authorization: Bearer 标头中包含 ID 令牌以向受 IAP 保护的资源发出经过身份验证的请求。

从桌面应用进行身份验证

本部分介绍如何使用桌面命令行对用户账号进行身份验证。

  1. 如需允许开发者使用命令行访问您的应用,请创建桌面 OAuth 2.0 客户端 ID共享现有的桌面 OAuth 客户端 ID
  2. 将 OAuth ID 添加到应用的程序化访问许可名单中。

登录应用

每个想要访问受 IAP 保护的应用的开发者都需要先登录。您可以使用 gcloud CLI 等工具将流程打包至脚本中。以下示例演示了如何使用 curl 登录并生成一个可用于访问应用的令牌:

  1. 登录有权访问 Google Cloud 资源的账号。
  2. 启动一个可以回显传入请求的本地服务器。

      # Example using Netcat (http://netcat.sourceforge.net/)
      nc -k -l 4444
    
  3. 前往以下 URI,其中 DESKTOP_CLIENT_ID桌面应用客户端 ID:

      https://accounts.google.com/o/oauth2/v2/auth?client_id=DESKTOP_CLIENT_ID&response_type=code&scope=openid%20email&access_type=offline&redirect_uri=http://localhost:4444&cred_ref=true
    
  4. 在本地服务器输出中,查找请求参数:

      GET /?code=CODE&scope=email%20openid%20https://www.googleapis.com/auth/userinfo.email&hd=google.com&prompt=consent HTTP/1.1
    
  5. 复制 CODE 值以替换以下命令中的 AUTH_CODE,同时复制桌面应用客户端 ID 和密钥:

      curl --verbose \
        --data client_id=DESKTOP_CLIENT_ID \
        --data client_secret=DESKTOP_CLIENT_SECRET \
        --data code=CODE \
        --data redirect_uri=http://localhost:4444 \
        --data grant_type=authorization_code \
        https://oauth2.googleapis.com/token
    

    此命令会返回一个带有 id_token 字段的 JSON 对象,您可以使用该字段访问应用。

访问应用

如需访问该应用,请使用 id_token

curl --verbose --header 'Authorization: Bearer ID_TOKEN' URL

刷新令牌

您可以使用在登录流程中生成的刷新令牌来获取新的 ID 令牌。当原始 ID 令牌过期时,此方法非常有用。每个 ID 令牌的有效期约为一小时,在此期间,您可以向特定应用发出多个请求。

以下示例使用 curl 通过刷新令牌获取新的 ID 令牌。 在此示例中,REFRESH_TOKEN 是登录流程中的令牌。DESKTOP_CLIENT_IDDESKTOP_CLIENT_SECRET 与登录流程中使用的相同:

curl --verbose \
  --data client_id=DESKTOP_CLIENT_ID \
  --data client_secret=DESKTOP_CLIENT_SECRET \
  --data refresh_token=REFRESH_TOKEN \
  --data grant_type=refresh_token \
  https://oauth2.googleapis.com/token

此命令会返回一个带有新 id_token 字段的 JSON 对象,您可以使用该字段访问应用。

对服务账号进行身份验证

您可以使用服务账号 JWTOpenID Connect (OIDC) 令牌向受 IAP 保护的资源对服务账号进行身份验证。下表概述了不同身份验证令牌及其功能之间的一些差异。

Authentication 功能 服务账号 JWT OpenID Connect 令牌
情境感知访问权限支持
OAuth 2.0 客户端 ID 要求
令牌范围 受 IAP 保护的资源的网址 OAuth 2.0 客户端 ID

使用服务账号 JWT 进行身份验证

IAP 支持对 Google 身份、Identity Platform 和配置了员工身份联合的应用进行服务账号 JWT 身份验证。

使用 JWT 对服务账号进行身份验证包括以下主要步骤:

  1. 向调用服务账号授予 Service Account Token Creator 角色 (roles/iam.serviceAccountTokenCreator)。

    该角色授予主账号创建短期有效凭据(例如 JWT)的权限。

  2. 为受 IAP 保护的资源创建 JWT。

  3. 使用服务账号私钥对 JWT 进行签名。

创建 JWT

创建的 JWT 应具有类似于以下示例的载荷:

{
  "iss": SERVICE_ACCOUNT_EMAIL_ADDRESS,
  "sub": SERVICE_ACCOUNT_EMAIL_ADDRESS,
  "aud": TARGET_URL,
  "iat": IAT,
  "exp": EXP,
}
  • 对于 isssub 字段,请指定服务账号的电子邮件地址。此信息可在服务账号 JSON 文件的 client_email 字段中找到,也可以通过传入方式提供。典型格式: service-account@PROJECT_ID.iam.gserviceaccount.com

  • 对于 aud 字段,请指定受 IAP 保护的资源的网址。

  • 对于 iat 字段,请指定当前的 Unix 纪元时间;对于 exp 字段,请指定 3600 秒内的稍后时间。用于定义 JWT 的过期时间。

对 JWT 签名

您可以使用以下方法之一对 JWT 进行签名:

  • 使用 IAM Credentials API 对 JWT 进行签名,而无需直接访问私钥。
  • 使用本地凭据密钥文件在本地签署 JWT。
使用 IAM Service Account Credentials API 为 JWT 签名

使用 IAM Service Account Credentials API 为服务账号 JWT 签名。该方法会提取与您的服务账号关联的私钥,并使用该私钥对 JWT 载荷进行签名。这样一来,无需直接访问私钥即可对 JWT 进行签名。

如需向 IAP 进行身份验证,请设置应用默认凭据。如需了解详情,请参阅为本地开发环境设置身份验证

gcloud

  1. 运行以下命令以准备包含 JWT 载荷的请求
cat > claim.json << EOM
{
  "iss": "SERVICE_ACCOUNT_EMAIL_ADDRESS",
  "sub": "SERVICE_ACCOUNT_EMAIL_ADDRESS",
  "aud": "TARGET_URL",
  "iat": $(date +%s),
  "exp": $((`date +%s` + 3600))
}
EOM
  1. 使用以下 Google Cloud CLI 命令对 claim.json 中的载荷进行签名:
gcloud iam service-accounts sign-jwt --iam-account="SERVICE_ACCOUNT_EMAIL_ADDRESS" claim.json output.jwt

成功发出请求后,output.jwt 会包含一个已签名的 JWT,您可以使用该 JWT 访问受 IAP 保护的资源。

Python

import datetime
import json

import google.auth
from google.cloud import iam_credentials_v1

def generate_jwt_payload(service_account_email: str, resource_url: str) -> str:
    """Generates JWT payload for service account.

    Creates a properly formatted JWT payload with standard claims (iss, sub, aud,
    iat, exp) needed for IAP authentication.

    Args:
        service_account_email (str): Specifies service account JWT is created for.
        resource_url (str): Specifies scope of the JWT, the URL that the JWT will
            be allowed to access.

    Returns:
        str: JSON string containing the JWT payload with properly formatted claims.
    """
    # Create current time and expiration time (1 hour later) in UTC
    iat = datetime.datetime.now(tz=datetime.timezone.utc)
    exp = iat + datetime.timedelta(seconds=3600)

    # Convert datetime objects to numeric timestamps (seconds since epoch)
    # as required by JWT standard (RFC 7519)
    payload = {
        "iss": service_account_email,
        "sub": service_account_email,
        "aud": resource_url,
        "iat": int(iat.timestamp()),
        "exp": int(exp.timestamp()),
    }

    return json.dumps(payload)

def sign_jwt(target_sa: str, resource_url: str) -> str:
    """Signs JWT payload using ADC and IAM credentials API.

    Uses Google Cloud's IAM Credentials API to sign a JWT. This requires the
    caller to have iap.webServiceVersions.accessViaIap permission on the target
    service account.

    Args:
        target_sa (str): Service Account JWT is being created for.
            iap.webServiceVersions.accessViaIap permission is required.
        resource_url (str): Audience of the JWT, and scope of the JWT token.
            This is the url of the IAP protected application.

    Returns:
        str: A signed JWT that can be used to access IAP protected apps.
            Use in Authorization header as: 'Bearer <signed_jwt>'
    """
    # Get default credentials from environment or application credentials
    source_credentials, project_id = google.auth.default()

    # Initialize IAM credentials client with source credentials
    iam_client = iam_credentials_v1.IAMCredentialsClient(credentials=source_credentials)

    # Generate the service account resource name
    # If project_id is None, use '-' as placeholder as per API requirements
    project = project_id if project_id else "-"
    name = iam_client.service_account_path(project, target_sa)

    # Create and sign the JWT payload
    payload = generate_jwt_payload(target_sa, resource_url)

    # Sign the JWT using the IAM credentials API
    response = iam_client.sign_jwt(name=name, payload=payload)

    return response.signed_jwt

curl

  1. 运行以下命令,准备包含 JWT 载荷的请求:

    cat << EOF > request.json
    {
      "payload": JWT_PAYLOAD
    }
    EOF
    
  2. 使用 IAM 对 JWT 进行签名

    Service Account Credentials API:

    curl -X POST \
      -H "Authorization: Bearer $(gcloud auth print-access-token)" \
      -H "Content-Type: application/json; charset=utf-8" \
      -d @request.json \
      "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/SERVICE_ACCOUNT_EMAIL_ADDRESS:signJwt"
    

    成功发出请求后,系统会在响应中返回已签名的 JWT。

  3. 使用 JWT 访问受 IAP 保护的资源。

使用本地凭据密钥文件为 JWT 签名

JWT 使用服务账号的私钥进行签名。

如果您有服务账号密钥文件,则可以在本地签署 JWT。

脚本会随载荷一起发送 JWT 标头。对于标头中的 kid 字段,请使用服务账号的私钥 ID,该 ID 位于服务账号凭据 JSON 文件的 private_key_id 字段中。该密钥还用于为 JWT 签名。

访问应用

在所有情况下,如需访问该应用,请使用 signed-jwt

curl --verbose --header 'Authorization: Bearer SIGNED_JWT' URL

使用 OIDC 令牌进行身份验证

  1. 创建或使用现有的 OAuth 2.0 客户端 ID。如需使用现有的 OAuth 2.0 客户端 ID,请按照如何共享 OAuth 客户端中的步骤操作。
  2. 将 OAuth ID 添加到应用的程序化访问许可名单中。
  3. 确保将默认服务账号添加到受 IAP 保护的项目的访问权限列表

向受 IAP 保护的资源发出请求时,您必须在 Authorization 标头中添加令牌:Authorization: 'Bearer OIDC_TOKEN'

以下代码示例演示了如何获取 OIDC 令牌。

获取默认服务账号的 OIDC 令牌

如需为 Compute Engine、App Engine 或 Cloud Run 的默认服务账号获取 OIDC 令牌,请参考以下代码示例生成令牌,以访问受 IAP 保护的资源:

C#

如需向 IAP 进行身份验证,请设置应用默认凭据。如需了解详情,请参阅为本地开发环境设置身份验证


using Google.Apis.Auth.OAuth2;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;

public class IAPClient
{
    /// <summary>
    /// Makes a request to a IAP secured application by first obtaining
    /// an OIDC token.
    /// </summary>
    /// <param name="iapClientId">The client ID observed on 
    /// https://console.cloud.google.com/apis/credentials. </param>
    /// <param name="uri">HTTP URI to fetch.</param>
    /// <param name="cancellationToken">The token to propagate operation cancel notifications.</param>
    /// <returns>The HTTP response message.</returns>
    public async Task<HttpResponseMessage> InvokeRequestAsync(
        string iapClientId, string uri, CancellationToken cancellationToken = default)
    {
        // Get the OidcToken.
        // You only need to do this once in your application
        // as long as you can keep a reference to the returned OidcToken.
        OidcToken oidcToken = await GetOidcTokenAsync(iapClientId, cancellationToken);

        // Before making an HTTP request, always obtain the string token from the OIDC token,
        // the OIDC token will refresh the string token if it expires.
        string token = await oidcToken.GetAccessTokenAsync(cancellationToken);

        // Include the OIDC token in an Authorization: Bearer header to 
        // IAP-secured resource
        // Note: Normally you would use an HttpClientFactory to build the httpClient.
        // For simplicity we are building the HttpClient directly.
        using HttpClient httpClient = new HttpClient();
        httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
        return await httpClient.GetAsync(uri, cancellationToken);
    }

    /// <summary>
    /// Obtains an OIDC token for authentication an IAP request.
    /// </summary>
    /// <param name="iapClientId">The client ID observed on 
    /// https://console.cloud.google.com/apis/credentials. </param>
    /// <param name="cancellationToken">The token to propagate operation cancel notifications.</param>
    /// <returns>The HTTP response message.</returns>
    public async Task<OidcToken> GetOidcTokenAsync(string iapClientId, CancellationToken cancellationToken)
    {
        // Obtain the application default credentials.
        GoogleCredential credential = await GoogleCredential.GetApplicationDefaultAsync(cancellationToken);

        // Request an OIDC token for the Cloud IAP-secured client ID.
       return await credential.GetOidcTokenAsync(OidcTokenOptions.FromTargetAudience(iapClientId), cancellationToken);
    }
}

Go

如需向 IAP 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证

import (
	"context"
	"fmt"
	"io"
	"net/http"

	"google.golang.org/api/idtoken"
)

// makeIAPRequest makes a request to an application protected by Identity-Aware
// Proxy with the given audience.
func makeIAPRequest(w io.Writer, request *http.Request, audience string) error {
	// request, err := http.NewRequest("GET", "http://example.com", nil)
	// audience := "IAP_CLIENT_ID.apps.googleusercontent.com"
	ctx := context.Background()

	// client is a http.Client that automatically adds an "Authorization" header
	// to any requests made.
	client, err := idtoken.NewClient(ctx, audience)
	if err != nil {
		return fmt.Errorf("idtoken.NewClient: %w", err)
	}

	response, err := client.Do(request)
	if err != nil {
		return fmt.Errorf("client.Do: %w", err)
	}
	defer response.Body.Close()
	if _, err := io.Copy(w, response.Body); err != nil {
		return fmt.Errorf("io.Copy: %w", err)
	}

	return nil
}

Java

如需向 IAP 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证


import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.auth.oauth2.IdTokenCredentials;
import com.google.auth.oauth2.IdTokenProvider;
import com.google.common.base.Preconditions;
import java.io.IOException;
import java.util.Collections;

public class BuildIapRequest {
  private static final String IAM_SCOPE = "https://www.googleapis.com/auth/iam";

  private static final HttpTransport httpTransport = new NetHttpTransport();

  private BuildIapRequest() {}

  private static IdTokenProvider getIdTokenProvider() throws IOException {
    GoogleCredentials credentials =
        GoogleCredentials.getApplicationDefault().createScoped(Collections.singleton(IAM_SCOPE));

    Preconditions.checkNotNull(credentials, "Expected to load credentials");
    Preconditions.checkState(
        credentials instanceof IdTokenProvider,
        String.format(
            "Expected credentials that can provide id tokens, got %s instead",
            credentials.getClass().getName()));

    return (IdTokenProvider) credentials;
  }

  /**
   * Clone request and add an IAP Bearer Authorization header with ID Token.
   *
   * @param request Request to add authorization header
   * @param iapClientId OAuth 2.0 client ID for IAP protected resource
   * @return Clone of request with Bearer style authorization header with ID Token.
   * @throws IOException exception creating ID Token
   */
  public static HttpRequest buildIapRequest(HttpRequest request, String iapClientId)
      throws IOException {

    IdTokenProvider idTokenProvider = getIdTokenProvider();
    IdTokenCredentials credentials =
        IdTokenCredentials.newBuilder()
            .setIdTokenProvider(idTokenProvider)
            .setTargetAudience(iapClientId)
            .build();

    HttpRequestInitializer httpRequestInitializer = new HttpCredentialsAdapter(credentials);

    return httpTransport
        .createRequestFactory(httpRequestInitializer)
        .buildRequest(request.getRequestMethod(), request.getUrl(), request.getContent());
  }
}

Node.js

如需向 IAP 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */
// const url = 'https://some.iap.url';
// const targetAudience = 'IAP_CLIENT_ID.apps.googleusercontent.com';

const {GoogleAuth} = require('google-auth-library');
const auth = new GoogleAuth();

async function request() {
  console.info(`request IAP ${url} with target audience ${targetAudience}`);
  const client = await auth.getIdTokenClient(targetAudience);
  const res = await client.fetch(url);
  console.info(res.data);
}

request().catch(err => {
  console.error(err.message);
  process.exitCode = 1;
});

PHP

如需向 IAP 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证

namespace Google\Cloud\Samples\Iap;

# Imports Auth libraries and Guzzle HTTP libraries.
use Google\Auth\ApplicationDefaultCredentials;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;

/**
 * Make a request to an application protected by Identity-Aware Proxy.
 *
 * @param string $url The Identity-Aware Proxy-protected URL to fetch.
 * @param string $clientId The client ID used by Identity-Aware Proxy.
 */
function make_iap_request($url, $clientId)
{
    // create middleware, using the client ID as the target audience for IAP
    $middleware = ApplicationDefaultCredentials::getIdTokenMiddleware($clientId);
    $stack = HandlerStack::create();
    $stack->push($middleware);

    // create the HTTP client
    $client = new Client([
        'handler' => $stack,
        'auth' => 'google_auth'
    ]);

    // make the request
    $response = $client->get($url);
    print('Printing out response body:');
    print($response->getBody());
}

Python

如需向 IAP 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证

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


def make_iap_request(url, client_id, method="GET", **kwargs):
    """Makes a request to an application protected by Identity-Aware Proxy.

    Args:
      url: The Identity-Aware Proxy-protected URL to fetch.
      client_id: The client ID used by Identity-Aware Proxy.
      method: The request method to use
              ('GET', 'OPTIONS', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE')
      **kwargs: Any of the parameters defined for the request function:
                https://github.com/requests/requests/blob/master/requests/api.py
                If no timeout is provided, it is set to 90 by default.

    Returns:
      The page body, or raises an exception if the page couldn't be retrieved.
    """
    # Set the default timeout, if missing
    if "timeout" not in kwargs:
        kwargs["timeout"] = 90

    # Obtain an OpenID Connect (OIDC) token from metadata server or using service
    # account.
    open_id_connect_token = id_token.fetch_id_token(Request(), client_id)

    # Fetch the Identity-Aware Proxy-protected URL, including an
    # Authorization header containing "Bearer " followed by a
    # Google-issued OpenID Connect token for the service account.
    resp = requests.request(
        method,
        url,
        headers={"Authorization": "Bearer {}".format(open_id_connect_token)},
        **kwargs
    )
    if resp.status_code == 403:
        raise Exception(
            "Service account does not have permission to "
            "access the IAP-protected application."
        )
    elif resp.status_code != 200:
        raise Exception(
            "Bad response from application: {!r} / {!r} / {!r}".format(
                resp.status_code, resp.headers, resp.text
            )
        )
    else:
        return resp.text

Ruby

如需向 IAP 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证

# url = "The Identity-Aware Proxy-protected URL to fetch"
# client_id = "The client ID used by Identity-Aware Proxy"
require "googleauth"
require "faraday"

# The client ID as the target audience for IAP
id_token_creds = Google::Auth::Credentials.default target_audience: client_id

headers = {}
id_token_creds.client.apply! headers

resp = Faraday.get url, nil, headers

if resp.status == 200
  puts "X-Goog-Iap-Jwt-Assertion:"
  puts resp.body
else
  puts "Error requesting IAP"
  puts resp.status
  puts resp.headers
end

从本地服务账号密钥文件获取 OIDC 令牌

如需使用服务账号密钥文件生成 OIDC 令牌,您将使用该密钥文件创建并签署 JWT 断言,然后将该断言换成 ID 令牌。以下 Bash 脚本演示了此过程:

Bash

#!/usr/bin/env bash
#
# Example script that generates an OIDC token using a service account key file
# and uses it to access an IAP-secured resource

set -euo pipefail

get_token() {
  # Get the bearer token in exchange for the service account credentials
  local service_account_key_file_path="${1}"
  local iap_client_id="${2}"

  # Define the scope and token endpoint
  local iam_scope="https://www.googleapis.com/auth/iam"
  local oauth_token_uri="https://www.googleapis.com/oauth2/v4/token"

  # Extract data from service account key file
  local private_key_id="$(cat "${service_account_key_file_path}" | jq -r '.private_key_id')"
  local client_email="$(cat "${service_account_key_file_path}" | jq -r '.client_email')"
  local private_key="$(cat "${service_account_key_file_path}" | jq -r '.private_key')"

  # Set token timestamps (current time and expiration 10 minutes later)
  local issued_at="$(date +%s)"
  local expires_at="$((issued_at + 600))"

  # Create JWT header and payload
  local header="{'alg':'RS256','typ':'JWT','kid':'${private_key_id}'}"
  local header_base64="$(echo "${header}" | base64 | tr -d '\n')"
  local payload="{'iss':'${client_email}','aud':'${oauth_token_uri}','exp':${expires_at},'iat':${issued_at},'sub':'${client_email}','target_audience':'${iap_client_id}'}"
  local payload_base64="$(echo "${payload}" | base64 | tr -d '\n')"

  # Create JWT signature using the private key
  local signature_base64="$(printf %s "${header_base64}.${payload_base64}" | openssl dgst -binary -sha256 -sign <(printf '%s\n' "${private_key}")  | base64 | tr -d '\n')"
  local assertion="${header_base64}.${payload_base64}.${signature_base64}"

  # Exchange the signed JWT assertion for an ID token
  local token_payload="$(curl -s \
    --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" \
    --data-urlencode "assertion=${assertion}" \
    https://www.googleapis.com/oauth2/v4/token)"

  # Extract just the ID token from the response
  local bearer_id_token="$(echo "${token_payload}" | jq -r '.id_token')"
  echo "${bearer_id_token}"
}

main() {
  # Check if required arguments are provided
  if [[ $# -lt 3 ]]; then
    echo "Usage: $0 <service_account_key_file.json> <iap_client_id> <url>"
    exit 1
  fi

  # Assign parameters to variables
  SERVICE_ACCOUNT_KEY="$1"
  IAP_CLIENT_ID="$2"
  URL="$3"

  # Generate the ID token
  echo "Generating token..."
  ID_TOKEN=$(get_token "${SERVICE_ACCOUNT_KEY}" "${IAP_CLIENT_ID}")

  # Access the IAP-secured resource with the token
  echo "Accessing: ${URL}"
  curl --header "Authorization: Bearer ${ID_TOKEN}" "${URL}"
}

# Run the main function with all provided arguments
main "$@"

此脚本会执行以下步骤:

  1. 从 JSON 密钥文件中提取服务账号密钥信息
  2. 创建包含必要字段的 JWT,包括作为目标受众群体的 IAP 客户端 ID
  3. 使用服务账号的私钥对 JWT 进行签名
  4. 通过 Google 的 OAuth 服务将此 JWT 换成 OIDC 令牌
  5. 使用生成的令牌向受 IAP 保护的资源发出经过身份验证的请求

如需使用此脚本,请执行以下操作:

  1. 将其保存到文件中,例如:get_iap_token.sh
  2. 将其设为可执行文件:chmod +x get_iap_token.sh
  3. 使用以下三个参数运行该函数:
  ./get_iap_token.sh service-account-key.json \
    OAUTH_CLIENT_ID \
    URL

其中:

  • service-account-key.json 是您下载的服务账号密钥文件
  • OAUTH_CLIENT_ID 是受 IAP 保护的资源的 OAuth 客户端 ID
  • URL 是您要访问的网址

在所有其他情况下获取 OIDC 令牌

在其他所有情况下,请在访问受 IAP 保护的资源之前,通过模拟目标服务账号使用 IAM Credentials API 生成 OIDC 令牌。此流程包括以下步骤:

  1. 为调用服务账号(与获取 ID 令牌的代码关联的服务账号)提供 Service Account OpenID Connect Identity Token Creator 角色 (roles/iam.serviceAccountOpenIdTokenCreator)。

    这样,调用服务账号就可以模拟目标服务账号。

  2. 使用调用服务账号提供的凭据对目标服务账号调用 generateIdToken 方法。

    audience 字段设置为您的客户端 ID。

如需查看分步说明,请参阅创建 ID 令牌

从 Proxy-Authorization 标头进行身份验证

如果您的应用使用 Authorization 请求标头,您可以改为在 Proxy-Authorization: Bearer 标头中添加 ID 令牌。如果在 Proxy-Authorization 标头中找到了有效的 ID 令牌,则 IAP 会用它授权请求。授权请求后,IAP 会将 Authorization 标头传递给您的应用,而不处理内容。

如果在 Proxy-Authorization 标头中未找到有效的 ID 令牌,则 IAP 会继续处理 Authorization 标头并在将请求传递给应用之前去除 Proxy-Authorization 标头。

后续步骤