Переход на Google Identity Services

Обзор

Чтобы получить токен доступа для каждого пользователя для вызова API Google, Google предлагает несколько библиотек JavaScript:

В этом руководстве приведены инструкции по переходу с этих библиотек на библиотеку Google Identity Services .

Следуя этому руководству, вы:

  • заменить устаревшую библиотеку платформы библиотекой Identity Services и
  • при использовании клиентской библиотеки API удалите устаревший модуль gapi.auth2 , его методы и объекты, заменив их эквивалентами Identity Services.

Чтобы узнать, что изменилось в библиотеке JavaScript Identity Services, ознакомьтесь с обзором и тем, как работает авторизация пользователей , чтобы ознакомиться с ключевыми терминами и понятиями.

Если вам нужна аутентификация для регистрации пользователя и входа в систему, вместо этого см. Миграция из Google Sign-In .

Определите свой поток авторизации

Существует два возможных потока авторизации пользователя: неявный и код авторизации.

Просмотрите свое веб-приложение, чтобы определить, какой тип потока авторизации используется в настоящее время.

Признаки того, что ваше веб-приложение использует неявный поток :

  • Ваше веб-приложение основано исключительно на браузере, без серверной платформы.
  • Пользователь должен присутствовать, чтобы вызывать API Google, ваше приложение использует только маркеры доступа и не требует маркеров обновления.
  • Ваше веб-приложение загружает apis.google.com/js/api.js .
  • Ваша реализация основана на OAuth 2.0 для клиентских веб-приложений .
  • В вашем приложении используются модули gapi.client или gapi.auth2 из клиентской библиотеки Google API для JavaScript .

Признаки того, что ваше веб-приложение использует поток кода авторизации :

  • Ваша реализация основана на:

  • Ваше приложение выполняется как в браузере пользователя, так и на вашей серверной платформе.

  • На вашей серверной платформе размещена конечная точка кода авторизации.

  • Ваша серверная платформа вызывает API Google от имени пользователей, не требуя их присутствия, что также называется автономным режимом.

  • Токены обновления управляются и хранятся на вашей серверной платформе.

В некоторых случаях ваша кодовая база может поддерживать оба потока.

Выберите поток авторизации

Прежде чем приступить к миграции, вам необходимо определить, что лучше всего соответствует вашим потребностям — продолжение существующего потока или переход на другой поток.

Просмотрите выбор потока авторизации, чтобы понять основные различия и компромиссы между двумя потоками.

В большинстве случаев рекомендуется использовать поток кода авторизации, поскольку он обеспечивает самый высокий уровень безопасности пользователя. Реализация этого потока также позволяет вашей платформе более легко добавлять новые автономные функции, такие как получение обновлений для уведомления пользователей о заметных изменениях в их календаре, фотографиях, подписках и т. д.

Выберите поток авторизации, используя селекторы ниже.

Неявный поток

Получите токен доступа для использования в браузере, пока пользователь присутствует.

В примерах неявного потока показаны веб-приложения до и после миграции в Identity Services.

Поток кода авторизации

Код авторизации для каждого пользователя, выданный Google, доставляется на вашу серверную платформу, где затем он обменивается на токен доступа и токен обновления.

В примерах потока кода авторизации показаны веб-приложения до и после миграции в Identity Services.

В этом руководстве следуйте инструкциям, выделенным жирным шрифтом, для добавления , удаления , обновления или замены существующих функций.

Изменения в веб-приложении в браузере

В этом разделе рассматриваются изменения, которые вы внесете в свое браузерное веб-приложение при переходе на библиотеку JavaScript Google Identity Services.

Выявление затронутого кода и тестирование

Файл cookie отладки может помочь найти затронутый код и протестировать поведение после устаревания.

В больших или сложных приложениях может быть сложно найти весь код, затронутый устаревшим модулем gapi.auth2 . Чтобы протоколировать использование функций, которые вскоре станут устаревшими, в консоль, установите для файла cookie G_AUTH2_MIGRATION значение informational . При необходимости добавьте двоеточие, за которым следует значение ключа, чтобы также вести журнал в хранилище сеансов . После входа в систему и получения учетных данных просмотрите или отправьте собранные журналы на серверную часть для последующего анализа. Например, informational:showauth2use сохраняет источник и URL-адрес в ключе хранения сеанса с именем showauth2use .

Чтобы проверить поведение приложения, когда модуль gapi.auth2 больше не загружается, установите для файла cookie G_AUTH2_MIGRATION значение enforced . Это позволяет протестировать поведение после устаревания до даты принудительного применения.

