Не удалось получить токен с помощью API Google [google-oauth-java-client-1.12.0-beta] для потока сервисного аккаунта.

Я использую API Google (версия google-oauth-java-client-1.12.0-beta), чтобы получить токен доступа OAuth2, но получил «invalid_grant». Ссылка: https://developers.google.com/accounts/docs/OAuth2ServiceAccount

Вот код:

import com.google.api.client.auth.jsontoken.JsonWebSignature;
import com.google.api.client.auth.jsontoken.JsonWebToken;
import com.google.api.client.auth.jsontoken.RsaSHA256Signer;
import com.google.api.client.auth.oauth2.TokenRequest;
import com.google.api.client.auth.oauth2.TokenResponse;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.Clock;

import java.io.FileInputStream;
import java.io.IOException;

import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;


public class TestClient
{
  private static PrivateKey getPrivateKey(String keyFile, String alias, String password)
    throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException
  {
    KeyStore keystore = KeyStore.getInstance("PKCS12");
    keystore.load(new FileInputStream(keyFile), password.toCharArray());
    PrivateKey privateKey = (PrivateKey) keystore.getKey(alias, password.toCharArray());

    return privateKey;
  }

  public static void main(String[] args)
    throws GeneralSecurityException, IOException
  {
    String password = "notasecret";
    String alias = "privatekey";
    String keyFile = "<private key file>.p12";
    String serviceAccountScopes = "https://www.googleapis.com/auth/urlshortener";
    String serviceAccountUser = "[email protected]";
    String serviceAccountId = "<a/c id>.apps.googleusercontent.com";
    JsonWebSignature.Header header = new JsonWebSignature.Header();
    header.setAlgorithm("RS256");
    header.setType("JWT"); 

    JsonWebToken.Payload payload = new JsonWebToken.Payload(Clock.SYSTEM);
    long currentTime = Clock.SYSTEM.currentTimeMillis();
    payload.setIssuer(serviceAccountId)
       .setAudience("https://accounts.google.com/o/oauth2/token")
       .setIssuedAtTimeSeconds(currentTime / 1000)
       .setExpirationTimeSeconds(currentTime / 1000 + 3600)
       .setPrincipal(serviceAccountUser);
    payload.put("scope", serviceAccountScopes); 
    System.out.println(payload.toPrettyString());

    PrivateKey serviceAccountPrivateKey = getPrivateKey(keyFile, alias, password);
    String assertion = RsaSHA256Signer.sign(serviceAccountPrivateKey, getJsonFactory(), header, payload);     
    TokenRequest request = new TokenRequest(getTransport(), getJsonFactory(), new GenericUrl(getTokenServerEncodedUrl()), "assertion");     

    request.put("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer");     
    request.put("assertion", assertion);     
    TokenResponse resp = request.execute();    
    System.out.println("token : " + resp.getAccessToken());
  }

  private static String getTokenServerEncodedUrl()
  {
    return "https://accounts.google.com/o/oauth2/token";
  }

  private static JsonFactory getJsonFactory()
  {
    return new JacksonFactory();
  }

  private static HttpTransport getTransport()
  {
    return new NetHttpTransport();
  }
}

Результат:

Exception in thread "main" com.google.api.client.auth.oauth2.TokenResponseException: 400 Bad Request
{
  "error" : "invalid_grant"
}
    at com.google.api.client.auth.oauth2.TokenResponseException.from(TokenResponseException.java:103)
    at com.google.api.client.auth.oauth2.TokenRequest.executeUnparsed(TokenRequest.java:303)
    at com.google.api.client.auth.oauth2.TokenRequest.execute(TokenRequest.java:323)

В чем проблема? любой намек будет оценен.


person dan22    schedule 07.01.2013    source источник


Ответы (1)


При использовании учетных записей служб в службах Google и клиентской библиотеке API Google вам не нужно создавать подпись и конструировать токен JWT самостоятельно, поскольку существуют служебные классы, которые можно использовать для чтения, чтобы просто выполнять авторизацию учетных записей служб через OAuth 2.0.

