Использование OAuth 2.0 для веб-серверных приложений

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

Клиентские библиотеки

В приведенных здесь примерах для конкретных языков программирования показана реализация OAuth 2.0 с помощью клиентских библиотек Google API. Чтобы выполнить примеры кода, сначала необходимо установить клиентскую библиотеку для соответствующего языка.

Если вы используете клиентскую библиотеку API Google для обработки процесса OAuth 2.0 в приложении, она выполняет многие действия, которые в противном случае пришлось бы выполнять приложению. Например, он определяет, когда приложение может использовать или обновлять сохраненные токены доступа, а также когда приложению необходимо повторно получить согласие. Клиентская библиотека также генерирует правильные URL перенаправления и помогает реализовать обработчики перенаправления, которые обменивают коды авторизации на токены доступа.

Клиентские библиотеки API Google для серверных приложений доступны для следующих языков:

Требования

Как включить API для проекта

Приложения, которые вызывают Google API, должны включить эти API в API Console.

Чтобы включить API для вашего проекта:

  1. Откройте библиотеку API в Google API Console.
  2. Выберите существующий проект или создайте новый.
  3. Найдите и включите YouTube Data API на странице Библиотека. Найдите и включите другие API, которые будут использоваться в вашем приложении.

Как создать учетные данные для авторизации

Любое приложение, которое использует OAuth 2.0 для доступа к API Google, должно иметь учетные данные для авторизации, которые идентифицируют приложение на сервере OAuth 2.0 Google. Ниже описано, как создать учетные данные для проекта. Затем ваши приложения смогут использовать эти учетные данные для доступа к API, которые вы включили для этого проекта.

  1. Перейдите на страницу Клиенты.
  2. Нажмите Create Client (Создать клиента).
  3. Выберите тип приложения Веб-приложение.
  4. Заполните форму и нажмите Создать. В приложениях, использующих такие языки и фреймворки, как 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

Полный список областей действия, которые можно использовать для доступа к API Google, приведен в документе Области действия API OAuth 2.0.

Требования к языкам

Чтобы запустить любой из примеров кода в этом документе, вам понадобится аккаунт 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 и связанное с ним руководство по переносу данных, ознакомьтесь с примечаниями к выпуску клиентской библиотеки API Google для Python.

Ruby

Чтобы запустить примеры кода на Ruby, приведенные в этом документе, вам понадобится:

  • Ruby 2.6 или более поздней версии
  • Библиотека аутентификации Google для Ruby:

    gem install googleauth
  • Фреймворк Sinatra Ruby для создания веб-приложений.

    gem install sinatra

Node.js

Чтобы запустить примеры кода Node.js из этого документа, вам потребуется:

  • Версия Node.js с долгосрочной поддержкой (LTS) или текущая версия.
  • Клиент 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, требующий авторизации пользователя.

Ниже приведен краткий список этих шагов.

  1. Приложение определяет, какие разрешения ему нужны.
  2. Приложение перенаправляет пользователя на страницу Google, где перечислены запрашиваемые разрешения.
  3. Пользователь решает, предоставлять ли вашему приложению разрешения.
  4. Приложение узнает, какое решение принял пользователь.
  5. Если пользователь предоставил запрошенные разрешения, ваше приложение получает токены, необходимые для выполнения запросов к 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'])

# 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'

# 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 
scope = 'https://www.googleapis.com/auth/youtube.force-ssl'

# 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 YouTube API
const scopes = [
  'https://www.googleapis.com/auth/youtube.force-ssl'
];

// 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 перенаправления для указанного client_id, вы получите ошибку redirect_uri_mismatch.

Обратите внимание, что схема (http или https), регистр и косая черта в конце (/) должны совпадать.

response_type Обязательно

Определяет, возвращает ли конечная точка Google OAuth 2.0 код авторизации.

Для приложений веб-сервера установите значение параметра code.

scope Обязательно

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

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

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

Полный список областей действия, которые можно использовать для доступа к API Google, приведен в документе Области действия API OAuth 2.0.

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

access_type Рекомендуется

Указывает, может ли ваше приложение обновлять токены доступа, когда пользователь не находится за браузером. Допустимые значения параметра: online (по умолчанию) и offline.

Установите значение offline, если вашему приложению нужно обновлять токены доступа, когда пользователь не работает в браузере. Этот способ обновления токенов доступа описан далее в этом документе. Это значение указывает серверу авторизации Google вернуть токен обновления и токен доступа при первом обмене кодом авторизации на токены.