Возможные значения cookie G_AUTH2_MIGRATION :

  • enforced Не загружать модуль gapi.auth2 .
  • informational журнал использования устаревших функций в консоли JS. Также регистрируйтесь в хранилище сеансов, когда задано необязательное имя ключа: informational:key-name .

Чтобы свести к минимуму влияние на пользователя, рекомендуется сначала установить этот файл cookie локально во время разработки и тестирования, прежде чем использовать его в производственной среде.

Библиотеки и модули

Модуль gapi.auth2 управляет аутентификацией пользователя для входа и неявным потоком авторизации. Замените этот устаревший модуль, а также его объекты и методы библиотекой Google Identity Services.

Добавьте библиотеку Identity Services в свое веб-приложение, включив ее в свой документ:

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

Удалите все случаи загрузки модуля auth2 с помощью gapi.load('auth2', function) .

Библиотека Google Identity Services заменяет использование модуля gapi.auth2 . Вы можете спокойно продолжать использовать модуль gapi.client из клиентской библиотеки API Google для JavaScript и воспользоваться его преимуществами автоматического создания вызываемых методов JS из документа обнаружения, пакетной обработки нескольких вызовов API и функций управления CORS.

Печенье

Авторизация пользователя не требует использования файлов cookie.

Подробную информацию о том, как аутентификация пользователя использует файлы cookie, см. в разделе « Миграция с входа в систему Google » и «Как Google использует файлы cookie для использования файлов cookie другими продуктами и службами Google».

Реквизиты для входа

Google Identity Services разделяет аутентификацию и авторизацию пользователя на две отдельные операции, а учетные данные пользователя являются отдельными: токен идентификатора, используемый для идентификации пользователя, возвращается отдельно от токена доступа, используемого для авторизации.

Чтобы просмотреть эти изменения, см. пример учетных данных .

Неявный поток

Разделите аутентификацию и авторизацию пользователей, удалив обработку профилей пользователей из потоков авторизации.

Удалите эти ссылки на клиент JavaScript для входа в систему Google :

Методы

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

Поток кода авторизации

Identity Services разделяет учетные данные в браузере на токен идентификатора и токен доступа. Это изменение не распространяется на учетные данные, полученные путем прямого обращения к конечным точкам Google OAuth 2.0 с вашей серверной платформы или через библиотеки, работающие на защищенном сервере на вашей платформе, таком как клиент Node.js API Google .

Состояние сеанса

Ранее функция Google Sign-In помогала вам управлять статусом входа пользователя в систему, используя:

Вы несете ответственность за управление состоянием входа и сеансами пользователей в своем веб-приложении.

Удалите эти ссылки на клиент JavaScript для входа в систему Google :

Объекты:

  • gapi.auth2.SignInOptions

Методы:

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

Конфигурация клиента

Обновите свое веб-приложение, чтобы инициализировать клиент маркера для потока неявного кода или кода авторизации.

Удалите эти ссылки на клиент JavaScript для входа в систему Google :

Объекты:

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

Методы:

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

Неявный поток

Добавьте объект TokenClientConfig и initTokenClient() для настройки вашего веб-приложения, следуя примеру в инициализации клиента токена .

Замените ссылки клиента JavaScript для входа в Google на Google Identity Services :

Объекты:

  • gapi.auth2.AuthorizeConfig с помощью TokenClientConfig

Методы:

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

Параметры:

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

Поток кода авторизации

Добавьте объект CodeClientConfig и initCodeClient() для настройки вашего веб-приложения, следуя примеру в Initialize a Code Client .

При переключении с неявного на поток кода авторизации:

Удалить ссылки на клиент JavaScript для входа в Google

Объекты:

  • gapi.auth2.AuthorizeConfig

Методы:

  • gapi.auth2.init()

Параметры:

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

Запрос токена

Жест пользователя, например нажатие кнопки, генерирует запрос, в результате которого токен доступа возвращается непосредственно в браузер пользователя с неявным потоком или на вашу серверную платформу после обмена кода авторизации для каждого пользователя на токен доступа и токен обновления. .

Неявный поток

Токены доступа можно получить и использовать в браузере, пока пользователь вошел в систему и имеет активный сеанс с Google. В неявном режиме для запроса маркера доступа требуется жест пользователя, даже если ранее был запрос.

Замените ссылки клиента JavaScript для входа в систему Google : на Google Identity Services :

Методы:

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

Добавьте ссылку или кнопку для вызова requestAccessToken() , чтобы инициировать всплывающий поток UX для запроса токена доступа или для получения нового токена, когда срок действия существующего токена истекает.

