Autenticação programática

Nesta página, você aprenderá como fazer a autenticação em um recurso protegido pelo Identity-Aware Proxy (IAP) usando uma conta de usuário ou uma conta de serviço.

  • A conta de usuário pertence a um usuário individual. Ela é usada na autenticação quando o aplicativo precisa acessar recursos protegidos pelo IAP em nome de um usuário. Leia sobre as credenciais de conta de usuário.
  • A conta de serviço pertence a um aplicativo, e não a um usuário individual. Ela é usada na autenticação quando você quer permitir que um aplicativo acesse seus recursos protegidos pelo IAP. Conheça as noções básicas sobre contas de serviço.

Antes de começar

Antes de começar, você precisará de:

  • um aplicativo protegido pelo IAP a que você quer se conectar de maneira programática usando uma conta de desenvolvedor, uma conta de serviço ou credenciais de app para dispositivos móveis.

Como autenticar uma conta de usuário

É possível liberar o acesso de usuários a seu aplicativo por meio de um app para computador ou dispositivos móveis para permitir que um programa interaja com um recurso protegido pelo IAP.

Como autenticar usando um app para dispositivos móveis

  1. Crie um ID do cliente OAuth 2.0 para seu app para dispositivos móveis no mesmo projeto em que está o recurso protegido pelo IAP:
    1. Acesse a página "Credenciais".
      Acessar a página "Credenciais"
    2. Selecione o projeto com o recurso protegido pelo IAP.
    3. Clique em Criar credenciais e selecione ID do cliente OAuth.
    4. Selecione o Tipo de aplicativo para que você quer criar credenciais.
    5. Inclua as informações de Nome e Restrições, se apropriado, e clique em Criar.
  2. Uma janela Cliente OAuth será exibida. Anote o ID do cliente do recurso protegido pelo IAP a que você quer se conectar.
  3. Consiga um token para o ID do cliente protegido pelo IAP:
  4. Inclua o token de ID em um cabeçalho Authorization: Bearer para fazer a solicitação autenticada ao recurso protegido pelo IAP.

Como autenticar usando um app para computador

Esta seção descreve como autenticar uma conta de usuário de uma linha de comando de um desktop.

Como configurar o ID do cliente

Para permitir que os desenvolvedores acessem seu aplicativo a partir da linha de comando, primeiro é necessário criar credenciais de ID do cliente OAuth do tipo Outro:

  1. Acesse a página "Credenciais".
    Acessar a página "Credenciais"
  2. Selecione o projeto com o recurso protegido pelo IAP.
  3. Clique em Criar credenciais e selecione ID do cliente OAuth.
  4. Em Tipo de aplicativo, selecione Outro, adicione um Nome e clique em Criar.
  5. Na janela Cliente OAuth que é exibida, anote o ID do cliente e a chave secreta do cliente. Você precisará usá-los em um script para gerenciar credenciais ou compartilhar com seus desenvolvedores.
  6. Na janela Credenciais, as novas credenciais Outros aparecem junto com o ID do cliente principal que é usado para acessar o aplicativo.

Como fazer login no aplicativo

Cada desenvolvedor que quiser acessar um app protegido pelo IAP precisará fazer login nele. É possível empacotar o processo em um script, por exemplo, usando o SDK do Cloud. Veja abaixo um exemplo que usa a curl para fazer login e gerar um token que pode ser usado para acessar o aplicativo:

  1. Faça login na conta que tem acesso ao recurso do Google Cloud.
  2. Acesse o URI a seguir, em que OTHER_CLIENT_ID é o ID do cliente do tipo Outro que você criou acima:

    https://accounts.google.com/o/oauth2/v2/auth?client_id=OTHER_CLIENT_ID&response_type=code&scope=openid%20email&access_type=offline&redirect_uri=urn:ietf:wg:oauth:2.0:oob

  3. Será exibida uma janela. Anote o Código de autorização, que você usará para substituir o valor de AUTH_CODE abaixo, assim como o ID do cliente do tipo Outro e a chave secreta que você criou acima:

    curl --verbose \
          --data client_id=OTHER_CLIENT_ID \
          --data client_secret=OTHER_CLIENT_SECRET \
          --data code=AUTH_CODE \
          --data redirect_uri=urn:ietf:wg:oauth:2.0:oob \
          --data grant_type=authorization_code \
          https://oauth2.googleapis.com/token

    Esse código retorna um objeto JSON com um campo refresh_token. Salve essa informação como um token de login para acessar o aplicativo.

Como acessar o aplicativo

