Advertencia: Estos datos se proporcionan conforme a la Política de Datos del Usuario de Google. Revise y satisfaga la política. De lo contrario, el proyecto o la cuenta podrían suspenderse.

Migra a los Servicios de identidad de Google

Descripción general

A fin de obtener un token de acceso por usuario para llamar a las API de Google, Google ofrece varias bibliotecas de JavaScript:

En esta guía, se proporcionan instrucciones para migrar de estas bibliotecas a la biblioteca de Servicios de identidad de Google.

Si sigues esta guía, podrás hacer lo siguiente:

  • Reemplazar la biblioteca de la plataforma obsoleta por la biblioteca de servicios de identidad
  • Si usas la biblioteca cliente de la API, quita el módulo obsoleto gapi.auth2, sus métodos y objetos, y reemplázalos con equivalentes de Identity Services.

A fin de obtener una descripción de lo que cambió con la biblioteca de JavaScript de los servicios de identidad, consulta la descripción general y cómo funciona la autorización del usuario para revisar los conceptos y términos clave.

Si buscas autenticación para el registro y acceso de los usuarios, consulta Cómo migrar el Acceso con Google.

Identifica el flujo de autorización

Existen dos flujos de autorización de usuarios posibles: implícito y código de autorización.

Revisa tu aplicación web para identificar el tipo de flujo de autorización que se usa actualmente.

Indica que tu aplicación web usa el flujo implícito:

Indica que tu aplicación web utiliza el flujo de código de autorización:

  • Su implementación se basa en lo siguiente:

  • La app se ejecuta tanto en el navegador del usuario como en tu plataforma de backend.

  • Tu plataforma de backend aloja un extremo de código de autorización.

  • Tu plataforma de backend llama a las API de Google en nombre de los usuarios sin necesidad de que estén presentes, también conocido como modo sin conexión.

  • Tu plataforma de backend administra y almacena los tokens de actualización.

En algunos casos, tu base de código puede admitir ambos flujos.

Elige un flujo de autorización

Antes de comenzar la migración, debes determinar si debes continuar con tu flujo existente o adoptar uno diferente para satisfacer tus necesidades.

Revisa Elige un flujo de autorización para comprender las diferencias y las compensaciones clave entre ambos.

En la mayoría de los casos, se recomienda el flujo de código de autorización, ya que ofrece el nivel más alto de seguridad del usuario. Implementar este flujo también permite que tu plataforma agregue con facilidad funciones sin conexión nuevas, como recuperar actualizaciones para notificar a los usuarios sobre cambios notables en su calendario, fotos, suscripciones, etc.

Elige un flujo de autorización con los selectores que se encuentran debajo.

Flujo implícito

Obtén un token de acceso para usar en el navegador mientras el usuario esté presente.

En los ejemplos de flujo implícito, se muestran las aplicaciones web antes y después de la migración a los servicios de identidad.

Flujo de código de autorización

Se envía a tu plataforma de backend un código de autorización por usuario emitido por Google. Este se intercambia por un token de acceso y un token de actualización.

En los ejemplos de flujo de código de autorización, se muestran las aplicaciones web antes y después de la migración a los servicios de identidad.

En esta guía, sigue las instrucciones que se indican en negrita para agregar, quitar, actualizar o reemplazar las funciones existentes.

Cambios en la aplicación web en el navegador

En esta sección, se revisan los cambios que realizarás en la aplicación web en el navegador cuando migres a la biblioteca de JavaScript de los servicios de identidad de Google.

Identifica el código afectado y realiza pruebas

Una cookie de depuración puede ayudar a localizar el código afectado y probar el comportamiento posterior a la baja.

En apps grandes o complejas, puede ser difícil encontrar todo el código afectado por la baja del módulo gapi.auth2. Para registrar el uso existente de la funcionalidad que pronto dejará de estar disponible en la consola, establece el valor de la cookie G_AUTH2_MIGRATION en informational. De manera opcional, agrega dos puntos seguidos de un valor de clave para acceder al almacenamiento de sesión. Después del acceso y la recepción de las credenciales, se revisan o envían registros recopilados a un backend para analizarlos más adelante. Por ejemplo, informational:showauth2use guarda el origen y la URL en una clave de almacenamiento de sesión llamada showauth2use.

Para verificar el comportamiento de la app cuando el módulo gapi.auth2 ya no esté cargado, establece el valor de la cookie G_AUTH2_MIGRATION en enforced. Esto permite probar el comportamiento posterior a la baja antes de la fecha de aplicación.

Valores de cookie G_AUTH2_MIGRATION posibles:

  • enforced No cargues el módulo gapi.auth2.
  • informational Registra el uso de la función obsoleta en la consola de JS. También accede al almacenamiento de la sesión cuando se establezca un nombre de clave opcional: informational:key-name.

Para minimizar el impacto en el usuario, se recomienda que primero configures esta cookie localmente durante el desarrollo y la prueba, antes de usarla en entornos de producción.

Bibliotecas y módulos

El módulo gapi.auth2 administra la autenticación del usuario para el acceso y el flujo implícito de autorización, reemplaza este módulo obsoleto y sus objetos y métodos por la biblioteca de Google Identity Services.

Agrega la biblioteca de servicios de identidad a tu aplicación web; para ello, inclúyela en tu documento:

<script src="https://accounts.google.com/gsi/client" async defer></script>

Quita las instancias de carga del módulo auth2 con gapi.load('auth2', function).

La biblioteca de los Servicios de identidad de Google reemplaza el uso del módulo gapi.auth2. Puedes seguir usando de forma segura el módulo gapi.client de la Biblioteca cliente de la API de Google para JavaScript y aprovechar su creación automática de métodos JS que admiten llamadas desde un documento de descubrimiento, agrupando varias llamadas a la API y la funcionalidad de administración de CORS.

Cookies

La autorización del usuario no requiere el uso de cookies.

Consulta Cómo migrar desde el Acceso con Google para obtener detalles sobre cómo la autenticación de usuarios usa cookies y cómo Google usa cookies para el uso de cookies de otros productos y servicios de Google.

Credenciales

Los Servicios de identidad de Google separa la autenticación y la autorización del usuario en dos operaciones distintas, y las credenciales del usuario son independientes: el token de ID que se usa para identificar a un usuario se muestra por separado del token de acceso que se usó para la autorización.

Para ver estos cambios, consulta los ejemplos de credenciales.

Flujo implícito

Quita la administración del perfil de usuario de los flujos de autorización para separar la autenticación y la autorización del usuario.

Quita estas referencias de clientes de JavaScript con Acceso con Google:

