Autentikasi terprogram

Halaman ini menjelaskan cara melakukan autentikasi ke resource yang diamankan Identity-Aware Proxy (IAP) dari akun pengguna atau akun layanan.

  • Akun pengguna dimiliki oleh pengguna individual. Anda mengautentikasi akun pengguna saat aplikasi Anda memerlukan akses ke resource yang diamankan IAP atas nama pengguna. Untuk informasi selengkapnya, lihat Akun pengguna.

  • Akun layanan adalah milik aplikasi, bukan milik pengguna individual. Anda mengautentikasi akun layanan jika ingin mengizinkan aplikasi mengakses resource yang diamankan oleh IAP. Untuk mengetahui informasi selengkapnya, lihat Akun layanan.

Sebelum memulai

Sebelum memulai, Anda akan memerlukan hal berikut:

  • Aplikasi yang diamankan oleh IAP yang ingin Anda hubungkan secara terprogram menggunakan akun developer, akun layanan, atau kredensial aplikasi seluler.

Mengautentikasi akun pengguna

Anda dapat mengaktifkan akses pengguna ke aplikasi dari desktop atau aplikasi seluler agar program dapat berinteraksi dengan resource yang diamankan oleh IAP.

Mengautentikasi dari aplikasi seluler

  1. Buat atau gunakan client ID OAuth 2.0 yang sudah ada untuk aplikasi seluler Anda. Untuk menggunakan client ID OAuth 2.0 yang sudah ada, ikuti langkah-langkah di bagian Cara berbagi Klien OAuth.
  2. Izinkan client ID OAuth untuk akses terprogram bagi aplikasi.
  3. Dapatkan token ID untuk client ID yang diamankan IAP.
  4. Sertakan token ID dalam header Authorization: Bearer untuk membuat permintaan terautentikasi ke resource yang diamankan IAP.

Mengautentikasi dari aplikasi desktop

Bagian ini menjelaskan cara mengautentikasi akun pengguna dari command line desktop.

  1. Untuk mengizinkan developer mengakses aplikasi Anda dari command line, buat client ID OAuth 2.0 desktop atau bagikan client ID OAuth desktop yang sudah ada.
  2. Izinkan client ID OAuth untuk akses terprogram bagi aplikasi.

Login ke aplikasi

Setiap developer yang ingin mengakses aplikasi yang diamankan oleh IAP harus login terlebih dahulu. Anda dapat mengemas proses ini menjadi skrip, misalnya dengan menggunakan gcloud CLI. Berikut adalah contoh penggunaan curl untuk login dan menghasilkan token yang dapat digunakan untuk mengakses aplikasi:

  1. Login ke akun Anda yang memiliki akses ke resource Google Cloud.
  2. Memulai server lokal yang dapat menjalankan echo permintaan masuk.
        $ nc -k -l 4444
        
    CATATAN: Perintah ini menggunakan utilitas NetCat. Anda dapat menggunakan utilitas pilihan Anda.
  3. Buka URI berikut dengan DESKTOP_CLIENT_ID sebagai client ID Aplikasi desktop:

    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. Pada output server lokal, cari parameter permintaan. Anda akan melihat sesuatu seperti berikut: GET /?code=$CODE&scope=email%20openid%20https://www.googleapis.com/auth/userinfo.email&hd=google.com&prompt=consent HTTP/1.1 salin CODE untuk mengganti AUTH_CODE di bawah beserta client ID dan secret aplikasi Desktop:

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

    Kode ini menampilkan objek JSON dengan kolom id_token yang dapat Anda gunakan untuk mengakses aplikasi.

Mengakses aplikasi

Untuk mengakses aplikasi, gunakan id_token sebagai berikut:

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

Token Refresh

Anda dapat menggunakan token refresh yang dibuat selama alur login untuk mendapatkan token ID baru. Hal ini berguna ketika token ID asli kedaluwarsa. Setiap token ID berlaku selama sekitar satu jam. Selama waktu tersebut, Anda dapat membuat beberapa permintaan ke aplikasi tertentu.

Berikut adalah contoh penggunaan curl untuk menggunakan token refresh guna mendapatkan token ID baru. Pada contoh berikut, REFRESH_TOKEN adalah token dari alur login. DESKTOP_CLIENT_ID, dan DESKTOP_CLIENT_SECRET sama seperti yang digunakan dalam alur login:

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

Kode ini menampilkan objek JSON dengan kolom id_token baru yang dapat Anda gunakan untuk mengakses aplikasi.

Mengautentikasi dari akun layanan

Gunakan token OpenID Connect (OIDC) untuk mengautentikasi akun layanan ke resource yang diamankan IAP.

  1. Buat atau gunakan client ID OAuth 2.0 yang sudah ada. Untuk menggunakan client ID OAuth 2.0 yang ada, ikuti langkah-langkah dalam Cara berbagi Klien OAuth.
  2. Izinkan client ID OAuth untuk akses terprogram bagi aplikasi.

