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. 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, должно иметь учетные данные авторизации, которые идентифицируют приложение на сервере Google OAuth 2.0. Следующие шаги объясняют, как создать учетные данные для вашего проекта. Затем ваши приложения смогут использовать учетные данные для доступа к API, которые вы включили для этого проекта.

  1. Go to the Credentials page.
  2. Нажмите Создать учетные данные > Идентификатор клиента OAuth .
  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 от имени пользователя. Ваше приложение должно получить это согласие, прежде чем оно сможет выполнить запрос Google API, требующий авторизации пользователя.

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

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

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

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

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

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

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

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

Определяет, куда сервер API перенаправляет пользователя после того, как пользователь завершает процесс авторизации. Значение должно точно совпадать с одним из авторизованных URI перенаправления для клиента OAuth 2.0, который вы настроили в настройках вашего клиента. API ConsoleCredentials page. Если это значение не соответствует авторизованному 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 или другое значение, фиксирующее состояние клиента, вы можете проверить ответ, чтобы дополнительно убедиться, что запрос и ответ исходят из одного и того же браузера, обеспечивая защиту от таких атак, как межсайтовые атаки. запросить подделку . См. документацию OpenID Connect , где приведен пример создания и подтверждения токена state .

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

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

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

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

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

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

Ошибки

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

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 OpenID Foundation для Android .

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

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 .

invalid_grant

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

origin_mismatch

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

redirect_uri_mismatch

redirect_uri переданный в запросе авторизации, не соответствует авторизованному URI перенаправления для идентификатора клиента OAuth. Просмотрите разрешенные URI перенаправления в Google API Console Credentials page.

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

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

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

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

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

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

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

  {
    "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-заголовок, поскольку строки запроса обычно видны в журналах сервера. В большинстве случаев вы можете использовать клиентскую библиотеку для настройки вызовов Google API (например, при вызове Drive Files 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 (совместное использование ресурсов между источниками) для отправки запроса в API Google. В этом примере не используется клиентская библиотека API Google для 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 без использования клиентской библиотеки API Google для 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. Если пользователь предоставил доступ к запрошенным областям(), страница использует токен доступа для выполнения примера запроса API.

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

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

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

Чтобы запустить этот код локально, вам необходимо установить значения для переменных YOUR_CLIENT_ID и YOUR_REDIRECT_URI , соответствующие вашим учетным данным для авторизации . В переменной YOUR_REDIRECT_URI должен быть установлен тот же URL-адрес, по которому обслуживается страница. Значение должно точно совпадать с одним из авторизованных URI перенаправления для клиента OAuth 2.0, который вы настроили в разделе API Console Credentials page. Если это значение не соответствует авторизованному 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-кодирования, состоящей из знака процента, за которым следуют две шестнадцатеричные цифры).
    • Нулевые символы (закодированный NULL-символ, например, %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 в свой запрос к серверу OAuth 2.0 Google .

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

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

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

    См . страницу «Защита учетных записей пользователей с помощью защиты нескольких учетных записей» для получения дополнительной информации о том, как реализовать защиту между учетными записями, а также для получения полного списка доступных событий.