Migra a Google Identity Services

Descripción general

Para obtener un token de acceso por usuario y llamar a las APIs de Google, Google ofrece varias bibliotecas de JavaScript:

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

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 gapi.auth2 obsoleto, sus métodos y objetos, y reemplázalos por los equivalentes de los Servicios de identidad.

Para obtener una descripción de los cambios en la biblioteca de JavaScript de los Servicios de identidad, lee la descripción general y cómo funciona la autorización del usuario para revisar los términos y conceptos clave.

Si buscas autenticación para el registro y el acceso de los usuarios, consulta Migra desde el Acceso con Google.

Identifica tu flujo de autorización

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

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

Indicios de que tu app web usa el flujo implícito:

Indicaciones de que tu app web usa el flujo de código de autorización:

  • Tu implementación se basa en lo siguiente:

  • Tu 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 APIs de Google en nombre de los usuarios sin que estos tengan que estar presentes, lo que también se conoce como modo sin conexión.

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

En algunos casos, es posible que tu base de código admita ambos flujos.

Elige un flujo de autorización

Antes de comenzar la migración, debes determinar si continuar con tu flujo existente o adoptar uno diferente se adapta mejor a tus necesidades.

Revisa cómo elegir un flujo de autorización para comprender las diferencias clave y las compensaciones entre los dos flujos.

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 para el usuario. La implementación de este flujo también permite que tu plataforma agregue nuevas funciones sin conexión, como recuperar actualizaciones para notificar a los usuarios sobre cambios importantes en su calendario, fotos y suscripciones.

Elige un flujo de autorización con los selectores.

Flujo implícito

Obtener un token de acceso para usarlo en el navegador mientras el usuario está presente

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

Flujo de código de autorización

Se entrega un código de autorización por usuario emitido por Google a tu plataforma de backend, donde 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 apps web antes y después de la migración a los Servicios de identidad.

En esta guía, sigue las instrucciones en negrita para agregar, quitar, actualizar o reemplazar la funcionalidad existente.

Cambios en tu app web en el navegador

En esta sección, se revisan los cambios que realizarás en tu app web integrada en el navegador cuando migres a la biblioteca de JavaScript de Google Identity Services.

Identifica el código afectado y realiza pruebas

Una cookie de depuración puede ayudar a ubicar 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 en la consola el uso existente de la funcionalidad que pronto dejará de estar disponible, establece el valor de la cookie G_AUTH2_MIGRATION en informational. De manera opcional, agrega dos puntos seguidos de un valor de clave para registrar también en el almacenamiento de sesión. Después de acceder y recibir las credenciales, revisa o envía los registros recopilados a un backend para su análisis posterior. 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 ya no se carga el módulo gapi.auth2, 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 de medidas por incumplimiento.

Posibles valores de la cookie G_AUTH2_MIGRATION:

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

Para minimizar el impacto en los usuarios, se recomienda que primero configures esta cookie de forma local durante el desarrollo y las pruebas, antes de usarla en entornos de producción.

Bibliotecas y módulos

El módulo gapi.auth2 administra la autenticación de usuarios para el acceso y el flujo implícito para la 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 app web incluyéndola en tu documento:

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

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

La biblioteca de Google Identity Services 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 de JS que se pueden llamar desde un documento de descubrimiento, la solicitud por lotes de 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 Migra desde Acceso con Google para obtener detalles sobre cómo la autenticación de usuarios usa cookies y Cómo usa Google las cookies para conocer el uso de cookies en otros productos y servicios de Google.

Credenciales

Los Servicios de identidad de Google separan 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 devuelve por separado del token de acceso que se usa para la autorización.

Para ver estos cambios, consulta las credenciales de ejemplo.

Flujo implícito

Separa la autenticación y la autorización de los usuarios quitando el manejo de los perfiles de usuario de los flujos de autorización.