Métodos

  • GoogleUser.getBasicProfile()
  • GoogleUser.getId()

Flujo de código de autorización

Los servicios de identidad separan las credenciales en el navegador en token de ID y token de acceso. Este cambio no se aplica a las credenciales obtenidas mediante llamadas directas a los extremos de Google OAuth 2.0 desde tu plataforma de backend ni a las bibliotecas que se ejecutan en un servidor seguro de tu plataforma, como el cliente de Node.js de las API de Google.

Estado de sesión

Anteriormente, Acceso con Google te ayudaba a administrar el estado de acceso de los usuarios mediante los siguientes métodos:

Eres responsable de administrar el estado de acceso y las sesiones de usuario en tu aplicación web.

Quita estas referencias de clientes de JavaScript con Acceso con Google:

Objetos:

  • gapi.auth2.SignInOptions

Métodos:

  • GoogleAuth.attachClickHandler()
  • GoogleAuth.isSignedIn()
  • GoogleAuth.isSignedIn.get()
  • GoogleAuth.isSignedIn.listen()
  • GoogleAuth.signIn()
  • GoogleAuth.signOut()
  • GoogleAuth.currentUser.get()
  • GoogleAuth.currentUser.listen()
  • GoogleUser.isSignedIn()

Configuración del cliente

Actualiza tu aplicación web a fin de inicializar un cliente de token para el flujo de código implícito o de autorización.

Quita estas referencias de clientes de JavaScript con Acceso con Google:

Objetos:

  • gapi.auth2.ClientConfig
  • gapi.auth2.OfflineAccessOptions

Métodos:

  • gapi.auth2.getAuthInstance()
  • GoogleUser.grant()

Flujo implícito

Agrega un objeto TokenClientConfig y una llamada initTokenClient() a fin de configurar tu aplicación web según el ejemplo para inicializar un cliente de token.

Reemplaza las referencias de clientes de JavaScript del Acceso con Google por Google Identity Services:

Objetos:

  • gapi.auth2.AuthorizeConfig con TokenClientConfig

Métodos:

  • gapi.auth2.init() con google.accounts.oauth2.initTokenClient()

Parámetros:

  • gapi.auth2.AuthorizeConfig.login_hint con TokenClientConfig.hint.
  • gapi.auth2.GoogleUser.getHostedDomain() con TokenClientConfig.hosted_domain.

Flujo de código de autorización

Agrega un objeto CodeClientConfig y una llamada initCodeClient() a fin de configurar tu aplicación web, según el ejemplo de inicializar un cliente de código.

Cuando se pasa del flujo implícito al código de autorización, sucede lo siguiente:

Quitar Referencias de clientes de JavaScript del Acceso con Google

Objetos:

  • gapi.auth2.AuthorizeConfig

Métodos:

  • gapi.auth2.init()

Parámetros:

  • gapi.auth2.AuthorizeConfig.login_hint
  • gapi.auth2.GoogleUser.getHostedDomain()

Solicitud de token

Un gesto del usuario, como un clic en un botón, genera una solicitud que hace que el token de acceso se muestre directamente al navegador del usuario con el flujo implícito o a tu plataforma de backend después de intercambiar un código de autorización por usuario para un token de acceso y un token de actualización.

Flujo implícito

Se pueden obtener y usar tokens de acceso en el navegador mientras el usuario accedió y tiene una sesión activa con Google. En el modo implícito, se requiere un gesto del usuario para solicitar un token de acceso, incluso si ya hubo una solicitud anterior.

Reemplaza las referencias de clientes de JavaScript del Acceso con Google por Google Identity Services:

Métodos:

  • gapi.auth2.authorize() con TokenClient.requestAccessToken()
  • GoogleUser.reloadAuthResponse() con TokenClient.requestAccessToken()

Agrega un vínculo o un botón a fin de llamar a requestAccessToken() para iniciar el flujo emergente de UX a fin de solicitar un token de acceso o para obtener un token nuevo cuando se venza el token existente.

Actualiza tu base de código a:

  • Activa el flujo de tokens de OAuth 2.0 con requestAccessToken().
  • A fin de admitir la autorización incremental, usa requestAccessToken y OverridableTokenClientConfig para separar una solicitud de muchos alcances en varias solicitudes más pequeñas.
  • Solicitar un token nuevo cuando el token existente caduque o se revoque

Trabajar con varios alcances puede requerir cambios estructurales en la base de código para solicitar acceso a los permisos solo cuando se necesiten, en lugar de todos los casos a la vez. Esto se conoce como autorización incremental. Cada solicitud debe contener el menor alcance posible y, en el mejor de los casos, un solo alcance. Consulta cómo manejar el consentimiento del usuario para obtener más información sobre cómo actualizar la app para la autorización incremental.

Cuando un token de acceso vence, el módulo gapi.auth2 obtiene automáticamente un token de acceso nuevo y válido para tu aplicación web. A fin de mejorar la seguridad del usuario, la biblioteca de los Servicios de identidad de Google no admite este proceso de actualización automática de tokens. Tu app web debe actualizarse para detectar un token de acceso vencido y solicitar uno nuevo. Consulta la sección de control de tokens que aparece a continuación para obtener más información.

Flujo de código de autorización

Agrega un vínculo o botón para llamar a requestCode() a fin de solicitar un código de autorización de Google. Para ver un ejemplo, consulta Activar el flujo de código de OAuth 2.0.

Consulta la sección de control de tokens que aparece a continuación para obtener más información sobre cómo responder a un token de acceso vencido o revocado.

Manejo de tokens

Agregar errores de detección para detectar llamadas fallidas de la API de Google cuando se usa un token de acceso vencido o revocado y solicitar uno nuevo.

Las API de Google muestran un código de estado HTTP de 401 Unauthorized y invalid_token cuando se usa un token de acceso vencido o revocado. Para ver un ejemplo, consulta Respuesta de token no válida.

Tokens vencidos

Los tokens de acceso son de corta duración y, a menudo, son válidos solo por unos minutos.

Revocación de tokens

En cualquier momento, el propietario de una cuenta de Google puede revocar el consentimiento que se le otorgó anteriormente. Esta acción invalida los tokens de acceso y de actualización existentes. La revocación se puede activar desde tu plataforma con revoke() o a través de una Cuenta de Google.

Reemplaza las referencias de clientes de JavaScript del Acceso con Google por Google Identity Services:

Métodos:

  • getAuthInstance().disconnect() con google.accounts.oauth2.revoke()
  • GoogleUser.disconnect() con google.accounts.oauth2.revoke()

Llama a revoke cuando un usuario borre su cuenta en tu plataforma o quiera quitar el consentimiento para compartir datos con tu app.

