Обзор
Для получения токена доступа для каждого пользователя и вызова API Google компания Google предлагает несколько библиотек JavaScript:
В этом руководстве приведены инструкции по миграции из этих библиотек в библиотеку Google Identity Services .
Следуя этому руководству, вы сможете:
- заменить устаревшую библиотеку Platform Library библиотекой 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 Client Library for JavaScript .
Это указывает на то, что ваше веб-приложение использует протокол авторизации по коду :
Ваша реализация основана на следующем:
Ваше приложение работает как в браузере пользователя, так и на вашей серверной платформе.
Ваша серверная платформа размещает конечную точку для ввода кода авторизации.
Ваша серверная платформа вызывает API Google от имени пользователей, не требуя их присутствия, что также известно как автономный режим.
Токены обновления управляются и хранятся вашей серверной платформой.
В некоторых случаях ваш код может поддерживать оба подхода.
Выберите схему авторизации
Перед началом миграции необходимо определить, какой подход лучше всего соответствует вашим потребностям: продолжать использовать существующий процесс или внедрить другой.
Ознакомьтесь с вариантами выбора процесса авторизации , чтобы понять ключевые различия и компромиссы между двумя типами процессов.
В большинстве случаев рекомендуется использовать процесс авторизации по коду, поскольку он обеспечивает наивысший уровень безопасности пользователей. Внедрение этого процесса также позволяет вашей платформе добавлять новые функции для работы в автономном режиме, такие как получение обновлений для уведомления пользователей о важных изменениях в их календаре, фотографиях и подписках.
Выберите алгоритм авторизации, используя селекторы.
Неявный поток
Получите токен доступа для использования в браузере, пока пользователь находится в браузере.
Примеры неявного потока данных демонстрируют веб-приложения до и после миграции на службы идентификации.
Поток авторизационного кода
Выданный 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. -
informationalLog use of deprexed functionality to JS console. Also log to session storage when is given optional key name: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 из библиотеки Google API Client Library for JavaScript и воспользоваться преимуществами автоматического создания вызываемых методов JS из документа обнаружения, пакетной обработки нескольких вызовов API и функциональности управления CORS.
Печенье
Для авторизации пользователя использование файлов cookie не требуется.
See Migrating from Google Sign-In for details of how user authentication makes use of cookies, and How Google uses cookies for cookie use by other Google products and services.
Реквизиты для входа
Сервис Google Identity Services разделяет аутентификацию и авторизацию пользователей на две отдельные операции, и учетные данные пользователя также разделены: токен идентификации, используемый для идентификации пользователя, возвращается отдельно от токена доступа, используемого для авторизации.
Чтобы просмотреть эти изменения, см. пример учетных данных .
Неявный поток
Разделите аутентификацию и авторизацию пользователей, исключив обработку профилей пользователей из процессов авторизации.
Удалите следующие ссылки на JavaScript-клиент для входа через Google :
Методы
-
GoogleUser.getBasicProfile() -
GoogleUser.getId()
Поток авторизационного кода
Служба идентификации разделяет учетные данные, передаваемые через браузер, на токен идентификации и токен доступа. Это изменение не распространяется на учетные данные, полученные посредством прямых вызовов к конечным точкам Google OAuth 2.0 с вашей серверной платформы или через библиотеки, работающие на защищенном сервере вашей платформы, такие как клиент Google APIs Node.js.
состояние сессии
Ранее функция Google Sign-In позволяла управлять статусом авторизации пользователя с помощью следующих инструментов:
- Обработчики обратных вызовов для мониторинга состояния пользовательской сессии .
- Слушатели событий и изменений статуса авторизации пользователя в учетной записи 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()
Неявный поток
Add a TokenClientConfig object and initTokenClient() call to configure your web app, following the example in initialize a token client .
Замените ссылки на JavaScript-клиенты Google Sign-In на Google Identity Services :
Объекты:
-
gapi.auth2.AuthorizeConfigwithTokenClientConfig
Методы:
-
gapi.auth2.init()withgoogle.accounts.oauth2.initTokenClient()
Параметры:
-
gapi.auth2.AuthorizeConfig.login_hintwithTokenClientConfig.login_hint. -
gapi.auth2.GoogleUser.getHostedDomain()withTokenClientConfig.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 Identity Services :
Методы:
-
gapi.auth2.authorize()withTokenClient.requestAccessToken() -
GoogleUser.reloadAuthResponse()withTokenClient.requestAccessToken()
Добавьте ссылку или кнопку для вызова функции requestAccessToken() , чтобы запустить всплывающее окно для запроса токена доступа или получения нового токена после истечения срока действия существующего.
Обновите свой код следующим образом:
- Запустите процесс получения токена OAuth 2.0 с помощью
requestAccessToken(). - Поддержка поэтапной авторизации достигается за счет использования
requestAccessTokenиOverridableTokenClientConfig, позволяющих разделить один запрос для множества областей действия на несколько более мелких запросов. - Запросите новый токен, когда срок действия существующего токена истечет или он будет аннулирован.
Работа с несколькими областями видимости может потребовать структурных изменений в вашем коде для запроса доступа к областям видимости только по мере необходимости, а не ко всем сразу; это называется инкрементальной авторизацией. Каждый запрос должен содержать как можно меньше областей видимости, и в идеале — одну область видимости. Подробнее о том, как обновить приложение для инкрементальной авторизации, см. в разделе «Обработка согласия пользователя» .
Когда срок действия токена доступа истекает, модуль gapi.auth2 автоматически получает новый действительный токен доступа для вашего веб-приложения. Для повышения безопасности пользователей этот процесс автоматического обновления токена не поддерживается библиотекой Google Identity Services. Ваше веб-приложение должно быть обновлено, чтобы обнаруживать истекший токен доступа и запрашивать новый. Подробнее см. раздел «Обработка токенов».
Поток авторизационного кода
Добавьте ссылку или кнопку для вызова функции requestCode() , чтобы запросить код авторизации у Google. Пример см. в разделе «Запуск потока кода OAuth 2.0» .
Дополнительную информацию о том, как реагировать на истекший или отозванный токен доступа, см. в разделе «Обработка токенов».
Обработка токенов
Добавить обработку ошибок для обнаружения неудачных вызовов Google API при использовании просроченного или отозванного токена доступа, а также для запроса нового, действительного токена доступа.
При использовании просроченного или отозванного токена доступа API Google возвращает HTTP-статус 401 Unauthorized и сообщение об ошибке invalid_token . Пример см. в разделе «Ответ на запрос о недействительном токене» .
Истекший срок действия токенов
Срок действия токенов доступа невелик и часто составляет всего несколько минут.
аннулирование токена
Владелец аккаунта Google может в любой момент отозвать ранее предоставленное согласие. Это аннулирует существующие токены доступа и токены обновления. Отзыв согласия может быть инициирован с вашей платформы с помощью revoke() или через аккаунт Google .
Замените ссылки на JavaScript-клиенты для входа через Google на ссылки на Google Identity Services :
Методы:
-
getAuthInstance().disconnect()withgoogle.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 Sign-In на Google Identity Services :
Объекты:
-
gapi.auth2.AuthorizeResponsewithTokenClient.TokenResponse -
gapi.auth2.AuthResponsewithTokenClient.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>
Клиентская библиотека JavaScript
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 Google Cloud console.
// 'scope' field specifies space-delimited list of access scopes.
gapi.client.init({
'apiKey': 'YOUR_API_KEY',
'clientId': 'YOUR_CLIENT_ID',
'discoveryDocs': [discoveryUrl],
'scope': SCOPE
}).then(function () {
GoogleAuth = gapi.auth2.getAuthInstance();
// Listen for sign-in state changes.
GoogleAuth.isSignedIn.listen(updateSigninStatus);
// Handle initial sign-in state. (Determine if user is already signed in.)
var user = GoogleAuth.currentUser.get();
setSigninStatus();
// Call handleAuthClick function when user clicks on
// "Sign In/Authorize" button.
$('#sign-in-or-out-button').click(function() {
handleAuthClick();
});
$('#revoke-access-button').click(function() {
revokeAccess();
});
});
}
function handleAuthClick() {
if (GoogleAuth.isSignedIn.get()) {
// User is authorized and has clicked "Sign out" button.
GoogleAuth.signOut();
} else {
// User is not signed in. Start Google auth flow.
GoogleAuth.signIn();
}
}
function revokeAccess() {
GoogleAuth.disconnect();
}
function setSigninStatus() {
var user = GoogleAuth.currentUser.get();
var isAuthorized = user.hasGrantedScopes(SCOPE);
if (isAuthorized) {
$('#sign-in-or-out-button').html('Sign out');
$('#revoke-access-button').css('display', 'inline-block');
$('#auth-status').html('You are currently signed in and have granted ' +
'access to this app.');
} else {
$('#sign-in-or-out-button').html('Sign In/Authorize');
$('#revoke-access-button').css('display', 'none');
$('#auth-status').html('You have not authorized this app or you are ' +
'signed out.');
}
}
function updateSigninStatus() {
setSigninStatus();
}
</script>
<button id="sign-in-or-out-button"
style="margin-left: 25px">Sign In/Authorize</button>
<button id="revoke-access-button"
style="display: none; margin-left: 25px">Revoke access</button>
<div id="auth-status" style="display: inline; padding-left: 25px"></div><hr>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script async defer src="https://apis.google.com/js/api.js"
onload="this.onload=function(){};handleClientLoad()"
onreadystatechange="if (this.readyState === 'complete') this.onload()">
</script>
</body></html>
Конечные точки 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 Identity Service с моделью токенов и всплывающим диалоговым окном для подтверждения согласия пользователя. Он представлен для иллюстрации минимального количества шагов, необходимых для настройки клиента, запроса и получения токена доступа, а также вызова 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 async/await
В этом примере показано, как добавить библиотеку Google Identity Service , используя модель токенов , удалить модуль gapi.auth2 и вызвать API с помощью клиентской библиотеки Google API для JavaScript .
Промисы, асинхронные операции и ожидание используются для обеспечения порядка загрузки библиотек, а также для перехвата и повторной обработки ошибок авторизации. Вызов API выполняется только после получения действительного токена доступа.
Предполагается, что пользователи будут нажимать кнопку «Показать календарь», если токен доступа отсутствует при первой загрузке страницы или позже, после истечения срока действия токена доступа.
<!DOCTYPE html>
<html>
<head>
<title>GAPI and GIS Example</title>
<script async defer src="https://apis.google.com/js/api.js" onload="gapiLoad()"></script>
<script async defer src="https://accounts.google.com/gsi/client" onload="gisLoad()"></script>
</head>
<body>
<h1>GAPI Client with GIS Authorization</h1>
<button id="authorizeBtn" style="visibility:hidden;">Authorize and Load Events</button>
<button id="revokeBtn" style="visibility:hidden;">Revoke Access</button>
<div id="content"></div>
<script>
const YOUR_CLIENT_ID = "YOUR_CLIENT_ID";
const YOUR_API_KEY = 'YOUR_API_KEY';
const CALENDAR_SCOPE = 'https://www.googleapis.com/auth/calendar.readonly';
let tokenClient;
let libsLoaded = 0;
function gapiLoad() {
gapi.load('client', initGapiClient);
}
async function initGapiClient() {
try {
await gapi.client.init({ apiKey: YOUR_API_KEY });
await gapi.client.load('https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest');
console.log('GAPI client initialized.');
checkAllLoaded();
} catch (err) {
handleError('GAPI initialization failed:', err);
}
}
function gisLoad() {
try {
tokenClient = google.accounts.oauth2.initTokenClient({
client_id: YOUR_CLIENT_ID,
scope: CALENDAR_SCOPE,
callback: '', // Will be set dynamically
error_callback: handleGisError,
});
console.log('GIS TokenClient initialized.');
checkAllLoaded();
} catch (err) {
handleError('GIS initialization failed:', err);
}
}
function checkAllLoaded() {
libsLoaded++;
if (libsLoaded === 2) {
document.getElementById('authorizeBtn').style.visibility = 'visible';
document.getElementById('revokeBtn').style.visibility = 'visible';
document.getElementById('authorizeBtn').onclick = makeApiCall;
document.getElementById('revokeBtn').onclick = revokeAccess;
console.log('Ready to authorize.');
}
}
function handleGisError(err) {
console.error('GIS Error:', err);
let message = 'An error occurred during authorization.';
if (err && err.type === 'popup_failed_to_open') {
message = 'Failed to open popup. Please disable popup blockers.';
} else if (err && err.type === 'popup_closed') {
message = 'Authorization popup was closed.';
}
document.getElementById('content').textContent = message;
}
function handleError(message, error) {
console.error(message, error);
document.getElementById('content').textContent = `${message} ${error.message || JSON.stringify(error)}`;
}
async function makeApiCall() {
document.getElementById('content').textContent = 'Processing...';
try {
let token = gapi.client.getToken();
if (!token || !token.access_token) {
console.log('No token, fetching one...');
await getToken();
}
console.log('Calling Calendar API...');
const response = await gapi.client.calendar.events.list({ 'calendarId': 'primary' });
displayEvents(response.result);
} catch (err) {
console.error('API call failed:', err);
const errorInfo = err.result && err.result.error;
if (errorInfo && (errorInfo.code === 401 || (errorInfo.code === 403 && errorInfo.status === "PERMISSION_DENIED"))) {
console.log('Auth error on API call, refreshing token...');
try {
await getToken({ prompt: 'consent' }); // Force refresh
const retryResponse = await gapi.client.calendar.events.list({ 'calendarId': 'primary' });
displayEvents(retryResponse.result);
} catch (refreshErr) {
handleError('Failed to refresh token or retry API call:', refreshErr);
}
} else {
handleError('Error loading events:', err.result ? err.result.error : err);
}
}
}
async function getToken(options = { prompt: '' }) {
return new Promise((resolve, reject) => {
if (!tokenClient) return reject(new Error("GIS TokenClient not initialized."));
tokenClient.callback = (tokenResponse) => {
if (tokenResponse.error) {
reject(new Error(`Token Error: ${tokenResponse.error} - ${tokenResponse.error_description}`));
} else {
console.log('Token acquired.');
resolve(tokenResponse);
}
};
tokenClient.requestAccessToken(options);
});
}
function displayEvents(result) {
const events = result.items;
if (events && events.length > 0) {
let eventList = '<h3>Upcoming Events:</h3><ul>' + events.map(event =>
`<li>${event.summary} (${event.start.dateTime || event.start.date})</li>`
).join('') + '</ul>';
document.getElementById('content').innerHTML = eventList;
} else {
document.getElementById('content').textContent = 'No upcoming events found.';
}
}
function revokeAccess() {
const token = gapi.client.getToken();
if (token && token.access_token) {
google.accounts.oauth2.revoke(token.access_token, () => {
console.log('Access revoked.');
document.getElementById('content').textContent = 'Access has been revoked.';
gapi.client.setToken(null);
});
} else {
document.getElementById('content').textContent = 'No token to revoke.';
}
}
</script>
</body>
</html>
Обратный вызов GAPI
В этом примере показано, как добавить библиотеку Google Identity Service , используя модель токенов , удалить модуль gapi.auth2 и вызвать API с помощью клиентской библиотеки Google API для 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 <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();
}
Новый способ
Всплывающее окно ГИС UX
В этом примере используется только библиотека JavaScript Google Identity Service , работающая по модели кода авторизации, всплывающее диалоговое окно для получения согласия пользователя и обработчик обратного вызова для получения кода авторизации от 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>
GIS Redirect 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, используемая для аутентификации и авторизации пользователей, которая объединяет и заменяет функции и возможности, присутствующие в нескольких различных библиотеках и модулях:
Действия, которые необходимо предпринять при миграции на службы идентификации:
| Существующая библиотека JavaScript | Новая библиотека JavaScript | Примечания |
|---|---|---|
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() | Replace old with new |
| GoogleUser.getBasicProfile() | Удалить. Вместо этого используйте ID-токен, см. раздел «Переход с Google Sign-In» . | |
| GoogleUser.getGrantedScopes() | hasGrantedAnyScope() | Replace old with new |
| GoogleUser.getHostedDomain() | Удалять | |
| GoogleUser.getId() | Удалять | |
| GoogleUser.grantOfflineAccess() | Remove, follow the authorization code flow. | |
| GoogleUser.grant() | Удалять | |
| GoogleUser.hasGrantedScopes() | hasGrantedAnyScope() | Replace old with new |
| GoogleUser.isSignedIn() | Удалять | |
| GoogleUser.reloadAuthResponse() | requestAccessToken() | Удалите старый токен доступа и вызовите новый для его замены. |
| gapi.auth2 object and associated methods: | ||
| gapi.auth2.AuthorizeConfig object | TokenClientConfig или CodeClientConfig | Replace old with new |
| gapi.auth2.AuthorizeResponse object | Удалять | |
| объект gapi.auth2.AuthResponse | Удалять | |
| gapi.auth2.authorize() | requestCode() или requestAccessToken() | Replace old with new |
| gapi.auth2.ClientConfig() | TokenClientConfig или CodeClientConfig | Replace old with new |
| gapi.auth2.getAuthInstance() | Удалять | |
| gapi.auth2.init() | initTokenClient() or initCodeClient() | Замените старое на новое |
| gapi.auth2.OfflineAccessOptions object | Удалять | |
| gapi.auth2.SignInOptions object | Удалять | |
| gapi.signin2 object and associated methods: | ||
| gapi.signin2.render() | Удалить. Загрузка HTML DOM элемента g_id_signin или вызов JavaScript-функции google.accounts.id.renderButton запускает вход пользователя в учетную запись Google. | |
Пример учетных данных
Имеющиеся учетные данные
Библиотека платформы Google Sign-In , клиентская библиотека Google API для JavaScript или прямые вызовы к конечным точкам Google OAuth 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 Identity Services
The Google Identity Services library returns:
либо токен доступа, используемый для авторизации:
{ "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"
}
}