OAuth 2.0 для клиентских веб-приложений

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

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

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

Клиентская библиотека API Google и службы идентификации Google

Если вы используете клиентскую библиотеку Google API для JavaScript для авторизованных вызовов в Google, вам следует использовать библиотеку JavaScript Google Identity Services для обработки потока OAuth 2.0. См. модель токенов Google Identity Services, основанную на неявном потоке предоставления прав OAuth 2.0.

Предпосылки

Включите API для вашего проекта

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

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

  1. Open the API Library в Google API Console.
  2. If prompted, select a project, or create a new one.
  3. The API Library Здесь перечислены все доступные API, сгруппированные по семействам продуктов и популярности. Если API, который вы хотите включить, отсутствует в списке, воспользуйтесь поиском или нажмите «Просмотреть все» в семействе продуктов, к которому он принадлежит.
  4. Выберите API, который вы хотите включить, затем нажмите кнопку Включить .
  5. If prompted, enable billing.
  6. If prompted, read and accept the API's Terms of Service.

Создать учетные данные авторизации

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

  1. Go to the Credentials page.
  2. Нажмите «Создать клиента» .
  3. Выберите тип приложения Веб-приложение .
  4. Заполните форму. Приложения, использующие JavaScript для выполнения авторизованных запросов к API Google, должны указывать авторизованные источники JavaScript . Источники определяют домены, с которых ваше приложение может отправлять запросы на сервер OAuth 2.0. Эти источники должны соответствовать правилам валидации Google .

Определить области доступа

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

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

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

Получение токенов доступа OAuth 2.0

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

Шаг 1: Перенаправление на сервер OAuth 2.0 Google

Чтобы запросить разрешение на доступ к данным пользователя, перенаправьте пользователя на сервер Google OAuth 2.0.

Конечные точки OAuth 2.0

Создайте URL-адрес для запроса доступа к конечной точке OAuth 2.0 Google по адресу https://accounts.google.com/o/oauth2/v2/auth . Эта конечная точка доступна по протоколу HTTPS; соединения по обычному протоколу HTTP отклоняются.

Сервер авторизации Google поддерживает следующие параметры строки запроса для приложений веб-сервера:

Параметры
client_id Необходимый

Идентификатор клиента для вашего приложения. Вы можете найти это значение в .

redirect_uri Необходимый

Определяет, куда API-сервер перенаправляет пользователя после завершения процесса авторизации. Значение должно точно соответствовать одному из разрешенных URI перенаправления для клиента OAuth 2.0, настроенных в настройках клиента. . Если это значение не соответствует авторизованному URI перенаправления для предоставленного client_id , вы получите ошибку redirect_uri_mismatch .

Обратите внимание, что схема http или https , регистр и завершающий слеш (« / ») должны совпадать.

response_type Необходимый

