Acceso con Google para apps del servidor

Para usar los servicios de Google en nombre de un usuario cuando este no tiene conexión, debes usar un flujo híbrido del servidor en el que un usuario autoriza tu app del cliente con el cliente de la API de JavaScript y envías un código de autorización único especial a tu servidor. Tu servidor intercambia este código de uso único para adquirir sus propios tokens de acceso y actualización de Google, de modo que pueda realizar sus propias llamadas a la API, lo que se puede hacer mientras el usuario está desconectado. Este flujo de código único tiene ventajas de seguridad sobre un flujo solo del servidor y sobre el envío de tokens de acceso a tu servidor.

A continuación, se ilustra el flujo de acceso para obtener un token de acceso para tu aplicación del servidor.

Los códigos de un solo uso tienen varias ventajas de seguridad. Con los códigos, Google proporciona tokens directamente a tu servidor sin intermediarios. Aunque no recomendamos filtrar códigos, son muy difíciles de usar sin tu secreto cliente. Mantén en secreto tu secreto de cliente.

Cómo implementar el flujo de código de un solo uso

El botón de Acceso con Google proporciona un token de acceso y un código de autorización. El código es un código único que tu servidor puede intercambiar con los servidores de Google por un token de acceso.

En el siguiente código de muestra, se muestra cómo realizar el flujo de código único.

Para autenticar el Acceso con Google con el flujo de código de un solo uso, debes hacer lo siguiente:

Paso 1: Crea un ID de cliente y un secreto de cliente

Para crear un ID de cliente y un secreto de cliente, crea un proyecto de la Consola de APIs de Google, configura un ID de cliente de OAuth y registra tus orígenes de JavaScript:

  1. Ve a la Consola de API de Google.

  2. En el menú desplegable del proyecto, selecciona un proyecto existente o crea uno nuevo seleccionando Crear un proyecto nuevo.

  3. En la barra lateral, en "APIs y servicios", selecciona Credenciales y, luego, haz clic en Configurar pantalla de consentimiento.

    Elige una dirección de correo electrónico, especifica un nombre del producto y presiona Guardar.

  4. En la pestaña Credentials, selecciona la lista desplegable Create credentials y elige OAuth client ID.

  5. En Tipo de aplicación, selecciona Aplicación web.

    Registra los orígenes desde los que tu app puede acceder a las APIs de Google, como se indica a continuación. Un origen es una combinación única de protocolo, nombre de host y puerto.

    1. En el campo Orígenes autorizados de JavaScript, ingresa el origen de tu app. Puedes ingresar varios orígenes para permitir que tu app se ejecute en diferentes protocolos, dominios o subdominios. No puedes usar comodines. En el siguiente ejemplo, la segunda URL podría ser una URL de producción.

      http://localhost:8080
      https://myproductionurl.example.com
      
    2. El campo URI de redireccionamiento autorizado no requiere un valor. Los URIs de redireccionamiento no se usan con las APIs de JavaScript.

    3. Presiona el botón Crear.

  6. En el cuadro de diálogo Cliente de OAuth resultante, copia el ID de cliente. El ID de cliente permite que tu app acceda a las APIs de Google habilitadas.

Paso 2: Incluye la biblioteca de la plataforma de Google en tu página

Incluye las siguientes secuencias de comandos que demuestran una función anónima que inserta una secuencia de comandos en el DOM de esta página web index.html.

<!-- The top of file index.html -->
<html itemscope itemtype="http://schema.org/Article">
<head>
  <!-- BEGIN Pre-requisites -->
  <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js">
  </script>
  <script src="https://apis.google.com/js/client:platform.js?onload=start" async defer>
  </script>
  <!-- END Pre-requisites -->

Paso 3: Inicializa el objeto GoogleAuth

Carga la biblioteca auth2 y llama a gapi.auth2.init() para inicializar el objeto GoogleAuth. Especifica tu ID de cliente y los permisos que deseas solicitar cuando llames a init().

<!-- Continuing the <head> section -->
  <script>
    function start() {
      gapi.load('auth2', function() {
        auth2 = gapi.auth2.init({
          client_id: 'YOUR_CLIENT_ID.apps.googleusercontent.com',
          // Scopes to request in addition to 'profile' and 'email'
          //scope: 'additional_scope'
        });
      });
    }
  </script>
</head>
<body>
  <!-- ... -->
</body>
</html>

Paso 4: Agrega el botón de acceso a tu página

Agrega el botón de acceso a tu página web y adjunta un controlador de clics para llamar a grantOfflineAccess() y comenzar el flujo de código de un solo uso.

<!-- Add where you want your sign-in button to render -->
<!-- Use an image that follows the branding guidelines in a real app -->
<button id="signinButton">Sign in with Google</button>
<script>
  $('#signinButton').click(function() {
    // signInCallback defined in step 6.
    auth2.grantOfflineAccess().then(signInCallback);
  });
</script>

Paso 5: Realiza el acceso del usuario

El usuario hace clic en el botón de acceso y le otorga a tu app acceso a los permisos que solicitaste. Luego, a la función de devolución de llamada que especificaste en el método grantOfflineAccess().then() se le pasa un objeto JSON con un código de autorización. Por ejemplo:

{"code":"4/yU4cQZTMnnMtetyFcIWNItG32eKxxxgXXX-Z4yyJJJo.4qHskT-UtugceFc0ZRONyF4z7U4UmAI"}