Обновите свою кодовую базу до:

  • Запустите поток токенов OAuth 2.0 с помощью requestAccessToken() .
  • Поддержка добавочной авторизации с помощью requestAccessToken и OverridableTokenClientConfig для разделения одного запроса для многих областей на несколько более мелких запросов.
  • Запросите новый токен, когда срок действия существующего токена истечет или будет отозван.

Работа с несколькими областями может потребовать структурных изменений в вашей кодовой базе, чтобы запрашивать доступ к областям только по мере необходимости, а не ко всем сразу, это называется инкрементной авторизацией. Каждый запрос должен содержать как можно меньше областей действия, а в идеале — одну область действия. Узнайте , как обрабатывать согласие пользователя , чтобы узнать больше о том, как обновить приложение для дополнительной авторизации.

Когда срок действия токена доступа истекает, модуль gapi.auth2 автоматически получает новый действительный токен доступа для вашего веб-приложения. В целях повышения безопасности пользователей этот процесс автоматического обновления токена не поддерживается библиотекой Google Identity Services. Ваше веб-приложение должно быть обновлено, чтобы обнаружить маркер доступа с истекшим сроком действия и запросить новый. Дополнительную информацию см. в разделе «Обработка токенов» ниже.

Поток кода авторизации

Добавьте ссылку или кнопку для вызова requestCode() , чтобы запросить код авторизации у Google. Пример см. в разделе Поток кода Trigger OAuth 2.0 .

Подробнее о том, как реагировать на просроченный или отозванный токен доступа, см. в разделе «Обработка токенов» ниже.

Обработка токенов

Добавьте обработку ошибок для обнаружения неудачных вызовов API Google, когда используется просроченный или отозванный токен доступа, и для запроса нового действительного токена доступа.

API-интерфейсы Google возвращают код состояния HTTP 401 Unauthorized and invalid_token , когда используется просроченный или отозванный токен доступа. Пример см. в разделе Недопустимый ответ токена .

Токены с истекшим сроком действия

Жетоны доступа недолговечны и часто действительны всего несколько минут.

Отзыв токена

В любое время владелец аккаунта Google может отозвать ранее предоставленное согласие. Это делает недействительными существующие токены доступа и токены обновления. Отзыв может быть инициирован с вашей платформы с помощью revoke() или через учетную запись Google .

Замените ссылки клиента JavaScript для входа в систему Google : на Google Identity Services :

Методы:

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

Вызывайте revoke , когда пользователь удаляет свою учетную запись на вашей платформе или хочет отозвать согласие на обмен данными с вашим приложением.

Google отображает диалоговое окно согласия пользователя, когда ваше веб-приложение или серверная платформа запрашивает токен доступа. См. примеры диалоговых окон согласия , отображаемых Google для пользователей.

Перед выдачей токена доступа к вашему приложению требуется существующий и активный сеанс Google, чтобы запрашивать согласие пользователя и записывать результат. Пользователю может потребоваться войти в учетную запись Google, если существующий сеанс еще не установлен.

Вход пользователя

Пользователи могут войти в учетную запись Google на отдельной вкладке браузера или непосредственно через браузер или операционную систему. Мы рекомендуем добавить на ваш сайт функцию « Войти через Google », чтобы установить активный сеанс между учетной записью Google и браузером, когда пользователь впервые открывает ваше приложение. Это дает следующие преимущества:

  • Сводит к минимуму количество раз, когда пользователь должен войти в систему, запрос токена доступа инициирует процесс входа в учетную запись Google, если активный сеанс еще не существует.
  • Прямое использование поля email учетных данных JWT ID Token в качестве значения параметра hint в CodeClientConfig или TokenClientConfig . Это особенно полезно, если ваша платформа не поддерживает систему управления учетными записями пользователей.
  • Найдите и свяжите учетную запись Google с существующей локальной учетной записью пользователя на вашей платформе, что поможет свести к минимуму дублирование учетных записей на вашей платформе.
  • Когда создается новая локальная учетная запись, ваши диалоги и поток регистрации могут быть четко отделены от диалогов и потоков аутентификации пользователя, что сокращает количество необходимых шагов и повышает процент отказов.

После входа в систему и до выдачи маркера доступа пользователи должны дать согласие на использование вашего приложения в запрошенных областях.

После согласия возвращается токен доступа вместе со списком областей, одобренных или отклоненных пользователем.

Гранулированные разрешения позволяют пользователям одобрять или отклонять отдельные области. При запросе доступа к нескольким областям каждая область предоставляется или отклоняется независимо от других областей. В зависимости от выбора пользователя ваше приложение выборочно включает функции и функциональные возможности, которые зависят от отдельной области.

Неявный поток

Замените ссылки клиента JavaScript для входа в Google на Google Identity Services :