Google muestra un diálogo de consentimiento al usuario cuando tu aplicación web o la plataforma de backend solicitan un token de acceso. Consulta los diálogos de consentimiento de ejemplo que muestra Google a los usuarios.

Antes de emitir un token de acceso a la app, se requiere una sesión de Google existente y activa para solicitar el consentimiento del usuario y registrar el resultado. Es posible que el usuario deba acceder a una Cuenta de Google si aún no se estableció una sesión existente.

Acceso de usuarios

Los usuarios pueden acceder a una Cuenta de Google en otra pestaña del navegador o de forma nativa por medio de un navegador o sistema operativo. Te recomendamos que agregues Acceder con Google a tu sitio para establecer una sesión activa entre una Cuenta de Google y el navegador cuando el usuario abre tu app por primera vez. De esta manera, se ofrecen los siguientes beneficios:

  • Minimiza la cantidad de veces que un usuario debe acceder, si solicitas un token de acceso, se inicia el proceso de acceso a una Cuenta de Google si aún no existe una sesión activa.
  • Usa directamente el campo credencial email del token de ID de JWT como el valor del parámetro hint en los objetos CodeClientConfig o TokenClientConfig. Esto es muy útil si tu plataforma no mantiene un sistema de administración de cuentas de usuario.
  • Busca y asocia una Cuenta de Google con una cuenta de usuario local existente en tu plataforma, lo que ayuda a minimizar las cuentas duplicadas en tu plataforma.
  • Cuando se crea una cuenta local nueva, tus diálogos y flujo de registro se pueden separar claramente de los diálogos y flujos de autenticación de los usuarios, lo que reduce la cantidad de pasos necesarios y mejora el porcentaje de abandono.

Después del acceso, y antes de que se emita un token de acceso, los usuarios deben proporcionar el consentimiento para tu aplicación en los permisos solicitados.

Después del consentimiento, se muestra un token de acceso junto con una lista de alcances aprobados o rechazados por el usuario.

Los permisos detallados permiten que los usuarios aprueben o rechacen permisos individuales. Cuando se solicita acceso a varios permisos, cada alcance se otorga o se rechaza independientemente de los otros alcances. Según la elección del usuario, tu app habilita selectivamente las características y las funciones que dependen de un alcance individual.

Flujo implícito

Reemplaza las referencias de clientes de JavaScript del Acceso con Google por Google Identity Services:

Objetos:

  • gapi.auth2.AuthorizeResponse con TokenClient.TokenResponse
  • gapi.auth2.AuthResponse con TokenClient.TokenResponse

Métodos:

  • GoogleUser.hasGrantedScopes() con google.accounts.oauth2.hasGrantedAllScopes()
  • GoogleUser.getGrantedScopes() con google.accounts.oauth2.hasGrantedAllScopes()

Quita las referencias de clientes de JavaScript con Acceso con Google:

Métodos:

  • GoogleUser.getAuthResponse()

Para actualizar tu app web con hasGrantedAllScopes() y hasGrantedAnyScope(), sigue este ejemplo de permisos detallados.

Flujo de código de autorización

Actualizar o agregar un extremo de código de autorización a tu plataforma de backend; para ello, sigue las instrucciones en manejo del código de autorización.

Actualiza tu plataforma a fin de seguir los pasos descritos en la guía Usa el modelo de código para validar la solicitud y obtener un token de acceso y una actualización.

Actualiza tu plataforma para habilitar o inhabilitar de forma selectiva las características y funciones según los permisos individuales que aprobó el usuario. Para ello, sigue las instrucciones sobre la autorización incremental y los permisos de acceso que otorga el usuario.

Ejemplos de flujo implícito

Método antiguo

Biblioteca cliente de GAPI

Ejemplo de la biblioteca cliente de la API de Google para JavaScript que se ejecuta en el navegador mediante un diálogo emergente para el consentimiento del usuario

El módulo gapi.auth2 se carga y usa automáticamente por gapi.client.init(), por lo que está oculto.

<!DOCTYPE html>
  <html>
    <head>
      <script src="https://apis.google.com/js/api.js"></script>
      <script>
        function start() {
          gapi.client.init({
            'apiKey': 'YOUR_API_KEY',
            'clientId': 'YOUR_CLIENT_ID',
            'scope': 'https://www.googleapis.com/auth/cloud-translation',
            'discoveryDocs': ['https://www.googleapis.com/discovery/v1/apis/translate/v2/rest'],
          }).then(function() {
            // Execute an API request which is returned as a Promise.
            // The method name language.translations.list comes from the API discovery.
            return gapi.client.language.translations.list({
              q: 'hello world',
              source: 'en',
              target: 'de',
            });
          }).then(function(response) {
            console.log(response.result.data.translations[0].translatedText);
          }, function(reason) {
            console.log('Error: ' + reason.result.error.message);
          });
        };

        // Load the JavaScript client library and invoke start afterwards.
        gapi.load('client', start);
      </script>
    </head>
    <body>
      <div id="results"></div>
    </body>
  </html>

Biblioteca cliente de JS

OAuth 2.0 para aplicaciones web del cliente que se ejecutan en el navegador mediante un diálogo emergente para el consentimiento del usuario

El módulo gapi.auth2 se carga de forma manual.

<!DOCTYPE html>
<html><head></head><body>
<script>
  var GoogleAuth;
  var SCOPE = 'https://www.googleapis.com/auth/drive.metadata.readonly';
  function handleClientLoad() {
    // Load the API's client and auth2 modules.
    // Call the initClient function after the modules load.
    gapi.load('client:auth2', initClient);
  }

  function initClient() {
    // In practice, your app can retrieve one or more discovery documents.
    var discoveryUrl = 'https://www.googleapis.com/discovery/v1/apis/drive/v3/rest';

    // Initialize the gapi.client object, which app uses to make API requests.
    // Get API key and client ID from API Console.
    // 'scope' field specifies space-delimited list of access scopes.
    gapi.client.init({
        'apiKey': 'YOUR_API_KEY',
        'clientId': 'YOUR_CLIENT_ID',
        'discoveryDocs': [discoveryUrl],
        'scope': SCOPE
    }).then(function () {
      GoogleAuth = gapi.auth2.getAuthInstance();

      // Listen for sign-in state changes.
      GoogleAuth.isSignedIn.listen(updateSigninStatus);

      // Handle initial sign-in state. (Determine if user is already signed in.)
      var user = GoogleAuth.currentUser.get();
      setSigninStatus();

      // Call handleAuthClick function when user clicks on
      //      "Sign In/Authorize" button.
      $('#sign-in-or-out-button').click(function() {
        handleAuthClick();
      });
      $('#revoke-access-button').click(function() {
        revokeAccess();
      });
    });
  }

  function handleAuthClick() {
    if (GoogleAuth.isSignedIn.get()) {
      // User is authorized and has clicked "Sign out" button.
      GoogleAuth.signOut();
    } else {
      // User is not signed in. Start Google auth flow.
      GoogleAuth.signIn();
    }
  }

  function revokeAccess() {
    GoogleAuth.disconnect();
  }

  function setSigninStatus() {
    var user = GoogleAuth.currentUser.get();
    var isAuthorized = user.hasGrantedScopes(SCOPE);
    if (isAuthorized) {
      $('#sign-in-or-out-button').html('Sign out');
      $('#revoke-access-button').css('display', 'inline-block');
      $('#auth-status').html('You are currently signed in and have granted ' +
          'access to this app.');
    } else {
      $('#sign-in-or-out-button').html('Sign In/Authorize');
      $('#revoke-access-button').css('display', 'none');
      $('#auth-status').html('You have not authorized this app or you are ' +
          'signed out.');
    }
  }

  function updateSigninStatus() {
    setSigninStatus();
  }