Однако это не очень хорошо документировано, за исключением документации Google Диска, которая содержит подробные объяснения и примеры кода на нескольких языках программирования. Вам следует прочитать: https://developers.google.com/drive/service-accounts#google_apis_console_project_service_accounts.

Некоторые проблемы с вашим кодом:

  • Идентификатор учетной записи службы должен быть в виде: <some-id>@developer.gserviceaccount.com (да, адрес электронной почты вместо идентификатора клиента, я знаю, что это странно)
  • Вы устанавливаете principal только при выполнении Делегирования домена Служб Google, но, конечно, вы не можете сделать это в аккаунтах Gmail, только в учетных записях Google Apps, к домену которых вам предоставил доступ администратор, поэтому в вашем случае: не устанавливайте его.

Ниже приведен пример кода для OAuth 2.0 с учетными записями службы в Java.

Примечание. Вам также потребуется загрузить библиотеку сокращения URL.

/** Email of the Service Account */
private static final String SERVICE_ACCOUNT_EMAIL = "<some-id>@developer.gserviceaccount.com";

/** Path to the Service Account's Private Key file */
private static final String SERVICE_ACCOUNT_PKCS12_FILE_PATH = "/path/to/<public_key_fingerprint>-privatekey.p12";

/**
 * Build and returns a URL Shortner service object authorized with the service accounts.
 *
 * @return URL Shortner service object that is ready to make requests.
 */
public static Drive getDriveService() throws GeneralSecurityException, IOException, URISyntaxException {
  HttpTransport httpTransport = new NetHttpTransport();
  JacksonFactory jsonFactory = new JacksonFactory();
  GoogleCredential credential = new GoogleCredential.Builder()
      .setTransport(httpTransport)
      .setJsonFactory(jsonFactory)
      .setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
      .setServiceAccountScopes(UrlshortenerScopes.URLSHORTENER)
      .setServiceAccountPrivateKeyFromP12File(
          new java.io.File(SERVICE_ACCOUNT_PKCS12_FILE_PATH))
      .build();
  Urlshortener service = new Urlshortener.Builder(httpTransport, jsonFactory, null)
      .setHttpRequestInitializer(credential).build();
  return service;
}
person Nicolas Garnier    schedule 10.01.2013
comment
Все еще получаю это исключение invalid_grant при работе с AdSense. Идентификатор клиента действителен, область действия — AdSenseScopes.ADSENSE. API управления AdSense активирован. Без понятия как это исправить - person user12384512; 14.04.2013
comment
Очень полезно для меня, даже если я работаю с оболочкой NodeJS. Моя проблема заключалась в том, что я использовал адрес электронной почты своей учетной записи вместо адреса электронной почты приложения. Спасибо! - person Ionică Bizău; 15.03.2014
comment
Какой великолепный ответ! Я получал ошибки JWT с API Google Sheets Java. Не удалось найти решения. Было почти невозможно (за исключением этой темы) найти пример рабочего кода Java для получения учетных данных для сервисов Google API через учетную запись службы. Когда я поменял свои учетные данные, чтобы отразить ваш ответ, теперь я могу успешно делать запросы к привату через API Google Sheets. Кроме того, при работе с листами убедитесь, что вы разрешаете разрешение в личном листе (с помощью кнопки «Поделиться») для адреса электронной почты вашей учетной записи службы (в загруженном файле ключа/удостоверения json) - person ganta; 17.07.2017
comment
Я ПОЛУЧАЛ ошибки JWT при использовании библиотек Java, хотя, как говорит @Nivco, мне не нужно генерировать сертификат JWT, когда я использую библиотеку, которая должна была сделать это для меня, но я был, и мое решение было никогда не терпит неудачу на шаге учетных данных. Как бы то ни было, я думаю, что использование setServiceAccountPrivateKeyFromP12File вместо использования новой структуры json для учетных данных исправило мою конкретную проблему. Мораль этой истории такова: я считаю, что учетные данные влияют на эту проблему JWT при использовании API-библиотек Java. - person ganta; 17.07.2017