Quita estas referencias del cliente de JavaScript de 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 un token de ID y un token de acceso. Este cambio no se aplica a las credenciales obtenidas a través de llamadas directas a los extremos de Google OAuth 2.0 desde tu plataforma de backend o a través de bibliotecas que se ejecutan en un servidor seguro de tu plataforma, como el cliente de Node.js para las APIs de Google.

Estado de la sesión

Anteriormente, el Acceso con Google te ayudaba a administrar el estado de acceso de los usuarios con lo siguiente:

Eres responsable de administrar el estado de acceso y las sesiones de los usuarios en tu app web.

Quita estas referencias del cliente de JavaScript de 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 app web para inicializar un cliente de token para el flujo implícito o de código de autorización.

Quita estas referencias del cliente de JavaScript de 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 a initTokenClient() para configurar tu app web, siguiendo el ejemplo de inicializa un cliente de tokens.

Reemplaza las referencias del cliente de JavaScript de Acceso con Google por los Servicios de identidad de Google:

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.login_hint.
  • gapi.auth2.GoogleUser.getHostedDomain() con TokenClientConfig.hd.

Flujo de código de autorización

Agrega un objeto CodeClientConfig y una llamada a initCodeClient() para configurar tu app web, siguiendo el ejemplo de inicializa un cliente de código.

Cuando cambies del flujo implícito al de código de autorización, ten en cuenta lo siguiente:

Quita las referencias del cliente de JavaScript de 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 hacer clic en un botón, genera una solicitud que hace que se devuelva un token de acceso 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 por un token de acceso y un token de actualización.

Flujo implícito

Los tokens de acceso se pueden obtener y usar 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 hubo una solicitud anterior.

Reemplaza las referencias del cliente de JavaScript de Acceso con Google por los Servicios de identidad de Google:

Métodos:

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

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

Actualiza tu base de código para lo siguiente:

  • Activa el flujo de tokens de OAuth 2.0 con requestAccessToken().
  • Admite la autorización incremental con requestAccessToken y OverridableTokenClientConfig para separar una solicitud de muchos permisos en varias solicitudes más pequeñas.
  • Solicita un token nuevo cuando el existente venza o se revoque.

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

Cuando vence un token de acceso, el módulo gapi.auth2 obtiene automáticamente un token de acceso nuevo y válido para tu app web. Para mejorar la seguridad del usuario, la biblioteca de Google Identity Services 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 Control de tokens para obtener más información.

Flujo de código de autorización

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

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

Control de tokens

Agrega un manejo de errores para detectar llamadas a la API de Google con errores cuando se usa un token de acceso vencido o revocado, y para solicitar un token de acceso nuevo y válido.

Las APIs de Google devuelven un código de estado HTTP de 401 Unauthorized y un mensaje de error invalid_token cuando se usa un token de acceso vencido o revocado. Para ver un ejemplo, consulta Respuesta de token no válido.

Tokens vencidos

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

Revocación de tokens

En cualquier momento, el propietario de una Cuenta de Google puede revocar el consentimiento otorgado anteriormente. Si lo haces, se invalidarán 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 del cliente de JavaScript de Acceso con Google por los Servicios de identidad de Google:

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 cuadro de diálogo de consentimiento al usuario cuando tu app web o plataforma de backend solicitan un token de acceso. Consulta los ejemplos de cuadros de diálogo de consentimiento que Google muestra a los usuarios.

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

Acceso de usuarios

Es posible que los usuarios hayan accedido a una Cuenta de Google en una pestaña del navegador independiente o de forma nativa a través de un navegador o un 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 abra tu app por primera vez. Esto ofrece los siguientes beneficios:

  • Minimiza la cantidad de veces que un usuario debe acceder, ya que solicitar un token de acceso inicia el proceso de acceso a la Cuenta de Google si aún no existe una sesión activa.
  • Uso directo del campo email de la credencial del token de ID de JWT como el valor del parámetro login_hint en los objetos CodeClientConfig o TokenClientConfig. Esto es especialmente ú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, los diálogos y el flujo de registro se pueden separar claramente de los diálogos y los flujos de autenticación del usuario, lo que reduce la cantidad de pasos necesarios y mejora el porcentaje de abandono.

