В этой статье рассказывается, как веб-серверные приложения используют клиентские библиотеки Google API или конечные точки Google OAuth 2.0 для реализации авторизации OAuth 2.0, чтобы получить доступ к YouTube Data API.
OAuth 2.0 позволяет пользователям делиться с приложениями определенными данными, не разглашая при этом имена пользователей, пароли и другую конфиденциальную информацию. Например, приложение может использовать OAuth 2.0, чтобы получить разрешение на извлечение данных канала YouTube.
Этот процесс OAuth 2.0 предназначен для авторизации пользователей. Он предназначен для приложений, которые могут хранить конфиденциальную информацию и поддерживать состояние. Правильно авторизованное приложение веб-сервера может обращаться к API, пока пользователь взаимодействует с приложением или после того, как он его покинул.
Веб-серверные приложения часто используют сервисные аккаунты для авторизации запросов к API, особенно при вызове Cloud API для доступа к данным на уровне проекта, а не пользователя. Приложения веб-сервера могут использовать сервисные аккаунты в сочетании с авторизацией пользователей.
YouTube Live Streaming API не поддерживает работу с сервисными аккаунтами. Поскольку сервисный аккаунт нельзя связать с аккаунтом YouTube, попытки авторизовать запросы с помощью этого потока приведут к ошибке NoLinkedYouTubeAccount.
Клиентские библиотеки
В приведенных здесь примерах для конкретных языков программирования показана реализация OAuth 2.0 с помощью клиентских библиотек Google API. Чтобы выполнить примеры кода, сначала необходимо установить клиентскую библиотеку для соответствующего языка.
Если вы используете клиентскую библиотеку API Google для обработки процесса OAuth 2.0 в приложении, она выполняет многие действия, которые в противном случае пришлось бы выполнять приложению. Например, он определяет, когда приложение может использовать или обновлять сохраненные токены доступа, а также когда приложению необходимо повторно получить согласие. Клиентская библиотека также генерирует правильные URL перенаправления и помогает реализовать обработчики перенаправления, которые обменивают коды авторизации на токены доступа.
Клиентские библиотеки API Google для серверных приложений доступны для следующих языков:
Требования
Как включить API для проекта
Любое приложение, вызывающее API Google, должно включить эти API в API Console.
Чтобы включить API для вашего проекта:
- Откройте библиотеку API в Google API Console.
- Выберите существующий проект или создайте новый.
- На странице "Библиотека" найдите и включите YouTube Data API. Найдите другие API, которые будет использовать ваше приложение, и включите их.
Как создать учетные данные для авторизации
Любое приложение, использующее OAuth 2.0 для доступа к API Google, должно иметь учетные данные для авторизации, которые идентифицируют приложение на сервере OAuth 2.0 Google. Ниже описано, как создать учетные данные для проекта. После этого ваши приложения смогут использовать учетные данные для доступа к API, которые вы включили для этого проекта.
- Перейдите на страницу клиентов.
- Нажмите Create Client (Создать клиента).
- Выберите тип приложения Веб-приложение.
- Заполните форму и нажмите Создать. В приложениях, использующих такие языки и фреймворки, как PHP, Java, Python, Ruby и .NET, необходимо указывать авторизованные URI перенаправления. URI перенаправления – это конечные точки, на которые сервер OAuth 2.0 может отправлять ответы. Эти конечные точки должны соответствовать правилам проверки Google.
Для тестирования можно указать URI, которые ссылаются на локальный компьютер, например
http://localhost:8080. Во всех примерах в этом документе в качестве URI переадресации используетсяhttp://localhost:8080.Рекомендуем разработать конечные точки авторизации приложения так, чтобы оно не раскрывало коды авторизации другим ресурсам на странице.
После создания учетных данных скачайте файл client_secret.json из API Console. Сохраните файл в папке, доступ к которой есть только у вашего приложения.
Как определить области доступа
Области действия позволяют приложению запрашивать доступ только к тем ресурсам, которые ему нужны, а пользователям – контролировать объем доступа, который они предоставляют приложению. Таким образом, между количеством запрошенных областей и вероятностью получения согласия пользователя может существовать обратная зависимость.
Прежде чем приступать к реализации авторизации OAuth 2.0, рекомендуем определить области действия, к которым вашему приложению потребуется доступ.
Мы также рекомендуем, чтобы ваше приложение запрашивало доступ к областям авторизации с помощью процесса инкрементной авторизации, в котором ваше приложение запрашивает доступ к пользовательским данным в контексте. Это поможет пользователям понять, почему приложению нужен доступ к тем или иным функциям.
YouTube Data API v3 использует следующие области действия:
| Объем | Описание |
|---|---|
https://www. googleapis. com/ auth/ youtube | Управляйте своим аккаунтом YouTube |
https://www. googleapis. com/ auth/ youtube. channel-memberships. creator | Посмотрите список текущих активных участников канала, их текущий уровень и дату, когда они стали участниками. |
https://www. googleapis. com/ auth/ youtube. force-ssl | Просмотр, редактирование и окончательное удаление ваших видео, оценок, комментариев и субтитров на YouTube. |
https://www. googleapis. com/ auth/ youtube. readonly | Просмотр вашего аккаунта YouTube |
https://www. googleapis. com/ auth/ youtube. upload | Управляйте своими видео на YouTube |
https://www. googleapis. com/ auth/ youtubepartner | Просмотр и управление вашими активами и связанным с ними контентом на YouTube |
https://www. googleapis. com/ auth/ youtubepartner-channel-audit | Просмотр конфиденциальной информации вашего канала YouTube, актуальной во время процесса аудита с партнером YouTube |
В документе Области действия OAuth 2.0 API приведен полный список областей действия, которые можно использовать для доступа к API Google.
Требования к языкам
Чтобы запустить любой из примеров кода в этом документе, вам понадобится аккаунт Google, доступ к интернету и веб-браузер. Если вы используете одну из клиентских библиотек API, ознакомьтесь с требованиями для конкретного языка в разделах ниже.
PHP
Чтобы запустить примеры кода PHP из этого документа, вам потребуется:
- PHP 8.0 или более поздней версии с установленным интерфейсом командной строки (CLI) и расширением JSON.
- Инструмент управления зависимостями Composer.
-
Клиентская библиотека API Google для PHP:
composer require google/apiclient:^2.15.0
Подробную информацию вы найдете в статье Клиентская библиотека API Google для PHP.
Python
Чтобы запустить примеры кода Python, приведенные в этом документе, вам потребуется:
- Python 3.7 или более поздней версии;
- Инструмент управления пакетами pip.
- В версии 2.0 клиентской библиотеки API Google для Python:
pip install --upgrade google-api-python-client
google-auth,google-auth-oauthlibиgoogle-auth-httplib2для авторизации пользователей.pip install --upgrade google-auth google-auth-oauthlib google-auth-httplib2
- Фреймворк Flask для веб-приложений на Python.
pip install --upgrade flask
- HTTP-библиотека
requests.pip install --upgrade requests
Если вы не можете обновить Python и связанное руководство по переносу, ознакомьтесь с примечаниями к выпуску клиентской библиотеки Python для API Google.
Ruby
Чтобы запустить примеры кода Ruby, приведенные в этом документе, вам потребуется:
- Ruby 2.6 или более поздней версии;
-
Библиотека Google Auth для Ruby:
gem install googleauth
-
Клиентские библиотеки для API Google Диска и Календаря:
gem install google-apis-drive_v3 google-apis-calendar_v3
-
Веб-фреймворк Sinatra Ruby.
gem install sinatra
Node.js
Чтобы запустить примеры кода Node.js из этого документа, вам потребуется:
- Поддерживаемая версия LTS, активная версия LTS или текущая версия Node.js.
-
Клиент Node.js для API Google:
npm install googleapis crypto express express-session
HTTP/REST
Для прямого вызова конечных точек OAuth 2.0 не нужно устанавливать библиотеки.
Получение токенов доступа OAuth 2.0
Ниже описано, как ваше приложение взаимодействует с сервером OAuth 2.0 от Google, чтобы получить согласие пользователя на выполнение запроса к API от его имени. Приложение должно получить согласие, прежде чем выполнять запрос к API Google, требующий авторизации пользователя.
Ниже приведен краткий список этих шагов.
- Приложение определяет, какие разрешения ему нужны.
- Ваше приложение перенаправляет пользователя в Google вместе со списком запрошенных разрешений.
- Пользователь решает, предоставлять ли вашему приложению разрешения.
- Ваше приложение узнает, какое решение принял пользователь.
- Если пользователь предоставил запрошенные разрешения, ваше приложение получает токены, необходимые для отправки запросов к API от имени пользователя.
Шаг 1. Задайте параметры авторизации
Сначала создайте запрос на авторизацию. В этом запросе задаются параметры, которые идентифицируют ваше приложение и определяют разрешения, которые пользователю будет предложено предоставить вашему приложению.
- Если вы используете клиентскую библиотеку Google для аутентификации и авторизации OAuth 2.0, создайте и настройте объект, определяющий эти параметры.
- Если вы напрямую вызываете конечную точку Google OAuth 2.0, то создаете URL и задаете параметры в этом URL.
На вкладках ниже перечислены поддерживаемые параметры авторизации для приложений веб-сервера. В примерах для конкретных языков программирования также показано, как использовать клиентскую библиотеку или библиотеку авторизации для настройки объекта, который задает эти параметры:
PHP
Следующий фрагмент кода создает объект Google\Client(), который определяет параметры в запросе авторизации.
Этот объект использует информацию из файла client_secret.json для идентификации вашего приложения. Подробнее о том, как создать учетные данные для авторизации… Объект также определяет области действия, для которых ваше приложение запрашивает разрешение на доступ, и URL конечной точки авторизации приложения, которая будет обрабатывать ответ от сервера OAuth 2.0 Google. Наконец, код задает необязательные параметры access_type и include_granted_scopes.
Например, чтобы запросить офлайн-доступ к данным пользователя YouTube, используйте следующий код:
use Google\Client; $client = new Client(); // Required, call the setAuthConfig function to load authorization credentials from // client_secret.json file. $client->setAuthConfig('client_secret.json'); // Required, to set the scope value, call the addScope function $client->addScope(Google_Service_YouTube::YOUTUBE_FORCE_SSL); // Required, call the setRedirectUri function to specify a valid redirect URI for the // provided client_id $client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php'); // Recommended, offline access will give you both an access and refresh token so that // your app can refresh the access token without user interaction. $client->setAccessType('offline'); // Recommended, call the setState function. Using a state value can increase your assurance that // an incoming connection is the result of an authentication request. $client->setState($sample_passthrough_value); // Optional, if your application knows which user is trying to authenticate, it can use this // parameter to provide a hint to the Google Authentication Server. $client->setLoginHint('hint@example.com'); // Optional, call the setPrompt function to set "consent" will prompt the user for consent $client->setPrompt('consent'); // Optional, call the setIncludeGrantedScopes function with true to enable incremental // authorization $client->setIncludeGrantedScopes(true);
Python
В следующем фрагменте кода для создания запроса авторизации используется модуль google-auth-oauthlib.flow.
Код создает объект Flow, который идентифицирует ваше приложение, используя информацию из файла client_secret.json, скачанного после создания учетных данных для авторизации. Этот объект также определяет области действия, для которых приложение запрашивает разрешение на доступ, и URL конечной точки аутентификации приложения, которая будет обрабатывать ответ от сервера OAuth 2.0 Google. Наконец, код задает необязательные параметры access_type и include_granted_scopes.
Например, чтобы запросить офлайн-доступ для получения данных пользователя на YouTube, сделайте следующее:
import google.oauth2.credentials import google_auth_oauthlib.flow # Required, call the from_client_secrets_file method to retrieve the client ID from a # client_secret.json file. The client ID (from that file) and access scopes are required. (You can # also use the from_client_config method, which passes the client configuration as it originally # appeared in a client secrets file but doesn't access the file itself.) flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file('client_secret.json', scopes=['https://www.googleapis.com/auth/youtube.force-ssl', 'https://www.googleapis.com/auth/calendar.readonly']) # Required, indicate where the API server will redirect the user after the user completes # the authorization flow. The redirect URI is required. The value must exactly # match one of the authorized redirect URIs for the OAuth 2.0 client, which you # configured in the API Console. If this value doesn't match an authorized URI, # you will get a 'redirect_uri_mismatch' error. flow.redirect_uri = 'https://www.example.com/oauth2callback' # Generate URL for request to Google's OAuth 2.0 server. # Use kwargs to set optional request parameters. authorization_url, state = flow.authorization_url( # Recommended, enable offline access so that you can refresh an access token without # re-prompting the user for permission. Recommended for web server apps. access_type='offline', # Optional, enable incremental authorization. Recommended as a best practice. include_granted_scopes='true', # Optional, if your application knows which user is trying to authenticate, it can use this # parameter to provide a hint to the Google Authentication Server. login_hint='hint@example.com', # Optional, set prompt to 'consent' will prompt the user for consent prompt='consent')
Ruby
Используйте файл client_secrets.json, который вы создали, чтобы настроить объект клиента в приложении. При настройке объекта клиента вы указываете области, к которым вашему приложению требуется доступ, а также URL конечной точки аутентификации приложения, которая будет обрабатывать ответ от сервера OAuth 2.0.
Например, чтобы запросить офлайн-доступ для получения данных пользователя на YouTube, сделайте следующее:
require 'googleauth' require 'googleauth/web_user_authorizer' require 'googleauth/stores/redis_token_store' require 'google/apis/youtube_v3' require 'google/apis/calendar_v3' # Required, call the from_file method to retrieve the client ID from a # client_secret.json file. client_id = Google::Auth::ClientId.from_file('/path/to/client_secret.json') # Required, scope value # Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar. scope = ['Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY', 'Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY'] # Required, Authorizers require a storage instance to manage long term persistence of # access and refresh tokens. token_store = Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new) # Required, indicate where the API server will redirect the user after the user completes # the authorization flow. The redirect URI is required. The value must exactly # match one of the authorized redirect URIs for the OAuth 2.0 client, which you # configured in the API Console. If this value doesn't match an authorized URI, # you will get a 'redirect_uri_mismatch' error. callback_uri = '/oauth2callback' # To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI # from the client_secret.json file. To get these credentials for your application, visit # https://console.cloud.google.com/apis/credentials. authorizer = Google::Auth::WebUserAuthorizer.new(client_id, scope, token_store, callback_uri)
Ваше приложение использует объект клиента для выполнения операций OAuth 2.0, например для создания URL запросов авторизации и применения токенов доступа к HTTP-запросам.
Node.js
Следующий фрагмент кода создает объект google.auth.OAuth2, который определяет параметры в запросе авторизации.
Этот объект использует информацию из файла client_secret.json, чтобы идентифицировать ваше приложение. Чтобы запросить у пользователя разрешения на получение токена доступа, перенаправьте его на страницу согласия. Чтобы создать URL страницы согласия, выполните следующие действия:
const {google} = require('googleapis'); const crypto = require('crypto'); const express = require('express'); const session = require('express-session'); /** * To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI * from the client_secret.json file. To get these credentials for your application, visit * https://console.cloud.google.com/apis/credentials. */ const oauth2Client = new google.auth.OAuth2( YOUR_CLIENT_ID, YOUR_CLIENT_SECRET, YOUR_REDIRECT_URL ); // Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar. const scopes = [ 'https://www.googleapis.com/auth/youtube.force-ssl', 'https://www.googleapis.com/auth/calendar.readonly' ]; // Generate a secure random state value. const state = crypto.randomBytes(32).toString('hex'); // Store state in the session req.session.state = state; // Generate a url that asks permissions for the Drive activity and Google Calendar scope const authorizationUrl = oauth2Client.generateAuthUrl({ // 'online' (default) or 'offline' (gets refresh_token) access_type: 'offline', /** Pass in the scopes array defined above. * Alternatively, if only one scope is needed, you can pass a scope URL as a string */ scope: scopes, // Enable incremental authorization. Recommended as a best practice. include_granted_scopes: true, // Include the state parameter to reduce the risk of CSRF attacks. state: state });
Важное примечание. Значение refresh_token возвращается только при первой авторизации.
Подробнее…
HTTP/REST
Конечная точка OAuth 2.0 Google находится по адресу https://accounts.google.com/o/oauth2/v2/auth. Этот конечный пункт доступен только по протоколу HTTPS. Обычные HTTP-подключения отклоняются.
Сервер авторизации Google поддерживает следующие параметры строки запроса для приложений веб-сервера:
| Параметры | |||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
client_id |
Обязательно
Идентификатор клиента для вашего приложения. Это значение можно найти на странице клиентов в Cloud Console. |
||||||||||||||||
redirect_uri |
Обязательно
Определяет, куда сервер API перенаправляет пользователя после завершения процесса авторизации. Значение должно точно совпадать с одним из авторизованных URI перенаправления для клиента OAuth 2.0, который вы настроили на странице клиентов в Cloud Console. Если это значение не совпадает с авторизованным URI перенаправления для указанного Обратите внимание, что схема |
||||||||||||||||
response_type |
Обязательно
Определяет, возвращает ли конечная точка Google OAuth 2.0 код авторизации. Для приложений веб-сервера установите значение параметра |
||||||||||||||||
scope |
Обязательно
Список областей действия, разделенных пробелами, которые определяют ресурсы, к которым ваше приложение может получить доступ от имени пользователя. Эти значения используются в окне запроса согласия, которое Google показывает пользователю. Области действия позволяют приложению запрашивать доступ только к тем ресурсам, которые ему нужны, а пользователям – контролировать объем доступа, предоставляемого приложению. Таким образом, существует обратная зависимость между количеством запрашиваемых областей и вероятностью получения согласия пользователя. YouTube Data API v3 использует следующие области действия:
В документе Области действия API OAuth 2.0 приведен полный список областей действия, которые можно использовать для доступа к API Google. Рекомендуем по возможности запрашивать доступ к областям авторизации в контексте. Запрашивая доступ к пользовательским данным в контексте с помощью поэтапной авторизации, вы помогаете пользователям понять, зачем вашему приложению нужен запрашиваемый доступ. |
||||||||||||||||
access_type |
Рекомендуется
Указывает, может ли приложение обновлять токены доступа, когда пользователь не работает в браузере. Допустимые значения параметра: Установите значение |
||||||||||||||||
state |
Рекомендуется
Любое строковое значение, которое ваше приложение использует для сохранения состояния между запросом авторизации и ответом сервера авторизации.
Сервер возвращает точное значение, которое вы отправляете в виде пары Этот параметр можно использовать для разных целей, например чтобы направлять пользователя к нужному ресурсу в приложении, отправлять одноразовые коды и предотвращать подделку межсайтовых запросов. Поскольку ваш |
||||||||||||||||
include_granted_scopes |
Необязательно
Позволяет приложениям использовать пошаговую авторизацию для запроса доступа к дополнительным областям действия в контексте. Если вы зададите для этого параметра значение |
||||||||||||||||
enable_granular_consent |
Необязательно
Значение по умолчанию – Когда Google включит детализированные разрешения для приложения, этот параметр перестанет действовать. |
||||||||||||||||
login_hint |
Необязательно
Если приложение знает, какой пользователь пытается выполнить аутентификацию, оно может использовать этот параметр, чтобы предоставить подсказку серверу аутентификации Google. Сервер использует подсказку, чтобы упростить процесс входа, заполнив поле адреса электронной почты в форме входа или выбрав соответствующий сеанс с несколькими входами. В качестве значения параметра укажите адрес электронной почты или идентификатор |
||||||||||||||||
prompt |
Необязательно
Список запросов, разделенных пробелами, с учетом регистра, которые будут показаны пользователю. Если вы не укажете этот параметр, запрос будет показан только при первом обращении вашего проекта. Подробнее о том, как запросить повторное согласие… Возможные значения:
|
||||||||||||||||
Шаг 2. Перенаправление на сервер OAuth 2.0 Google
Перенаправьте пользователя на сервер OAuth 2.0 Google, чтобы запустить процесс аутентификации и авторизации. Обычно это происходит, когда приложению впервые требуется доступ к пользовательским данным. В случае инкрементной авторизации этот шаг также выполняется, когда приложению впервые требуется доступ к дополнительным ресурсам, на которые у него нет разрешения.
PHP
- Сгенерируйте URL, чтобы запросить доступ у сервера OAuth 2.0 от Google:
$auth_url = $client->createAuthUrl(); - Перенаправьте пользователя на страницу
$auth_url:header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
Python
В этом примере показано, как перенаправить пользователя на URL авторизации с помощью веб-фреймворка Flask:
return flask.redirect(authorization_url)
Ruby
- Сгенерируйте URL, чтобы запросить доступ у сервера OAuth 2.0 от Google:
auth_uri = authorizer.get_authorization_url(request: request)
- Перенаправьте пользователя на страницу
auth_uri.
Node.js
-
Используйте URL
authorizationUrl, сгенерированный методомgenerateAuthUrlиз шага 1, чтобы запросить доступ у сервера OAuth 2.0 Google. -
Перенаправьте пользователя на страницу
authorizationUrl.res.redirect(authorizationUrl);
HTTP/REST
Пример перенаправления на сервер авторизации Google
Ниже приведен пример URL с разрывами строк и пробелами для удобства чтения. URL-адрес запрашивает доступ к области, которая позволяет получить данные пользователя YouTube. При этом используется пошаговая авторизация (include_granted_scopes=true), чтобы новый токен доступа охватывал все области действия, к которым пользователь ранее предоставил доступ приложению. В примере также заданы другие параметры.
https://accounts.google.com/o/oauth2/v2/auth? scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.force-ssl& access_type=offline& include_granted_scopes=true& response_type=code& state=state_parameter_passthrough_value& redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback& client_id=client_id
После создания URL запроса перенаправьте на него пользователя.
Сервер OAuth 2.0 Google выполняет аутентификацию пользователя и получает от него согласие на доступ вашего приложения к запрошенным областям действия. Ответ отправляется обратно в приложение с помощью указанного вами URL перенаправления.
Шаг 3. Google запрашивает согласие пользователя
На этом этапе пользователь решает, предоставлять ли вашему приложению запрашиваемый доступ. На этом этапе Google показывает окно запроса согласия, в котором указаны название вашего приложения и сервисы API Google, к которым оно запрашивает доступ с учетными данными авторизации пользователя, а также краткое описание областей действия, которые будут предоставлены. Затем пользователь может предоставить доступ к одному или нескольким областям действия, запрошенным вашим приложением, или отклонить запрос.
На этом этапе приложению не нужно ничего делать. Оно ожидает ответа от сервера OAuth 2.0 Google, в котором будет указано, был ли предоставлен доступ. Ответ описан на следующем шаге.
Ошибки
Запросы к конечной точке авторизации OAuth 2.0 от Google могут приводить к показу пользовательских сообщений об ошибках вместо ожидаемых процессов аутентификации и авторизации. Ниже приведены распространенные коды ошибок и способы их устранения.
admin_policy_enforced
Аккаунт Google не может авторизовать одну или несколько запрошенных областей действия из-за правил, установленных администратором Google Workspace. Подробнее о том, как администратор может ограничить доступ ко всем областям действия или только к конфиденциальным и с ограниченным доступом, пока доступ не будет явно предоставлен вашему идентификатору клиента OAuth, рассказывается в справочной статье Справочного центра для администраторов Google Workspace Как управлять доступом сторонних и внутренних приложений к данным Google Workspace.
disallowed_useragent
Конечная точка авторизации отображается во встроенном агенте пользователя, запрещенном правилами OAuth 2.0 Google.
Разработчики для iOS и macOS могут столкнуться с этой ошибкой при открытии запросов на авторизацию в WKWebView.
Вместо этого разработчикам следует использовать библиотеки iOS, например вход с аккаунтом Google для iOS или AppAuth для iOS от OpenID Foundation.
Веб-разработчики могут столкнуться с этой ошибкой, когда приложение для iOS или macOS открывает общую веб-ссылку во встроенном агенте пользователя и пользователь переходит к конечной точке авторизации OAuth 2.0 Google с вашего сайта. Разработчики должны разрешить открывать общие ссылки в обработчике ссылок по умолчанию операционной системы, который включает как обработчики универсальных ссылок, так и браузер по умолчанию. Также поддерживается библиотека SFSafariViewController.
org_internal
Идентификатор клиента OAuth в запросе относится к проекту, который ограничивает доступ к аккаунтам Google в определенной организации Google Cloud. Подробную информацию об этом параметре конфигурации можно найти в разделе Тип пользователя справочной статьи "Как настроить окно запроса доступа OAuth".
invalid_client
Неправильный секретный код клиента OAuth. Проверьте конфигурацию клиента OAuth, в том числе идентификатор клиента и секретный код, использованные для этого запроса.
deleted_client
Клиент OAuth, используемый для выполнения запроса, был удален. Удаление может быть выполнено вручную или автоматически в случае неиспользуемых клиентов . Удаленных клиентов можно восстановить в течение 30 дней. Подробнее…
invalid_grant
При обновлении токена доступа или использовании инкрементной авторизации срок действия токена мог истечь или он мог быть аннулирован. Повторно выполните аутентификацию пользователя и запросите согласие пользователя на получение новых токенов. Если вы продолжаете видеть эту ошибку, убедитесь, что ваше приложение настроено правильно и что вы используете правильные токены и параметры в своем запросе. В противном случае аккаунт пользователя может быть удален или отключен.
redirect_uri_mismatch
Параметр redirect_uri, переданный в запросе на авторизацию, не соответствует авторизованному URI переадресации для идентификатора клиента OAuth. Проверьте авторизованные URI перенаправления на странице клиентов в консоли Google Cloud.
Параметр redirect_uri может относиться к внеполосному потоку OAuth, который устарел и больше не поддерживается. Чтобы обновить интеграцию, следуйте инструкциям в руководстве по переносу.
invalid_request
В вашем запросе что-то было не так. Это может быть связано со следующими причинами:
- Запрос был неправильно отформатирован.
- В запросе отсутствовали обязательные параметры.
- В запросе используется способ авторизации, который не поддерживается Google. Убедитесь, что в вашей интеграции OAuth используется рекомендуемый метод интеграции.
Шаг 4. Обработайте ответ сервера OAuth 2.0
Сервер OAuth 2.0 отвечает на запрос доступа, отправленный приложением, используя URL, указанный в запросе.
Если пользователь одобрит запрос на доступ, ответ будет содержать код авторизации. Если пользователь не одобрит запрос, в ответе будет сообщение об ошибке. Код авторизации или сообщение об ошибке, возвращаемые веб-серверу, появляются в строке запроса, как показано в следующих примерах:
Ответ об ошибке:
https://oauth2.example.com/auth?error=access_denied
Ответ с кодом авторизации:
https://oauth2.example.com/auth?code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7
Пример ответа сервера OAuth 2.0
Вы можете протестировать этот процесс, нажав на следующий пример URL, который запрашивает доступ только для чтения для просмотра метаданных файлов на вашем Google Диске и доступ только для чтения для просмотра мероприятий в вашем Google Календаре:
https://accounts.google.com/o/oauth2/v2/auth? scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.force-ssl& access_type=offline& include_granted_scopes=true& response_type=code& state=state_parameter_passthrough_value& redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback& client_id=client_id
После завершения процесса OAuth 2.0 браузер перенаправит вас на OAuth 2.0 Playground – инструмент для тестирования процессов OAuth. Вы увидите, что OAuth 2.0 Playground автоматически получил код авторизации.
Шаг 5. Обменяйте код авторизации на токены обновления и доступа
Получив код авторизации, веб-сервер может обменять его на токен доступа.
PHP
Чтобы обменять код авторизации на токен доступа, используйте метод fetchAccessTokenWithAuthCode:
$access_token = $client->fetchAccessTokenWithAuthCode($_GET['code']);Python
На странице обратного вызова используйте библиотеку google-auth, чтобы проверить ответ сервера авторизации. Затем используйте метод flow.fetch_token, чтобы обменять код авторизации в этом ответе на токен доступа:
state = flask.session['state'] flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( 'client_secret.json', scopes=['https://www.googleapis.com/auth/youtube.force-ssl'], state=state) flow.redirect_uri = flask.url_for('oauth2callback', _external=True) authorization_response = flask.request.url flow.fetch_token(authorization_response=authorization_response) # Store the credentials in browser session storage, but for security: client_id, client_secret, # and token_uri are instead stored only on the backend server. credentials = flow.credentials flask.session['credentials'] = { 'token': credentials.token, 'refresh_token': credentials.refresh_token, 'granted_scopes': credentials.granted_scopes}
Ruby
На странице обратного вызова используйте библиотеку googleauth, чтобы проверить ответ сервера авторизации. Используйте метод authorizer.handle_auth_callback_deferred, чтобы сохранить код авторизации и перенаправить пользователя на URL, с которого был отправлен запрос на авторизацию. Это откладывает обмен кодом, временно сохраняя результаты в сеансе пользователя.
target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request) redirect target_url
Node.js
Чтобы обменять код авторизации на токен доступа, используйте метод getToken:
const url = require('url'); // Receive the callback from Google's OAuth 2.0 server. app.get('/oauth2callback', async (req, res) => { let q = url.parse(req.url, true).query; if (q.error) { // An error response e.g. error=access_denied console.log('Error:' + q.error); } else if (q.state !== req.session.state) { //check state value console.log('State mismatch. Possible CSRF attack'); res.end('State mismatch. Possible CSRF attack'); } else { // Get access and refresh tokens (if access_type is offline) let { tokens } = await oauth2Client.getToken(q.code); oauth2Client.setCredentials(tokens); });
HTTP/REST
Чтобы обменять код авторизации на токен доступа, вызовите конечную точку https://oauth2.googleapis.com/token и задайте следующие параметры:
| Поля | |
|---|---|
client_id |
Идентификатор клиента, полученный на странице клиентов Cloud Console. |
client_secret |
Необязательно
Секретный код клиента, полученный на странице клиентов в Cloud Console. |
code |
Код авторизации, возвращенный в ответ на первоначальный запрос. |
grant_type |
Согласно спецификации OAuth 2.0, значение этого поля должно быть равно authorization_code. |
redirect_uri |
Один из URI переадресации, указанных для вашего проекта на странице клиентов в Cloud Console для client_id. |
Использовать DPoP необязательно, но рекомендуется для повышения безопасности. Безопасность DPoP зависит от того, ограничен ли закрытый ключ одним устройством. Мы рекомендуем хранить его так, чтобы его нельзя было скопировать с устройства, например с помощью TPM, Secure Enclave или других аппаратных хранилищ ключей. Чтобы использовать DPoP, ваше приложение должно генерировать новый уникальный JWT-токен подтверждения DPoP для каждого запроса к конечной точке токена и добавлять его в качестве заголовка HTTP-запроса.
| Заголовок | Обязательно | Описание |
|---|---|---|
DPoP |
Необязательно | Доказательство DPoP – это токен JWT, который подтверждает владение закрытым ключом. Это заголовок, а не параметр. Если он указан, возвращаемые токены будут привязаны к этому ключу. Для каждого запроса необходимо создать новое уникальное доказательство, которое должно включать утверждения htm (метод HTTP) и htu (URI HTTP), соответствующие запросу. |
Ниже приведен пример запроса.
POST /token HTTP/1.1 Host: oauth2.googleapis.com Content-Type: application/x-www-form-urlencoded DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6Ik\ VDIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCR\ nMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERWclUwQ2R2R1JE\ QSIsImNydiI6IlAtMjU2In19.eyJqdGkiOiItQndDM0VTYzZhY2MybFRjIiwiaHRtIj\ oiUE9TVCIsImh0dSI6Imh0dHBzOi8vc2VydmVyLmV4YW1wbGUuY29tL3Rva2VuIiwia\ WF0IjoxNTYyMjYyNjE2fQ.2-GxA6T8lP4vfrg8v-FdWP0A0zdrj8igiMLvqRMUvwnQg\ 4PtFLbdLXiOSsX0x7NVY-FNyJK70nfbV37xRZT3Lg code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7& client_id=your_client_id& redirect_uri=https%3A//developers.google.com/oauthplayground& grant_type=authorization_code
Как создать доказательство DPoP
Ниже описано, как создать доказательство DPoP с помощью OpenSSL из командной строки.
- Создайте пару ключей EC P-256:
openssl ecparam -name prime256v1 -genkey -noout -out dpop_private.pem openssl ec -in dpop_private.pem -pubout -out dpop_public.pem
- Как создать заголовок DPoP
Заголовок должен содержать утверждения
typ,algиjwk(открытый ключ). Значенияxиy– это закодированные в Base64Url координаты вашего открытого ключа EC. Закодируйте этот JSON-код с помощью Base64Url:{ "typ":"dpop+jwt", "alg":"ES256", "jwk": { "kty":"EC", "x":"YOUR_PUBLIC_KEY_X", "y":"YOUR_PUBLIC_KEY_Y", "crv":"P-256" } } - Создайте полезную нагрузку DPoP.
Полезная нагрузка должна включать
jti(уникальный идентификатор запроса),htm(метод HTTP, напримерPOST),htu(URI HTTP, напримерhttps://oauth2.googleapis.com/token) иiat(время выдачи). Если вы получили одноразовый код от сервера в заголовкеDPoP-Nonceв ответе на предыдущий запрос, вы должны включить это значение одноразового кода в утверждениеnonce. Заявление nonce является необязательным для обмена кода авторизации и используется только в том случае, если ранее был получен заголовок DPoP-Nonce. Закодируйте этот JSON-код с помощью Base64Url:{ "jti":"JTI_VALUE", "htm":"POST", "htu":"https://oauth2.googleapis.com/token", "iat":YOUR_JWT_ISSUED_TIME, "nonce":"SERVER_PROVIDED_NONCE" }Значение
jtiзависит от типа биржи:- При обмене кода авторизации параметр
jtiдолжен представлять собой хеш SHA256 кода авторизации, закодированный в Base64Url:"jti":"BASE64URL(SHA256(AUTHORIZATION_CODE))". - Для обмена токенов обновления параметр
jtiдолжен быть уникальным идентификатором для каждого запроса:"jti":"YOUR_UNIQUE_PER_REQUEST_IDENTIFIER".
- При обмене кода авторизации параметр
- Подпишите доказательство.
Объедините закодированный заголовок и полезную нагрузку с помощью точки (
.), а затем подпишите результат с помощью закрытого ключа, используя алгоритм ES256. Обратите внимание, что JWS требует, чтобы подпись была в необработанном объединенном форматеR | S(64 байта для P-256). Если вы используете OpenSSL напрямую, вам необходимо преобразовать подпись, закодированную по умолчанию в формате ASN.1 DER, в необработанный формат.
Успешный обмен обозначается ответом 200 OK, содержащим токены. Если при обмене используется действительное доказательство DPoP, возвращаемый Google токен обновления будет связан с вашим ключом, но токены доступа не будут связаны с DPoP. Токены доступа будут сохранять значение token_type из Bearer, а не DPoP.
Кроме того, Google возвращает в ответе HTTP-заголовок DPoP-Nonce.
Клиент должен кешировать этот nonce и включать его в утверждение nonce подтверждения DPoP в последующих запросах (например, при обмене токена обновления на новый токен доступа или при вызове API, защищенных DPoP). Используя этот nonce, вы избежите дополнительного обмена данными туда и обратно и ошибки (use_dpop_nonce) в следующем запросе.
Доказательства DPoP должны быть включены в запросы на обмен токенов, созданные с помощью токенов обновления, связанных с DPoP.
Сбой обмена происходит, если заголовок DPoP отсутствует, когда он ожидается, недействителен или если в подтверждении используется ключ, отличный от того, который привязан к токену. В таких случаях сервер возвращает ошибку 400 Bad Request.
Если в подтверждении DPoP есть несовпадающие утверждения htm или htu, просроченное утверждение iat, повторно использованное утверждение jti или недействительная подпись, Google возвращает код ошибки invalid_dpop_proof.
Если требуется одноразовый код DPoP, например при обмене токена обновления, а в подтверждении DPoP отсутствует утверждение nonce или значение одноразового кода неприемлемо для сервера (например, срок его действия истек, он уже использовался или неверен), Google возвращает код ошибки use_dpop_nonce вместе с HTTP-заголовком DPoP-Nonce, содержащим новый одноразовый код, который можно использовать в последующем запросе.
В других случаях может возвращаться значение invalid_grant.
В ответ на этот запрос Google возвращает объект JSON, содержащий токен доступа с ограниченным сроком действия и токен обновления.
Обратите внимание, что токен обновления возвращается только в том случае, если в первоначальном запросе к серверу авторизации Google приложение установило для параметра access_type значение offline.
Ответ содержит следующие поля:
| Поля | |
|---|---|
access_token |
Токен, который приложение отправляет для авторизации запроса к API Google. |
expires_in |
Оставшееся время действия токена доступа в секундах. |
refresh_token |
Токен, который можно использовать для получения нового токена доступа. Токены обновления действительны, пока пользователь не отзовет доступ или не истечет срок действия токена обновления. Если использовался DPoP, токен обновления привязывается к закрытому ключу, который использовался для подписи подтверждения DPoP.
Это поле будет присутствовать в ответе, только если в исходном запросе к серверу авторизации Google вы задали для параметра access_type значение offline.
|
refresh_token_expires_in |
Оставшийся срок действия токена обновления в секундах. Это значение задается, только если пользователь предоставляет доступ на определенное время. |
scope |
Области действия доступа, предоставленные access_token, в виде списка строк, разделенных пробелами, с учетом регистра. |
token_type |
Тип возвращенного токена. Это значение всегда равно Bearer, даже если используется DPoP. |
Ниже приведен пример заголовков и тела успешного ответа при использовании DPoP:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
DPoP-Nonce: AN3XwJjZsjnb0ZuWkRlek8QU7wY-Zhf-5IP6tO0tORz0KgtDT1Bo8FX-w4nz3r5lnepI
{
"access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in": 3920,
"token_type": "Bearer",
"scope": "https://www.googleapis.com/auth/youtube.force-ssl https://www.googleapis.com/auth/calendar.readonly",
"refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
}Ошибки
При обмене кода авторизации на токен доступа вместо ожидаемого ответа может появиться следующая ошибка: В этом разделе перечислены распространенные коды ошибок и способы их устранения.
invalid_grant
Указан недействительный код авторизации или код в неверном формате. Запросите новый код, перезапустив процесс OAuth, чтобы снова запросить согласие пользователя.
Шаг 6. Проверьте, какие области действия разрешили пользователи
При запросе нескольких разрешений (областей действия) пользователи могут не предоставить приложению доступ ко всем из них. Приложение должно проверять, какие области доступа были предоставлены, и корректно обрабатывать ситуации, когда в некоторых разрешениях отказано. Обычно в таких случаях отключаются функции, которые зависят от этих разрешений.
Однако из этого правила бывают исключения. Приложения Google Workspace Enterprise с делегированием доступа к данным в домене или приложения, помеченные как надежные, обходят окно запроса доступа с детализированными разрешениями. В таких приложениях пользователи не будут видеть окно запроса доступа к отдельным разрешениям. Приложение получит либо все запрошенные области действия, либо ни одной.
Подробную информацию можно найти в статье Как работать с детализированными разрешениями.
PHP
Чтобы проверить, какие области действия были предоставлены пользователем, используйте метод getGrantedScope():
// Space-separated string of granted scopes if it exists, otherwise null. $granted_scopes = $client->getOAuth2Service()->getGrantedScope(); // Determine which scopes user granted and build a dictionary $granted_scopes_dict = [ 'Drive' => str_contains($granted_scopes, Google\Service\Drive::DRIVE_METADATA_READONLY), 'Calendar' => str_contains($granted_scopes, Google\Service\Calendar::CALENDAR_READONLY) ];
Python
Возвращенный объект credentials имеет свойство granted_scopes, которое представляет собой список областей, предоставленных пользователем вашему приложению.
credentials = flow.credentials flask.session['credentials'] = { 'token': credentials.token, 'refresh_token': credentials.refresh_token, 'granted_scopes': credentials.granted_scopes}
Следующая функция проверяет, какие области действия пользователь предоставил приложению.
def check_granted_scopes(credentials): features = {} if 'https://www.googleapis.com/auth/drive.metadata.readonly' in credentials['granted_scopes']: features['drive'] = True else: features['drive'] = False if 'https://www.googleapis.com/auth/calendar.readonly' in credentials['granted_scopes']: features['calendar'] = True else: features['calendar'] = False return features
Ruby
При запросе нескольких областей действия одновременно проверьте, какие из них были предоставлены, с помощью свойства scope объекта credentials.
# User authorized the request. Now, check which scopes were granted. if credentials.scope.include?(Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY) # User authorized read-only Drive activity permission. # Calling the APIs, etc else # User didn't authorize read-only Drive activity permission. # Update UX and application accordingly end # Check if user authorized Calendar read permission. if credentials.scope.include?(Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY) # User authorized Calendar read permission. # Calling the APIs, etc. else # User didn't authorize Calendar read permission. # Update UX and application accordingly end
Node.js
При запросе нескольких областей действия одновременно проверьте, какие из них были предоставлены, с помощью свойства scope объекта tokens.
// User authorized the request. Now, check which scopes were granted. if (tokens.scope.includes('https://www.googleapis.com/auth/youtube.force-ssl')) { // User authorized read-only Drive activity permission. // Calling the APIs, etc. } else { // User didn't authorize read-only Drive activity permission. // Update UX and application accordingly } // Check if user authorized Calendar read permission. if (tokens.scope.includes('https://www.googleapis.com/auth/calendar.readonly')) { // User authorized Calendar read permission. // Calling the APIs, etc. } else { // User didn't authorize Calendar read permission. // Update UX and application accordingly }
HTTP/REST
Чтобы проверить, предоставил ли пользователь приложению доступ к определенной области действия, изучите поле scope в ответе с токеном доступа. Области действия доступа, предоставленные токеном доступа, выраженные в виде списка строк с учетом регистра, разделенных пробелами.
Например, в следующем примере ответа на запрос токена доступа указано, что пользователь предоставил вашему приложению доступ к разрешениям на чтение активности на Диске и мероприятий в Календаре:
{ "access_token": "1/fFAGRNJru1FTz70BzhT3Zg", "expires_in": 3920, "token_type": "Bearer", "scope": "https://www.googleapis.com/auth/youtube.force-ssl https://www.googleapis.com/auth/calendar.readonly", "refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI" }
Вызов API Google
PHP
Чтобы вызвать API Google с помощью токена доступа, выполните следующие действия:
- Если вам нужно применить токен доступа к новому объекту
Google\Client, например если вы сохранили токен доступа в сеансе пользователя, используйте методsetAccessToken:$client->setAccessToken($access_token); - Создайте объект сервиса для API, который вы хотите вызвать. Чтобы создать объект сервиса, передайте авторизованный объект
Google\Clientконструктору нужного API. Например, чтобы вызвать YouTube Data API:$youtube = new Google_Service_YouTube($client); - Отправьте запросы к сервису API, используя
интерфейс, предоставляемый объектом сервиса.
Например, чтобы получить список прямых трансляций для канала аутентифицированного пользователя с помощью YouTube Data API, выполните следующие действия:
$broadcasts = $youtube->liveBroadcasts->listLiveBroadcasts('id,snippet', [ 'mine' => true ]);
Python
Получив токен доступа, ваше приложение может использовать его для авторизации запросов к API от имени определенного аккаунта пользователя или сервисного аккаунта. Используйте учетные данные авторизации пользователя, чтобы создать объект сервиса для API, который вы хотите вызвать, а затем используйте этот объект для выполнения авторизованных запросов к API.
- Создайте объект сервиса для API, который вы хотите вызвать. Чтобы создать объект сервиса, вызовите метод
buildбиблиотекиgoogleapiclient.discovery, указав название и версию API, а также учетные данные пользователя. Например, чтобы вызвать версию 3 YouTube Data API, используйте следующий код:from googleapiclient.discovery import build youtube = build('youtube', 'v3', credentials=credentials)
- Отправьте запросы к сервису API, используя интерфейс, предоставляемый объектом сервиса.
Например, чтобы получить список прямых трансляций на канале аутентифицированного пользователя с помощью YouTube Data API, выполните следующие действия:
broadcasts = youtube.liveBroadcasts().list(part='id,snippet', mine=True).execute()
Ruby
После получения токена доступа ваше приложение может использовать его для выполнения запросов к API от имени определенного аккаунта пользователя или сервисного аккаунта. Используйте учетные данные авторизации пользователя, чтобы создать служебный объект для API, который вы хотите вызвать, а затем используйте этот объект для выполнения авторизованных запросов к API.
- Создайте объект сервиса для API, который вы хотите вызвать.
Например, чтобы вызвать версию 3 YouTube Data API:
youtube = Google::Apis::YoutubeV3::YouTubeService.new
- Укажите учетные данные в сервисе:
youtube.authorization = credentials
- Отправьте запросы к сервису API, используя интерфейс, предоставляемый объектом сервиса.
Например, чтобы получить список прямых трансляций на канале аутентифицированного пользователя с помощью YouTube Data API, выполните следующие действия:
broadcasts = youtube.list_liveBroadcasts('id,snippet', mine: true)
Также можно предоставить авторизацию для каждого метода отдельно, указав параметр options:
broadcasts = youtube.list_liveBroadcasts('id,snippet', mine: true)
Node.js
Получив токен доступа и задав его для объекта OAuth2, используйте этот объект для вызова API Google. Приложение может использовать этот токен для авторизации запросов к API от имени определенного аккаунта пользователя или сервисного аккаунта. Создайте объект сервиса для API, который вы хотите вызвать.
Например, в следующем коде используется Google Drive API, чтобы получить список названий файлов на Диске пользователя.
const { google } = require('googleapis'); // Example of using Google Drive API to list filenames in user's Drive. const drive = google.drive('v3'); drive.files.list({ auth: oauth2Client, pageSize: 10, fields: 'nextPageToken, files(id, name)', }, (err1, res1) => { if (err1) return console.log('The API returned an error: ' + err1); const files = res1.data.files; if (files.length) { console.log('Files:'); files.map((file) => { console.log(`${file.name} (${file.id})`); }); } else { console.log('No files found.'); } });
HTTP/REST
После того как ваше приложение получит токен доступа, вы можете использовать его для вызовов Google API от имени определенного аккаунта пользователя, если предоставлены необходимые для API области доступа. Для этого включите токен доступа в запрос к API, добавив параметр запроса access_token или значение заголовка HTTP Authorization Bearer. По возможности используйте заголовок HTTP, поскольку строки запросов обычно видны в журналах сервера. В большинстве случаев для настройки вызовов API Google можно использовать клиентскую библиотеку (например, при вызове YouTube Live Streaming API).
Обратите внимание, что YouTube Live Streaming API не поддерживает сервисные аккаунты. Поскольку сервисный аккаунт нельзя связать с аккаунтом YouTube, попытки авторизовать запросы с помощью этого потока приведут к ошибке NoLinkedYouTubeAccount.
Вы можете попробовать все API Google и посмотреть их области действия в OAuth 2.0 Playground.
Примеры HTTP GET
Вызов конечной точки
liveBroadcasts.list (YouTube Live Streaming API) с использованием HTTP-заголовка Authorization: Bearer может выглядеть следующим образом: Обратите внимание, что вам нужно указать собственный токен доступа:
GET /youtube/v3/liveBroadcasts?part=id%2Csnippet&mine=true HTTP/1.1 Host: www.googleapis.com Authorization: Bearer access_token
Вот пример вызова того же API для аутентифицированного пользователя с помощью параметра строки запроса access_token:
GET https://www.googleapis.com/youtube/v3/liveBroadcasts?access_token=access_token&part=id%2Csnippet&mine=true
Примеры использования оператора curl
Вы можете протестировать эти команды с помощью приложения командной строки curl. Вот пример с использованием заголовка HTTP (рекомендуемый вариант):
curl -H "Authorization: Bearer access_token" https://www.googleapis.com/youtube/v3/liveBroadcasts?part=id%2Csnippet&mine=true
Или параметр строки запроса:
curl https://www.googleapis.com/youtube/v3/liveBroadcasts?access_token=access_token&part=id%2Csnippet&mine=true
Полный пример
В приведенном ниже примере выводится объект в формате JSON, в котором показаны прямые трансляции на канале YouTube аутентифицированного пользователя после того, как он разрешил приложению получить эти данные.
PHP
Чтобы запустить этот пример:
- В API Console добавьте URL локального компьютера в список URL переадресации. Например, добавьте
http://localhost:8080. - Создайте новый каталог и перейдите в него. Пример:
mkdir ~/php-oauth2-example cd ~/php-oauth2-example
- Установите клиентскую библиотеку API Google для PHP с помощью Composer:
composer require google/apiclient:^2.15.0
- Создайте файлы
index.phpиoauth2callback.phpсо следующим содержимым. - Запустите пример с помощью встроенного тестового веб-сервера PHP:
php -S localhost:8080 ~/php-oauth2-example
index.php
<?php require_once __DIR__.'/vendor/autoload.php'; session_start(); $client = new Google\Client(); $client->setAuthConfig('client_secret.json'); // User granted permission as an access token is in the session. if (isset($_SESSION['access_token']) && $_SESSION['access_token']) { $client->setAccessToken($_SESSION['access_token']); // Check if user granted Drive permission if ($_SESSION['granted_scopes_dict']['Drive']) { echo "Drive feature is enabled."; echo "</br>"; $drive = new Drive($client); $files = array(); $response = $drive->files->listFiles(array()); foreach ($response->files as $file) { echo "File: " . $file->name . " (" . $file->id . ")"; echo "</br>"; } } else { echo "Drive feature is NOT enabled."; echo "</br>"; } // Check if user granted Calendar permission if ($_SESSION['granted_scopes_dict']['Calendar']) { echo "Calendar feature is enabled."; echo "</br>"; } else { echo "Calendar feature is NOT enabled."; echo "</br>"; } } else { // Redirect users to outh2call.php which redirects users to Google OAuth 2.0 $redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php'; header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL)); } ?>
oauth2callback.php
<?php require_once __DIR__.'/vendor/autoload.php'; session_start(); $client = new Google\Client(); // Required, call the setAuthConfig function to load authorization credentials from // client_secret.json file. $client->setAuthConfigFile('client_secret.json'); $client->setRedirectUri('http://' . $_SERVER['HTTP_HOST']. $_SERVER['PHP_SELF']); // Required, to set the scope value, call the addScope function. $client->addScope(Google_Service_YouTube::YOUTUBE_FORCE_SSL); // Enable incremental authorization. Recommended as a best practice. $client->setIncludeGrantedScopes(true); // Recommended, offline access will give you both an access and refresh token so that // your app can refresh the access token without user interaction. $client->setAccessType("offline"); // Generate a URL for authorization as it doesn't contain code and error if (!isset($_GET['code']) && !isset($_GET['error'])) { // Generate and set state value $state = bin2hex(random_bytes(16)); $client->setState($state); $_SESSION['state'] = $state; // Generate a url that asks permissions. $auth_url = $client->createAuthUrl(); header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL)); } // User authorized the request and authorization code is returned to exchange access and // refresh tokens. if (isset($_GET['code'])) { // Check the state value if (!isset($_GET['state']) || $_GET['state'] !== $_SESSION['state']) { die('State mismatch. Possible CSRF attack.'); } // Get access and refresh tokens (if access_type is offline) $token = $client->fetchAccessTokenWithAuthCode($_GET['code']); /** Save access and refresh token to the session variables. * ACTION ITEM: In a production app, you likely want to save the * refresh token in a secure persistent storage instead. */ $_SESSION['access_token'] = $token; $_SESSION['refresh_token'] = $client->getRefreshToken(); // Space-separated string of granted scopes if it exists, otherwise null. $granted_scopes = $client->getOAuth2Service()->getGrantedScope(); // Determine which scopes user granted and build a dictionary $granted_scopes_dict = [ 'Drive' => str_contains($granted_scopes, Google\Service\Drive::DRIVE_METADATA_READONLY), 'Calendar' => str_contains($granted_scopes, Google\Service\Calendar::CALENDAR_READONLY) ]; $_SESSION['granted_scopes_dict'] = $granted_scopes_dict; $redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/'; header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL)); } // An error response e.g. error=access_denied if (isset($_GET['error'])) { echo "Error: ". $_GET['error']; } ?>
Python
В этом примере используется фреймворк Flask. Он запускает веб-приложение по адресу http://localhost:8080, которое позволяет тестировать процесс OAuth 2.0. Если вы перейдете по этому URL, то увидите пять ссылок:
- Вызов Drive API. Эта ссылка ведет на страницу, на которой выполняется пример запроса API, если пользователь предоставил разрешение. При необходимости запускается процесс авторизации. Если все пройдет успешно, на странице появится ответ API.
- Mock page to call Calendar API (Макет страницы для вызова Calendar API). Эта ссылка указывает на макет страницы, которая пытается выполнить пример запроса к Calendar API, если пользователи предоставили разрешение. При необходимости запускается процесс авторизации. Если все пройдет успешно, на странице появится ответ API.
- Протестировать процесс авторизации. Эта ссылка ведет на страницу, которая пытается направить пользователя по процессу авторизации. Приложение запрашивает разрешение на отправку авторизованных запросов к API от имени пользователя.
- Отзыв текущих учетных данных. Ссылка на страницу, на которой можно отозвать разрешения, уже предоставленные приложению.
- Clear Flask session credentials (Очистить учетные данные сеанса Flask). Эта ссылка позволяет удалить учетные данные, которые хранятся в сеансе Flask. Это позволит вам увидеть, что произойдет, если пользователь, который уже предоставил разрешение вашему приложению, попытается выполнить запрос к API в новом сеансе. Кроме того, вы можете посмотреть ответ API, который получит ваше приложение, если пользователь отзовет разрешения, предоставленные вашему приложению, а оно все равно попытается авторизовать запрос с помощью отозванного токена доступа.
# -*- coding: utf-8 -*- import os import flask import json import requests import google.oauth2.credentials import google_auth_oauthlib.flow import googleapiclient.discovery # This variable specifies the name of a file that contains the OAuth 2.0 # information for this application, including its client_id and client_secret. CLIENT_SECRETS_FILE = "client_secret.json" # The OAuth 2.0 access scope allows for access to the # authenticated user's account and requires requests to use an SSL connection. SCOPES = ['https://www.googleapis.com/auth/youtube.force-ssl', 'https://www.googleapis.com/auth/calendar.readonly'] API_SERVICE_NAME = 'youtube' API_VERSION = 'v3' app = flask.Flask(__name__) # Note: A secret key is included in the sample so that it works. # If you use this code in your application, replace this with a truly secret # key. See https://flask.palletsprojects.com/quickstart/#sessions. app.secret_key = 'REPLACE ME - this value is here as a placeholder.' @app.route('/') def index(): return print_index_table() @app.route('/drive') def drive_api_request(): if 'credentials' not in flask.session: return flask.redirect('authorize') features = flask.session['features'] if features['drive']: # Load client secrets from the server-side file. with open(CLIENT_SECRETS_FILE, 'r') as f: client_config = json.load(f)['web'] # Load user-specific credentials from browser session storage. session_credentials = flask.session['credentials'] # Reconstruct the credentials object. credentials = google.oauth2.credentials.Credentials( refresh_token=session_credentials.get('refresh_token'), scopes=session_credentials.get('granted_scopes'), token=session_credentials.get('token'), client_id=client_config.get('client_id'), client_secret=client_config.get('client_secret'), token_uri=client_config.get('token_uri')) youtube = googleapiclient.discovery.build( API_SERVICE_NAME, API_VERSION, credentials=credentials) broadcasts = youtube.liveBroadcasts().list(part='id,snippet', mine=True).execute() # Save credentials back to session in case access token was refreshed. flask.session['credentials'] = credentials_to_dict(credentials) return flask.jsonify(**broadcasts) else: # User didn't authorize read-only Drive activity permission. return '<p>Drive feature is not enabled.</p>' @app.route('/calendar') def calendar_api_request(): if 'credentials' not in flask.session: return flask.redirect('authorize') features = flask.session['features'] if features['calendar']: # User authorized Calendar read permission. # Calling the APIs, etc. return ('<p>User granted the Google Calendar read permission. '+ 'This sample code does not include code to call Calendar</p>') else: # User didn't authorize Calendar read permission. # Update UX and application accordingly return '<p>Calendar feature is not enabled.</p>' @app.route('/authorize') def authorize(): # Create flow instance to manage the OAuth 2.0 Authorization Grant Flow steps. flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( CLIENT_SECRETS_FILE, scopes=SCOPES) # The URI created here must exactly match one of the authorized redirect URIs # for the OAuth 2.0 client, which you configured in the API Console. If this # value doesn't match an authorized URI, you will get a 'redirect_uri_mismatch' # error. flow.redirect_uri = flask.url_for('oauth2callback', _external=True) authorization_url, state = flow.authorization_url( # Enable offline access so that you can refresh an access token without # re-prompting the user for permission. Recommended for web server apps. access_type='offline', # Enable incremental authorization. Recommended as a best practice. include_granted_scopes='true') # Store the state so the callback can verify the auth server response. flask.session['state'] = state return flask.redirect(authorization_url) @app.route('/oauth2callback') def oauth2callback(): # Specify the state when creating the flow in the callback so that it can # verified in the authorization server response. state = flask.session['state'] flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( CLIENT_SECRETS_FILE, scopes=SCOPES, state=state) flow.redirect_uri = flask.url_for('oauth2callback', _external=True) # Use the authorization server's response to fetch the OAuth 2.0 tokens. authorization_response = flask.request.url flow.fetch_token(authorization_response=authorization_response) # Store credentials in the session. # ACTION ITEM: In a production app, you likely want to save these # credentials in a persistent database instead. credentials = flow.credentials credentials = credentials_to_dict(credentials) flask.session['credentials'] = credentials # Check which scopes user granted features = check_granted_scopes(credentials) flask.session['features'] = features return flask.redirect('/') @app.route('/revoke') def revoke(): if 'credentials' not in flask.session: return ('You need to <a href="/authorize">authorize</a> before ' + 'testing the code to revoke credentials.') # Load client secrets from the server-side file. with open(CLIENT_SECRETS_FILE, 'r') as f: client_config = json.load(f)['web'] # Load user-specific credentials from the session. session_credentials = flask.session['credentials'] # Reconstruct the credentials object. credentials = google.oauth2.credentials.Credentials( refresh_token=session_credentials.get('refresh_token'), scopes=session_credentials.get('granted_scopes'), token=session_credentials.get('token'), client_id=client_config.get('client_id'), client_secret=client_config.get('client_secret'), token_uri=client_config.get('token_uri')) revoke = requests.post('https://oauth2.googleapis.com/revoke', params={'token': credentials.token}, headers = {'content-type': 'application/x-www-form-urlencoded'}) status_code = getattr(revoke, 'status_code') if status_code == 200: # Clear the user's session credentials after successful revocation if 'credentials' in flask.session: del flask.session['credentials'] del flask.session['features'] return('Credentials successfully revoked.' + print_index_table()) else: return('An error occurred.' + print_index_table()) @app.route('/clear') def clear_credentials(): if 'credentials' in flask.session: del flask.session['credentials'] return ('Credentials have been cleared.<br><br>' + print_index_table()) def credentials_to_dict(credentials): return {'token': credentials.token, 'refresh_token': credentials.refresh_token, 'granted_scopes': credentials.granted_scopes} def check_granted_scopes(credentials): features = {} if 'https://www.googleapis.com/auth/youtube.force-ssl' in credentials['granted_scopes']: features['drive'] = True else: features['drive'] = False if 'https://www.googleapis.com/auth/calendar.readonly' in credentials['granted_scopes']: features['calendar'] = True else: features['calendar'] = False return features def print_index_table(): return ('<table>' + '<tr><td><a href="/authorize">Test the auth flow directly</a></td>' + '<td>Go directly to the authorization flow. If there are stored ' + ' credentials, you still might not be prompted to reauthorize ' + ' the application.</td></tr>' + '<tr><td><a href="/drive">Call Drive API directly</a></td>' + '<td> Use stored credentials to call the API, you still might not be prompted to reauthorize ' + ' the application.</td></tr>' + '<tr><td><a href="/calendar">Call Calendar API directly</a></td>' + '<td> Use stored credentials to call the API, you still might not be prompted to reauthorize ' + ' the application.</td></tr>' + '<tr><td><a href="/revoke">Revoke current credentials</a></td>' + '<td>Revoke the access token associated with the current user ' + ' session. After revoking credentials, if you go to the test ' + ' page, you should see an <code>invalid_grant</code> error.' + '</td></tr>' + '<tr><td><a href="/clear">Clear Flask session credentials</a></td>' + '<td>Clear the access token currently stored in the user session. ' + ' After clearing the token, if you <a href="/authorize">authorize</a> ' + ' again, you should go back to the auth flow.' + '</td></tr></table>') if __name__ == '__main__': # When running locally, disable OAuthlib's HTTPs verification. # ACTION ITEM for developers: # When running in production *do not* leave this option enabled. os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' # This disables the requested scopes and granted scopes check. # If users only grant partial request, the warning would not be thrown. os.environ['OAUTHLIB_RELAX_TOKEN_SCOPE'] = '1' # Specify a hostname and port that are set as a valid redirect URI # for your API project in the Google API Console. app.run('localhost', 8080, debug=True)
Ruby
В этом примере используется фреймворк Sinatra.
require 'googleauth' require 'googleauth/web_user_authorizer' require 'googleauth/stores/redis_token_store' require 'google/apis/youtube_v3' require 'google/apis/calendar_v3' require 'sinatra' configure do enable :sessions # Required, call the from_file method to retrieve the client ID from a # client_secret.json file. set :client_id, Google::Auth::ClientId.from_file('/path/to/client_secret.json') # Required, scope value # Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar. scope = ['Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY', 'Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY'] # Required, Authorizers require a storage instance to manage long term persistence of # access and refresh tokens. set :token_store, Google::Auth::Stores::RedisTokenStore.new(redis: Redis.new) # Required, indicate where the API server will redirect the user after the user completes # the authorization flow. The redirect URI is required. The value must exactly # match one of the authorized redirect URIs for the OAuth 2.0 client, which you # configured in the API Console. If this value doesn't match an authorized URI, # you will get a 'redirect_uri_mismatch' error. set :callback_uri, '/oauth2callback' # To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI # from the client_secret.json file. To get these credentials for your application, visit # https://console.cloud.google.com/apis/credentials. set :authorizer, Google::Auth::WebUserAuthorizer.new(settings.client_id, settings.scope, settings.token_store, callback_uri: settings.callback_uri) end get '/' do # NOTE: Assumes the user is already authenticated to the app user_id = request.session['user_id'] # Fetch stored credentials for the user from the given request session. # nil if none present credentials = settings.authorizer.get_credentials(user_id, request) if credentials.nil? # Generate a url that asks the user to authorize requested scope(s). # Then, redirect user to the url. redirect settings.authorizer.get_authorization_url(request: request) end # User authorized the request. Now, check which scopes were granted. if credentials.scope.include?(Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY) # User authorized read-only Drive activity permission. # Example of using Google Drive API to list filenames in user's Drive. youtube = Google::Apis::YoutubeV3::YouTubeService.new broadcasts = youtube.list_liveBroadcasts('id,snippet', mine: true) "<pre>#{JSON.pretty_generate(broadcasts.to_h)}</pre>" else # User didn't authorize read-only Drive activity permission. # Update UX and application accordingly end # Check if user authorized Calendar read permission. if credentials.scope.include?(Google::Apis::CalendarV3::AUTH_CALENDAR_READONLY) # User authorized Calendar read permission. # Calling the APIs, etc. else # User didn't authorize Calendar read permission. # Update UX and application accordingly end end # Receive the callback from Google's OAuth 2.0 server. get '/oauth2callback' do # Handle the result of the oauth callback. Defers the exchange of the code by # temporarily stashing the results in the user's session. target_url = Google::Auth::WebUserAuthorizer.handle_auth_callback_deferred(request) redirect target_url end
Node.js
Чтобы запустить этот пример:
-
В API Console добавьте URL локального компьютера в список URL переадресации. Например, добавьте
http://localhost. - Убедитесь, что у вас установлена версия Node.js с долгосрочной поддержкой (LTS) или текущая версия.
-
Создайте новый каталог и перейдите в него. Пример:
mkdir ~/nodejs-oauth2-example cd ~/nodejs-oauth2-example
-
Установите клиентскую библиотеку Google API для Node.js с помощью npm:
npm install googleapis
-
Создайте файлы
main.jsсо следующим содержимым. -
Запустите пример:
node .\main.js
main.js
const http = require('http'); const https = require('https'); const url = require('url'); const { google } = require('googleapis'); const crypto = require('crypto'); const express = require('express'); const session = require('express-session'); /** * To use OAuth2 authentication, we need access to a CLIENT_ID, CLIENT_SECRET, AND REDIRECT_URI. * To get these credentials for your application, visit * https://console.cloud.google.com/apis/credentials. */ const oauth2Client = new google.auth.OAuth2( YOUR_CLIENT_ID, YOUR_CLIENT_SECRET, YOUR_REDIRECT_URL ); // Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar. const scopes = [ 'https://www.googleapis.com/auth/youtube.force-ssl', 'https://www.googleapis.com/auth/calendar.readonly' ]; /* Global variable that stores user credential in this code example. * ACTION ITEM for developers: * Store user's refresh token in your data store if * incorporating this code into your real app. * For more information on handling refresh tokens, * see https://github.com/googleapis/google-api-nodejs-client#handling-refresh-tokens */ let userCredential = null; async function main() { const app = express(); app.use(session({ secret: 'your_secure_secret_key', // Replace with a strong secret resave: false, saveUninitialized: false, })); // Example on redirecting user to Google's OAuth 2.0 server. app.get('/', async (req, res) => { // Generate a secure random state value. const state = crypto.randomBytes(32).toString('hex'); // Store state in the session req.session.state = state; // Generate a url that asks permissions for the Drive activity and Google Calendar scope const authorizationUrl = oauth2Client.generateAuthUrl({ // 'online' (default) or 'offline' (gets refresh_token) access_type: 'offline', /** Pass in the scopes array defined above. * Alternatively, if only one scope is needed, you can pass a scope URL as a string */ scope: scopes, // Enable incremental authorization. Recommended as a best practice. include_granted_scopes: true, // Include the state parameter to reduce the risk of CSRF attacks. state: state }); res.redirect(authorizationUrl); }); // Receive the callback from Google's OAuth 2.0 server. app.get('/oauth2callback', async (req, res) => { // Handle the OAuth 2.0 server response let q = url.parse(req.url, true).query; if (q.error) { // An error response e.g. error=access_denied console.log('Error:' + q.error); } else if (q.state !== req.session.state) { //check state value console.log('State mismatch. Possible CSRF attack'); res.end('State mismatch. Possible CSRF attack'); } else { // Get access and refresh tokens (if access_type is offline) let { tokens } = await oauth2Client.getToken(q.code); oauth2Client.setCredentials(tokens); /** Save credential to the global variable in case access token was refreshed. * ACTION ITEM: In a production app, you likely want to save the refresh token * in a secure persistent database instead. */ userCredential = tokens; // User authorized the request. Now, check which scopes were granted. if (tokens.scope.includes('https://www.googleapis.com/auth/youtube.force-ssl')) { // User authorized read-only Drive activity permission. // Example of using Google Drive API to list filenames in user's Drive. const drive = google.drive('v3'); drive.files.list({ auth: oauth2Client, pageSize: 10, fields: 'nextPageToken, files(id, name)', }, (err1, res1) => { if (err1) return console.log('The API returned an error: ' + err1); const files = res1.data.files; if (files.length) { console.log('Files:'); files.map((file) => { console.log(`${file.name} (${file.id})`); }); } else { console.log('No files found.'); } }); } else { // User didn't authorize read-only Drive activity permission. // Update UX and application accordingly } // Check if user authorized Calendar read permission. if (tokens.scope.includes('https://www.googleapis.com/auth/calendar.readonly')) { // User authorized Calendar read permission. // Calling the APIs, etc. } else { // User didn't authorize Calendar read permission. // Update UX and application accordingly } } }); // Example on revoking a token app.get('/revoke', async (req, res) => { // Build the string for the POST request let postData = "token=" + userCredential.access_token; // Options for POST request to Google's OAuth 2.0 server to revoke a token let postOptions = { host: 'oauth2.googleapis.com', port: '443', path: '/revoke', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': Buffer.byteLength(postData) } }; // Set up the request const postReq = https.request(postOptions, function (res) { res.setEncoding('utf8'); res.on('data', d => { console.log('Response: ' + d); }); }); postReq.on('error', error => { console.log(error) }); // Post the request with data postReq.write(postData); postReq.end(); }); const server = http.createServer(app); server.listen(8080); } main().catch(console.error);
HTTP/REST
В этом примере на Python используется фреймворк Flask и библиотека Requests, чтобы продемонстрировать веб-процесс OAuth 2.0. Для этого процесса мы рекомендуем использовать клиентскую библиотеку API Google для Python. (В примере на вкладке Python используется клиентская библиотека.)
import json import flask import requests app = flask.Flask(__name__) # To get these credentials (CLIENT_ID CLIENT_SECRET) and for your application, visit # https://console.cloud.google.com/apis/credentials. CLIENT_ID = '123456789.apps.googleusercontent.com' CLIENT_SECRET = 'abc123' # Read from a file or environmental variable in a real app # Access scopes for two non-Sign-In scopes: Read-only Drive activity and Google Calendar. SCOPE = 'https://www.googleapis.com/auth/youtube.force-ssl https://www.googleapis.com/auth/calendar.readonly' # Indicate where the API server will redirect the user after the user completes # the authorization flow. The redirect URI is required. The value must exactly # match one of the authorized redirect URIs for the OAuth 2.0 client, which you # configured in the API Console. If this value doesn't match an authorized URI, # you will get a 'redirect_uri_mismatch' error. REDIRECT_URI = 'http://example.com/oauth2callback' @app.route('/') def index(): if 'credentials' not in flask.session: return flask.redirect(flask.url_for('oauth2callback')) credentials = json.loads(flask.session['credentials']) if credentials['expires_in'] <= 0: return flask.redirect(flask.url_for('oauth2callback')) else: # User authorized the request. Now, check which scopes were granted. if 'https://www.googleapis.com/auth/drive.metadata.readonly' in credentials['scope']: # User authorized read-only Drive activity permission. # Example of using Google Drive API to list filenames in user's Drive. headers = {'Authorization': 'Bearer {}'.format(credentials['access_token'])} req_uri = 'https://youtube.googleapis.com/youtube/v3/liveBroadcasts' r = requests.get(req_uri, headers=headers).text else: # User didn't authorize read-only Drive activity permission. # Update UX and application accordingly r = 'User did not authorize Drive permission.' # Check if user authorized Calendar read permission. if 'https://www.googleapis.com/auth/calendar.readonly' in credentials['scope']: # User authorized Calendar read permission. # Calling the APIs, etc. r += 'User authorized Calendar permission.' else: # User didn't authorize Calendar read permission. # Update UX and application accordingly r += 'User did not authorize Calendar permission.' return r @app.route('/oauth2callback') def oauth2callback(): if 'code' not in flask.request.args: state = str(uuid.uuid4()) flask.session['state'] = state # Generate a url that asks permissions for the Drive activity # and Google Calendar scope. Then, redirect user to the url. auth_uri = ('https://accounts.google.com/o/oauth2/v2/auth?response_type=code' '&client_id={}&redirect_uri={}&scope={}&state={}').format(CLIENT_ID, REDIRECT_URI, SCOPE, state) return flask.redirect(auth_uri) else: if 'state' not in flask.request.args or flask.request.args['state'] != flask.session['state']: return 'State mismatch. Possible CSRF attack.', 400 auth_code = flask.request.args.get('code') data = {'code': auth_code, 'client_id': CLIENT_ID, 'client_secret': CLIENT_SECRET, 'redirect_uri': REDIRECT_URI, 'grant_type': 'authorization_code'} # Exchange authorization code for access and refresh tokens (if access_type is offline) r = requests.post('https://oauth2.googleapis.com/token', data=data) flask.session['credentials'] = r.text return flask.redirect(flask.url_for('index')) if __name__ == '__main__': import uuid app.secret_key = str(uuid.uuid4()) app.debug = False app.run()
Правила проверки URI переадресации
Чтобы помочь разработчикам защитить свои приложения, Google применяет к URI перенаправления следующие правила проверки: Ваши URI перенаправления должны соответствовать этим правилам. Определения домена, хоста, пути, запроса, схемы и информации о пользователе, используемые в этих правилах, приведены в разделе 3 RFC 3986.
| Правила проверки данных | |
|---|---|
| Схема |
URI перенаправления должны использовать схему HTTPS, а не обычный HTTP. URI localhost (включая URI IP-адресов localhost) не подпадают под это правило. |
| Хост |
Хосты не могут быть IP-адресами. Это правило не распространяется на IP-адреса localhost. |
| Домен |
“googleusercontent.com”.goo.gl), если только домен не принадлежит приложению. Кроме того, если приложение, которому принадлежит домен сокращателя, перенаправляет на этот домен, URI перенаправления должен содержать “/google-callback/” в пути или заканчиваться на “/google-callback”. |
| Userinfo |
URI переадресации не могут содержать подкомпонент userinfo. |
| Путь |
URI переадресации не могут содержать переход к родительскому каталогу (также называемый возвратом к каталогу), который представлен символами |
| Запрос |
URI перенаправления не могут содержать открытые перенаправления. |
| Фрагмент |
URI перенаправления не могут содержать фрагмент. |
| Персонажи |
URI перенаправления не могут содержать определенные символы, в том числе:
|
Дополнительная авторизация
В протоколе OAuth 2.0 приложение запрашивает авторизацию для доступа к ресурсам, которые определяются областями доступа. Для удобства пользователей рекомендуется запрашивать авторизацию для ресурсов в тот момент, когда они вам нужны. Чтобы обеспечить такую возможность, сервер авторизации Google поддерживает пошаговую авторизацию. Эта функция позволяет запрашивать области действия по мере необходимости. Если пользователь предоставляет разрешение для новой области действия, возвращается код авторизации, который можно обменять на токен, содержащий все области действия, предоставленные пользователем проекту.
Например, приложение может получать данные о канале YouTube, принадлежащем пользователю, прошедшему аутентификацию, и позволять ему получать данные YouTube Аналитики через специальный поток. В этом случае при входе в аккаунт приложение может запросить доступ только к области действия https://www.googleapis.com/auth/youtube.force-ssl. Однако если пользователь попытался получить доступ к данным Аналитики для своего канала, приложение также может запросить доступ к области https://www.googleapis.com/auth/yt-analytics.readonly.
Чтобы реализовать поэтапную авторизацию, выполните обычный процесс запроса токена доступа, но убедитесь, что запрос на авторизацию включает ранее предоставленные области действия. Это позволяет избежать необходимости управлять несколькими токенами доступа.
К токену доступа, полученному в результате поэтапной авторизации, применяются следующие правила:
- Токен можно использовать для доступа к ресурсам, соответствующим любому из областей действия, включенных в новую комбинированную авторизацию.
- Когда вы используете токен обновления для комбинированной авторизации, чтобы получить токен доступа, токен доступа представляет комбинированную авторизацию и может использоваться для любого из значений
scope, включенных в ответ. - Объединенная авторизация включает все области действия, которые пользователь предоставил проекту API, даже если запросы на предоставление были отправлены от разных клиентов. Например, если пользователь предоставил доступ к одной области действия, используя клиентское приложение для ПК, а затем предоставил доступ к другой области действия для того же приложения через мобильный клиент, объединенная авторизация будет включать обе области действия.
- Если вы отзовете токен, представляющий комбинированное разрешение, доступ ко всем областям действия этого разрешения от имени связанного пользователя будет отозван одновременно.
В примерах кода для разных языков программирования, приведенных в разделе Шаг 1. Настройте авторизацию и перенаправление на сервер OAuth 2.0 Google, используется пошаговая авторизация. В примерах кода ниже также показано, как использовать пошаговую авторизацию.
PHP
$client->setIncludeGrantedScopes(true);Python
В Python задайте для аргумента ключевого слова include_granted_scopes значение true, чтобы запрос авторизации включал ранее предоставленные области действия. Вполне возможно, что include_granted_scopes будет не единственным аргументом ключевого слова, как показано в примере ниже.
authorization_url, state = flow.authorization_url( # Enable offline access so that you can refresh an access token without # re-prompting the user for permission. Recommended for web server apps. access_type='offline', # Enable incremental authorization. Recommended as a best practice. include_granted_scopes='true')
Ruby
auth_client.update!( :additional_parameters => {"include_granted_scopes" => "true"} )
Node.js
const authorizationUrl = oauth2Client.generateAuthUrl({ // 'online' (default) or 'offline' (gets refresh_token) access_type: 'offline', /** Pass in the scopes array defined above. * Alternatively, if only one scope is needed, you can pass a scope URL as a string */ scope: scopes, // Enable incremental authorization. Recommended as a best practice. include_granted_scopes: true });
HTTP/REST
В этом примере вызывающее приложение запрашивает доступ к данным пользователя в YouTube в дополнение к другим разрешениям, которые пользователь уже предоставил приложению.
GET https://accounts.google.com/o/oauth2/v2/auth?
scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.force-ssl&
access_type=offline&
state=security_token%3D138rk%3Btarget_url%3Dhttp...index&
redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback&
response_type=code&
client_id=client_id&
include_granted_scopes=true
Refresh an access token (offline access)
Access tokens periodically expire and become invalid credentials for a related API request. You can refresh an access token without prompting the user for permission (including when the user is not present) if you requested offline access to the scopes associated with the token.
- If you use a Google API Client Library, the client object refreshes the access token as needed as long as you configure that object for offline access.
- If you are not using a client library, you need to set the
access_typeHTTP query parameter toofflinewhen redirecting the user to Google's OAuth 2.0 server. In that case, Google's authorization server returns a refresh token when you exchange an authorization code for an access token. Then, if the access token expires (or at any other time), you can use a refresh token to obtain a new access token.
Requesting offline access is a requirement for any application that needs to access a Google
API when the user is not present. For example, an app that performs backup services or
executes actions at predetermined times needs to be able to refresh its access token when the
user is not present. The default style of access is called online.
Server-side web applications, installed applications, and devices all obtain refresh tokens during the authorization process. Refresh tokens are not typically used in client-side (JavaScript) web applications.
PHP
If your application needs offline access to a Google API, set the API client's access type to
offline:
$client->setAccessType("offline");
После того как пользователь предоставит офлайн-доступ к запрошенным областям действия, вы сможете продолжать использовать клиент API для доступа к API Google от имени пользователя, когда он не в сети. Объект клиента будет обновлять токен доступа по мере необходимости.
Python
В Python задайте для ключевого аргумента access_type значение offline, чтобы иметь возможность обновить токен доступа, не запрашивая у пользователя разрешение повторно. Вполне возможно, что access_type будет не единственным аргументом ключевого слова, который вы зададите, как показано в примере ниже.
authorization_url, state = flow.authorization_url( # Enable offline access so that you can refresh an access token without # re-prompting the user for permission. Recommended for web server apps. access_type='offline', # Enable incremental authorization. Recommended as a best practice. include_granted_scopes='true')
После того как пользователь предоставит офлайн-доступ к запрошенным областям действия, вы сможете использовать API-клиент для доступа к API Google от имени пользователя, когда он не в сети. Объект клиента будет обновлять токен доступа по мере необходимости.
Ruby
Если вашему приложению нужен офлайн-доступ к Google API, задайте для типа доступа API-клиента значение offline:
auth_client.update!( :additional_parameters => {"access_type" => "offline"} )
После того как пользователь предоставит офлайн-доступ к запрошенным областям действия, вы сможете использовать API-клиент для доступа к API Google от имени пользователя, когда он не в сети. Объект клиента будет обновлять маркер доступа по мере необходимости.
Node.js
Если вашему приложению нужен офлайн-доступ к Google API, задайте для типа доступа API-клиента значение offline:
const authorizationUrl = oauth2Client.generateAuthUrl({ // 'online' (default) or 'offline' (gets refresh_token) access_type: 'offline', /** Pass in the scopes array defined above. * Alternatively, if only one scope is needed, you can pass a scope URL as a string */ scope: scopes, // Enable incremental authorization. Recommended as a best practice. include_granted_scopes: true });
После того как пользователь предоставит офлайн-доступ к запрошенным областям действия, вы сможете продолжать использовать клиент API для доступа к API Google от имени пользователя, когда он не в сети. Объект клиента будет обновлять токен доступа по мере необходимости.
Срок действия токенов доступа истекает. Эта библиотека автоматически использует токен обновления, чтобы получить новый токен доступа, если срок действия текущего истекает. Чтобы у вас всегда были актуальные токены, используйте событие tokens:
oauth2Client.on('tokens', (tokens) => { if (tokens.refresh_token) { // store the refresh_token in your secure persistent database console.log(tokens.refresh_token); } console.log(tokens.access_token); });
Это событие происходит только при первой авторизации. Чтобы получить токен обновления, при вызове метода generateAuthUrl необходимо задать для параметра access_type значение offline. Если вы уже предоставили приложению необходимые разрешения, но не задали ограничения для получения токена обновления, вам нужно будет повторно авторизовать приложение, чтобы получить новый токен обновления.
Чтобы задать refresh_token позже, используйте метод setCredentials:
oauth2Client.setCredentials({ refresh_token: `STORED_REFRESH_TOKEN` });
После того как клиент получит токен обновления, токены доступа будут автоматически получаться и обновляться при следующем вызове API.
HTTP/REST
Чтобы обновить токен доступа, приложение отправляет HTTPS-запрос POST на сервер авторизации Google (https://oauth2.googleapis.com/token), который включает в тело запроса следующие параметры:
| Название | Значение |
|---|---|
client_id |
Идентификатор клиента, полученный из API Console. |
client_secret |
Необязательно
Секретный код клиента, полученный из API Console. |
grant_type |
В соответствии со спецификацией OAuth 2.0 в этом поле должно быть указано значение refresh_token. |
refresh_token |
Маркер обновления, возвращенный при обмене кода авторизации. |
Использовать DPoP необязательно, но рекомендуется для повышения безопасности. Чтобы использовать DPoP с токеном обновления, ваше приложение должно генерировать новый уникальный JWT-токен подтверждения DPoP для каждого запроса к конечной точке токена. Это подтверждение должно быть подписано тем же закрытым ключом, который использовался при первоначальном обмене кодами авторизации. Приложение добавляет подтверждение в качестве заголовка HTTP-запроса.
| Заголовок | Обязательно | Описание |
|---|---|---|
DPoP |
Необязательно | Доказательство DPoP – это токен JWT, который подтверждает владение закрытым ключом. Это заголовок, а не параметр. Если он указан, возвращаемые токены будут привязаны к этому ключу. Для каждого запроса необходимо генерировать новое уникальное подтверждение, которое должно включать утверждения htm (метод HTTP) и htu (URI HTTP), соответствующие запросу. |
Ниже приведен пример запроса.
POST /token HTTP/1.1 Host: oauth2.googleapis.com Content-Type: application/x-www-form-urlencoded DPoP: DPOP_PROOF_JWT client_id=your_client_id& refresh_token=refresh_token& grant_type=refresh_token
Чтобы использовать DPoP с токеном обновления, необходимо сгенерировать новый уникальный JWT-токен подтверждения DPoP для запроса. Пошаговые инструкции по созданию пары ключей и JWT приведены в разделе Как создать подтверждение DPoP.
Успешный обмен обозначается ответом 200 OK, содержащим новый токен доступа. Если используется DPoP, значение параметра token_type – Bearer. Успешный ответ подтверждает, что доказательство DPoP для токена обновления было принято. Google также может вернуть в ответе новый HTTP-заголовок DPoP-Nonce. В этом случае ваш клиент должен кешировать этот одноразовый код и включать его в утверждение nonce подтверждения DPoP в последующих запросах.
Если заголовок DPoP отсутствует, недействителен или в нем используется неправильный ключ, обмен не выполняется. Подробнее о кодах ошибок DPoP и обработке однократно используемых номеров…
Ниже приведен пример заголовков и тела успешного ответа при использовании DPoP:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
DPoP-Nonce: AN3XwJjZsjnb0ZuWkRlek8QU7wY-Zhf-5IP6tO0tORz0KgtDT1Bo8FX-w4nz3r5lnepI
{
"access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in": 3920,
"scope": "https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly",
"token_type": "Bearer"
}Обратите внимание, что количество токенов обновления ограничено. Ограничения действуют для каждой комбинации клиента и пользователя, а также для каждого пользователя во всех клиентах. Токены обновления следует сохранять в долгосрочном хранилище и использовать, пока они действительны. Если ваше приложение запрашивает слишком много токенов обновления, оно может столкнуться с этими ограничениями, и в этом случае старые токены обновления перестанут работать.
Отзыв токена
В некоторых случаях пользователь может захотеть отозвать доступ, предоставленный приложению. Пользователь может отозвать доступ в настройках аккаунта. Подробную информацию вы найдете в разделе Как закрыть доступ к аккаунту для стороннего сайта или приложения.
Приложение также может программно отозвать предоставленный ему доступ. Программный отзыв важен в тех случаях, когда пользователь отменяет подписку, удаляет приложение или когда ресурсы API, необходимые для приложения, значительно изменились. Другими словами, в процессе удаления может выполняться запрос к API, чтобы убедиться, что разрешения, ранее предоставленные приложению, удалены.
PHP
Чтобы отозвать токен программно, вызовите revokeToken():
$client->revokeToken();Python
Чтобы отозвать токен программным способом, отправьте запрос к https://oauth2.googleapis.com/revoke, включив токен в качестве параметра и задав заголовок Content-Type:
requests.post('https://oauth2.googleapis.com/revoke', params={'token': credentials.token}, headers = {'content-type': 'application/x-www-form-urlencoded'})
Ruby
Чтобы отозвать токен программным способом, отправьте HTTP-запрос в конечную точку oauth2.revoke:
uri = URI('https://oauth2.googleapis.com/revoke') response = Net::HTTP.post_form(uri, 'token' => auth_client.access_token)
Это может быть токен доступа или токен обновления. Если токен является токеном доступа и у него есть соответствующий токен обновления, токен обновления также будет отозван.
Если отзыв успешно обработан, то код статуса ответа – 200. В случае ошибки возвращается код статуса 400 и код ошибки.
Node.js
Чтобы отозвать токен программным способом, отправьте запрос HTTPS POST к конечной точке /revoke:
const https = require('https'); // Build the string for the POST request let postData = "token=" + userCredential.access_token; // Options for POST request to Google's OAuth 2.0 server to revoke a token let postOptions = { host: 'oauth2.googleapis.com', port: '443', path: '/revoke', method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': Buffer.byteLength(postData) } }; // Set up the request const postReq = https.request(postOptions, function (res) { res.setEncoding('utf8'); res.on('data', d => { console.log('Response: ' + d); }); }); postReq.on('error', error => { console.log(error) }); // Post the request with data postReq.write(postData); postReq.end();
Параметр token может быть токеном доступа или токеном обновления. Если токен является токеном доступа и у него есть соответствующий токен обновления, токен обновления также будет отозван.
Если отзыв успешно обработан, то код статуса ответа – 200. В случае ошибки возвращается код статуса 400 и код ошибки.
HTTP/REST
Чтобы отозвать токен программно, приложение отправляет запрос на адрес https://oauth2.googleapis.com/revoke и включает токен в качестве параметра:
curl -d -X -POST --header "Content-type:application/x-www-form-urlencoded" \
https://oauth2.googleapis.com/revoke?token={token}Это может быть токен доступа или токен обновления. Если токен является токеном доступа и у него есть соответствующий токен обновления, токен обновления также будет отозван.
Если отзыв успешно обработан, то код статуса HTTP ответа – 200. В случае ошибки возвращается код статуса HTTP 400 и код ошибки.
Доступ на основе времени
Временный доступ позволяет пользователю предоставить приложению доступ к своим данным на ограниченный период времени, чтобы выполнить определенное действие. В некоторых продуктах Google во время запроса согласия пользователям предлагается предоставить доступ на ограниченный период времени. Пример – Data Portability API, который позволяет выполнить однократный перенос данных.
Если пользователь предоставляет вашему приложению доступ на определенный период времени, токен обновления станет недействительным по истечении этого периода. Обратите внимание, что в некоторых случаях токены обновления могут быть аннулированы раньше. Подробнее об этом рассказывается в этой статье. В таких случаях поле refresh_token_expires_in, возвращаемое в ответе на обмен кодами авторизации, представляет собой время, оставшееся до истечения срока действия токена обновления.
Как реализовать защиту связанных аккаунтов
Чтобы дополнительно защитить аккаунты пользователей, включите защиту связанных аккаунтов с помощью сервиса Google. Этот сервис позволяет подписаться на уведомления о событиях безопасности, которые предоставляют вашему приложению информацию о важных изменениях в аккаунте пользователя. Затем вы можете использовать эту информацию, чтобы предпринять действия в зависимости от того, как вы решите реагировать на события.
Вот несколько примеров типов событий, которые сервис защиты связанных аккаунтов Google отправляет в ваше приложение:
-
https://schemas.openid.net/secevent/risc/event-type/sessions-revoked -
https://schemas.openid.net/secevent/oauth/event-type/token-revoked -
https://schemas.openid.net/secevent/risc/event-type/account-disabled
Дополнительную информацию о том, как реализовать защиту связанных аккаунтов, и полный список доступных событий можно найти на странице Защита аккаунтов пользователей с помощью защиты связанных аккаунтов .