Para acessar o aplicativo, troque o valor de refresh_token gerado durante o fluxo de login por um token de ID. O token de ID permanece válido por cerca de uma hora. Durante esse período, é possível fazer várias solicitações a um app específico. Veja abaixo um exemplo de como usar a curl para utilizar o token e acessar o aplicativo:

  1. Use o código abaixo, em que REFRESH_TOKEN é o token do fluxo de login, IAP_CLIENT_ID é o ID do cliente principal usado para acessar o aplicativo e OTHER_CLIENT_ID e OTHER_CLIENT_SECRET são o ID e a chave secreta do cliente que você criou quando configurou o ID do cliente acima:

    curl --verbose \
          --data client_id=OTHER_CLIENT_ID \
          --data client_secret=OTHER_CLIENT_SECRET \
          --data refresh_token=REFRESH_TOKEN \
          --data grant_type=refresh_token \
          --data audience=IAP_CLIENT_ID \
          https://oauth2.googleapis.com/token

    Esse código retorna um objeto JSON com um campo id_token, que pode ser usado para acessar o app.

  2. Para acessar o app, use id_token da seguinte maneira:

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

Como autenticar uma conta de serviço

Use um token do OpenID Connect (OIDC) para autenticar uma conta de serviço em um recurso protegido pelo IAP.

  1. Inclua a conta de serviço na lista de acesso do projeto protegido pelo IAP.
  2. Gere um token de acesso baseado em JWT. Ele usa uma declaração target_audience extra que requer um ID do cliente. Para encontrar seu ID do cliente, siga as etapas abaixo:

    1. Acesse a página do IAP.
    2. Encontre o recurso que você quer acessar e clique em Mais > Editar cliente OAuth.
      Edite o cliente OAuth no menu

    3. Anote o ID do cliente na página Credenciais que é exibida.

  3. Solicite um token do OIDC para o ID do cliente protegido pelo IAP.

  4. Inclua esse token em um cabeçalho Authorization: Bearer para fazer a solicitação autenticada ao recurso protegido pelo IAP.

Na amostra de código a seguir, a conta de serviço padrão é autenticada em um recurso protegido pelo IAP:

C#


using System;
using System.Collections.Generic;
using System.Globalization;
using System.IdentityModel.Tokens.Jwt;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Threading;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Requests;
using Google.Apis.Json;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;

namespace GoogleCloudSamples
{
    class IAPClient
    {
        /// <summary>
        /// Authenticates using the client id and credentials, then fetches
        /// the uri.
        /// </summary>
        /// <param name="iapClientId">The client id observed on
        /// https://console.cloud.google.com/apis/credentials.</param>
        /// <param name="credentialsFilePath">Path to the credentials .json file
        /// download from https://console.cloud.google.com/apis/credentials.
        /// </param>
        /// <param name="uri">HTTP uri to fetch.</param>
        /// <returns>The http response body as a string.</returns>
        public static string InvokeRequest(string iapClientId,
            string credentialsFilePath, string uri)
        {
            // Read credentials from the credentials .json file.
            ServiceAccountCredential saCredential;
            using (var fs = new FileStream(credentialsFilePath,
                FileMode.Open, FileAccess.Read))
            {
                saCredential = ServiceAccountCredential
                    .FromServiceAccountData(fs);
            }

            // Generate a JWT signed with the service account's private key
            // containing a special "target_audience" claim.
            var jwtBasedAccessToken =
                CreateAccessToken(saCredential, iapClientId);

            // Request an OIDC token for the Cloud IAP-secured client ID.
            var req = new GoogleAssertionTokenRequest()
            {
                Assertion = jwtBasedAccessToken
            };
            var result = req.ExecuteAsync(saCredential.HttpClient,
                saCredential.TokenServerUrl, CancellationToken.None,
                saCredential.Clock).Result;
            string token = result.IdToken;

            // Include the OIDC token in an Authorization: Bearer header to
            // IAP-secured resource
            var httpClient = new HttpClient();
            httpClient.DefaultRequestHeaders.Authorization =
                new AuthenticationHeaderValue("Bearer", token);
            string response = httpClient.GetStringAsync(uri).Result;
            return response;
        }