Después de acceder y antes de que se emita un token de acceso, los usuarios deben otorgar su consentimiento para que tu aplicación use los permisos solicitados.

Después de que se otorga el consentimiento, se devuelve un token de acceso junto con una lista de permisos aprobados o rechazados por el usuario.

Los permisos detallados permiten que los usuarios aprueben o rechacen permisos individuales. Cuando solicitas acceso a varios permisos, cada uno se otorga o se rechaza de forma independiente de los demás. Según la selección del usuario, tu app habilita de forma selectiva las funciones que dependen de un permiso individual.

Flujo implícito

Reemplaza las referencias del cliente de JavaScript de Acceso con Google por los Servicios de identidad de Google:

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 del cliente de JavaScript de Acceso con Google:

Métodos:

  • GoogleUser.getAuthResponse()

Actualiza tu app web con hasGrantedAllScopes() y hasGrantedAnyScope() siguiendo este ejemplo de permisos detallados.

Flujo de código de autorización

Actualiza o agrega un extremo de código de autorización a tu plataforma de backend siguiendo las instrucciones que se indican en Manejo de códigos de autenticación.

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

Actualiza tu plataforma para habilitar o inhabilitar de forma selectiva las funciones y las funcionalidades según los permisos individuales que el usuario haya aprobado. Para ello, sigue las instrucciones de autorización incremental y examina los permisos de acceso otorgados por el usuario.

Ejemplos de flujo implícito

Método anterior

Biblioteca cliente de GAPI

Ejemplo de la biblioteca cliente de la API de Google para JavaScript que se ejecuta en el navegador con un diálogo emergente para obtener 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 con 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 Google Cloud 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 con redireccionamientos al servidor de autorización de Google para obtener el consentimiento del usuario

En este ejemplo, se muestran 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 the Google authorization 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 the Google authorization 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>

Método nuevo

Solo GIS

En este ejemplo, solo se muestra la biblioteca de JavaScript de Google Identity Service con el modelo de token y el diálogo emergente para el consentimiento del usuario. Se proporciona para 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>

Async/await de GAPI

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

Las promesas, async y await se usan para aplicar el orden de carga de la biblioteca y para detectar y reintentar los errores de autorización. Solo se realiza una llamada a la API después de que está disponible un token de acceso válido.

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

<!DOCTYPE html>
<html>
<head>
    <title>GAPI and GIS Example</title>
    <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="gisLoad()"></script>
