Переход на службы идентификации Google

Обзор

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

В этом разделе рассматриваются изменения, которые вы внесете в свое веб-приложение в браузере при переходе на библиотеку 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 gapi.load('auth2', function) .

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

Файлы cookie

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

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

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

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

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

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

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

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

Методы

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

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

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

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

Раньше вход в Google помогал вам управлять статусом входа пользователя в систему с помощью:

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

Удалите следующие ссылки на клиентские 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 :

Объекты:

  • gapi.auth2.AuthorizeConfig с TokenClientConfig

Методы:

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

Параметры:

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

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

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

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

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

Объекты:

  • gapi.auth2.AuthorizeConfig

Методы:

  • gapi.auth2.init()

Параметры:

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

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

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

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

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

Замените ссылки на клиент JavaScript для входа в Google : на Службы идентификации Google :

Методы:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Отзыв токена

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

Замените ссылки на клиент JavaScript для входа в Google : на Службы идентификации Google :

Методы:

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

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

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

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

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

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

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

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

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

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

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

Замените ссылки клиента JavaScript для входа в Google на службы идентификации Google :

Объекты:

  • 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

Пример клиентской библиотеки Google API для 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 с помощью клиентской библиотеки Google API для JavaScript .

Promises, async и await используются для обеспечения порядка загрузки библиотеки, а также для обнаружения и повторения ошибок авторизации. Вызов 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 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>

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

Пользовательский интерфейс всплывающего окна библиотеки 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=' + 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

Модель кода авторизации поддерживает режимы всплывающего окна и перенаправления 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, используемая для аутентификации и авторизации пользователей, которая объединяет и заменяет функции и возможности, имеющиеся в нескольких различных библиотеках и модулях:

Действия, которые необходимо предпринять при переходе на Службы идентификации:

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

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

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

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

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

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

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