</script>

<button id="sign-in-or-out-button"
        style="margin-left: 25px">Sign In/Authorize</button>
<button id="revoke-access-button"
        style="display: none; margin-left: 25px">Revoke access</button>

<div id="auth-status" style="display: inline; padding-left: 25px"></div><hr>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script async defer src="https://apis.google.com/js/api.js"
        onload="this.onload=function(){};handleClientLoad()"
        onreadystatechange="if (this.readyState === 'complete') this.onload()">
</script>
</body></html>

Extremos de OAuth 2.0

OAuth 2.0 para aplicaciones web del cliente que se ejecutan en el navegador mediante redireccionamientos a Google para el consentimiento de los usuarios

En este ejemplo, se muestran las llamadas directas a los extremos de OAuth 2.0 de Google desde el navegador del usuario y no se usa el módulo gapi.auth2 ni una biblioteca de JavaScript.

<!DOCTYPE html>
<html><head></head><body>
<script>
  var YOUR_CLIENT_ID = 'REPLACE_THIS_VALUE';
  var YOUR_REDIRECT_URI = 'REPLACE_THIS_VALUE';
  var fragmentString = location.hash.substring(1);

  // Parse query string to see if page request is coming from OAuth 2.0 server.
  var params = {};
  var regex = /([^&=]+)=([^&]*)/g, m;
  while (m = regex.exec(fragmentString)) {
    params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
  }
  if (Object.keys(params).length > 0) {
    localStorage.setItem('oauth2-test-params', JSON.stringify(params) );
    if (params['state'] && params['state'] == 'try_sample_request') {
      trySampleRequest();
    }
  }

  // If there's an access token, try an API request.
  // Otherwise, start OAuth 2.0 flow.
  function trySampleRequest() {
    var params = JSON.parse(localStorage.getItem('oauth2-test-params'));
    if (params && params['access_token']) {
      var xhr = new XMLHttpRequest();
      xhr.open('GET',
          'https://www.googleapis.com/drive/v3/about?fields=user&' +
          'access_token=' + params['access_token']);
      xhr.onreadystatechange = function (e) {
        if (xhr.readyState === 4 && xhr.status === 200) {
          console.log(xhr.response);
        } else if (xhr.readyState === 4 && xhr.status === 401) {
          // Token invalid, so prompt for user permission.
          oauth2SignIn();
        }
      };
      xhr.send(null);
    } else {
      oauth2SignIn();
    }
  }

  /*
   * Create form to request access token from Google's OAuth 2.0 server.
   */
  function oauth2SignIn() {
    // Google's OAuth 2.0 endpoint for requesting an access token
    var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';

    // Create element to open OAuth 2.0 endpoint in new window.
    var form = document.createElement('form');
    form.setAttribute('method', 'GET'); // Send as a GET request.
    form.setAttribute('action', oauth2Endpoint);

    // Parameters to pass to OAuth 2.0 endpoint.
    var params = {'client_id': YOUR_CLIENT_ID,
                  'redirect_uri': YOUR_REDIRECT_URI,
                  'scope': 'https://www.googleapis.com/auth/drive.metadata.readonly',
                  'state': 'try_sample_request',
                  'include_granted_scopes': 'true',
                  'response_type': 'token'};

    // Add form parameters as hidden input values.
    for (var p in params) {
      var input = document.createElement('input');
      input.setAttribute('type', 'hidden');
      input.setAttribute('name', p);
      input.setAttribute('value', params[p]);
      form.appendChild(input);
    }

    // Add form to page and submit it to open the OAuth 2.0 endpoint.
    document.body.appendChild(form);
    form.submit();
  }
</script>

<button onclick="trySampleRequest();">Try sample request</button>
</body></html>

La nueva forma

Solo GIS

En este ejemplo, se muestra solo la biblioteca de JavaScript del servicio de Google Identity que usa el modelo de token y el diálogo emergente para el consentimiento del usuario. Se proporciona a fin de ilustrar la cantidad mínima de pasos necesarios para configurar un cliente, solicitar y obtener un token de acceso y llamar a una API de Google.

<!DOCTYPE html>
<html>
  <head>
    <script src="https://accounts.google.com/gsi/client" onload="initClient()" async defer></script>
  </head>
  <body>
    <script>
      var client;
      var access_token;

      function initClient() {
        client = google.accounts.oauth2.initTokenClient({
          client_id: 'YOUR_CLIENT_ID',
          scope: 'https://www.googleapis.com/auth/calendar.readonly \
                  https://www.googleapis.com/auth/contacts.readonly',
          callback: (tokenResponse) => {
            access_token = tokenResponse.access_token;
          },
        });
      }
      function getToken() {
        client.requestAccessToken();
      }
      function revokeToken() {
        google.accounts.oauth2.revoke(access_token, () => {console.log('access token revoked')});
      }
      function loadCalendar() {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', 'https://www.googleapis.com/calendar/v3/calendars/primary/events');
        xhr.setRequestHeader('Authorization', 'Bearer ' + access_token);
        xhr.send();
      }
    </script>
    <h1>Google Identity Services Authorization Token model</h1>
    <button onclick="getToken();">Get access token</button><br><br>
    <button onclick="loadCalendar();">Load Calendar</button><br><br>
    <button onclick="revokeToken();">Revoke token</button>
  </body>
</html>

GAPI asíncrona o de espera