Paso 6: Envía el código de autorización al servidor

code es tu código único que tu servidor puede intercambiar por su propio token de acceso y token de actualización. Solo puedes obtener un token de actualización después de que se le muestre al usuario un diálogo de autorización que solicite acceso sin conexión. Si especificaste el prompt select-account en OfflineAccessOptions en el paso 4, debes almacenar el token de actualización que recuperas para usarlo más adelante, ya que los intercambios posteriores mostrarán null para el token de actualización. Este flujo proporciona mayor seguridad que tu flujo estándar de OAuth 2.0.

Los tokens de acceso siempre se devuelven con el intercambio de un código de autorización válido.

En la siguiente secuencia de comandos, se define una función de devolución de llamada para el botón de acceso. Cuando un acceso se realiza correctamente, la función almacena el token de acceso para su uso en el cliente y envía el código de un solo uso a tu servidor en el mismo dominio.

<!-- Last part of BODY element in file index.html -->
<script>
function signInCallback(authResult) {
  if (authResult['code']) {

    // Hide the sign-in button now that the user is authorized, for example:
    $('#signinButton').attr('style', 'display: none');

    // Send the code to the server
    $.ajax({
      type: 'POST',
      url: 'http://example.com/storeauthcode',
      // Always include an `X-Requested-With` header in every AJAX request,
      // to protect against CSRF attacks.
      headers: {
        'X-Requested-With': 'XMLHttpRequest'
      },
      contentType: 'application/octet-stream; charset=utf-8',
      success: function(result) {
        // Handle or verify the server response.
      },
      processData: false,
      data: authResult['code']
    });
  } else {
    // There was an error.
  }
}
</script>

Paso 7: Intercambia el código de autorización por un token de acceso

En el servidor, intercambia el código de autenticación por tokens de acceso y actualización. Usa el token de acceso para llamar a las APIs de Google en nombre del usuario y, de manera opcional, almacena el token de actualización para adquirir un token de acceso nuevo cuando este venza.

Si solicitaste acceso al perfil, también obtendrás un token de ID que contiene información básica del perfil del usuario.

Por ejemplo:

Java
// (Receive authCode via HTTPS POST)


if (request.getHeader("X-Requested-With") == null) {
  // Without the `X-Requested-With` header, this request could be forged. Aborts.
}

// Set path to the Web application client_secret_*.json file you downloaded from the
// Google API Console: https://console.cloud.google.com/apis/credentials
// You can also find your Web application client ID and client secret from the
// console and specify them directly when you create the GoogleAuthorizationCodeTokenRequest
// object.
String CLIENT_SECRET_FILE = "/path/to/client_secret.json";

// Exchange auth code for access token
GoogleClientSecrets clientSecrets =
    GoogleClientSecrets.load(
        JacksonFactory.getDefaultInstance(), new FileReader(CLIENT_SECRET_FILE));
GoogleTokenResponse tokenResponse =
          new GoogleAuthorizationCodeTokenRequest(
              new NetHttpTransport(),
              JacksonFactory.getDefaultInstance(),
              "https://oauth2.googleapis.com/token",
              clientSecrets.getDetails().getClientId(),
              clientSecrets.getDetails().getClientSecret(),
              authCode,
              REDIRECT_URI)  // Specify the same redirect URI that you use with your web
                             // app. If you don't have a web version of your app, you can
                             // specify an empty string.
              .execute();

String accessToken = tokenResponse.getAccessToken();

// Use access token to call API
GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken);
Drive drive =
    new Drive.Builder(new NetHttpTransport(), JacksonFactory.getDefaultInstance(), credential)
        .setApplicationName("Auth Code Exchange Demo")
        .build();
File file = drive.files().get("appfolder").execute();

// Get profile info from ID token
GoogleIdToken idToken = tokenResponse.parseIdToken();
GoogleIdToken.Payload payload = idToken.getPayload();
String userId = payload.getSubject();  // Use this value as a key to identify a user.
String email = payload.getEmail();
boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
String name = (String) payload.get("name");
String pictureUrl = (String) payload.get("picture");
String locale = (String) payload.get("locale");
String familyName = (String) payload.get("family_name");
String givenName = (String) payload.get("given_name");
Python
from apiclient import discovery
import httplib2
from oauth2client import client

# (Receive auth_code by HTTPS POST)


# If this request does not have `X-Requested-With` header, this could be a CSRF
if not request.headers.get('X-Requested-With'):
    abort(403)

# Set path to the Web application client_secret_*.json file you downloaded from the
# Google API Console: https://console.cloud.google.com/apis/credentials
CLIENT_SECRET_FILE = '/path/to/client_secret.json'

# Exchange auth code for access token, refresh token, and ID token
credentials = client.credentials_from_clientsecrets_and_code(
    CLIENT_SECRET_FILE,
    ['https://www.googleapis.com/auth/drive.appdata', 'profile', 'email'],
    auth_code)

# Call Google API
http_auth = credentials.authorize(httplib2.Http())
drive_service = discovery.build('drive', 'v3', http=http_auth)
appfolder = drive_service.files().get(fileId='appfolder').execute()

# Get profile info from ID token
userid = credentials.id_token['sub']
email = credentials.id_token['email']