state Рекомендуется

Любое строковое значение, которое приложение использует для сохранения состояния между запросом авторизации и ответом сервера авторизации. Сервер возвращает точное значение, которое вы отправили в виде пары name=value в компоненте запроса URL (?) redirect_uri после того, как пользователь предоставит или отклонит запрос доступа вашего приложения.

Этот параметр можно использовать для разных целей, например чтобы направлять пользователя к нужному ресурсу в приложении, отправлять одноразовые коды и предотвращать подделку межсайтовых запросов. Поскольку redirect_uri можно подобрать, использование значения state повышает вероятность того, что входящее подключение является результатом запроса на аутентификацию. Если вы сгенерируете случайную строку или закодируете хеш файла cookie или другого значения, которое фиксирует состояние клиента, вы сможете проверить ответ, чтобы дополнительно убедиться, что запрос и ответ поступили из одного и того же браузера. Это обеспечит защиту от таких атак, как межсайтовая подделка запроса. Пример создания и подтверждения токена state можно найти в документации по OpenID Connect.

include_granted_scopes Необязательно

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

login_hint Необязательно

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

Установите для значения параметра адрес электронной почты или идентификатор sub, который эквивалентен идентификатору Google пользователя.

prompt Необязательно

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

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

none Не показывайте экраны аутентификации или окна запроса доступа. Нельзя указывать вместе с другими значениями.
consent Запросите согласие пользователя.
select_account Предложить пользователю выбрать аккаунт.

Шаг 2. Перенаправьте на сервер OAuth 2.0 от Google

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

PHP

  1. Сгенерируйте URL, чтобы запросить доступ у сервера OAuth 2.0 от Google:
    $auth_url = $client->createAuthUrl();
  2. Перенаправьте пользователя на страницу $auth_url:
    header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));

Python

В примере ниже показано, как перенаправить пользователя на URL авторизации с помощью фреймворка веб-приложений Flask.

return flask.redirect(authorization_url)

Ruby

  1. Сгенерируйте URL, чтобы запросить доступ у сервера OAuth 2.0 от Google:
    auth_uri = authorizer.get_authorization_url(request: request)
  2. Перенаправьте пользователя на страницу auth_uri.

Node.js

  1. Используйте сгенерированный URL authorizationUrl из шага 1 generateAuthUrl, чтобы запросить доступ у сервера OAuth 2.0 Google.
  2. Перенаправьте пользователя на страницу authorizationUrl.
    res.redirect(authorizationUrl);

HTTP/REST

Пример перенаправления на сервер авторизации Google

В примере URL ниже запрашивается офлайн-доступ (access_type=offline) к области действия, которая позволяет просматривать аккаунт пользователя YouTube. При этом используется поэтапная авторизация, чтобы новый токен доступа охватывал все области, к которым пользователь ранее предоставил приложению доступ. В URL также заданы значения для обязательных параметров redirect_uri, response_type и client_id, а также для параметра state. URL содержит переносы строк и пробелы для удобства чтения.

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.readonly&
 access_type=offline&
 include_granted_scopes=true&
 state=state_parameter_passthrough_value&
 redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback&
 response_type=code&
 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.readonly&
 access_type=offline&
 include_granted_scopes=true&
 state=state_parameter_passthrough_value&
 redirect_uri=http%3A%2F%2Flocalhost%2Foauth2callback&
 response_type=code&
 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 в командной строке.

  1. Создайте пару ключей EC P-256:
    openssl ecparam -name prime256v1 -genkey -noout -out dpop_private.pem
    openssl ec -in dpop_private.pem -pubout -out dpop_public.pem
  2. Как создать заголовок 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"
      }
    }
  3. Создайте полезную нагрузку 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 кода авторизации, закодированным по стандарту Base64 для URL: "jti":"BASE64URL(SHA256(AUTHORIZATION_CODE))".
    • Для обмена токенов обновления параметр jti должен быть уникальным идентификатором для каждого запроса: "jti":"YOUR_UNIQUE_PER_REQUEST_IDENTIFIER".
  4. Подпишите подтверждение:

    Объедините закодированный заголовок и полезную нагрузку с помощью точки (.), а затем подпишите результат с помощью закрытого ключа, используя алгоритм 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 доказательства DPoP в последующих запросах (например, при обмене токена обновления на новый токен доступа или при вызове API, защищенных DPoP). Используя этот одноразовый код, вы можете избежать дополнительной ошибки (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",
  "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();

Python

Возвращенный объект credentials имеет свойство granted_scopes, которое представляет собой список областей, предоставленных пользователем вашему приложению.

credentials = flow.credentials
flask.session['credentials'] = {
    'token': credentials.token,
    'refresh_token': credentials.refresh_token,
    'granted_scopes': credentials.granted_scopes}

Ruby

Если вы запрашиваете несколько областей одновременно, проверьте, какие из них были предоставлены, с помощью свойства scope объекта credentials.

# User authorized the request. Now, check which scopes were granted.
if credentials.scope.include?(Google::Apis::YoutubeV3::AUTH_YOUTUBE_FORCE_SSL)
  # User authorized permission to see, edit, and permanently delete the
  # YouTube videos, ratings, comments and captions.
  # Calling the APIs, etc
else
  # User didn't authorize the 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 permission to see, edit, and permanently delete the
  // YouTube videos, ratings, comments and captions.
  // Calling the APIs, etc.
}
else
{
  // User didn't authorize read-only Drive activity permission.
  // Update UX and application accordingly
}

