Usa OAuth 2.0 con la biblioteca cliente de las APIs de Google para Java

Descripción general

Propósito: Este documento explica cómo usar la GoogleCredential para autorizar OAuth 2.0 con los servicios de Google. Para más detallada sobre las funciones genéricas de OAuth 2.0 que proporcionamos, consulte OAuth 2.0 y la biblioteca cliente de Google OAuth para Java.

Resumen: Para acceder a los datos protegidos que se almacenan en los servicios de Google, usa OAuth 2.0 para autorización. Las APIs de Google admiten flujos de OAuth 2.0 para diferentes tipos de aplicaciones cliente. En todos estos flujos, la aplicación cliente solicita un token de acceso que se se asocia solo con tu aplicación cliente y con el propietario de los datos protegidos. a las que se está accediendo. El token de acceso también está asociado con un permiso limitado que define el tipo de datos a los que tiene acceso tu aplicación cliente (por ejemplo, "Administrar tus tareas"). Un objetivo importante de OAuth 2.0 es brindar un acceso conveniente a los datos protegidos, a la vez que se minimiza el impacto potencial si se roba un token de acceso.

Los paquetes de OAuth 2.0 en la biblioteca cliente de la API de Google para Java se basan en de uso general Biblioteca cliente de Google OAuth 2.0 para Java.

Para obtener información detallada, consulta la documentación de Javadoc para ver los siguientes paquetes:

Consola de la API de Google

Antes de poder acceder a las APIs de Google, necesitas configurar un proyecto en la Consola de APIs de Google para autenticación y facturación ya sea que tu cliente sea una aplicación instalada, una aplicación móvil, un servidor web o un cliente que se ejecuta en un navegador.

Si deseas obtener instrucciones para configurar tus credenciales correctamente, consulta el Ayuda de la Consola de APIs.

Credencial

GoogleCredential

GoogleCredential es una clase auxiliar de seguridad de subprocesos para OAuth 2.0 que permite acceder a recursos protegidos con un token de acceso. Por ejemplo, si ya tienes un token de acceso, puedes realizar una solicitud de la siguiente manera:

GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken);
Plus plus = new Plus.builder(new NetHttpTransport(),
                             GsonFactory.getDefaultInstance(),
                             credential)
    .setApplicationName("Google-PlusSample/1.0")
    .build();

Identidad de Google App Engine

Esta credencial alternativa se basa en el API de Java de App Identity de Google App Engine. A diferencia de la credencial en la que una aplicación cliente solicita acceso a un los datos del usuario final, la API de App Identity brinda acceso al cliente los datos propios de tu aplicación.

Usa AppIdentityCredential (de google-api-client-appengine). Esta credencial es mucho más sencilla porque Google App Engine se ocupa de los detalles. Solo debes especificar el alcance de OAuth 2.0 que necesitas.

Ejemplo de código extraído de urlshortener-robots-appengine-sample:

static Urlshortener newUrlshortener() {
  AppIdentityCredential credential =
      new AppIdentityCredential(
          Collections.singletonList(UrlshortenerScopes.URLSHORTENER));
  return new Urlshortener.Builder(new UrlFetchTransport(),
                                  GsonFactory.getDefaultInstance(),
                                  credential)
      .build();
}

Almacén de datos

Los tokens de acceso suelen tener una fecha de vencimiento de 1 hora. Luego de esa fecha, y recibes un error si intentas usarlo. GoogleCredential se encarga de actualizar automáticamente el token, lo que simplemente significa obtener un token de acceso nuevo. Esto se logra mediante un token de actualización de larga duración, junto con el token de acceso si usas el parámetro access_type=offline durante el flujo de código de autorización (consulta GoogleAuthorizationCodeFlow.Builder.setAccessType(String)).

La mayoría de las aplicaciones deberán conservar el token de acceso de la credencial o token de actualización. Para conservar el acceso de la credencial o los tokens de actualización, puedes Proporcionar tu propia implementación de DataStoreFactory con StoredCredential; o puedes usar una de las siguientes implementaciones que proporciona la biblioteca:

Usuarios de App Engine: AppEngineCredentialStore dejó de estar disponible y se quitará pronto. Te recomendamos que utilices AppEngineDataStoreFactory con StoredCredential. Si tienes credenciales almacenadas de forma antigua, puedes usar el complemento métodos auxiliares migrateTo(AppEngineDataStoreFactory) o migrateTo(DataStore) para realizar la migración.