En este ejemplo, se muestra cómo agregar la biblioteca del servicio de identidad de Google con el modelo de token, quitar el módulo gapi.auth2 y llamar a una API mediante la Biblioteca cliente de la API de Google para JavaScript.

Las promesas, las asíncronas y las esperas se usan para aplicar el orden de carga de la biblioteca y detectar y reintentar errores de autorización. Se realiza una llamada a la API solo después de que haya un token de acceso válido disponible.

Se espera que los usuarios presionen el botón "Mostrar calendario" cuando falte el token de acceso cuando la página se haya cargado por primera vez o más tarde después de que caduque.

<!DOCTYPE html>
<html>
<head></head>
<body>
  <h1>GAPI with GIS async/await</h1>
  <button id="showEventsBtn" onclick="showEvents();">Show Calendar</button><br><br>
  <button id="revokeBtn" onclick="revokeToken();">Revoke access token</button>

  <script>

    const gapiLoadPromise = new Promise((resolve, reject) => {
      gapiLoadOkay = resolve;
      gapiLoadFail = reject;
    });
    const gisLoadPromise = new Promise((resolve, reject) => {
      gisLoadOkay = resolve;
      gisLoadFail = reject;
    });

    var tokenClient;

    (async () => {
      document.getElementById("showEventsBtn").style.visibility="hidden";
      document.getElementById("revokeBtn").style.visibility="hidden";

      // First, load and initialize the gapi.client
      await gapiLoadPromise;
      await new Promise((resolve, reject) => {
        // NOTE: the 'auth2' module is no longer loaded.
        gapi.load('client', {callback: resolve, onerror: reject});
      });
      await gapi.client.init({
        // NOTE: OAuth2 'scope' and 'client_id' parameters have moved to initTokenClient().
      })
      .then(function() {  // Load the Calendar API discovery document.
        gapi.client.load('https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest');
      });

      // Now load the GIS client
      await gisLoadPromise;
      await new Promise((resolve, reject) => {
        try {
          tokenClient = google.accounts.oauth2.initTokenClient({
              client_id: 'YOUR_CLIENT_ID',
              scope: 'https://www.googleapis.com/auth/calendar.readonly',
              prompt: 'consent',
              callback: '',  // defined at request time in await/promise scope.
          });
          resolve();
        } catch (err) {
          reject(err);
        }
      });

      document.getElementById("showEventsBtn").style.visibility="visible";
      document.getElementById("revokeBtn").style.visibility="visible";
    })();

    async function getToken(err) {

      if (err.result.error.code == 401 || (err.result.error.code == 403) &&
          (err.result.error.status == "PERMISSION_DENIED")) {

        // The access token is missing, invalid, or expired, prompt for user consent to obtain one.
        await new Promise((resolve, reject) => {
          try {
            // Settle this promise in the response callback for requestAccessToken()
            tokenClient.callback = (resp) => {
              if (resp.error !== undefined) {
                reject(resp);
              }
              // GIS has automatically updated gapi.client with the newly issued access token.
              console.log('gapi.client access token: ' + JSON.stringify(gapi.client.getToken()));
              resolve(resp);
            };
            tokenClient.requestAccessToken();
          } catch (err) {
            console.log(err)
          }
        });
      } else {
        // Errors unrelated to authorization: server errors, exceeding quota, bad requests, and so on.
        throw new Error(err);
      }
    }

    function showEvents() {

      // Try to fetch a list of Calendar events. If a valid access token is needed,
      // prompt to obtain one and then retry the original request.
      gapi.client.calendar.events.list({ 'calendarId': 'primary' })
      .then(calendarAPIResponse => console.log(JSON.stringify(calendarAPIResponse)))
      .catch(err  => getToken(err))  // for authorization errors obtain an access token
      .then(retry => gapi.client.calendar.events.list({ 'calendarId': 'primary' }))
      .then(calendarAPIResponse => console.log(JSON.stringify(calendarAPIResponse)))
      .catch(err  => console.log(err));   // cancelled by user, timeout, etc.
    }

    function revokeToken() {
      let cred = gapi.client.getToken();
      if (cred !== null) {
        google.accounts.oauth2.revoke(cred.access_token, () => {console.log('Revoked: ' + cred.access_token)});
        gapi.client.setToken('');
      }
    }

  </script>

  <script async defer src="https://apis.google.com/js/api.js" onload="gapiLoadOkay()" onerror="gapiLoadFail(event)"></script>
  <script async defer src="https://accounts.google.com/gsi/client" onload="gisLoadOkay()" onerror="gisLoadFail(event)"></script>

</body>
</html>

Devolución de llamada de GAPI

En este ejemplo, se muestra cómo agregar la biblioteca del servicio de identidad de Google con el modelo de token, quitar el módulo gapi.auth2 y llamar a una API mediante la Biblioteca cliente de la API de Google para JavaScript.

Las variables se usan para aplicar el orden de carga de la biblioteca. Las llamadas a la GAPI se realizan desde la devolución de llamada después de que se muestra un token de acceso válido.

Se espera que los usuarios presionen el botón Mostrar calendario cuando la página se cargue por primera vez y vuelvan a hacerlo cuando quieran actualizar su información de calendario.

<!DOCTYPE html>
<html>
<head>
  <script async defer src="https://apis.google.com/js/api.js" onload="gapiLoad()"></script>
  <script async defer src="https://accounts.google.com/gsi/client" onload="gisInit()"></script>