Anda juga harus menambahkan akun layanan ke daftar akses untuk project yang diamankan oleh IAP. Contoh kode berikut menunjukkan cara mendapatkan token OIDC. Anda harus menyertakan token dalam header Authorization: Bearer untuk membuat permintaan autentikasi ke resource yang diamankan IAP.

Mendapatkan token OIDC untuk akun layanan default

Jika ingin mendapatkan token OIDC untuk akun layanan default untuk Compute Engine, App Engine, atau Cloud Run, Anda dapat menggunakan contoh kode berikut untuk membuat token agar dapat mengakses resource yang diamankan oleh IAP:

C#


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

Untuk mengautentikasi ke IAP, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, baca Menyiapkan autentikasi untuk lingkungan pengembangan lokal.

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


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

/**
 * 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.request({url});
  console.info(res.data);
}

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

PHP

Untuk mengautentikasi ke IAP, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, baca Menyiapkan autentikasi untuk lingkungan pengembangan lokal.

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

Untuk mengautentikasi ke IAP, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, baca Menyiapkan autentikasi untuk lingkungan pengembangan lokal.

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

Untuk mengautentikasi ke IAP, siapkan Kredensial Default Aplikasi. Untuk mengetahui informasi selengkapnya, baca Menyiapkan autentikasi untuk lingkungan pengembangan lokal.

# 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

Mendapatkan token OIDC dari file kunci akun layanan lokal

Jika memiliki file kunci akun layanan, Anda dapat menyesuaikan contoh kode sebelumnya untuk menyediakan file kunci akun layanan.

Bash

  #!/usr/bin/env bash
  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}"

    local iam_scope="https://www.googleapis.com/auth/iam"
    local oauth_token_uri="https://www.googleapis.com/oauth2/v4/token"

    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')"
    local issued_at="$(date +%s)"
    local expires_at="$((issued_at + 600))"
    local header="{'alg':'RS256','typ':'JWT','kid':'${private_key_id}'}"
    local header_base64="$(echo "${header}" | base64)"
    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)"
    local signature_base64="$(printf %s "${header_base64}.${payload_base64}" | openssl dgst -binary -sha256 -sign <(printf '%s\n' "${private_key}")  | base64)"
    local assertion="${header_base64}.${payload_base64}.${signature_base64}"
    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)"
    local bearer_id_token="$(echo "${token_payload}" | jq -r '.id_token')"
    echo "${bearer_id_token}"
  }

  main(){
    # TODO: Replace the following variables:
    SERVICE_ACCOUNT_KEY="service_account_key_file_path"
    IAP_CLIENT_ID="iap_client_id"
    URL="application_url"

    # Obtain the ID token.
    ID_TOKEN=$(get_token "${SERVICE_ACCOUNT_KEY}" "${IAP_CLIENT_ID}")
    # Access the application with the ID token.
    curl --header "Authorization: Bearer ${ID_TOKEN}" "${URL}"
  }

  main "$@"

Mendapatkan token OIDC dalam semua kasus lainnya

Dalam kasus lainnya, gunakan API kredensial IAM untuk membuat token OIDC dengan meniru identitas akun layanan target tepat sebelum mengakses resource yang diamankan IAP. Proses ini melibatkan langkah-langkah berikut:

  1. Berikan akun layanan panggilan (akun layanan yang terkait dengan kode yang mendapatkan token ID) dengan peran Service Account OpenID Connect Identity Token Creator (roles/iam.serviceAccountOpenIdTokenCreator).

    Hal ini memungkinkan akun layanan pemanggil untuk meniru akun layanan target.

  2. Gunakan kredensial yang diberikan oleh akun layanan panggilan untuk memanggil metode generateIdToken pada akun layanan target.

    Tetapkan kolom audience ke client ID.

Untuk petunjuk langkah demi langkah, lihat Membuat token ID.

Mengautentikasi dari Header Otorisasi Proxy

Jika aplikasi Anda menggunakan header permintaan Authorization, Anda dapat menyertakan token ID dalam header Proxy-Authorization: Bearer. Jika token ID yang valid ditemukan di header Proxy-Authorization, IAP akan mengizinkan permintaan tersebut bersama token tersebut. Setelah memberi otorisasi permintaan, IAP meneruskan header Authorization ke aplikasi Anda tanpa memproses konten.

Jika tidak ada token ID yang valid di header Proxy-Authorization, IAP akan terus memproses header Authorization dan menghapus header Proxy-Authorization sebelum meneruskan permintaan tersebut ke aplikasi Anda.

Langkah selanjutnya