Приложениям JavaScript необходимо задать значение параметра token . Это значение указывает серверу авторизации Google возвращать токен доступа в виде пары « name=value » в идентификаторе фрагмента URI ( # ), на который перенаправляется пользователь после завершения процесса авторизации.

scope Необходимый

Список областей действия, разделенных пробелами, которые определяют ресурсы, к которым ваше приложение может получить доступ от имени пользователя. Эти значения используются в окне согласия, которое 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 Предложить пользователю выбрать учетную запись.

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

Пример URL-адреса показан ниже с переносами строк и пробелами для удобства чтения.

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly%20https%3A//www.googleapis.com/auth/calendar.readonly&
 include_granted_scopes=true&
 response_type=token&
 state=state_parameter_passthrough_value&
 redirect_uri=https%3A//oauth2.example.com/code&
 client_id=client_id

После создания URL-адреса запроса перенаправьте на него пользователя.

Пример кода JavaScript

Следующий фрагмент кода JavaScript показывает, как инициировать процесс авторизации в JavaScript без использования клиентской библиотеки API Google для JavaScript. Поскольку эта конечная точка OAuth 2.0 не поддерживает Cross-Origin Resource Sharing (CORS), фрагмент создаёт форму, которая открывает запрос к этой конечной точке.

/*
 * Create form to request access token from Google's OAuth 2.0 server.
 */
function oauthSignIn() {
  // Google's OAuth 2.0 endpoint for requesting an access token
  var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';

  // Create <form> element to submit parameters to OAuth 2.0 endpoint.
  var form = document.createElement('form');
  form.setAttribute('method', 'GET'); // Send as a GET request.
  form.setAttribute('action', oauth2Endpoint);

  // Parameters to pass to OAuth 2.0 endpoint.
  var params = {'client_id': 'YOUR_CLIENT_ID',
                'redirect_uri': 'YOUR_REDIRECT_URI',
                'response_type': 'token',
                'scope': 'https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly',
                'include_granted_scopes': 'true',
                'state': 'pass-through value'};

  // Add form parameters as hidden input values.
  for (var p in params) {
    var input = document.createElement('input');
    input.setAttribute('type', 'hidden');
    input.setAttribute('name', p);
    input.setAttribute('value', params[p]);
    form.appendChild(input);
  }

  // Add form to page and submit it to open the OAuth 2.0 endpoint.
  document.body.appendChild(form);
  form.submit();
}

Шаг 2: Google запрашивает у пользователя согласие

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

На этом этапе вашему приложению не нужно ничего делать, так как оно ожидает ответа от сервера Google OAuth 2.0 о предоставлении доступа. Этот ответ поясняется на следующем шаге.

Ошибки

Запросы к конечной точке авторизации Google OAuth 2.0 могут отображать сообщения об ошибках вместо ожидаемых процессов аутентификации и авторизации. Ниже перечислены распространённые коды ошибок и рекомендуемые способы их устранения.

admin_policy_enforced

Учётная запись Google не может авторизовать одну или несколько запрошенных областей действия из-за политик администратора Google Workspace. Подробнее о том, как администратор может ограничить доступ ко всем областям действия или к конфиденциальным и ограниченным областям действия, пока доступ не будет явно предоставлен вашему идентификатору клиента OAuth, см. в справочной статье Google Workspace Управление доступом сторонних и внутренних приложений к данным Google Workspace .

disallowed_useragent

Конечная точка авторизации отображается внутри встроенного пользовательского агента, запрещенного политиками Google OAuth 2.0 .

Андроид

Разработчики Android могут столкнуться с этим сообщением об ошибке при открытии запросов на авторизацию вandroid.webkit.WebView . Вместо этого разработчикам следует использовать библиотеки Android, такие как Google Sign-In для Android или AppAuth для Android от OpenID Foundation.

Веб-разработчики могут столкнуться с этой ошибкой, когда приложение Android открывает общую веб-ссылку во встроенном пользовательском агенте, а пользователь переходит с вашего сайта на конечную точку авторизации Google OAuth 2.0. Разработчикам следует разрешить открытие общих ссылок в обработчике ссылок по умолчанию операционной системы, который включает как обработчики ссылок приложений Android, так и приложение браузера по умолчанию. Библиотека Android Custom Tabs также поддерживается.

iOS

Разработчики приложений для iOS и macOS могут столкнуться с этой ошибкой при открытии запросов на авторизацию вWKWebView . Вместо этого разработчикам следует использовать библиотеки iOS, такие как Google Sign-In для iOS или AppAuth от OpenID Foundation для iOS .

Веб-разработчики могут столкнуться с этой ошибкой, когда приложение iOS или macOS открывает общую веб-ссылку во встроенном пользовательском агенте, а пользователь переходит с вашего сайта на конечную точку авторизации Google OAuth 2.0. Разработчикам следует разрешить открытие общих ссылок в обработчике ссылок по умолчанию операционной системы, который включает как обработчики универсальных ссылок , так и приложение браузера по умолчанию. БиблиотекаSFSafariViewController также поддерживается.

org_internal

Идентификатор клиента OAuth в запросе является частью проекта, ограничивающего доступ к аккаунтам Google в конкретной организации Google Cloud . Подробнее об этом параметре конфигурации см. в разделе «Тип пользователя» справочной статьи «Настройка экрана согласия OAuth».

invalid_client

Источник, из которого был сделан запрос, не авторизован для этого клиента. См. origin_mismatch .

deleted_client

Клиент OAuth, использованный для выполнения запроса, был удалён. Удаление может быть выполнено вручную или автоматически в случае неиспользуемых клиентов . Удалённые клиенты могут быть восстановлены в течение 30 дней с момента удаления. Подробнее .

invalid_grant

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

origin_mismatch

Схема, домен и/или порт JavaScript, инициирующего запрос авторизации, могут не соответствовать авторизованному URI источника JavaScript, зарегистрированному для идентификатора клиента OAuth. Проверьте авторизованные источники JavaScript в .

redirect_uri_mismatch

redirect_uri переданный в запросе авторизации, не соответствует авторизованному URI перенаправления для идентификатора клиента OAuth. Проверьте авторизованные URI перенаправления в .

Схема, домен и/или порт JavaScript, инициирующего запрос авторизации, могут не соответствовать авторизованному URI источника JavaScript, зарегистрированному для идентификатора клиента OAuth. Проверьте авторизованные источники JavaScript в .

Параметр redirect_uri может ссылаться на внеполосный (OOB) поток OAuth, который устарел и больше не поддерживается. Чтобы обновить интеграцию, обратитесь к руководству по миграции .

invalid_request

С вашим запросом возникла ошибка. Это может быть связано с рядом причин:

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

Шаг 3: Обработка ответа сервера OAuth 2.0

Конечные точки OAuth 2.0

Сервер OAuth 2.0 отправляет ответ на redirect_uri , указанный в вашем запросе токена доступа.

Если пользователь одобряет запрос, ответ содержит токен доступа. Если пользователь не одобряет запрос, ответ содержит сообщение об ошибке. Токен доступа или сообщение об ошибке возвращается в хэш-фрагменте URI перенаправления, как показано ниже:

  • Ответ токена доступа:

    https://oauth2.example.com/callback#access_token=4/P7q7W91&token_type=Bearer&expires_in=3600

    Помимо параметра access_token , строка фрагмента также содержит параметр token_type , который всегда имеет значение Bearer , и параметр expires_in , который определяет время жизни токена в секундах. Если в запросе токена доступа был указан параметр state , его значение также включается в ответ.

  • Ответ об ошибке:
    https://oauth2.example.com/callback#error=access_denied

Пример ответа сервера OAuth 2.0

Вы можете протестировать этот процесс, нажав на следующий пример URL-адреса, который запрашивает доступ только для чтения для просмотра метаданных файлов на вашем Google Диске и доступ только для чтения для просмотра событий вашего Google Календаря:

https://accounts.google.com/o/oauth2/v2/auth?
 scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly%20https%3A//www.googleapis.com/auth/calendar.readonly&
 include_granted_scopes=true&
 response_type=token&
 state=state_parameter_passthrough_value&
 redirect_uri=https%3A//oauth2.example.com/code&
 client_id=client_id

После завершения процесса OAuth 2.0 вы будете перенаправлены на http://localhost/oauth2callback . Этот URL-адрес выдаст ошибку 404 NOT FOUND если только ваш локальный компьютер случайно не обслуживает файл по этому адресу. Следующий шаг предоставляет более подробную информацию об информации, возвращаемой в URI при перенаправлении пользователя обратно в ваше приложение.

Шаг 4: Проверьте, какие области действия предоставлены пользователями

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

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

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

Конечные точки OAuth 2.0

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

Например, следующий пример ответа токена доступа указывает, что пользователь предоставил вашему приложению доступ только для чтения к действиям Диска и событиям Календаря:

  {
    "access_token": "1/fFAGRNJru1FTz70BzhT3Zg",
    "expires_in": 3920,
    "token_type": "Bearer",
    "scope": "https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly",
    "refresh_token": "1//xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
  }

Вызов API Google

Конечные точки OAuth 2.0

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

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

Примеры HTTP GET

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

GET /drive/v2/files HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer access_token

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

GET https://www.googleapis.com/drive/v2/files?access_token=access_token

примеры curl

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

curl -H "Authorization: Bearer access_token" https://www.googleapis.com/drive/v2/files

Или, в качестве альтернативы, параметр строки запроса:

curl https://www.googleapis.com/drive/v2/files?access_token=access_token

Пример кода JavaScript

Приведённый ниже фрагмент кода демонстрирует, как использовать CORS (Cross-origin resource sharing) для отправки запроса к API Google. В этом примере не используется клиентская библиотека Google API для JavaScript. Однако, даже если вы не используете клиентскую библиотеку, руководство по поддержке CORS в документации к этой библиотеке, вероятно, поможет вам лучше понять эти запросы.

В этом фрагменте кода переменная access_token представляет собой токен, полученный вами для выполнения API-запросов от имени авторизованного пользователя. Полный пример демонстрирует, как сохранить этот токен в локальном хранилище браузера и извлечь его при выполнении API-запроса.

var xhr = new XMLHttpRequest();
xhr.open('GET',
    'https://www.googleapis.com/drive/v3/about?fields=user&' +
    'access_token=' + params['access_token']);
xhr.onreadystatechange = function (e) {
  console.log(xhr.response);
};
xhr.send(null);

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

Конечные точки OAuth 2.0

В этом примере кода показано, как выполнить процедуру OAuth 2.0 в JavaScript без использования клиентской библиотеки Google API для JavaScript. Код предназначен для HTML-страницы с кнопкой для попытки API-запроса. При нажатии кнопки код проверяет, сохранил ли страница токен доступа API в локальном хранилище браузера. Если да, то выполняется API-запрос. В противном случае инициируется процедура OAuth 2.0.

Для потока OAuth 2.0 страница выполняет следующие шаги:

  1. Он направляет пользователя на сервер OAuth 2.0 Google, который запрашивает доступ к областям https://www.googleapis.com/auth/drive.metadata.readonly и https://www.googleapis.com/auth/calendar.readonly .
  2. После предоставления (или запрета) доступа к одной или нескольким запрошенным областям пользователь перенаправляется на исходную страницу, которая анализирует токен доступа из строки идентификатора фрагмента.
  3. На странице проверяется, к каким областям доступа пользователь предоставил доступ приложению.
  4. Если пользователь предоставил доступ к запрошенным scope(), страница использует токен доступа для выполнения запроса API-примера.

    Запрос API вызывает метод about.get API Диска для получения информации об учетной записи авторизованного пользователя Google Диска.

  5. Если запрос выполнен успешно, ответ API регистрируется в консоли отладки браузера.

Вы можете отозвать доступ к приложению на странице «Разрешения » вашего аккаунта Google. Приложение будет указано как «OAuth 2.0 Demo for Google API Docs» .

Для локального запуска этого кода необходимо задать значения переменных YOUR_CLIENT_ID и YOUR_REDIRECT_URI , соответствующие вашим учётным данным авторизации . Переменная YOUR_REDIRECT_URI должна быть установлена на тот же URL-адрес, по которому отображается страница. Значение должно точно соответствовать одному из разрешённых URI перенаправления для клиента OAuth 2.0, настроенных в Если это значение не соответствует авторизованному URI, вы получите ошибку redirect_uri_mismatch . В вашем проекте также должен быть включен соответствующий API для этого запроса.

<html><head></head><body>
<script>
  var YOUR_CLIENT_ID = 'REPLACE_THIS_VALUE';
  var YOUR_REDIRECT_URI = 'REPLACE_THIS_VALUE';

  // Parse query string to see if page request is coming from OAuth 2.0 server.
  var fragmentString = location.hash.substring(1);
  var params = {};
  var regex = /([^&=]+)=([^&]*)/g, m;
  while (m = regex.exec(fragmentString)) {
    params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
  }
  if (Object.keys(params).length > 0 && params['state']) {
    if (params['state'] == localStorage.getItem('state')) {
      localStorage.setItem('oauth2-test-params', JSON.stringify(params) );

      trySampleRequest();
    } else {
      console.log('State mismatch. Possible CSRF attack');
    }
  }

  // Function to generate a random state value
  function generateCryptoRandomState() {
    const randomValues = new Uint32Array(2);
    window.crypto.getRandomValues(randomValues);

    // Encode as UTF-8
    const utf8Encoder = new TextEncoder();
    const utf8Array = utf8Encoder.encode(
      String.fromCharCode.apply(null, randomValues)
    );

    // Base64 encode the UTF-8 data
    return btoa(String.fromCharCode.apply(null, utf8Array))
      .replace(/\+/g, '-')
      .replace(/\//g, '_')
      .replace(/=+$/, '');
  }

  // If there's an access token, try an API request.
  // Otherwise, start OAuth 2.0 flow.
  function trySampleRequest() {
    var params = JSON.parse(localStorage.getItem('oauth2-test-params'));
    if (params && params['access_token']) { 
      // User authorized the request. Now, check which scopes were granted.
      if (params['scope'].includes('https://www.googleapis.com/auth/drive.metadata.readonly')) {
        // User authorized read-only Drive activity permission.
        // Calling the APIs, etc.
        var xhr = new XMLHttpRequest();
        xhr.open('GET',
          'https://www.googleapis.com/drive/v3/about?fields=user&' +
          'access_token=' + params['access_token']);
        xhr.onreadystatechange = function (e) {
          if (xhr.readyState === 4 && xhr.status === 200) {
            console.log(xhr.response);
          } else if (xhr.readyState === 4 && xhr.status === 401) {
            // Token invalid, so prompt for user permission.
            oauth2SignIn();
          }
        };
        xhr.send(null);
      }
      else {
        // User didn't authorize read-only Drive activity permission.
        // Update UX and application accordingly
        console.log('User did not authorize read-only Drive activity permission.');
      }

      // Check if user authorized Calendar read permission.
      if (params['scope'].includes('https://www.googleapis.com/auth/calendar.readonly')) {
        // User authorized Calendar read permission.
        // Calling the APIs, etc.
        console.log('User authorized Calendar read permission.');
      }
      else {
        // User didn't authorize Calendar read permission.
        // Update UX and application accordingly
        console.log('User did not authorize Calendar read permission.');
      } 
    } else {
      oauth2SignIn();
    }
  }

  /*
   * Create form to request access token from Google's OAuth 2.0 server.
   */
  function oauth2SignIn() {
    // create random state value and store in local storage
    var state = generateCryptoRandomState();
    localStorage.setItem('state', state);

    // Google's OAuth 2.0 endpoint for requesting an access token
    var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';

    // Create element to open OAuth 2.0 endpoint in new window.
    var form = document.createElement('form');
    form.setAttribute('method', 'GET'); // Send as a GET request.
    form.setAttribute('action', oauth2Endpoint);

    // Parameters to pass to OAuth 2.0 endpoint.
    var params = {'client_id': YOUR_CLIENT_ID,
                  'redirect_uri': YOUR_REDIRECT_URI,
                  'scope': 'https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/calendar.readonly',
                  'state': state,
                  'include_granted_scopes': 'true',
                  'response_type': 'token'};

    // Add form parameters as hidden input values.
    for (var p in params) {
      var input = document.createElement('input');
      input.setAttribute('type', 'hidden');
      input.setAttribute('name', p);
      input.setAttribute('value', params[p]);
      form.appendChild(input);
    }

    // Add form to page and submit it to open the OAuth 2.0 endpoint.
    document.body.appendChild(form);
    form.submit();
  }
</script>

<button onclick="trySampleRequest();">Try sample request</button>
</body></html>

Правила проверки происхождения JavaScript

Google применяет следующие правила валидации к источникам JavaScript, чтобы помочь разработчикам обеспечить безопасность своих приложений. Ваши источники JavaScript должны соответствовать этим правилам. Определение домена, хоста и схемы, упомянутых ниже, см. в разделе 3 RFC 3986.

Правила проверки
Схема

Источники JavaScript должны использовать протокол HTTPS, а не обычный HTTP. URI локального хоста (включая URI IP-адресов локального хоста) не подпадают под это правило.

Хозяин

В качестве хостов не могут использоваться обычные IP-адреса. IP-адреса локальных хостов не подпадают под это правило.

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

    Источники JavaScript не могут содержать подкомпонент userinfo.

    Путь

    Источники JavaScript не могут содержать компонент пути.

    Запрос

    Источники JavaScript не могут содержать компонент запроса.

    Фрагмент

    Источники JavaScript не могут содержать компонент фрагмента.

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

    Инкрементная авторизация

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

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

    В этом случае во время входа в систему приложение может запросить области действия openid и profile для выполнения базового входа, а затем позже запросить область действия https://www.googleapis.com/auth/drive.file во время первого запроса, чтобы сохранить микс.

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

    • Токен можно использовать для доступа к ресурсам, соответствующим любой из областей, включенных в новую комбинированную авторизацию.
    • При использовании токена обновления для комбинированной авторизации с целью получения токена доступа токен доступа представляет собой комбинированную авторизацию и может использоваться для любого из значений scope , включенных в ответ.
    • Объединенная авторизация включает все области действия, предоставленные пользователем проекту API, даже если эти разрешения были запрошены у разных клиентов. Например, если пользователь предоставил доступ к одной области действия с помощью настольного клиента приложения, а затем предоставил другую область действия тому же приложению через мобильный клиент, объединенная авторизация будет включать обе области действия.
    • Если вы отзовете токен, представляющий комбинированную авторизацию, доступ ко всем областям этой авторизации от имени связанного пользователя будет отозван одновременно.

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

    Конечные точки OAuth 2.0

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

    Следующий фрагмент кода демонстрирует, как это сделать. Предполагается, что вы сохранили области действия, для которых ваш токен доступа действителен, в локальном хранилище браузера. ( Полный пример кода сохраняет список областей действия, для которых токен доступа действителен, путём установки свойства oauth2-test-params.scope в локальном хранилище браузера.)

    Этот фрагмент кода сравнивает области действия, для которых токен доступа действителен, с областью действия, которую вы хотите использовать для конкретного запроса. Если токен доступа не охватывает эту область, запускается процесс OAuth 2.0. Здесь функция oauth2SignIn аналогична той, что была представлена на шаге 2 (и представлена далее в полном примере ).

    var SCOPE = 'https://www.googleapis.com/auth/drive.metadata.readonly';
    var params = JSON.parse(localStorage.getItem('oauth2-test-params'));
    
    var current_scope_granted = false;
    if (params.hasOwnProperty('scope')) {
      var scopes = params['scope'].split(' ');
      for (var s = 0; s < scopes.length; s++) {
        if (SCOPE == scopes[s]) {
          current_scope_granted = true;
        }
      }
    }
    
    if (!current_scope_granted) {
      oauth2SignIn(); // This function is defined elsewhere in this document.
    } else {
      // Since you already have access, you can proceed with the API request.
    }

    Отзыв токена

    В некоторых случаях пользователь может захотеть отозвать доступ, предоставленный приложению. Это можно сделать в настройках учётной записи . Дополнительную информацию см. в разделе «Удаление доступа к сайту или приложению» документа поддержки «Сторонние сайты и приложения, имеющие доступ к вашей учётной записи» .

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

    Конечные точки OAuth 2.0

    Чтобы программно отозвать токен, ваше приложение отправляет запрос на 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 вместе с кодом ошибки.

    В следующем фрагменте JavaScript показано, как отозвать токен в JavaScript без использования клиентской библиотеки API Google для JavaScript. Поскольку конечная точка OAuth 2.0 Google для отзыва токенов не поддерживает Cross-origin Resource Sharing (CORS), код создаёт форму и отправляет её в конечную точку вместо использования метода XMLHttpRequest() для отправки запроса.

    function revokeAccess(accessToken) {
      // Google's OAuth 2.0 endpoint for revoking access tokens.
      var revokeTokenEndpoint = 'https://oauth2.googleapis.com/revoke';
    
      // Create <form> element to use to POST data to the OAuth 2.0 endpoint.
      var form = document.createElement('form');
      form.setAttribute('method', 'post');
      form.setAttribute('action', revokeTokenEndpoint);
    
      // Add access token to the form so it is set as value of 'token' parameter.
      // This corresponds to the sample curl request, where the URL is:
      //      https://oauth2.googleapis.com/revoke?token={token}
      var tokenField = document.createElement('input');
      tokenField.setAttribute('type', 'hidden');
      tokenField.setAttribute('name', 'token');
      tokenField.setAttribute('value', accessToken);
      form.appendChild(tokenField);
    
      // Add form to page and submit it to actually revoke the token.
      document.body.appendChild(form);
      form.submit();
    }

    Реализация защиты между аккаунтами

    Дополнительным шагом для защиты аккаунтов пользователей является реализация Cross-Account Protection с помощью сервиса Cross-Account Protection от Google. Этот сервис позволяет подписаться на уведомления о событиях безопасности, которые предоставляют вашему приложению информацию о важных изменениях в аккаунте пользователя. Затем вы можете использовать эту информацию для принятия мер в зависимости от того, как вы решите реагировать на события.

    Вот некоторые примеры типов событий, отправляемых в ваше приложение службой Cross-Account Protection от 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

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