Объекты:

  • gapi.auth2.AuthorizeResponse с помощью TokenClient.TokenResponse
  • gapi.auth2.AuthResponse с TokenClient.TokenResponse

Методы:

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

Удалите ссылки на клиент JavaScript для входа в систему Google :

Методы:

  • GoogleUser.getAuthResponse()

Обновите свое веб-приложение с помощью hasGrantedAllScopes() и hasGrantedAnyScope() , следуя этому примеру детальных разрешений .

Поток кода авторизации

Обновите или добавьте конечную точку кода авторизации на серверную платформу, следуя инструкциям в разделе Обработка кода авторизации.

Обновите свою платформу, следуя шагам, описанным в руководстве по использованию модели кода , чтобы проверить запрос и получить маркер доступа и маркер обновления.

Обновите свою платформу, чтобы выборочно включать или отключать функции и функции в зависимости от отдельных областей, утвержденных пользователем, следуя инструкциям по дополнительной авторизации и проверяя области доступа, предоставленные пользователем .

Примеры неявного потока

Старый способ

Клиентская библиотека GAPI

Пример клиентской библиотеки API Google для JavaScript , работающей в браузере с использованием всплывающего диалогового окна для согласия пользователя.

Модуль gapi.auth2 автоматически загружается и используется gapi.client.init() , поэтому он скрыт.

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

JS-клиентская библиотека

OAuth 2.0 для клиентских веб-приложений , работающих в браузере с использованием всплывающего диалогового окна для получения согласия пользователя.

Модуль gapi.auth2 загружается вручную.

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

Конечные точки OAuth 2.0

OAuth 2.0 для клиентских веб-приложений , работающих в браузере, использующих перенаправления в Google для получения согласия пользователя.

В этом примере показаны прямые вызовы к конечным точкам Google OAuth 2.0 из браузера пользователя и не используется модуль gapi.auth2 или библиотека 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>

Новый способ

только ГИС

В этом примере показана только библиотека JavaScript службы идентификации Google, использующая модель токена и всплывающее диалоговое окно для согласия пользователя. Он предназначен для демонстрации минимального количества шагов, необходимых для настройки клиента, запроса и получения маркера доступа и вызова API 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 асинхронно/ожидание

В этом примере показано, как добавить библиотеку Google Identity Service с помощью модели токена , удалить модуль gapi.auth2 и вызвать API с помощью клиентской библиотеки API Google для JavaScript .

Обещания, асинхронность и ожидание используются для обеспечения порядка загрузки библиотек, а также для перехвата и повторения ошибок авторизации. Вызов API выполняется только после того, как будет доступен действительный токен доступа.

Ожидается, что пользователи нажмут кнопку «Показать календарь», когда токен доступа отсутствует при первой загрузке страницы или позже, когда срок действия токена доступа истечет.

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

обратный вызов GAPI

В этом примере показано, как добавить библиотеку Google Identity Service с помощью модели токена , удалить модуль gapi.auth2 и вызвать API с помощью клиентской библиотеки API Google для JavaScript .

Переменные используются для обеспечения порядка загрузки библиотеки. Вызовы GAPI выполняются из обратного вызова после возврата действительного токена доступа.

Ожидается, что пользователи нажмут кнопку «Показать календарь» при первой загрузке страницы и еще раз, когда они захотят обновить данные своего календаря.

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

Примеры потока кода авторизации

UX всплывающего окна библиотеки Google Identity Service может либо использовать перенаправление URL-адреса для возврата кода авторизации непосредственно на конечную точку вашего серверного токена, либо обработчик обратного вызова JavaScript, работающий в браузере пользователя, который проксирует ответ на вашу платформу. В любом случае ваша серверная платформа завершит процесс OAuth 2.0, чтобы получить действительный маркер обновления и доступа.

Старый способ

Серверные веб-приложения

Вход в Google для серверных приложений , работающих на серверной платформе, с использованием перенаправления в Google для получения согласия пользователя.

<!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 с использованием перенаправления

Использование OAuth 2.0 для приложений веб-сервера для отправки кода авторизации из браузера пользователя на вашу серверную платформу. Согласие пользователя обрабатывается путем перенаправления браузера пользователя в 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();
}

Новый способ

Пользовательский интерфейс всплывающего окна ГИС

В этом примере показана только библиотека JavaScript службы идентификации Google, использующая модель кода авторизации , всплывающее диалоговое окно для согласия пользователя и обработчик обратного вызова для получения кода авторизации от Google. Он предназначен для демонстрации минимального количества шагов, необходимых для настройки клиента, получения согласия и отправки кода авторизации на вашу серверную платформу.

