Descripción general
Propósito: En este documento, se explica cómo usar la clase de utilidad GoogleCredential para realizar la autorización de OAuth 2.0 con los servicios de Google. Si deseas obtener información sobre las funciones genéricas de OAuth 2.0 que proporcionamos, consulta OAuth 2.0 y la biblioteca cliente de Google OAuth para Java.
Resumen: Para acceder a los datos protegidos almacenados en los servicios de Google, usa OAuth 2.0 para la 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 asocia solo con tu aplicación cliente y el propietario de los datos protegidos a los que se accede. El token de acceso también se asocia con un alcance 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 proporcionar acceso seguro y 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 la biblioteca cliente de Google OAuth 2.0 para Java de uso general.
Para obtener más información, consulta la documentación de Javadoc de los siguientes paquetes:
- com.google.api.client.googleapis.auth.oauth2 (de google-api-client)
- com.google.api.client.googleapis.extensions.appengine.auth.oauth2 (de google-api-client-appengine)
Consola de la API de Google
Antes de acceder a las APIs de Google, debes configurar un proyecto en la Consola de APIs de Google para fines de autorización y facturación, ya sea que tu cliente sea una aplicación instalada, una aplicación para dispositivos móviles, un servidor web o un cliente que se ejecute en el navegador.
Si quieres obtener instrucciones para configurar tus credenciales correctamente, consulta la Ayuda de la Consola de APIs.
Credencial
GoogleCredential
GoogleCredential es una clase auxiliar segura para subprocesos de OAuth 2.0 para 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 la API de App Identity de Google App Engine para Java. A diferencia de la credencial en la que una aplicación cliente solicita acceso a los datos de un usuario final, la API de App Identity proporciona acceso a los datos propios de la aplicación cliente.
Usa AppIdentityCredential (de google-api-client-appengine). Esta credencial es mucho más simple porque Google App Engine se encarga de todos los detalles. Solo debes especificar el permiso de OAuth 2.0 que necesitas.
Código de ejemplo tomado 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
Por lo general, un token de acceso tiene una fecha de vencimiento de 1 hora, tras la cual recibirás un error si intentas usarlo.
GoogleCredential se encarga de “actualizar” el token de manera automática, es decir, obtener un token de acceso nuevo. Esto se hace por medio de un token de actualización de larga duración, que suele recibirse junto con el token de acceso si usas el parámetro access_type=offline durante el flujo del código de autorización (consulta GoogleAuthorizationCodeFlow.Builder.setAccessType(String)).
La mayoría de las aplicaciones deberán conservar el token de acceso o el token de actualización de la credencial. Para conservar los tokens de acceso o actualización de las credenciales, puedes proporcionar tu propia implementación de DataStoreFactory con StoredCredential, o bien puedes usar una de las siguientes implementaciones proporcionadas por la biblioteca:
- AppEngineDataStoreFactory: Persiste la credencial con la API de Google App Engine Data Store.
- MemoryDataStoreFactory: "persiste" la credencial en la memoria, lo que solo es útil como almacenamiento a corto plazo durante el ciclo de vida del proceso.
- FileDataStoreFactory: Persiste la credencial en un archivo.
Usuarios de App Engine: AppEngineCredentialStore ya no está disponible y se quitará pronto. Te recomendamos que uses AppEngineDataStoreFactory con StoredCredential. Si tienes credenciales almacenadas de la manera anterior, puedes usar los métodos auxiliares agregados migrateTo(AppEngineDataStoreFactory) o migrateTo(DataStore) para realizar la migración.
Puedes usar DataStoreCredentialRefreshListener y configurarlo para la credencial con GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener)).
Flujo de código de autorización
Usa el flujo de código de autorización para permitir que el usuario final otorgue a tu aplicación acceso a sus datos protegidos en las APIs de Google. El protocolo para este flujo se especifica en Authorization Code Grant.
Este flujo se implementa con GoogleAuthorizationCodeFlow. Estos son los pasos:
- El usuario final accede a tu aplicación. Deberás asociar ese usuario con un ID de usuario único para tu aplicación.
- Llama a AuthorizationCodeFlow.loadCredential(String) según el ID del usuario para verificar si ya se conocen las credenciales del usuario final. Si es así, ya terminamos.
- De lo contrario, llama a AuthorizationCodeFlow.newAuthorizationUrl() y dirige el navegador del usuario final a una página de autorización para otorgarle a tu aplicación acceso a sus datos protegidos.
- Luego, el servidor de autorización de Google redireccionará el navegador a la URL de redireccionamiento especificada por tu aplicación, junto con un parámetro de consulta
code. Usa el parámetrocodepara solicitar un token de acceso con AuthorizationCodeFlow.newTokenRequest(String)). - Usa AuthorizationCodeFlow.createAndStoreCredential(TokenResponse, String) para almacenar y obtener una credencial para acceder a recursos protegidos.
Como alternativa, si no usas GoogleAuthorizationCodeFlow, puedes usar las clases de nivel inferior:
- Usa DataStore.get(String) para cargar la credencial del almacén según el ID del usuario.
- Usa GoogleAuthorizationCodeRequestUrl para dirigir el navegador a la página de autorización.
- Usa AuthorizationCodeResponseUrl para procesar la respuesta de autorización y analizar el código de autorización.
- Usa GoogleAuthorizationCodeTokenRequest para solicitar un token de acceso y, posiblemente, un token de actualización.
- Crea un nuevo objeto GoogleCredential y almacénalo con DataStore.set(String, V).
- Accede a los recursos protegidos con
GoogleCredential. Los tokens de acceso vencidos se actualizarán automáticamente con el token de actualización (si corresponde). Asegúrate de usar DataStoreCredentialRefreshListener y configúralo para la credencial con GoogleCredential.Builder.addRefreshListener(CredentialRefreshListener)).
Cuando configuras tu proyecto en Google API Console, seleccionas diferentes credenciales, según el flujo que uses. Para obtener más información, consulta Configura OAuth 2.0 y Situaciones de OAuth 2.0. A continuación, se incluyen 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 el flujo del código de autorización en casos de uso básicos. Solo debes proporcionar subclases concretas de AbstractAuthorizationCodeServlet y AbstractAuthorizationCodeCallbackServlet (de google-oauth-client-servlet) y agregarlas a tu archivo web.xml. Ten en cuenta que aún debes encargarte del acceso de los usuarios a tu aplicación web y extraer un ID de 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 del código de autorización en App Engine es casi idéntico al flujo del código de autorización de servlet, excepto que podemos aprovechar la API de Users de Java de Google App Engine. El usuario debe haber accedido para que se habilite la API de Users de Java. Para obtener información sobre cómo redireccionar a los usuarios a una página de acceso si aún no accedieron, consulta Seguridad y autenticación (en web.xml).
La principal diferencia con el caso del servlet es que proporcionas subclases concretas de AbstractAppEngineAuthorizationCodeServlet y AbstractAppEngineAuthorizationCodeCallbackServlet (de google-oauth-client-appengine).
Extienden las clases de servlet abstractas y, luego, implementan el método getUserId por ti con la API de Users Java. AppEngineDataStoreFactory (de google-http-client-appengine) es una buena opción para conservar la credencial con la API de Google App Engine DataStore.
Ejemplo tomado (con algunas modificaciones) 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 con la que una aplicación cliente solicita acceso a los datos de un usuario final, las cuentas de servicio proporcionan acceso a los datos propios de la aplicación cliente. Tu aplicación cliente firma la solicitud de un token de acceso con una clave privada descargada de Google API Console.
Ejemplo de uso:
HttpTransport httpTransport = new NetHttpTransport(); 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 la identidad de un usuario en un dominio que te pertenece. Este flujo es muy similar al flujo de la cuenta de servicio anterior, pero también llamas 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 Uso de OAuth 2.0 para aplicaciones instaladas.
Ejemplo de uso:
public static void main(String[] args) { try { httpTransport = new NetHttpTransport(); 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 navegador que se describe en Usa OAuth 2.0 para aplicaciones del cliente, normalmente seguirías estos pasos:
- Redirige al usuario final en el navegador a la página de autorización con GoogleBrowserClientRequestUrl para otorgar acceso a la aplicación del navegador a los datos protegidos del usuario final.
- 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 registrado 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
Qué biblioteca usar con Android:
Si desarrollas para Android y la API de Google que deseas usar se incluye en la biblioteca de los Servicios de Google Play, usa esa biblioteca para obtener el mejor rendimiento y experiencia. Si la API de Google que deseas usar con Android no forma parte de la biblioteca de Servicios de Google Play, puedes usar la biblioteca cliente de la API de Google para Java, que admite Android 4.0 (Ice Cream Sandwich) (o versiones posteriores) y que se describe aquí. La compatibilidad con Android en la biblioteca cliente de la API de Google para Java es @Beta.
Información general:
A partir de Eclair (SDK 2.1), las cuentas de usuario se administran en un dispositivo Android con el administrador de cuentas. El SDK administra de forma centralizada toda la autorización de la aplicación para Android con AccountManager. Especificas el permiso de OAuth 2.0 que necesita tu aplicación y esta devuelve un token de acceso para usar.
El alcance de OAuth 2.0 se especifica a través del parámetro authTokenType como oauth2: más el alcance. Por ejemplo:
oauth2:https://www.googleapis.com/auth/tasks
Especifica el acceso de lectura y escritura a la API de Google Tasks. Si necesitas varios 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 del ejemplo authtokenType que se muestra arriba.
También debes especificar la clave de API de la Consola de APIs de Google. De lo contrario, el token que te proporciona AccountManager solo te brinda una cuota anónima, que suele ser muy baja. En cambio, si especificas una clave de API, recibirás una cuota gratuita más alta y, de manera opcional, podrás configurar la facturación para el uso que supere esa cuota.
Fragmento de código de ejemplo tomado de 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; } }