HTTP/REST

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

Например, в следующем ответе с токеном доступа указано, что пользователь предоставил вашему приложению разрешение на просмотр, редактирование и удаление своих видео, оценок, комментариев и субтитров на YouTube:

  {
    "access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
    "expires_in": 3920,
    "token_type": "Bearer",
    "scope": "https://www.googleapis.com/auth/youtube.force-ssl",
    "refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
  }

Вызов API Google

PHP

Чтобы вызвать API Google с помощью токена доступа, выполните следующие действия:

  1. Если вам нужно применить токен доступа к новому объекту Google\Client (например, если вы сохранили токен доступа в сеансе пользователя), используйте метод setAccessToken:
    $client->setAccessToken($access_token);
  2. Создайте объект сервиса для API, который вы хотите вызвать. Чтобы создать объект сервиса, передайте авторизованный объект Google\Client конструктору нужного API. Например, чтобы вызвать YouTube Data API:
    $youtube = new Google_Service_YouTube($client);
  3. Отправьте запросы к сервису API, используя интерфейс, предоставляемый объектом сервиса. Например, чтобы получить данные о канале YouTube авторизованного пользователя, выполните следующие действия:
    $channel = $youtube->channels->listChannels('snippet', array('mine' => $mine));

Python

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

  1. Создайте объект сервиса для API, который вы хотите вызвать. Чтобы создать объект сервиса, вызовите метод build библиотеки googleapiclient.discovery, указав название и версию API, а также учетные данные пользователя. Например, чтобы вызвать версию 3 YouTube Data API, используйте следующий код:
    from googleapiclient.discovery import build
    
    youtube = build('youtube', 'v3', credentials=credentials)
  2. Отправляйте запросы к сервису API, используя интерфейс, предоставленный объектом сервиса. Например, чтобы получить данные о канале YouTube авторизованного пользователя, выполните следующие действия:
    channel = youtube.channels().list(mine=True, part='snippet').execute()

Ruby

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

  1. Создайте объект сервиса для API, который вы хотите вызвать. Например, чтобы вызвать версию 3 YouTube Data API:
    youtube = Google::Apis::YoutubeV3::YouTubeService.new
  2. Укажите учетные данные в сервисе:
    youtube.authorization = credentials
  3. Отправляйте запросы к сервису API, используя интерфейс, предоставленный объектом сервиса. Например, чтобы получить данные о канале YouTube авторизованного пользователя, выполните следующие действия:
    channel = youtube.list_channels(part, :mine => mine)

Кроме того, авторизацию можно выполнять для каждого метода отдельно, передавая ему параметр options:

channel = youtube.list_channels(part, :mine => mine, options: { authorization: auth_client })

Node.js

Получив токен доступа и задав его для объекта OAuth2, используйте этот объект для вызова API Google. Приложение может использовать этот токен для авторизации запросов к API от имени определенного аккаунта пользователя или сервисного аккаунта. Создайте объект сервиса для API, который вы хотите вызвать. Например, в приведенном ниже коде используется Google Drive API, чтобы получить список имен файлов на Диске пользователя.

const { google } = require('googleapis');