</head>
<body>
  <h1>GAPI with GIS callbacks</h1>
  <button id="showEventsBtn" onclick="showEvents();">Show Calendar</button><br><br>
  <button id="revokeBtn" onclick="revokeToken();">Revoke access token</button>
  <script>
    let tokenClient;
    let gapiInited;
    let gisInited;

    document.getElementById("showEventsBtn").style.visibility="hidden";
    document.getElementById("revokeBtn").style.visibility="hidden";

    function checkBeforeStart() {
       if (gapiInited && gisInited){
          // Start only when both gapi and gis are initialized.
          document.getElementById("showEventsBtn").style.visibility="visible";
          document.getElementById("revokeBtn").style.visibility="visible";
       }
    }

    function gapiInit() {
      gapi.client.init({
        // NOTE: OAuth2 'scope' and 'client_id' parameters have moved to initTokenClient().
      })
      .then(function() {  // Load the Calendar API discovery document.
        gapi.client.load('https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest');
        gapiInited = true;
        checkBeforeStart();
      });
    }

    function gapiLoad() {
        gapi.load('client', gapiInit)
    }

    function gisInit() {
     tokenClient = google.accounts.oauth2.initTokenClient({
                client_id: 'YOUR_CLIENT_ID',
                scope: 'https://www.googleapis.com/auth/calendar.readonly',
                callback: '',  // defined at request time
            });
      gisInited = true;
      checkBeforeStart();
    }

    function showEvents() {

      tokenClient.callback = (resp) => {
        if (resp.error !== undefined) {
          throw(resp);
        }
        // GIS has automatically updated gapi.client with the newly issued access token.
        console.log('gapi.client access token: ' + JSON.stringify(gapi.client.getToken()));

        gapi.client.calendar.events.list({ 'calendarId': 'primary' })
        .then(calendarAPIResponse => console.log(JSON.stringify(calendarAPIResponse)))
        .catch(err => console.log(err));

        document.getElementById("showEventsBtn").innerText = "Refresh Calendar";
      }

      // Conditionally ask users to select the Google Account they'd like to use,
      // and explicitly obtain their consent to fetch their Calendar.
      // NOTE: To request an access token a user gesture is necessary.
      if (gapi.client.getToken() === null) {
        // Prompt the user to select an Google Account and asked for consent to share their data
        // when establishing a new session.
        tokenClient.requestAccessToken({prompt: 'consent'});
      } else {
        // Skip display of account chooser and consent dialog for an existing session.
        tokenClient.requestAccessToken({prompt: ''});
      }
    }

    function revokeToken() {
      let cred = gapi.client.getToken();
      if (cred !== null) {
        google.accounts.oauth2.revoke(cred.access_token, () => {console.log('Revoked: ' + cred.access_token)});
        gapi.client.setToken('');
        document.getElementById("showEventsBtn").innerText = "Show Calendar";
      }
    }
  </script>
</body>
</html>

Ejemplos de flujo de código de autorización

La UX emergente de la biblioteca de Google Identity Service puede usar un redireccionamiento de URL para mostrar un código de autorización directamente a tu extremo del token de backend o un controlador de devolución de llamada de JavaScript que se ejecute en el navegador del usuario que envía la respuesta a tu plataforma. En cualquier caso, tu plataforma de backend completará el flujo de OAuth 2.0 para obtener un token válido de actualización y acceso.

Método antiguo

Aplicaciones web del servidor

Acceso con Google para apps del servidor que se ejecutan en la plataforma de backend mediante un redireccionamiento a Google con el fin de obtener el consentimiento del usuario.

<!DOCTYPE html>
<html>
  <head>
    <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>
    <script>
      function start() {
        gapi.load('auth2', function() {
          auth2 = gapi.auth2.init({
            client_id: 'YOUR_CLIENT_ID',
            api_key: 'YOUR_API_KEY',
            discovery_docs: ['https://www.googleapis.com/discovery/v1/apis/translate/v2/rest'],
            // Scopes to request in addition to 'profile' and 'email'
            scope: 'https://www.googleapis.com/auth/cloud-translation',
          });
        });
      }
      function signInCallback(authResult) {
        if (authResult['code']) {
          console.log("sending AJAX request");
          // Send authorization code obtained from Google to backend platform
          $.ajax({
            type: 'POST',
            url: 'YOUR_AUTHORIZATION_CODE_ENDPOINT_URL',
            // Always include an X-Requested-With header to protect against CSRF attacks.
            headers: {
              'X-Requested-With': 'XMLHttpRequest'
            },
            contentType: 'application/octet-stream; charset=utf-8',
            success: function(result) {
              console.log(result);
            },
            processData: false,
            data: authResult['code']
          });
        } else {
          console.log('error: failed to obtain authorization code')
        }
      }
    </script>
  </head>
  <body>
    <button id="signinButton">Sign In With Google</button>
    <script>
      $('#signinButton').click(function() {
        // Obtain an authorization code from Google
        auth2.grantOfflineAccess().then(signInCallback);
      });
    </script>
  </body>
</html>

HTTP/REST que usa el redireccionamiento

Uso de OAuth 2.0 para aplicaciones de servidor web a fin de enviar código de autorización del navegador del usuario a la plataforma de backend. El consentimiento del usuario se redirecciona mediante el redireccionamiento del navegador del usuario a Google.

/\*
 \* Create form to request access token from Google's OAuth 2.0 server.
 \*/
function oauthSignIn() {
  // Google's OAuth 2.0 endpoint for requesting an access token
  var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';
  // Create &lt;form> element to submit parameters to OAuth 2.0 endpoint.
  var form = document.createElement('form');
  form.setAttribute('method', 'GET'); // Send as a GET request.
  form.setAttribute('action', oauth2Endpoint);
  // Parameters to pass to OAuth 2.0 endpoint.
  var params = {'client\_id': 'YOUR_CLIENT_ID',
                'redirect\_uri': 'YOUR_AUTHORIZATION_CODE_ENDPOINT_URL',
                'response\_type': 'token',
                'scope': 'https://www.googleapis.com/auth/drive.metadata.readonly',
                'include\_granted\_scopes': 'true',
                'state': 'pass-through value'};
  // Add form parameters as hidden input values.
  for (var p in params) {
    var input = document.createElement('input');
    input.setAttribute('type', 'hidden');
    input.setAttribute('name', p);
    input.setAttribute('value', params[p]);
    form.appendChild(input);
  }

  // Add form to page and submit it to open the OAuth 2.0 endpoint.
  document.body.appendChild(form);
  form.submit();
}

La nueva forma

UX emergente de GIS

En este ejemplo, solo se muestra la biblioteca de JavaScript del servicio de identidad de Google con el modelo de código de autorización, un diálogo emergente para el consentimiento del usuario y el controlador de devolución de llamada para recibir el código de autorización de Google. Se proporciona a fin de ilustrar la cantidad mínima de pasos necesarios para configurar un cliente, obtener el consentimiento y enviar un código de autorización a tu plataforma de backend.

<!DOCTYPE html>
<html>
  <head>
    <script src="https://accounts.google.com/gsi/client" onload="initClient()" async defer></script>
  </head>
  <body>
    <script>
      var client;
      function initClient() {
        client = google.accounts.oauth2.initCodeClient({
          client_id: 'YOUR_CLIENT_ID',
          scope: 'https://www.googleapis.com/auth/calendar.readonly',
          ux_mode: 'popup',
          callback: (response) => {
            var code_receiver_uri = 'YOUR_AUTHORIZATION_CODE_ENDPOINT_URI',
            // Send auth code to your backend platform
            const xhr = new XMLHttpRequest();
            xhr.open('POST', code_receiver_uri, true);
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
            xhr.onload = function() {
              console.log('Signed in as: ' + xhr.responseText);
            };
            xhr.send('code=' + code);
            // After receipt, the code is exchanged for an access token and
            // refresh token, and the platform then updates this web app
            // running in user's browser with the requested calendar info.
          },
        });
      }
      function getAuthCode() {
        // Request authorization code and obtain user consent
          client.requestCode();
      }
    </script>
    <button onclick="getAuthCode();">Load Your Calendar</button>
  </body>