<!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 для отправки кода авторизации для каждого пользователя на конечную точку, размещенную на вашей платформе. Режим перенаправления UX показан здесь:

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

Библиотеки JavaScript

Google Identity Services — это единая библиотека JavaScript, используемая для аутентификации и авторизации пользователей, которая объединяет и заменяет функции и возможности, имеющиеся в нескольких различных библиотеках и модулях:

Действия, которые необходимо предпринять при переходе на Identity Services:

Существующая JS-библиотека Новая JS-библиотека Заметки
apis.google.com/js/api.js accounts.google.com/gsi/client Добавьте новую библиотеку и следуйте неявному потоку.
apis.google.com/js/client.js accounts.google.com/gsi/client Добавьте новую библиотеку и поток кода авторизации.

Краткий справочник по библиотеке

Сравнение объектов и методов между старой клиентской библиотекой JavaScript для входа в Google и новой библиотекой Google Identity Services и примечаниями с дополнительной информацией и действиями, которые необходимо выполнить во время миграции.

Старый Новый Заметки
Объект GoogleAuth и связанные с ним методы:
GoogleAuth.attachClickHandler() Удалять
GoogleAuth.currentUser.get() Удалять
GoogleAuth.currentUser.listen() Удалять
GoogleAuth.отключить() google.accounts.oauth2.revoke Замените старые на новые. Отзыв также может произойти с https://myaccount.google.com/permissions.
GoogleAuth.grantOfflineAccess() Удалите, следуйте потоку кода авторизации.
GoogleAuth.isSignedIn.get() Удалять
GoogleAuth.isSignedIn.listen() Удалять
GoogleAuth.signIn() Удалять
GoogleAuth.signOut() Удалять
GoogleAuth.затем() Удалять
Объект GoogleUser и связанные с ним методы:
GoogleUser.disconnect() google.accounts.id.revoke Замените старые на новые. Отзыв также может произойти с https://myaccount.google.com/permissions.
GoogleUser.getAuthResponse() requestCode() или requestAccessToken() Заменить старое на новое
GoogleUser.getBasicProfile() Удалять. Вместо этого используйте токен идентификатора, см. раздел Миграция с входа в систему Google .
GoogleUser.getGrantedScopes() hasGrantedAnyScope() Заменить старое на новое
GoogleUser.getHostedDomain() Удалять
GoogleUser.getId() Удалять
GoogleUser.grantOfflineAccess() Удалите, следуйте потоку кода авторизации.
GoogleUser.grant() Удалять
GoogleUser.hasGrantedScopes() hasGrantedAnyScope() Заменить старое на новое
GoogleUser.isSignedIn() Удалять
GoogleUser.reloadAuthResponse() ЗапросТокенДоступа() Удалите старый, вызовите новый, чтобы заменить просроченный или отозванный токен доступа.
Объект gapi.auth2 и связанные с ним методы:
Объект gapi.auth2.AuthorizeConfig Токенклиентконфиг или кодеклиентконфиг Заменить старое на новое
Объект gapi.auth2.AuthorizeResponse Удалять
Объект gapi.auth2.AuthResponse Удалять
gapi.auth2.авторизовать() requestCode() или requestAccessToken() Заменить старое на новое
gapi.auth2.ClientConfig() Токенклиентконфиг или кодеклиентконфиг Заменить старое на новое
gapi.auth2.getAuthInstance() Удалять
gapi.auth2.init() initTokenClient() или initCodeClient() Заменить старое на новое
Объект gapi.auth2.OfflineAccessOptions Удалять
Объект gapi.auth2.SignInOptions Удалять
Объект gapi.signin2 и связанные с ним методы:
gapi.signin2.render() Удалять. Загрузка HTML DOM элемента g_id_signin или JS-вызов google.accounts.id.renderButton инициирует вход пользователя в учетную запись Google.

Пример учетных данных

Существующие учетные данные

Библиотека платформы Google Sign-In , клиентская библиотека Google API для JavaScript или прямые вызовы конечных точек Google Auth 2.0 возвращают в одном ответе токен доступа OAuth 2.0 и токен OpenID Connect ID.

Пример ответа, содержащего как access_token , так и 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"
  }

Учетные данные Google Identity Services

Библиотека Google Identity Services возвращает:

  • либо токен доступа при использовании для авторизации:
  {
    "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"
  }
  • или токен ID при использовании для аутентификации:
  {
  "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"
  }

Неверный ответ токена

Пример ответа от Google при попытке сделать запрос API с использованием просроченного, отозванного или недействительного токена доступа:

Заголовки ответа HTTP

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

Тело ответа

  {
    "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"
    }
  }