Puedes usar DataStoreCredentialRefreshListener y configúrala para la credencial con GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener)).

Flujo de código de autorización

Usar el flujo de código de autorización para permitir que el usuario final otorgue tu aplicación acceso a sus datos protegidos en las APIs de Google. El protocolo para este flujo es especificadas en Otorgamiento del Código de Autorización.

Este flujo se implementa con GoogleAuthorizationCodeFlow. Estos son los pasos:

Como alternativa, si no usas GoogleAuthorizationCodeFlow, puedes utilizar las clases de nivel inferior:

Cuando configures tu proyecto en la Consola de APIs de Google, sigue estos pasos: puedes elegir entre diferentes credenciales, según el flujo que uses. Para conocer más detalles, consulta Configura OAuth 2.0. y situaciones de OAuth 2.0. A continuación, se muestran los fragmentos de código para cada uno de los flujos.

Aplicaciones de servidor web

El protocolo para este flujo se explica en Usa OAuth 2.0 para aplicaciones de servidor web.

Esta biblioteca proporciona clases auxiliares de servlet para simplificar significativamente la tarea de código de autorización para casos de uso básicos. Solo proporcionas subclases concretas de AbstractAuthorizationCodeServlet y AbstractAuthorizationCodeCallbackServlet. (de google-oauth-client-servlet) y agrégalos a tu archivo web.xml. Ten en cuenta que aún debes ocuparte de las licencias inicio de sesión para tu aplicación web y extraer el ID del usuario.

public class CalendarServletSample extends AbstractAuthorizationCodeServlet {

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws IOException {
    // do stuff
  }

  @Override
  protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
    GenericUrl url = new GenericUrl(req.getRequestURL().toString());
    url.setRawPath("/oauth2callback");
    return url.build();
  }

  @Override
  protected AuthorizationCodeFlow initializeFlow() throws IOException {
    return new GoogleAuthorizationCodeFlow.Builder(
        new NetHttpTransport(), GsonFactory.getDefaultInstance(),
        "[[ENTER YOUR CLIENT ID]]", "[[ENTER YOUR CLIENT SECRET]]",
        Collections.singleton(CalendarScopes.CALENDAR)).setDataStoreFactory(
        DATA_STORE_FACTORY).setAccessType("offline").build();
  }

  @Override
  protected String getUserId(HttpServletRequest req) throws ServletException, IOException {
    // return user ID
  }
}

public class CalendarServletCallbackSample extends AbstractAuthorizationCodeCallbackServlet {

  @Override
  protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential)
      throws ServletException, IOException {
    resp.sendRedirect("/");
  }

  @Override
  protected void onError(
      HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse)
      throws ServletException, IOException {
    // handle error
  }

  @Override
  protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
    GenericUrl url = new GenericUrl(req.getRequestURL().toString());
    url.setRawPath("/oauth2callback");
    return url.build();
  }

  @Override
  protected AuthorizationCodeFlow initializeFlow() throws IOException {
    return new GoogleAuthorizationCodeFlow.Builder(
        new NetHttpTransport(), GsonFactory.getDefaultInstance()
        "[[ENTER YOUR CLIENT ID]]", "[[ENTER YOUR CLIENT SECRET]]",
        Collections.singleton(CalendarScopes.CALENDAR)).setDataStoreFactory(
        DATA_STORE_FACTORY).setAccessType("offline").build();
  }

  @Override
  protected String getUserId(HttpServletRequest req) throws ServletException, IOException {
    // return user ID
  }
}

Aplicaciones de Google App Engine

El flujo de código de autorización en App Engine es casi idéntico al servlet de código de autorización, con la excepción de que podemos aprovechar la infraestructura API de Usuarios de Java. El usuario debe acceder a la cuenta para habilitar la API de usuarios de Java; para obtener información sobre redireccionar a los usuarios a una página de acceso si aún no lo han hecho, consulta Seguridad y autenticación (en web.xml).

La diferencia principal con respecto al caso del servlet subclases de AbstractAppEngineAuthorizationCodeServlet y AbstractAppEngineAuthorizationCodeCallbackServlet (de google-oauth-client-appengine. Extienden las clases abstractas de servlet y, luego, implementan el método getUserId con la API de Users de Java. AppEngineDataStoreFactory (de google-http-client-appengine) es una buena opción para conservar la credencial con el conjunto de datos API de Google Store.

Ejemplo tomado (ligeramente modificado) de calendar-appengine-sample:

public class CalendarAppEngineSample extends AbstractAppEngineAuthorizationCodeServlet {

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws IOException {
    // do stuff
  }

  @Override
  protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
    return Utils.getRedirectUri(req);
  }

  @Override
  protected AuthorizationCodeFlow initializeFlow() throws IOException {
    return Utils.newFlow();
  }
}