        /// <summary>
        /// Generate a JWT signed with the service account's private key
        /// containing a special "target_audience" claim.
        /// </summary>
        /// <param name="privateKey">The private key string pulled from
        /// a credentials .json file.</param>
        /// <param name="iapClientId">The client id observed on
        /// https://console.cloud.google.com/apis/credentials.</param>
        /// <param name="email">The e-mail address associated with the
        /// privateKey.</param>
        /// <returns>An access token.</returns>
        static string CreateAccessToken(ServiceAccountCredential saCredential,
            string iapClientId)
        {
            var now = saCredential.Clock.UtcNow;
            var currentTime = ToUnixEpochDate(now);
            var expTime = ToUnixEpochDate(now.AddHours(1));

            var claims = new[]
            {
                new Claim(JwtRegisteredClaimNames.Aud,
                    GoogleAuthConsts.OidcTokenUrl),
                new Claim(JwtRegisteredClaimNames.Sub, saCredential.Id),
                new Claim(JwtRegisteredClaimNames.Iat, currentTime.ToString()),
                new Claim(JwtRegisteredClaimNames.Exp, expTime.ToString()),
                new Claim(JwtRegisteredClaimNames.Iss, saCredential.Id),

                // We need to generate a JWT signed with the service account's
                // private key containing a special "target_audience" claim.
                // That claim should contain the clientId of IAP we eventually
                // want to access.
                new Claim("target_audience", iapClientId)
            };

            // Encryption algorithm must be RSA SHA-256, according to
            // https://developers.google.com/identity/protocols/OAuth2ServiceAccount
            var signingCredentials = new SigningCredentials(
                new RsaSecurityKey(saCredential.Key),
                SecurityAlgorithms.RsaSha256);
            var token = new JwtSecurityToken(
                claims: claims,
                signingCredentials: signingCredentials);
            return new JwtSecurityTokenHandler().WriteToken(token);
        }

        static long ToUnixEpochDate(DateTime date)
              => (long)Math.Round((date.ToUniversalTime() -
                                   new DateTimeOffset(1970, 1, 1, 0, 0, 0,
                                        TimeSpan.Zero)).TotalSeconds);
    }
}

Python

import google.auth
import google.auth.app_engine
import google.auth.compute_engine.credentials
import google.auth.iam
from google.auth.transport.requests import Request
import google.oauth2.credentials
import google.oauth2.service_account
import requests
import requests_toolbelt.adapters.appengine

IAM_SCOPE = 'https://www.googleapis.com/auth/iam'
OAUTH_TOKEN_URI = 'https://www.googleapis.com/oauth2/v4/token'

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

    # Figure out what environment we're running in and get some preliminary
    # information about the service account.
    bootstrap_credentials, _ = google.auth.default(
        scopes=[IAM_SCOPE])
    if isinstance(bootstrap_credentials,
                  google.oauth2.credentials.Credentials):
        raise Exception('make_iap_request is only supported for service '
                        'accounts.')
    elif isinstance(bootstrap_credentials,
                    google.auth.app_engine.Credentials):
        requests_toolbelt.adapters.appengine.monkeypatch()

    # For service account's using the Compute Engine metadata service,
    # service_account_email isn't available until refresh is called.
    bootstrap_credentials.refresh(Request())

    signer_email = bootstrap_credentials.service_account_email
    if isinstance(bootstrap_credentials,
                  google.auth.compute_engine.credentials.Credentials):
        # Since the Compute Engine metadata service doesn't expose the service
        # account key, we use the IAM signBlob API to sign instead.
        # In order for this to work:
        #
        # 1. Your VM needs the https://www.googleapis.com/auth/iam scope.
        #    You can specify this specific scope when creating a VM
        #    through the API or gcloud. When using Cloud Console,
        #    you'll need to specify the "full access to all Cloud APIs"
        #    scope. A VM's scopes can only be specified at creation time.
        #
        # 2. The VM's default service account needs the "Service Account Actor"
        #    role. This can be found under the "Project" category in Cloud
        #    Console, or roles/iam.serviceAccountActor in gcloud.
        signer = google.auth.iam.Signer(
            Request(), bootstrap_credentials, signer_email)
    else:
        # A Signer object can sign a JWT using the service account's key.
        signer = bootstrap_credentials.signer

    # Construct OAuth 2.0 service account credentials using the signer
    # and email acquired from the bootstrap credentials.
    service_account_credentials = google.oauth2.service_account.Credentials(
        signer, signer_email, token_uri=OAUTH_TOKEN_URI, additional_claims={
            'target_audience': client_id
        })

    # service_account_credentials gives us a JWT signed by the service
    # account. Next, we use that to obtain an OpenID Connect token,
    # which is a JWT signed by Google.
    google_open_id_connect_token = get_google_open_id_connect_token(
        service_account_credentials)

    # 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(
            google_open_id_connect_token)}, **kwargs)
    if resp.status_code == 403:
        raise Exception('Service account {} does not have permission to '
                        'access the IAP-protected application.'.format(
                            signer_email))
    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