</html>

UX de redireccionamiento de GIS

El modelo de código de autorización admite los modos emergente y de redireccionamiento de UX para enviar un código de autorización por usuario al extremo alojado por tu plataforma. El modo de UX de redireccionamiento se muestra aquí:

<!DOCTYPE html>
<html>
  <head>
    <script src="https://accounts.google.com/gsi/client" onload="initClient()" async defer></script>
  </head>
  <body>
    <script>
      var client;
      function initClient() {
        client = google.accounts.oauth2.initCodeClient({
          client_id: 'YOUR_CLIENT_ID',
          scope: 'https://www.googleapis.com/auth/calendar.readonly \
                  https://www.googleapis.com/auth/photoslibrary.readonly',
          ux_mode: 'redirect',
          redirect_uri: 'YOUR_AUTHORIZATION_CODE_ENDPOINT_URI'
        });
      }
      // Request an access token
      function getAuthCode() {
        // Request authorization code and obtain user consent
          client.requestCode();
      }
    </script>
    <button onclick="getAuthCode();">Load Your Calendar</button>
  </body>
</html>

Bibliotecas de JavaScript

Los Servicios de identidad de Google son una sola biblioteca de JavaScript que se usa para la autenticación y autorización de usuarios que consolida y reemplaza las funciones y las funciones que se encuentran en varios módulos y bibliotecas diferentes:

Acciones para realizar cuando migres a los servicios de identidad:

Biblioteca de JS existente Nueva biblioteca de JS Notas
apis.google.com/js/api.js accounts.google.com/gsi/client Agrega una biblioteca nueva y sigue el flujo implícito.
apis.google.com/js/client.js accounts.google.com/gsi/client Agrega una biblioteca nueva y el flujo de código de autorización.

Referencia rápida de la biblioteca

Comparación de objetos y métodos entre la biblioteca anterior del cliente de JavaScript de Acceso con Google y la biblioteca nueva de los Servicios de identidad de Google y las notas con información adicional y medidas para tomar durante la migración

Antigua New Notas
GoogleAuth y los métodos asociados:
GoogleAuth.attachClickHandler() Quitar
GoogleAuth.currentUser.get(): Quitar
GoogleAuth.currentUser.listen(). Quitar
GoogleAuth.disconnect() google.accounts.oauth2.revoke Reemplaza lo viejo por lo nuevo. La revocación también puede ocurrir en https://myaccount.google.com/permissions
GoogleAuth.grantOfflineAccess() Quite el flujo de código de autorización.
GoogleAuth.isSignedIn.get() Quitar
GoogleAuth.isSignedIn.listen(). Quitar
GoogleAuth.signIn(). Quitar
GoogleAuth.signOut(); Quitar
GoogleAuth.then() Quitar
GoogleUser y los métodos asociados:
GoogleUser.disconnect() google.accounts.id.revoke Reemplaza lo viejo por lo nuevo. La revocación también puede ocurrir en https://myaccount.google.com/permissions
GoogleUser.getAuthResponse(). requestCode() o requestAccessToken() Reemplazar el anterior por uno nuevo
GoogleUser.getBasicProfile(). Quitar. En su lugar, usa el token de ID. Consulta Cómo migrar el acceso con Google.
GoogleUser.getGrantedScopes() hasGrantedAnyScope() Reemplazar el anterior por uno nuevo
GoogleUser.getHostedDomain() Quitar
GoogleUser.getId() Quitar
GoogleUser.grantOfflineAccess() Quite el flujo de código de autorización.
GoogleUser.grant(). Quitar
GoogleUser.hasGrantedScopes() hasGrantedAnyScope() Reemplazar el anterior por uno nuevo
UsuarioDeGoogle.isSignedIn() Quitar
GoogleUser.reloadAuthResponse(). requestAccessToken() Quita el token anterior, llama al nuevo para reemplazar el token de acceso vencido o revocado.
gapi.auth2 y los métodos asociados:
Objeto gapi.auth2.AuthorizeConfig TokenClientConfig o CodeClientConfig Reemplazar el anterior por uno nuevo
Objeto gapi.auth2.AuthorizeResponse Quitar
Objeto gapi.auth2.AuthResponse Quitar
gapi.auth2.authorized() requestCode() o requestAccessToken() Reemplazar el anterior por uno nuevo
gapi.auth2.ClientConfig(). TokenClientConfig o CodeClientConfig Reemplazar el anterior por uno nuevo
gapi.auth2.getAuthInstance() Quitar
gapi.auth2.init(). initTokenClient() o initCodeClient() Reemplazar el anterior por uno nuevo
Objeto gapi.auth2.OfflineAccessOptions Quitar
Objeto gapi.auth2.SignInOptions Quitar
gapi.signin2 y los métodos asociados:
gapi.signin2.render() Quitar. La carga de DOM HTML del elemento g_id_signin o de la llamada JS a google.accounts.id.renderButton activa el acceso del usuario a una Cuenta de Google.

Credenciales de ejemplo

Credenciales existentes

La biblioteca de la plataforma de Acceso con Google, la biblioteca cliente de la API de Google para JavaScript o las llamadas directas a los extremos de Google Auth 2.0 muestran un token de acceso de OAuth 2.0 y un token de ID de OpenID Connect en una sola respuesta.

