Обзор
Чтобы получить токен доступа для каждого пользователя и вызывать API Google, Google предлагает несколько библиотек JavaScript:
В этом руководстве приведены инструкции по переходу из этих библиотек в библиотеку Google 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 от имени пользователей, не требуя их присутствия, что также называется автономным режимом.
Токены обновления управляются и хранятся на вашей внутренней платформе.
В некоторых случаях ваша кодовая база может поддерживать оба потока.
Выберите поток авторизации
Перед началом миграции вам необходимо определить, соответствует ли вашим потребностям продолжение работы с существующим процессом или переход на другой процесс.
Ознакомьтесь с выбором потока авторизации , чтобы понять основные различия и компромиссы между двумя потоками.
В большинстве случаев рекомендуется использовать поток с кодом авторизации, поскольку он обеспечивает максимальный уровень безопасности пользователя. Реализация этого потока также позволяет вашей платформе добавлять новые офлайн-функции, такие как загрузка обновлений для уведомления пользователей о важных изменениях в их календаре, фотографиях и подписках.
Выберите поток авторизации с помощью селекторов.
Неявный поток
Получите токен доступа для использования в браузере, пока пользователь присутствует.
Примеры неявного потока показывают веб-приложения до и после миграции на службы идентификации.
Поток кода авторизации
Код авторизации, выдаваемый Google для каждого пользователя, передается на вашу внутреннюю платформу, где он затем обменивается на токен доступа и токен обновления.
Примеры потока кода авторизации показывают веб-приложения до и после миграции на службы идентификации.
В этом руководстве следуйте инструкциям, выделенным жирным шрифтом, чтобы добавить , удалить , обновить или заменить существующие функции.
Изменения в вашем браузерном веб-приложении
В этом разделе рассматриваются изменения, которые вы внесете в свое браузерное веб-приложение при переходе на библиотеку JavaScript служб идентификации Google.
Определите затронутый код и протестируйте
Отладочный 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
ведение журнала использования устаревших функций в консоли JavaScript. Также ведение журнала в хранилище сеансов, если задано необязательное имя ключа: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 для JavaScript и воспользоваться его функциями автоматического создания вызываемых JS-методов из документа обнаружения, пакетирования нескольких вызовов API и управления CORS.
Файлы cookie
Авторизация пользователя не требует использования cookie-файлов.
Подробную информацию о том, как аутентификация пользователя использует файлы cookie, см . в разделе Переход со входа в Google , а также в разделе Как Google использует файлы cookie для использования файлов cookie другими продуктами и службами Google.
Реквизиты для входа
Службы идентификации Google разделяют аутентификацию и авторизацию пользователя на две отдельные операции, а учетные данные пользователя разделены: токен идентификатора, используемый для идентификации пользователя, возвращается отдельно от токена доступа, используемого для авторизации.
Чтобы просмотреть эти изменения, ознакомьтесь с примерами учетных данных .
Неявный поток
Разделите аутентификацию и авторизацию пользователей, исключив обработку профилей пользователей из потоков авторизации.
Удалите следующие ссылки на клиент JavaScript для входа в Google :
Методы
-
GoogleUser.getBasicProfile()
-
GoogleUser.getId()
Поток кода авторизации
Службы идентификации разделяют учётные данные в браузере на токен идентификатора и токен доступа. Это изменение не распространяется на учётные данные, полученные посредством прямых вызовов конечных точек Google OAuth 2.0 с вашей бэкэнд-платформы или через библиотеки, работающие на защищённом сервере вашей платформы, например, Node.js-клиент Google API .
Состояние сеанса
Ранее функция входа в систему Google помогала управлять статусом входа пользователя с помощью:
- Обработчики обратных вызовов для мониторинга состояния сеанса пользователя .
- Прослушиватели событий и изменений статуса входа пользователя в учетную запись 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()
для инициирования всплывающего UX-потока для запроса токена доступа или для получения нового токена по истечении срока действия существующего токена.
Обновите свою кодовую базу до:
- Запустите поток токенов OAuth 2.0 с помощью
requestAccessToken()
. - Поддерживайте поэтапную авторизацию с помощью
requestAccessToken
иOverridableTokenClientConfig
для разделения одного запроса для многих областей на несколько более мелких запросов. - Запросите новый токен, когда срок действия существующего токена истечет или он будет отозван.
Работа с несколькими областями действия может потребовать структурных изменений в кодовой базе, чтобы запрашивать доступ к областям действия только по мере необходимости, а не ко всем сразу. Это называется инкрементальной авторизацией. Каждый запрос должен содержать как можно меньше областей действия, а в идеале — только одну. Подробнее об обновлении приложения для инкрементальной авторизации см. в статье «Как обрабатывать согласие пользователя» .
По истечении срока действия токена доступа модуль gapi.auth2
автоматически получает новый действительный токен доступа для вашего веб-приложения. Для повышения безопасности пользователей этот процесс автоматического обновления токенов не поддерживается библиотекой Google Identity Services. Для обнаружения истекшего токена доступа и запроса нового необходимо обновить ваше веб-приложение. Подробнее см. в разделе «Обработка токенов».
Поток кода авторизации
Добавьте ссылку или кнопку для вызова метода requestCode()
для запроса кода авторизации у Google. Пример см. в разделе «Триггер OAuth 2.0 Code Flow» .
Дополнительную информацию о том, как реагировать на истекший срок действия или отозванный токен доступа, см. в разделе «Обработка токенов».
Обработка токенов
Добавьте обработку ошибок для обнаружения неудачных вызовов API Google при использовании просроченного или отозванного токена доступа, а также для запроса нового действительного токена доступа.
При использовании истёкшего или отозванного токена доступа API Google возвращают код статуса HTTP 401 Unauthorized
и сообщение об ошибке invalid_token
. Пример см. в разделе «Недействительный ответ о токене» .
Просроченные токены
Токены доступа недолговечны и часто действительны всего несколько минут.
Отзыв токена
Владелец учётной записи 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 ID Token в качестве значения параметра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 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 .
Промисы, асинхронные функции и функции await используются для обеспечения порядка загрузки библиотек, а также для перехвата и повторных попыток авторизации. Вызов 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, используемая для аутентификации и авторизации пользователей, которая объединяет и заменяет функции и возможности, присутствующие в нескольких различных библиотеках и модулях:
Действия, которые необходимо предпринять при переходе на службы идентификации:
Существующая библиотека 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, а также примечания с дополнительной информацией и действиями, которые необходимо предпринять во время миграции.
Старый | Новый | Примечания |
---|---|---|
Объект 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() | Удалить. Вместо этого используйте ID-токен, см. раздел «Миграция из аккаунта Google» . | |
GoogleUser.getGrantedScopes() | hasGrantedAnyScope() | Заменить старое на новое |
GoogleUser.getHostedDomain() | Удалять | |
GoogleUser.getId() | Удалять | |
GoogleUser.grantOfflineAccess() | Удалите, следуйте инструкциям по вводу кода авторизации. | |
GoogleUser.grant() | Удалять | |
GoogleUser.hasGrantedScopes() | hasGrantedAnyScope() | Заменить старое на новое |
GoogleUser.isSignedIn() | Удалять | |
GoogleUser.reloadAuthResponse() | requestAccessToken() | Удалите старый, вызовите новый для замены просроченного или отозванного токена доступа. |
Объект gapi.auth2 и связанные с ним методы: | ||
объект gapi.auth2.AuthorizeConfig | TokenClientConfig или CodeClientConfig | Заменить старое на новое |
объект gapi.auth2.AuthorizeResponse | Удалять | |
объект gapi.auth2.AuthResponse | Удалять | |
gapi.auth2.authorize() | requestCode() или requestAccessToken() | Заменить старое на новое |
gapi.auth2.ClientConfig() | TokenClientConfig или CodeClientConfig | Заменить старое на новое |
gapi.auth2.getAuthInstance() | Удалять | |
gapi.auth2.init() | initTokenClient() или initCodeClient() | Заменить старое на новое |
объект gapi.auth2.OfflineAccessOptions | Удалять | |
объект gapi.auth2.SignInOptions | Удалять | |
Объект gapi.signin2 и связанные с ним методы: | ||
gapi.signin2.render() | Удалить. Загрузка HTML DOM элемента g_id_signin или вызов JS google.accounts.id.renderButton запускает вход пользователя в учетную запись Google. |
Пример учетных данных
Существующие учетные данные
Библиотека платформы входа Google , клиентская библиотека API Google для 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
Библиотека служб идентификации Google возвращает:
либо токен доступа, если он используется для авторизации:
{ "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"
}
}