Présentation de l'API App Identity pour Java 8

ID de la région

Le REGION_ID est un code abrégé que Google attribue en fonction de la région que vous sélectionnez lors de la création de votre application. Le code ne correspond pas à un pays ou une province, même si certains ID de région peuvent ressembler aux codes de pays et de province couramment utilisés. L'ajout de REGION_ID.r dans les URL App Engine est facultatif pour les applications existantes. Il sera bientôt obligatoire pour toutes les applications nouvelles.

Pour assurer une transition en douceur, nous mettons lentement à jour App Engine afin d'utiliser les ID de région. Si nous n'avons pas encore mis à jour votre projet Google Cloud, vous ne verrez pas d'ID de région pour votre application. Étant donné que l'ID est facultatif pour les applications existantes, vous n'avez pas besoin de mettre à jour les URL ni d'effectuer d'autres modifications une fois l'ID de région disponible pour vos applications existantes.

En savoir plus sur les ID de région

L'API App Identity permet à une application de découvrir son ID application (également appelé ID du projet). Cet ID permet à l'application App Engine de valider son identité auprès d'autres applications App Engine, d'API Google et d'applications et services tiers. L'ID application peut également être utilisé pour générer une URL ou une adresse e-mail, ou pour prendre une décision d'exécution.

Obtenir l'ID du projet

L'ID du projet peut être trouvé à l'aide de la méthode "ApiProxy.getCurrentEnvironment().GetAppId()".

Obtenir le nom d'hôte de l'application

Par défaut, les applications App Engine sont diffusées à partir d'URL au format https://PROJECT_ID.REGION_ID.r.appspot.com, où l'ID du projet fait partie du nom d'hôte. Si une application est diffusée à partir d'un domaine personnalisé, il peut être nécessaire de récupérer l'intégralité du nom d'hôte. Pour ce faire, utilisez l'attribut com.google.appengine.runtime.default_version_hostname de 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"));
}

Valider l'identité auprès d'autres applications App Engine

Si vous souhaitez déterminer l'identité de l'application App Engine qui envoie une requête à votre application App Engine, vous pouvez utiliser l'en-tête de requête X-Appengine-Inbound-Appid. Cet en-tête est ajouté à la requête par le service URLFetch et n'est pas modifiable par l'utilisateur. Sa présence indique donc avec certitude l'ID de projet de l'application à l'origine de la requête, s'il existe.

Exigences :

  • Seuls les appels passés au domaine appspot.com de votre application contiennent l'en-tête X-Appengine-Inbound-Appid. Les appels aux domaines personnalisés ne contiennent pas cet en-tête.
  • Les requêtes doivent être configurées pour ne pas suivre les redirections. Si vous utilisez la classe URLFetchService, votre application doit spécifier doNotFollowRedirect. Les applications exécutées sur l'environnement d'exécution Java 8 n'utilisent pas le service URLFetch par défaut. Pour activer URLFetch, suivez ces instructions.
  • Si votre application utilise java.net, mettez à jour votre code pour ne pas suivre les redirections :
    connection.setInstanceFollowRedirects(false);

Dans votre gestionnaire d'applications, vous pouvez vérifier l'ID entrant en lisant l'en-tête X-Appengine-Inbound-Appid et en le comparant à une liste d'ID autorisés à effectuer des requêtes.

Valider l'identité auprès des API Google

Les API Google utilisent le protocole OAuth 2.0 pour l'authentification et l'autorisation. L'API App Identity peut créer des jetons OAuth qui permettent de confirmer que la source d'une requête est l'application elle-même. La méthode getAccessToken() renvoie un jeton d'accès pour un champ d'application ou une liste de champs d'application. Ce jeton peut ensuite être défini dans les en-têtes HTTP d'un appel pour identifier l'application appelante.

L'exemple suivant montre comment utiliser l'API App Identity pour passer un appel REST à l'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<String>();
  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()));
    }
  }
}

Notez que l'identité de l'application est représentée par le nom du compte de service, qui est généralement applicationid@appspot.gserviceaccount.com. Vous pouvez obtenir la valeur exacte en utilisant la méthode getServiceAccountName(). Pour les services qui offrent des LCA, vous pouvez accorder l'accès à l'application en accordant l'accès à ce compte.

Valider l'identité auprès de services tiers

Le jeton généré par getAccessToken() ne fonctionne qu'avec les services Google. Vous pouvez toutefois utiliser la technologie de signature sous-jacente pour valider l'identité de votre application auprès d'autres services. La méthode signForApp() signe les octets à l'aide d'une clé privée propre à votre application. La méthode getPublicCertificatesForApp() renvoie des certificats permettant de valider la signature.

Voici un exemple de procédure à suivre pour signer un blob et valider sa signature :
// 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));
}

Obtenir le nom du bucket Cloud Storage par défaut

Chaque application peut être associée à un bucket Cloud Storage par défaut, qui comprend 5 Go de stockage gratuit et un quota gratuit pour les opérations d'E/S.

Pour obtenir le nom du bucket par défaut, vous pouvez vous servir de l'API App Identity. Appelez AppIdentityService.getDefaultGcsBucketName.