class Utils {
  static String getRedirectUri(HttpServletRequest req) {
    GenericUrl url = new GenericUrl(req.getRequestURL().toString());
    url.setRawPath("/oauth2callback");
    return url.build();
  }

  static GoogleAuthorizationCodeFlow newFlow() throws IOException {
    return new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY,
        getClientCredential(), Collections.singleton(CalendarScopes.CALENDAR)).setDataStoreFactory(
        DATA_STORE_FACTORY).setAccessType("offline").build();
  }
}

public class OAuth2Callback extends AbstractAppEngineAuthorizationCodeCallbackServlet {

  private static final long serialVersionUID = 1L;

  @Override
  protected void onSuccess(HttpServletRequest req, HttpServletResponse resp, Credential credential)
      throws ServletException, IOException {
    resp.sendRedirect("/");
  }

  @Override
  protected void onError(
      HttpServletRequest req, HttpServletResponse resp, AuthorizationCodeResponseUrl errorResponse)
      throws ServletException, IOException {
    String nickname = UserServiceFactory.getUserService().getCurrentUser().getNickname();
    resp.getWriter().print("<h3>" + nickname + ", why don't you want to play with me?</h1>");
    resp.setStatus(200);
    resp.addHeader("Content-Type", "text/html");
  }

  @Override
  protected String getRedirectUri(HttpServletRequest req) throws ServletException, IOException {
    return Utils.getRedirectUri(req);
  }

  @Override
  protected AuthorizationCodeFlow initializeFlow() throws IOException {
    return Utils.newFlow();
  }
}

Para ver un ejemplo adicional, consulta storage-serviceaccount-appengine-sample.

Cuentas de servicio

GoogleCredential también admite cuentas de servicio. A diferencia de la credencial en la que una aplicación cliente solicita acceso a un a los datos del usuario final, las cuentas de servicio proporcionan acceso a la infraestructura propios datos. Tu aplicación cliente firma la solicitud para un token de acceso usando una clave privada descargada de la Consola de APIs de Google.

Ejemplo de código tomado de plus-serviceaccount-cmdline-sample:

HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory jsonFactory = GsonFactory.getDefaultInstance();
...
// Build service account credential.

GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json"))
    .createScoped(Collections.singleton(PlusScopes.PLUS_ME));
// Set up global Plus instance.
plus = new Plus.Builder(httpTransport, jsonFactory, credential)
    .setApplicationName(APPLICATION_NAME).build();
...

Para ver un ejemplo adicional, consulta storage-serviceaccount-cmdline-sample.

Robo de identidad

También puedes usar el flujo de la cuenta de servicio para suplantar a un usuario en un dominio que que posees. Esto es muy similar al flujo de la cuenta de servicio anterior, pero además de llamar a GoogleCredential.Builder.setServiceAccountUser(String).

Aplicaciones instaladas

Este es el flujo de código de autorización de línea de comandos que se describe en Cómo usar OAuth 2.0 para aplicaciones instaladas.

Fragmento de ejemplo de plus-cmdline-sample:

public static void main(String[] args) {
  try {
    httpTransport = GoogleNetHttpTransport.newTrustedTransport();
    dataStoreFactory = new FileDataStoreFactory(DATA_STORE_DIR);
    // authorization
    Credential credential = authorize();
    // set up global Plus instance
    plus = new Plus.Builder(httpTransport, JSON_FACTORY, credential).setApplicationName(
        APPLICATION_NAME).build();
   // ...
}

private static Credential authorize() throws Exception {
  // load client secrets
  GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY,
      new InputStreamReader(PlusSample.class.getResourceAsStream("/client_secrets.json")));
  // set up authorization code flow
  GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
      httpTransport, JSON_FACTORY, clientSecrets,
      Collections.singleton(PlusScopes.PLUS_ME)).setDataStoreFactory(
      dataStoreFactory).build();
  // authorize
  return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
}

Aplicaciones del cliente