</head>
<body>
    <h1>GAPI Client with GIS Authorization</h1>
    <button id="authorizeBtn" style="visibility:hidden;">Authorize and Load Events</button>
    <button id="revokeBtn" style="visibility:hidden;">Revoke Access</button>
    <div id="content"></div>

    <script>
        const YOUR_CLIENT_ID = "YOUR_CLIENT_ID";
        const YOUR_API_KEY = 'YOUR_API_KEY';
        const CALENDAR_SCOPE = 'https://www.googleapis.com/auth/calendar.readonly';

        let tokenClient;
        let libsLoaded = 0;

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

        async function initGapiClient() {
            try {
                await gapi.client.init({ apiKey: YOUR_API_KEY });
                await gapi.client.load('https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest');
                console.log('GAPI client initialized.');
                checkAllLoaded();
            } catch (err) {
                handleError('GAPI initialization failed:', err);
            }
        }

        function gisLoad() {
            try {
                tokenClient = google.accounts.oauth2.initTokenClient({
                    client_id: YOUR_CLIENT_ID,
                    scope: CALENDAR_SCOPE,
                    callback: '', // Will be set dynamically
                    error_callback: handleGisError,
                });
                console.log('GIS TokenClient initialized.');
                checkAllLoaded();
            } catch (err) {
                handleError('GIS initialization failed:', err);
            }
        }

        function checkAllLoaded() {
            libsLoaded++;
            if (libsLoaded === 2) {
                document.getElementById('authorizeBtn').style.visibility = 'visible';
                document.getElementById('revokeBtn').style.visibility = 'visible';
                document.getElementById('authorizeBtn').onclick = makeApiCall;
                document.getElementById('revokeBtn').onclick = revokeAccess;
                console.log('Ready to authorize.');
            }
        }

        function handleGisError(err) {
            console.error('GIS Error:', err);
            let message = 'An error occurred during authorization.';
            if (err && err.type === 'popup_failed_to_open') {
                message = 'Failed to open popup. Please disable popup blockers.';
            } else if (err && err.type === 'popup_closed') {
                message = 'Authorization popup was closed.';
            }
            document.getElementById('content').textContent = message;
        }

        function handleError(message, error) {
            console.error(message, error);
            document.getElementById('content').textContent = `${message} ${error.message || JSON.stringify(error)}`;
        }

        async function makeApiCall() {
            document.getElementById('content').textContent = 'Processing...';
            try {
                let token = gapi.client.getToken();
                if (!token || !token.access_token) {
                    console.log('No token, fetching one...');
                    await getToken();
                }

                console.log('Calling Calendar API...');
                const response = await gapi.client.calendar.events.list({ 'calendarId': 'primary' });
                displayEvents(response.result);
            } catch (err) {
                console.error('API call failed:', err);
                const errorInfo = err.result && err.result.error;
                if (errorInfo && (errorInfo.code === 401 || (errorInfo.code === 403 && errorInfo.status === "PERMISSION_DENIED"))) {
                    console.log('Auth error on API call, refreshing token...');
                    try {
                        await getToken({ prompt: 'consent' }); // Force refresh
                        const retryResponse = await gapi.client.calendar.events.list({ 'calendarId': 'primary' });
                        displayEvents(retryResponse.result);
                    } catch (refreshErr) {
                        handleError('Failed to refresh token or retry API call:', refreshErr);
                    }
                } else {
                    handleError('Error loading events:', err.result ? err.result.error : err);
                }
            }
        }

        async function getToken(options = { prompt: '' }) {
            return new Promise((resolve, reject) => {
                if (!tokenClient) return reject(new Error("GIS TokenClient not initialized."));
                tokenClient.callback = (tokenResponse) => {
                    if (tokenResponse.error) {
                        reject(new Error(`Token Error: ${tokenResponse.error} - ${tokenResponse.error_description}`));
                    } else {
                        console.log('Token acquired.');
                        resolve(tokenResponse);
                    }
                };
                tokenClient.requestAccessToken(options);
            });
        }

        function displayEvents(result) {
            const events = result.items;
            if (events && events.length > 0) {
                let eventList = '<h3>Upcoming Events:</h3><ul>' + events.map(event =>
                    `<li>${event.summary} (${event.start.dateTime || event.start.date})</li>`
                ).join('') + '</ul>';
                document.getElementById('content').innerHTML = eventList;
            } else {
                document.getElementById('content').textContent = 'No upcoming events found.';
            }
        }

        function revokeAccess() {
            const token = gapi.client.getToken();
            if (token && token.access_token) {
                google.accounts.oauth2.revoke(token.access_token, () => {
                    console.log('Access revoked.');
                    document.getElementById('content').textContent = 'Access has been revoked.';
                    gapi.client.setToken(null);
                });
            } else {
                document.getElementById('content').textContent = 'No token to revoke.';
            }
        }
    </script>
</body>
</html>

Devolución de llamada de GAPI

En este ejemplo, se muestra cómo agregar la biblioteca de Google Identity Service con el modelo de token, quitar el módulo gapi.auth2 y llamar a una API con 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 devuelve un token de acceso válido.

Se espera que los usuarios presionen el botón Mostrar calendario cuando se cargue la página por primera vez y, luego, cuando quieran actualizar la información del 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 a 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 Services puede usar un redireccionamiento de URL para devolver un código de autorización directamente al extremo del token de backend o un controlador de devolución de llamada de JavaScript que se ejecuta en el navegador del usuario y 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 de acceso y actualización válido.

Método anterior

Apps web del servidor