// Example of using YouTube API to list channels.
var service = google.youtube('v3');
service.channels.list({
  auth: oauth2Client,
  part: 'snippet,contentDetails,statistics',
  forUsername: 'GoogleDevelopers'
}, function (err, response) {
  if (err) {
    console.log('The API returned an error: ' + err);
    return;
  }
  var channels = response.data.items;
  if (channels.length == 0) {
    console.log('No channel found.');
  } else {
    console.log('This channel\'s ID is %s. Its title is \'%s\', and ' +
      'it has %s views.',
      channels[0].id,
      channels[0].snippet.title,
      channels[0].statistics.viewCount);
  }
});

HTTP/REST

Получив токен доступа, приложение может использовать его для вызова Google API от имени определенного аккаунта пользователя, если предоставлены необходимые для API области доступа. Для этого включите токен доступа в запрос к API, добавив параметр запроса access_token или значение Bearer заголовка HTTP Authorization. По возможности используйте заголовок HTTP, поскольку строки запросов обычно видны в журналах сервера. В большинстве случаев для настройки вызовов API Google (например, при вызове YouTube Data API) можно использовать клиентскую библиотеку.

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

Вы можете попробовать все API Google и посмотреть их области действия в OAuth 2.0 Playground.

Примеры HTTP GET

Вызов конечной точки youtube.channels (YouTube Data API) с использованием HTTP-заголовка Authorization: Bearer может выглядеть следующим образом: Обратите внимание, что вам нужно указать собственный токен доступа:

GET /youtube/v3/channels?part=snippet&mine=true HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer access_token

Ниже приведен вызов того же API для аутентифицированного пользователя с использованием параметра строки запроса access_token:

GET https://www.googleapis.com/youtube/v3/channels?access_token=access_token&part=snippet&mine=true

Примеры использования оператора curl

Вы можете протестировать эти команды с помощью приложения командной строки curl. Вот пример с использованием заголовка HTTP (рекомендуемый вариант):

curl -H "Authorization: Bearer access_token" https://www.googleapis.com/youtube/v3/channels?part=snippet&mine=true

Или параметр строки запроса:

curl https://www.googleapis.com/youtube/v3/channels?access_token=access_token&part=snippet&mine=true

Полный пример

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

PHP

Чтобы запустить этот пример:

  1. В API Console добавьте URL локального компьютера в список URL перенаправления. Например, добавьте http://localhost:8080.
  2. Создайте новый каталог и перейдите в него. Пример:
    mkdir ~/php-oauth2-example
    cd ~/php-oauth2-example
  3. Установите клиентскую библиотеку API Google для PHP с помощью Composer:
    composer require google/apiclient:^2.15.0
  4. Создайте файлы index.php и oauth2callback.php со следующим содержанием:
  5. Запустите пример с помощью встроенного тестового веб-сервера 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']);
  
  $youtube = new Google_Service_YouTube($client);
  $channel = $youtube->channels->listChannels('snippet', array('mine' => $mine));
  echo json_encode($channel);
  
}
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();
  
  $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, то увидите пять ссылок:

  • Проверить запрос к API. Эта ссылка ведет на страницу, на которой выполняется пример запроса к 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']
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('/test')
def test_api_request():
  if 'credentials' not in flask.session:
  return flask.redirect('authorize')

  # Load credentials from the session.
  credentials = google.oauth2.credentials.Credentials(
    **flask.session['credentials'])

  youtube = googleapiclient.discovery.build(
    API_SERVICE_NAME, API_VERSION, credentials=credentials)

  channel = youtube.channels().list(mine=True, part='snippet').execute()

  # Save credentials back to session in case access token was refreshed.
  # ACTION ITEM: In a production app, you likely want to save these
  #              credentials in a persistent database instead.
  flask.session['credentials'] = credentials_to_dict(credentials)

  return flask.jsonify(**channel)
@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
  
  flask.session['credentials'] = credentials_to_dict(credentials)

  return flask.redirect(flask.url_for('test_api_request'))
  