Para usar el flujo de cliente basado en el navegador descrito en Usar OAuth 2.0 para aplicaciones del cliente, normalmente seguirías estos pasos:

  1. Redireccionar al usuario final del navegador a la página de autorización mediante GoogleBrowserClientRequestUrl para otorgar a la aplicación del navegador acceso a los datos protegidos del usuario final.
  2. Usa la biblioteca cliente de la API de Google para JavaScript para procesar el token de acceso que se encuentra en el fragmento de URL en el URI de redireccionamiento registrados en la Consola de APIs de Google.

Ejemplo de uso para una aplicación web:

public void doGet(HttpServletRequest request, HttpServletResponse response)throws IOException {
  String url = new GoogleBrowserClientRequestUrl("812741506391.apps.googleusercontent.com",
      "https://oauth2.example.com/oauthcallback", Arrays.asList(
          "https://www.googleapis.com/auth/userinfo.email",
          "https://www.googleapis.com/auth/userinfo.profile")).setState("/profile").build();
  response.sendRedirect(url);
}

Android

@Beta

Biblioteca que debes usar con Android:

Si estás desarrollando para Android y se incluye la API de Google que quieres usar en la Biblioteca de Servicios de Google Play, usar esa biblioteca para obtener el mejor rendimiento y la mejor experiencia. Si la API de Google usar con Android no es parte de la biblioteca de Google Play Services, puedes usar la biblioteca cliente de la API de Google para Java, que es compatible con Android 4.0 (Ice Cream Sandwich) (o superior) y que se describe aquí. La compatibilidad de Android en el La biblioteca cliente de la API para Java es @Beta.

Información general:

A partir de Eclair (SDK 2.1), las cuentas de usuario se administran en un dispositivo Android. mediante el administrador de cuentas. Toda la autorización de aplicaciones para Android se administrados por el SDK con AccountManager. Especificas el alcance de OAuth 2.0 que necesita tu aplicación y se devuelve un acceso token que se usará.

El alcance de OAuth 2.0 se especifica mediante el parámetro authTokenType como oauth2:. además del alcance. Por ejemplo:

oauth2:https://www.googleapis.com/auth/tasks

Especifica el acceso de lectura/escritura a la API de Google Tasks. Si necesitas múltiples Para los permisos de OAuth 2.0, usa una lista separada por espacios.

Algunas APIs tienen parámetros authTokenType especiales que también funcionan. Por ejemplo: "Administra tus tareas" es un alias para el ejemplo de authtokenType que se muestra arriba.

También debes especificar la clave de API desde el Consola de APIs de Google. De lo contrario, el token que el AccountManager te proporciona solo te proporciona una cuota anónima, que suele ser muy baja. Por el contrario, si especificas una API recibirás una cuota gratuita más alta y, de manera opcional, podrás configurar la facturación por encima de ese.

Ejemplo del fragmento de código tomado del tasks-android-sample:

com.google.api.services.tasks.Tasks service;

@Override
public void onCreate(Bundle savedInstanceState) {
  credential =
      GoogleAccountCredential.usingOAuth2(this, Collections.singleton(TasksScopes.TASKS));
  SharedPreferences settings = getPreferences(Context.MODE_PRIVATE);
  credential.setSelectedAccountName(settings.getString(PREF_ACCOUNT_NAME, null));
  service =
      new com.google.api.services.tasks.Tasks.Builder(httpTransport, jsonFactory, credential)
          .setApplicationName("Google-TasksAndroidSample/1.0").build();
}

private void chooseAccount() {
  startActivityForResult(credential.newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
  switch (requestCode) {
    case REQUEST_GOOGLE_PLAY_SERVICES:
      if (resultCode == Activity.RESULT_OK) {
        haveGooglePlayServices();
      } else {
        checkGooglePlayServicesAvailable();
      }
      break;
    case REQUEST_AUTHORIZATION:
      if (resultCode == Activity.RESULT_OK) {
        AsyncLoadTasks.run(this);
      } else {
        chooseAccount();
      }
      break;
    case REQUEST_ACCOUNT_PICKER:
      if (resultCode == Activity.RESULT_OK && data != null && data.getExtras() != null) {
        String accountName = data.getExtras().getString(AccountManager.KEY_ACCOUNT_NAME);
        if (accountName != null) {
          credential.setSelectedAccountName(accountName);
          SharedPreferences settings = getPreferences(Context.MODE_PRIVATE);
          SharedPreferences.Editor editor = settings.edit();
          editor.putString(PREF_ACCOUNT_NAME, accountName);
          editor.commit();
          AsyncLoadTasks.run(this);
        }
      }
      break;
  }
}