Acceso con Google para apps del servidor que se ejecutan en la plataforma de backend con un redireccionamiento a Google para 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 con redireccionamiento

Usa OAuth 2.0 para aplicaciones de servidor web para enviar el código de autorización desde el navegador del usuario a tu plataforma de backend. El consentimiento del usuario se controla redireccionando el navegador del usuario al servidor de autorización de Google.

/*
 * Create form to request access token from the Google authorization 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();
}

Método nuevo

UX de la ventana emergente de GIS

En este ejemplo, solo se muestra la biblioteca de JavaScript de Google Identity Service con el modelo de código de autorización, un diálogo emergente para el consentimiento del usuario y un controlador de devolución de llamada para recibir el código de autorización de Google. Se proporciona para 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=' + response.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 de UX de ventana emergente y redireccionamiento para enviar un código de autorización por usuario al extremo alojado en tu plataforma. Aquí se muestra el modo de UX de redireccionamiento:

<!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

Google Identity Services es una biblioteca de JavaScript única que se usa para la autenticación y autorización de usuarios, y que consolida y reemplaza las funciones y características que se encuentran en varias bibliotecas y módulos diferentes:

Acciones que se deben realizar cuando se migra 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 sigue el flujo del código de autorización.

Referencia rápida de la biblioteca

Comparación de objetos y métodos entre la biblioteca cliente de JavaScript de Acceso con Google antigua y la biblioteca de Google Identity Services nueva, y notas con información adicional y acciones que se deben realizar durante la migración.

Antiguo Nuevo Notas
Objeto GoogleAuth y 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 realizarse desde https://myaccount.google.com/permissions
GoogleAuth.grantOfflineAccess() Quita 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
Objeto GoogleUser y métodos asociados:
GoogleUser.disconnect() google.accounts.id.revoke Reemplaza lo viejo por lo nuevo. La revocación también puede realizarse desde https://myaccount.google.com/permissions
GoogleUser.getAuthResponse() requestCode() o requestAccessToken() Reemplaza lo viejo por lo nuevo
GoogleUser.getBasicProfile() Quitar. En su lugar, usa el token de ID. Consulta Migra desde el Acceso con Google.
GoogleUser.getGrantedScopes() hasGrantedAnyScope() Reemplaza lo viejo por lo nuevo
GoogleUser.getHostedDomain() Quitar
GoogleUser.getId() Quitar
GoogleUser.grantOfflineAccess() Quita el flujo de código de autorización.
GoogleUser.grant() Quitar
GoogleUser.hasGrantedScopes() hasGrantedAnyScope() Reemplaza lo viejo por lo nuevo
GoogleUser.isSignedIn() Quitar
GoogleUser.reloadAuthResponse() requestAccessToken() Quita el token de acceso vencido o revocado, y llama al nuevo para reemplazarlo.
Objeto gapi.auth2 y métodos asociados:
Objeto gapi.auth2.AuthorizeConfig TokenClientConfig o CodeClientConfig Reemplaza lo viejo por lo nuevo
Objeto gapi.auth2.AuthorizeResponse Quitar
Objeto gapi.auth2.AuthResponse Quitar
gapi.auth2.authorize() requestCode() o requestAccessToken() Reemplaza lo viejo por lo nuevo
gapi.auth2.ClientConfig() TokenClientConfig o CodeClientConfig Reemplaza lo viejo por lo nuevo
gapi.auth2.getAuthInstance() Quitar
gapi.auth2.init() initTokenClient() o initCodeClient() Reemplaza lo viejo por lo nuevo
Objeto gapi.auth2.OfflineAccessOptions Quitar
Objeto gapi.auth2.SignInOptions Quitar
Objeto gapi.signin2 y métodos asociados:
gapi.signin2.render() Quitar. La carga del DOM HTML del elemento g_id_signin o la llamada de 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 OAuth 2.0 de Google devuelven 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 Google Identity Services

La biblioteca de Google Identity Services devuelve lo siguiente:

  • un token de acceso cuando se usa 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"
    }
  }