API App Identity para serviços agrupados antigos

ID da região

O REGION_ID é um código abreviado que a Google atribui com base na região que seleciona quando cria a sua app. O código não corresponde a um país ou uma província, embora alguns IDs de regiões possam parecer semelhantes aos códigos de países e províncias usados frequentemente. Para apps criadas após fevereiro de 2020, REGION_ID.r está incluído nos URLs do App Engine. Para apps existentes criadas antes desta data, o ID da região é opcional no URL.

Saiba mais acerca dos IDs de regiões.

A API App Identity permite que uma aplicação descubra o respetivo ID da aplicação (também denominado ID do projeto). Usando o ID, uma aplicação do App Engine pode afirmar a sua identidade a outras apps do App Engine, APIs Google e aplicações e serviços de terceiros. O ID da aplicação também pode ser usado para gerar um URL ou um endereço de email, ou para tomar uma decisão em tempo de execução.

Obter o ID do projeto

Pode encontrar o ID do projeto através do método ApiProxy.getCurrentEnvironment().getAppId().

Obter o nome do anfitrião da aplicação

Por predefinição, as apps do App Engine são publicadas a partir de URLs no formato https://PROJECT_ID.REGION_ID.r.appspot.com, em que o ID do projeto faz parte do nome de anfitrião. Se uma app for publicada a partir de um domínio personalizado, pode ser necessário obter o componente de nome de anfitrião completo. Pode fazê-lo através do atributo com.google.appengine.runtime.default_version_hostname do elemento CurrentEnvironment.

@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
  resp.setContentType("text/plain");
  ApiProxy.Environment env = ApiProxy.getCurrentEnvironment();
  resp.getWriter().print("default_version_hostname: ");
  resp.getWriter()
      .println(env.getAttributes().get("com.google.appengine.runtime.default_version_hostname"));
}

Afirmar a identidade a outras apps do App Engine

Se quiser determinar a identidade da app do App Engine que está a fazer um pedido à sua app do App Engine, pode usar o cabeçalho do pedido X-Appengine-Inbound-Appid. Este cabeçalho é adicionado ao pedido pelo serviço URLFetch e não é modificável pelo utilizador, pelo que indica com segurança o ID do projeto da aplicação que está a fazer o pedido, se estiver presente.

Requisitos:

  • Apenas as chamadas feitas para o domínio appspot.com da sua app vão conter o cabeçalho X-Appengine-Inbound-Appid. As chamadas para domínios personalizados não contêm o cabeçalho.
  • Os seus pedidos têm de estar definidos para não seguirem redirecionamentos. Se usar a classe URLFetchService, a sua app tem de especificar doNotFollowRedirect. As apps executadas no tempo de execução do Java 8 não usam o serviço URLFetch por predefinição. Para ativar o URLFetch siga estas instruções.
  • Se a sua app usar java.net, atualize o código para não seguir redirecionamentos:
    connection.setInstanceFollowRedirects(false);

No controlador da aplicação, pode verificar o ID recebido lendo o cabeçalho X-Appengine-Inbound-Appid e comparando-o com uma lista de IDs autorizados a fazer pedidos.

Afirmar a identidade às APIs Google

As APIs Google usam o protocolo OAuth 2.0 para autenticação e autorização. A API App Identity pode criar tokens OAuth que podem ser usados para afirmar que a origem de um pedido é a própria aplicação. O método getAccessToken() devolve um token de acesso para um âmbito ou uma lista de âmbitos. Este token pode ser definido nos cabeçalhos HTTP de uma chamada para identificar a aplicação de chamada.

O exemplo seguinte mostra como usar a API App Identity para fazer uma chamada REST para a API Google URL Shortener.

/**
 * Returns a shortened URL by calling the Google URL Shortener API.
 *
 * <p>Note: Error handling elided for simplicity.
 */