def get_google_open_id_connect_token(service_account_credentials):
    """Get an OpenID Connect token issued by Google for the service account.

    This function:

      1. Generates a JWT signed with the service account's private key
         containing a special "target_audience" claim.

      2. Sends it to the OAUTH_TOKEN_URI endpoint. Because the JWT in #1
         has a target_audience claim, that endpoint will respond with
         an OpenID Connect token for the service account -- in other words,
         a JWT signed by *Google*. The aud claim in this JWT will be
         set to the value from the target_audience claim in #1.

    For more information, see
    https://developers.google.com/identity/protocols/OAuth2ServiceAccount .
    The HTTP/REST example on that page describes the JWT structure and
    demonstrates how to call the token endpoint. (The example on that page
    shows how to get an OAuth2 access token; this code is using a
    modified version of it to get an OpenID Connect token.)
    """

    service_account_jwt = (
        service_account_credentials._make_authorization_grant_assertion())
    request = google.auth.transport.requests.Request()
    body = {
        'assertion': service_account_jwt,
        'grant_type': google.oauth2._client._JWT_GRANT_TYPE,
    }
    token_response = google.oauth2._client._token_endpoint_request(
        request, OAUTH_TOKEN_URI, body)
    return token_response['id_token']

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 java.time.Clock;
import java.util.Collections;

public class BuildIapRequest {
  private static final String IAM_SCOPE = "https://www.googleapis.com/auth/iam";
  private static final String OAUTH_TOKEN_URI = "https://www.googleapis.com/oauth2/v4/token";
  private static final String JWT_BEARER_TOKEN_GRANT_TYPE =
      "urn:ietf:params:oauth:grant-type:jwt-bearer";
  private static final long EXPIRATION_TIME_IN_SECONDS = 3600L;

  private static final HttpTransport httpTransport = new NetHttpTransport();

  private static Clock clock = Clock.systemUTC();

  private BuildIapRequest() {}

  private static IdTokenProvider getIdTokenProvider() throws Exception {
    GoogleCredentials credentials =
        GoogleCredentials.getApplicationDefault().createScoped(Collections.singleton(IAM_SCOPE));
    // service account credentials are required to sign the jwt token
    if (credentials == null || !(credentials instanceof IdTokenProvider)) {
      throw new Exception("Google credentials : credentials that can provide id tokens expected");
    }
    return (IdTokenProvider) credentials;
  }

  /**
   * Clone request and add an IAP Bearer Authorization header with signed JWT 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 signed jwt token.
   * @throws Exception exception creating signed JWT
   */
  public static HttpRequest buildIapRequest(HttpRequest request, String iapClientId)
      throws Exception {

    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());
  }
}

PHP

namespace Google\Cloud\Samples\Iap;

# Imports Auth libraries and Guzzle HTTP libraries.
use Google\Auth\OAuth2;
use Google\Auth\Middleware\ScopedAccessTokenMiddleware;
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.
 *
 * @return The response body.
 */
function make_iap_request($url, $clientId, $pathToServiceAccount)
{
    $serviceAccountKey = json_decode(file_get_contents($pathToServiceAccount), true);
    $oauth_token_uri = 'https://www.googleapis.com/oauth2/v4/token';
    $iam_scope = 'https://www.googleapis.com/auth/iam';

    # Create an OAuth object using the service account key
    $oauth = new OAuth2([
        'audience' => $oauth_token_uri,
        'issuer' => $serviceAccountKey['client_email'],
        'signingAlgorithm' => 'RS256',
        'signingKey' => $serviceAccountKey['private_key'],
        'tokenCredentialUri' => $oauth_token_uri,
    ]);
    $oauth->setGrantType(OAuth2::JWT_URN);
    $oauth->setAdditionalClaims(['target_audience' => $clientId]);

    # Obtain an OpenID Connect token, which is a JWT signed by Google.
    $token = $oauth->fetchAuthToken();
    $idToken = $oauth->getIdToken();

    # Construct a ScopedAccessTokenMiddleware with the ID token.
    $middleware = new ScopedAccessTokenMiddleware(
        function () use ($idToken) {
            return $idToken;
        },
        $iam_scope
    );

    $stack = HandlerStack::create();
    $stack->push($middleware);

    # Create an HTTP Client using Guzzle and pass in the credentials.
    $http_client = new Client([
        'handler' => $stack,
        'base_uri' => $url,
        'auth' => 'scoped'
    ]);

    # Make an authenticated HTTP Request
    $response = $http_client->request('GET', '/', []);
    return $response;
}

A seguir