@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 print_index_table():
  return ('<table>' + 
          '<tr><td><a href="/test">Test an API request</a></td>' +
          '<td>Submit an API request and see a formatted JSON response. ' +
          '    Go through the authorization flow if there are no stored ' +
          '    credentials for the user.</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="/test">test the ' +
          '    API request</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 '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 retrieving data about the user's YouTube channel.
  scope = 'Google::Apis::YoutubeV3::AUTH_YOUTUBE_FORCE_SSL'

  # 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 read-only YouTube Data API permission.
  # Example of using YouTube Data API to list user's YouTube channel
  youtube = Google::Apis::YoutubeV3::YouTubeService.new
  channel = youtube.list_channels(part, :mine => mine, options: { authorization: auth_client })
  
  "<pre>#{JSON.pretty_generate(channel.to_h)}</pre>"
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

Чтобы запустить этот пример:

  1. В API Console добавьте URL локального компьютера в список URL перенаправления. Например, добавьте http://localhost.
  2. Убедитесь, что у вас установлена версия Node.js с долгосрочной поддержкой (LTS) или текущая версия.
  3. Создайте новый каталог и перейдите в него. Пример:
    mkdir ~/nodejs-oauth2-example
    cd ~/nodejs-oauth2-example
  4. Установите клиентскую библиотеку Google API для Node.js с помощью npm:
    npm install googleapis
  5. Создайте файлы main.js со следующим содержимым.
  6. Запустите пример:
    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 YouTube API
const scopes = [
  'https://www.googleapis.com/auth/youtube.force-ssl'
];

/* 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;
      
      // Example of using YouTube API to list channels.
      var service = google.youtube('v3');
      service.channels.list({
        auth: oauth2Client,
        part: 'snippet,contentDetails,statistics',
        forUsername: 'GoogleDevelopers'
      }, function (err, response) {
        if (err) {
          console.log('The API returned an error: ' + err);
          return;
        }
        var channels = response.data.items;
        if (channels.length == 0) {
          console.log('No channel found.');
        } else {
          console.log('This channel\'s ID is %s. Its title is \'%s\', and ' +
            'it has %s views.',
            channels[0].id,
            channels[0].snippet.title,
            channels[0].statistics.viewCount);
        }
      });
    }
  });

  // 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 YouTube API
SCOPE = 'https://www.googleapis.com/auth/youtube.force-ssl'

# 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: 
    headers = {'Authorization': 'Bearer {}'.format(credentials['access_token'])}
    req_uri = 'https://www.googleapis.com/youtube/v3/channels/list'
    r = requests.get(req_uri, headers=headers)
    return r.text 

@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.

Домен
  • Домены верхнего уровня (TLD) хоста должны быть указаны в списке общедоступных суффиксов.
  • Домены хоста не могут быть “googleusercontent.com”.
  • URI переадресации не могут содержать домены сервисов сокращения URL (например, goo.gl), если только домен не принадлежит приложению. Кроме того, если приложение, которому принадлежит домен для сокращения ссылок, выполняет переадресацию на этот домен, URI переадресации должен содержать “/google-callback/” в пути или заканчиваться на “/google-callback”.
  • Userinfo

    URI перенаправления не могут содержать подкомпонент userinfo.

    Путь

    URI перенаправления не могут содержать обход каталога (также называемый возвратом по каталогу), который представлен символами “/..” или “\..” или их кодировкой URL.

    Запрос

    URI перенаправления не могут содержать открытые перенаправления.

    Fragment

    URI перенаправления не могут содержать фрагмент.

    Персонажи URI перенаправления не могут содержать некоторые символы, в том числе:
    • Подстановочные знаки ('*')
    • Непечатные символы ASCII
    • Недопустимые процентные кодировки (любые процентные кодировки, которые не соответствуют форме кодировки URL: знак процента, за которым следуют два шестнадцатеричных числа).
    • Нулевые символы (закодированный нулевой символ, например %00,%C0%80).

    Пошаговая авторизация

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

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

    В этом случае при входе в аккаунт приложению может не потребоваться доступ к областям действия. Однако если пользователь попытается оценить видео, добавить его в плейлист или выполнить другое действие на YouTube, приложение может запросить доступ к области действия https://www.googleapis.com/auth/youtube.force-ssl. Аналогично, приложение может запросить доступ к области действия https://www.googleapis.com/auth/calendar, если пользователь попытается добавить мероприятие в календарь.

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

    К токену доступа, полученному в результате пошаговой авторизации, применяются следующие правила:

    • Токен можно использовать для доступа к ресурсам, соответствующим любой из областей действия, включенных в новую комбинированную авторизацию.
    • Когда вы используете токен обновления для комбинированной авторизации, чтобы получить токен доступа, токен доступа представляет комбинированную авторизацию и может использоваться для любого из значений 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%2Fyt-analytics.readonly&
      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_type HTTP query parameter to offline when 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",
      "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

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