public String createShortUrl(String longUrl) throws Exception {
  ArrayList<String> scopes = new ArrayList<>();
  scopes.add("https://www.googleapis.com/auth/urlshortener");
  final AppIdentityService appIdentity = AppIdentityServiceFactory.getAppIdentityService();
  final AppIdentityService.GetAccessTokenResult accessToken = appIdentity.getAccessToken(scopes);
  // The token asserts the identity reported by appIdentity.getServiceAccountName()
  JSONObject request = new JSONObject();
  request.put("longUrl", longUrl);

  URL url = new URL("https://www.googleapis.com/urlshortener/v1/url?pp=1");
  HttpURLConnection connection = (HttpURLConnection) url.openConnection();
  connection.setDoOutput(true);
  connection.setRequestMethod("POST");
  connection.addRequestProperty("Content-Type", "application/json");
  connection.addRequestProperty("Authorization", "Bearer " + accessToken.getAccessToken());

  OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
  request.write(writer);
  writer.close();

  if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
    // Note: Should check the content-encoding.
    //       Any JSON parser can be used; this one is used for illustrative purposes.
    JSONTokener responseTokens = new JSONTokener(connection.getInputStream());
    JSONObject response = new JSONObject(responseTokens);
    return (String) response.get("id");
  } else {
    try (InputStream s = connection.getErrorStream();
        InputStreamReader r = new InputStreamReader(s, StandardCharsets.UTF_8)) {
      throw new RuntimeException(
          String.format(
              "got error (%d) response %s from %s",
              connection.getResponseCode(), CharStreams.toString(r), connection.toString()));
    }
  }
}

Tenha em atenção que a identidade da aplicação é representada pelo nome da conta de serviço, que é normalmente applicationid@appspot.gserviceaccount.com. Pode obter o valor exato através do método getServiceAccountName(). Para serviços que oferecem ACLs, pode conceder acesso à aplicação concedendo acesso a esta conta.

Afirmar a identidade a serviços de terceiros

O token gerado por getAccessToken() só funciona com os serviços Google. No entanto, pode usar a tecnologia de assinatura subjacente para afirmar a identidade da sua aplicação a outros serviços. O método signForApp() assina bytes através de uma chave privada exclusiva da sua aplicação, e o método getPublicCertificatesForApp() devolve certificados que podem ser usados para validar a assinatura.

Segue-se um exemplo que mostra como assinar um blob e validar a respetiva assinatura:
// Note that the algorithm used by AppIdentity.signForApp() and
// getPublicCertificatesForApp() is "SHA256withRSA"

private byte[] signBlob(byte[] blob) {
  AppIdentityService.SigningResult result = appIdentity.signForApp(blob);
  return result.getSignature();
}

private byte[] getPublicCertificate() throws UnsupportedEncodingException {
  Collection<PublicCertificate> certs = appIdentity.getPublicCertificatesForApp();
  PublicCertificate publicCert = certs.iterator().next();
  return publicCert.getX509CertificateInPemFormat().getBytes("UTF-8");
}

private Certificate parsePublicCertificate(byte[] publicCert)
    throws CertificateException, NoSuchAlgorithmException {
  InputStream stream = new ByteArrayInputStream(publicCert);
  CertificateFactory cf = CertificateFactory.getInstance("X.509");
  return cf.generateCertificate(stream);
}

private boolean verifySignature(byte[] blob, byte[] blobSignature, PublicKey pk)
    throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
  Signature signature = Signature.getInstance("SHA256withRSA");
  signature.initVerify(pk);
  signature.update(blob);
  return signature.verify(blobSignature);
}

private String simulateIdentityAssertion()
    throws CertificateException, UnsupportedEncodingException, NoSuchAlgorithmException,
    InvalidKeyException, SignatureException {
  // Simulate the sending app.
  String message = "abcdefg " + Calendar.getInstance().getTime().toString();
  byte[] blob = message.getBytes();
  byte[] blobSignature = signBlob(blob);
  byte[] publicCert = getPublicCertificate();

  // Simulate the receiving app, which gets the certificate, blob, and signature.
  Certificate cert = parsePublicCertificate(publicCert);
  PublicKey pk = cert.getPublicKey();
  boolean isValid = verifySignature(blob, blobSignature, pk);

  return String.format(
      "isValid=%b for message: %s\n\tsignature: %s\n\tpublic cert: %s",
      isValid, message, Arrays.toString(blobSignature), Arrays.toString(publicCert));
}

Obter o nome do contentor do Cloud Storage predefinido

Cada aplicação pode ter um contentor do Cloud Storage predefinido, que inclui 5 GB de armazenamento gratuito e uma quota gratuita para operações de I/O.

Para obter o nome do contentor predefinido, pode usar a API App Identity. Chame AppIdentityService.getDefaultGcsBucketName.