Ejemplo de respuesta que contiene access_token y id_token:

  {
    "token_type": "Bearer",
    "access_token": "ya29.A0ARrdaM-SmArZaCIh68qXsZSzyeU-8mxhQERHrP2EXtxpUuZ-3oW8IW7a6D2J6lRnZrRj8S6-ZcIl5XVEqnqxq5fuMeDDH_6MZgQ5dgP7moY-yTiKR5kdPm-LkuPM-mOtUsylWPd1wpRmvw_AGOZ1UUCa6UD5Hg",
    "scope": "https://www.googleapis.com/auth/calendar.readonly",
    "login_hint": "AJDLj6I2d1RH77cgpe__DdEree1zxHjZJr4Q7yOisoumTZUmo5W2ZmVFHyAomUYzLkrluG-hqt4RnNxrPhArd5y6p8kzO0t8xIfMAe6yhztt6v2E-_Bb4Ec3GLFKikHSXNh5bI-gPrsI",
    "expires_in": 3599,
    "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjkzNDFhYmM0MDkyYjZmYzAzOGU0MDNjOTEwMjJkZDNlNDQ1MzliNTYiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJhY2NvdW50cy5nb29nbGUuY29tIiwiYXpwIjoiNTM4MzQ0NjUzMjU1LTc1OGM1aDVpc2M0NXZnazI3ZDhoOGRlYWJvdnBnNnRvLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwiYXVkIjoiNTM4MzQ0NjUzMjU1LTc1OGM1aDVpc2M0NXZnazI3ZDhoOGRlYWJvdnBnNnRvLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwic3ViIjoiMTE3NzI2NDMxNjUxOTQzNjk4NjAwIiwiaGQiOiJnb29nbGUuY29tIiwiZW1haWwiOiJkYWJyaWFuQGdvb2dsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXRfaGFzaCI6IkJBSW55TjN2MS1ZejNLQnJUMVo0ckEiLCJuYW1lIjoiQnJpYW4gRGF1Z2hlcnR5IiwicGljdHVyZSI6Imh0dHBzOi8vbGgzLmdvb2dsZXVzZXJjb250ZW50LmNvbS9hLS9BT2gxNEdnenAyTXNGRGZvbVdMX3VDemRYUWNzeVM3ZGtxTE5ybk90S0QzVXNRPXM5Ni1jIiwiZ2l2ZW5fbmFtZSI6IkJyaWFuIiwiZmFtaWx5X25hbWUiOiJEYXVnaGVydHkiLCJsb2NhbGUiOiJlbiIsImlhdCI6MTYzODk5MTYzOCwiZXhwIjoxNjM4OTk1MjM4LCJqdGkiOiI5YmRkZjE1YWFiNzE2ZDhjYmJmNDYwMmM1YWM3YzViN2VhMDQ5OTA5In0.K3EA-3Adw5HA7O8nJVCsX1HmGWxWzYk3P7ViVBb4H4BoT2-HIgxKlx1mi6jSxIUJGEekjw9MC-nL1B9Asgv1vXTMgoGaNna0UoEHYitySI23E5jaMkExkTSLtxI-ih2tJrA2ggfA9Ekj-JFiMc6MuJnwcfBTlsYWRcZOYVw3QpdTZ_VYfhUu-yERAElZCjaAyEXLtVQegRe-ymScra3r9S92TA33ylMb3WDTlfmDpWL0CDdDzby2asXYpl6GQ7SdSj64s49Yw6mdGELZn5WoJqG7Zr2KwIGXJuSxEo-wGbzxNK-mKAiABcFpYP4KHPEUgYyz3n9Vqn2Tfrgp-g65BQ",
    "session_state": {
      "extraQueryParams": {
        "authuser": "0"
      }
    },
    "first_issued_at": 1638991637982,
    "expires_at": 1638995236982,
    "idpId": "google"
  }

Credencial de servicios de identidad de Google

La biblioteca de los Servicios de identidad de Google muestra lo siguiente:

  • un token de acceso cuando se utiliza para la autorización:
  {
    "access_token": "ya29.A0ARrdaM_LWSO-uckLj7IJVNSfnUityT0Xj-UCCrGxFQdxmLiWuAosnAKMVQ2Z0LLqeZdeJii3TgULp6hR_PJxnInBOl8UoUwWoqsrGQ7-swxgy97E8_hnzfhrOWyQBmH6zs0_sUCzwzhEr_FAVqf92sZZHphr0g",
    "token_type": "Bearer",
    "expires_in": 3599,
    "scope": "https://www.googleapis.com/auth/calendar.readonly"
  }
  • o un token de ID cuando se usa para la autenticación:
  {
  "clientId": "538344653255-758c5h5isc45vgk27d8h8deabovpg6to.apps.googleusercontent.com",
  "credential": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImMxODkyZWI0OWQ3ZWY5YWRmOGIyZTE0YzA1Y2EwZDAzMjcxNGEyMzciLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJuYmYiOjE2MzkxNTcyNjQsImF1ZCI6IjUzODM0NDY1MzI1NS03NThjNWg1aXNjNDV2Z2syN2Q4aDhkZWFib3ZwZzZ0by5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsInN1YiI6IjExNzcyNjQzMTY1MTk0MzY5ODYwMCIsIm5vbmNlIjoiZm9vYmFyIiwiaGQiOiJnb29nbGUuY29tIiwiZW1haWwiOiJkYWJyaWFuQGdvb2dsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYXpwIjoiNTM4MzQ0NjUzMjU1LTc1OGM1aDVpc2M0NXZnazI3ZDhoOGRlYWJvdnBnNnRvLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwibmFtZSI6IkJyaWFuIERhdWdoZXJ0eSIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS0vQU9oMTRHZ3pwMk1zRkRmb21XTF91Q3pkWFFjc3lTN2RrcUxOcm5PdEtEM1VzUT1zOTYtYyIsImdpdmVuX25hbWUiOiJCcmlhbiIsImZhbWlseV9uYW1lIjoiRGF1Z2hlcnR5IiwiaWF0IjoxNjM5MTU3NTY0LCJleHAiOjE2MzkxNjExNjQsImp0aSI6IjRiOTVkYjAyZjU4NDczMmUxZGJkOTY2NWJiMWYzY2VhYzgyMmI0NjUifQ.Cr-AgMsLFeLurnqyGpw0hSomjOCU4S3cU669Hyi4VsbqnAV11zc_z73o6ahe9Nqc26kPVCNRGSqYrDZPfRyTnV6g1PIgc4Zvl-JBuy6O9HhClAK1HhMwh1FpgeYwXqrng1tifmuotuLQnZAiQJM73Gl-J_6s86Buo_1AIx5YAKCucYDUYYdXBIHLxrbALsA5W6pZCqqkMbqpTWteix-G5Q5T8LNsfqIu_uMBUGceqZWFJALhS9ieaDqoxhIqpx_89QAr1YlGu_UO6R6FYl0wDT-nzjyeF5tonSs3FHN0iNIiR3AMOHZu7KUwZaUdHg4eYkU-sQ01QNY_11keHROCRQ",
  "select_by": "user"
  }

Respuesta de token no válida

Ejemplo de respuesta de Google cuando se intenta realizar una solicitud a la API con un token de acceso vencido, revocado o no válido:

Encabezados de respuesta HTTP

  www-authenticate: Bearer realm="https://accounts.google.com/", error="invalid_token"

Cuerpo de la respuesta

  {
    "error": {
      "code": 401,
      "message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
      "errors": [
        {
          "message": "Invalid Credentials",
          "domain": "global",
          "reason": "authError",
          "location": "Authorization",
          "locationType": "header"
        }
      ],
      "status": "UNAUTHENTICATED"
    }
  }