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

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

Обычно приложение использует учетную запись службы, когда приложение использует API Google для работы со своими собственными данными, а не с данными пользователя. Например, приложение, которое использует Google Cloud Datastore для сохранения данных, будет использовать учетную запись службы для аутентификации своих вызовов к API Google Cloud Datastore.

Администраторы домена Google Workspace также могут предоставить учетным записям служб права доступа на уровне домена для доступа к пользовательским данным от имени пользователей в домене.

В этом документе описывается, как приложение может выполнить поток OAuth 2.0 между серверами, используя клиентскую библиотеку API Google (рекомендуется) или HTTP.

Обзор

Для поддержки межсерверного взаимодействия сначала создайте сервисную учетную запись для своего проекта в API Console. Если вы хотите получить доступ к пользовательским данным пользователей в вашей учетной записи Google Workspace, делегируйте сервисному аккаунту доступ на уровне домена.

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

Наконец, ваше приложение может использовать токен доступа для вызова API Google.

Создание учетной записи службы

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

Если ваше приложение работает на Google App Engine, учетная запись службы настраивается автоматически при создании проекта.

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

Если ваше приложение не работает в Google App Engine или Google Compute Engine, вам необходимо получить эти учетные данные в Google API Console. Чтобы создать учетные данные сервисной учетной записи или просмотреть общедоступные учетные данные, которые вы уже создали, выполните следующие действия:

首先,创建一个服务帐户:

  1. 打开 Service accounts page
  2. If prompted, select a project, or create a new one.
  3. 单击创建服务帐户
  4. Service account details下,键入服务帐户的名称、ID 和描述,然后点击Create and continue
  5. 可选:在Grant this service account access to project下,选择要授予服务帐户的 IAM 角色。
  6. 单击继续
  7. 可选:在Grant users access to this service account下,添加允许使用和管理服务帐户的用户或组。
  8. 单击完成

接下来,创建一个服务帐户密钥:

  1. 单击您创建的服务帐户的电子邮件地址。
  2. 单击密钥选项卡。
  3. 添加密钥下拉列表中,选择创建新密钥
  4. 单击创建

您的新公钥/私钥对已生成并下载到您的机器上;它作为私钥的唯一副本。您有责任安全地存储它。如果您丢失了这个密钥对,您将需要生成一个新的。

Вы можете вернуться в API Console в любое время для просмотра адреса электронной почты, отпечатков открытых ключей и другой информации или для создания дополнительных пар открытого/закрытого ключей. Дополнительные сведения об учетных данных учетной записи службы см. API Console, см. Учетные записи служб в разделе API Consoleфайл справки.

Запишите адрес электронной почты учетной записи службы и сохраните файл закрытого ключа учетной записи службы в месте, доступном для вашего приложения. Они нужны вашему приложению для авторизованных вызовов API.

Делегирование полномочий на уровне домена сервисному аккаунту

Используя учетную запись Google Workspace, администратор Workspace организации может разрешить приложению доступ к данным пользователя Workspace от имени пользователей в домене Google Workspace. Например, приложение, которое использует API Календаря Google для добавления событий в календари всех пользователей в домене Google Workspace, будет использовать учетную запись службы для доступа к API Календаря Google от имени пользователей. Разрешение учетной записи службы на доступ к данным от имени пользователей в домене иногда называют «делегированием полномочий на уровне домена» учетной записи службы.

Чтобы делегировать полномочия на уровне домена сервисному аккаунту, суперадминистратор домена Google Workspace должен выполнить следующие действия:

  1. В консоли администратора вашего домена Google Workspace выберите Главное > Безопасность > Доступ и контроль данных > Элементы управления API .
  2. В области делегирования на уровне домена выберите «Управление делегированием на уровне домена» .
  3. Нажмите Добавить новый .
  4. В поле «Идентификатор клиента» введите идентификатор клиента сервисного аккаунта. Идентификатор клиента вашего сервисного аккаунта можно найти в Service accounts page.
  5. В поле «Области OAuth» (разделенные запятыми) введите список областей, к которым вашему приложению должен быть предоставлен доступ. Например, если вашему приложению требуется полный доступ на уровне домена к API Google Диска и API Календаря Google, введите: https://www.googleapis.com/auth/drive, https://www.googleapis.com/auth. /календарь .
  6. Нажмите «Авторизовать» .

Теперь ваше приложение имеет право выполнять вызовы API от имени пользователей в вашем домене Workspace (чтобы «выдавать себя за» пользователей). Когда вы готовитесь к выполнению этих делегированных вызовов API, вы явно указываете пользователя, которого нужно олицетворять.

Подготовка к делегированному вызову API

Ява

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

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.services.sqladmin.SQLAdminScopes;

// ...

GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json"))
    .createScoped(Collections.singleton(SQLAdminScopes.SQLSERVICE_ADMIN));

Если вы разрабатываете приложение на Google Cloud Platform, вместо этого вы можете использовать учетные данные приложения по умолчанию , что может упростить процесс.

Делегируйте полномочия на уровне домена

Если вы делегировали доступ к учетной записи службы на уровне домена и хотите выдать себя за учетную запись пользователя, укажите адрес электронной почты учетной записи пользователя с помощью метода createDelegated объекта GoogleCredential . Например:

GoogleCredential credential = GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json"))
    .createScoped(Collections.singleton(SQLAdminScopes.SQLSERVICE_ADMIN))
    .createDelegated("workspace-user@example.com");

В приведенном выше коде объект GoogleCredential используется для вызова метода createDelegated() . Аргументом метода createDelegated() должен быть пользователь, принадлежащий вашей учетной записи Workspace. Ваш код, отправляющий запрос, будет использовать эти учетные данные для вызова API Google с использованием вашей учетной записи службы.

Питон

После того, как вы получите адрес электронной почты клиента и закрытый ключ от API Consoleвоспользуйтесь клиентской библиотекой API Google для Python , чтобы выполнить следующие шаги:

  1. Создайте объект Credentials из учетных данных учетной записи службы и областей, к которым вашему приложению необходим доступ. Например:
    from google.oauth2 import service_account
    
    SCOPES = ['https://www.googleapis.com/auth/sqlservice.admin']
    SERVICE_ACCOUNT_FILE = '/path/to/service.json'
    
    credentials = service_account.Credentials.from_service_account_file(
            SERVICE_ACCOUNT_FILE, scopes=SCOPES)

    Если вы разрабатываете приложение на Google Cloud Platform, вместо этого вы можете использовать учетные данные приложения по умолчанию , что может упростить процесс.

  2. Делегируйте полномочия на уровне домена

    Если вы делегировали доступ к учетной записи службы на уровне домена и хотите выдать себя за учетную запись пользователя, используйте метод with_subject существующего объекта ServiceAccountCredentials . Например:

    delegated_credentials = credentials.with_subject('user@example.org')

Используйте объект Credentials для вызова API Google в вашем приложении.

HTTP/REST

После того, как вы получите идентификатор клиента и закрытый ключ от API Console, ваше приложение должно выполнить следующие шаги:

  1. Создайте веб-токен JSON (JWT, произносится как «jot»), который включает заголовок, набор утверждений и подпись.
  2. Запросите токен доступа у сервера авторизации Google OAuth 2.0.
  3. Обработайте ответ JSON, который возвращает сервер авторизации.

В следующих разделах описывается, как выполнить эти шаги.

Если ответ содержит токен доступа, вы можете использовать его для вызова API Google . (Если ответ не включает токен доступа, ваш запрос JWT и токена может быть неправильно сформирован или учетная запись службы может не иметь разрешения на доступ к запрошенным областям.)

По истечении срока действия токена доступа ваше приложение генерирует еще один JWT, подписывает его и запрашивает еще один токен доступа.

Ваше серверное приложение использует JWT для запроса токена у Google.                   Сервер авторизации затем использует токен для вызова конечной точки Google API. Нет                   участвует конечный пользователь.

Оставшаяся часть этого раздела описывает особенности создания JWT, подписания JWT, формирования запроса токена доступа и обработки ответа.

Создание JWT

JWT состоит из трех частей: заголовка, набора утверждений и подписи. Заголовок и набор утверждений являются объектами JSON. Эти объекты JSON сериализуются в байты UTF-8, а затем кодируются с использованием кодировки Base64url. Такое кодирование обеспечивает устойчивость к изменениям кодирования из-за повторяющихся операций кодирования. Заголовок, набор утверждений и подпись объединяются с помощью символа точки ( . ).

JWT состоит из следующего:

{Base64url encoded header}.{Base64url encoded claim set}.{Base64url encoded signature}

Базовая строка подписи выглядит следующим образом:

{Base64url encoded header}.{Base64url encoded claim set}
Формирование заголовка JWT

Заголовок состоит из трех полей, которые указывают алгоритм подписи, формат утверждения и [идентификатор ключа ключа учетной записи службы](https://cloud.google.com/iam/docs/reference/rest/v1/ project.serviceAccounts.keys), который использовался для подписи JWT. Алгоритм и формат являются обязательными, каждое поле имеет только одно значение. По мере введения дополнительных алгоритмов и форматов этот заголовок будет меняться соответствующим образом. Идентификатор ключа является необязательным, и если указан неправильный идентификатор ключа, GCP попытается проверить все ключи, связанные с учетной записью службы, и отклонит токен, если действительный ключ не найден. Google оставляет за собой право отклонять токены с неправильными идентификаторами ключей в будущем.

Учетные записи служб используют алгоритм RSA SHA-256 и формат токена JWT. В результате представление заголовка в формате JSON выглядит следующим образом:

{"alg":"RS256","typ":"JWT", "kid":"370ab79b4513eb9bad7c9bd16a95cb76b5b2a56a"}

Представление Base64url этого выглядит следующим образом:

          eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsICJraWQiOiIzNzBhYjc5YjQ1MTNlYjliYWQ3YzliZDE2YTk1Y2I3NmI1YjJhNTZhIn0=
Формирование набора утверждений JWT

Набор утверждений JWT содержит информацию о JWT, включая запрашиваемые разрешения (области), цель токена, эмитента, время выдачи токена и срок действия токена. Большинство полей являются обязательными. Как и заголовок JWT, набор утверждений JWT является объектом JSON и используется при вычислении подписи.

Обязательные претензии

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

Имя Описание
iss Адрес электронной почты учетной записи службы.
scope Список разрешений, разделенных пробелами, которые запрашивает приложение.
aud Дескриптор предполагаемой цели утверждения. При запросе токена доступа это значение всегда равно https://oauth2.googleapis.com/token .
exp Время истечения срока действия утверждения в секундах с 00:00:00 UTC 1 января 1970 г. Это значение имеет максимум 1 час после выданного времени.
iat Время выдачи утверждения в секундах с 00:00:00 UTC 1 января 1970 года.

JSON-представление обязательных полей в наборе утверждений JWT показано ниже:

{
  "iss": "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com",
  "scope": "https://www.googleapis.com/auth/devstorage.read_only",
  "aud": "https://oauth2.googleapis.com/token",
  "exp": 1328554385,
  "iat": 1328550785
}
Дополнительные претензии

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

Чтобы получить токен доступа, который предоставляет приложению делегированный доступ к ресурсу, включите адрес электронной почты пользователя в набор утверждений JWT в качестве значения sub .

Имя Описание
sub Адрес электронной почты пользователя, для которого приложение запрашивает делегированный доступ.

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

Ниже показан пример набора утверждений JWT, включающего sub :

{
  "iss": "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com",
  "sub": "some.user@example.com",
  "scope": "https://www.googleapis.com/auth/prediction",
  "aud": "https://oauth2.googleapis.com/token",
  "exp": 1328554385,
  "iat": 1328550785
}
Кодирование набора утверждений JWT

Как и заголовок JWT, набор утверждений JWT должен быть сериализован в кодировку UTF-8 и Base64url. Ниже приведен пример представления JSON набора утверждений JWT:

{
  "iss": "761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com",
  "scope": "https://www.googleapis.com/auth/prediction",
  "aud": "https://oauth2.googleapis.com/token",
  "exp": 1328554385,
  "iat": 1328550785
}
Вычисление подписи

Веб-подпись JSON (JWS) — это спецификация, определяющая механизм создания подписи для JWT. Входными данными для подписи является массив байтов следующего содержания:

{Base64url encoded header}.{Base64url encoded claim set}

Алгоритм подписи в заголовке JWT должен использоваться при вычислении подписи. Единственный алгоритм подписи, поддерживаемый сервером авторизации Google OAuth 2.0, — это RSA с использованием алгоритма хеширования SHA-256. Это выражается как RS256 в поле alg заголовка JWT.

Подпишите представление входных данных в формате UTF-8 с помощью SHA256withRSA (также известного как RSASSA-PKCS1-V1_5-SIGN с хеш-функцией SHA-256) с помощью закрытого ключа, полученного из Google API Console. Результатом будет массив байтов.

В этом случае подпись должна быть закодирована Base64url. Заголовок, набор утверждений и подпись объединяются с помощью символа точки ( . ). Результатом является JWT. Должно быть следующее (для ясности добавлены переносы строк):

{Base64url encoded header}.
{Base64url encoded claim set}.
{Base64url encoded signature}

Ниже приведен пример JWT перед кодировкой Base64url:

{"alg":"RS256","typ":"JWT"}.
{
"iss":"761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com",
"scope":"https://www.googleapis.com/auth/prediction",
"aud":"https://oauth2.googleapis.com/token",
"exp":1328554385,
"iat":1328550785
}.
[signature bytes]

Ниже приведен пример JWT, который был подписан и готов к передаче:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL29hdXRoMi92NC90b2tlbiIsImV4cCI6MTMyODU1NDM4NSwiaWF0IjoxMzI4NTUwNzg1fQ.UFUt59SUM2_AW4cRU8Y0BYVQsNTo4n7AFsNrqOpYiICDu37vVt-tw38UKzjmUKtcRsLLjrR3gFW3dNDMx_pL9DVjgVHDdYirtrCekUHOYoa1CMR66nxep5q5cBQ4y4u2kIgSvChCTc9pmLLNoIem-ruCecAJYgI9Ks7pTnW1gkOKs0x3YpiLpzplVHAkkHztaXiJdtpBcY1OXyo6jTQCa3Lk2Q3va1dPkh_d--GU2M5flgd8xNBPYw4vxyt0mP59XZlHMpztZt0soSgObf7G3GXArreF_6tpbFsS3z2t5zkEiHuWJXpzcYr5zWTRPDEHsejeBSG8EgpLDce2380ROQ

Выполнение запроса токена доступа

После создания подписанного JWT приложение может использовать его для запроса токена доступа. Этот запрос токена доступа представляет собой запрос HTTPS POST , а тело закодировано в URL-адресе. URL-адрес показан ниже:

https://oauth2.googleapis.com/token

В запросе HTTPS POST необходимы следующие параметры:

Имя Описание
grant_type Используйте следующую строку, при необходимости закодированную в URL-адресе: urn:ietf:params:oauth:grant-type:jwt-bearer
assertion JWT, включая подпись.

Ниже приведен необработанный дамп запроса HTTPS POST , используемого в запросе токена доступа:

POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsImV4cCI6MTMyODU3MzM4MSwiaWF0IjoxMzI4NTY5NzgxfQ.ixOUGehweEVX_UKXv5BbbwVEdcz6AYS-6uQV6fGorGKrHf3LIJnyREw9evE-gs2bmMaQI5_UbabvI4k-mQE4kBqtmSpTzxYBL1TCd7Kv5nTZoUC1CmwmWCFqT9RE6D7XSgPUh_jF1qskLa2w0rxMSjwruNKbysgRNctZPln7cqQ

Ниже приведен тот же запрос с использованием curl :

curl -d 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiI3NjEzMjY3OTgwNjktcjVtbGpsbG4xcmQ0bHJiaGc3NWVmZ2lncDM2bTc4ajVAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJzY29wZSI6Imh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvcHJlZGljdGlvbiIsImF1ZCI6Imh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi90b2tlbiIsImV4cCI6MTMyODU3MzM4MSwiaWF0IjoxMzI4NTY5NzgxfQ.RZVpzWygMLuL-n3GwjW1_yhQhrqDacyvaXkuf8HcJl8EtXYjGjMaW5oiM5cgAaIorrqgYlp4DPF_GuncFqg9uDZrx7pMmCZ_yHfxhSCXru3gbXrZvAIicNQZMFxrEEn4REVuq7DjkTMyCMGCY1dpMa8aWfTQFt3Eh7smLchaZsU
' https://oauth2.googleapis.com/token

Обработка ответа

Если запрос JWT и токена доступа сформирован правильно и учетная запись службы имеет разрешение на выполнение операции, то ответ JSON от сервера авторизации включает токен доступа. Ниже приведен пример ответа:

{
  "access_token": "1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M",
  "scope": "https://www.googleapis.com/auth/prediction"
  "token_type": "Bearer",
  "expires_in": 3600
}

Токены доступа можно повторно использовать в течение периода времени, указанного значением expires_in .

Вызов API Google

Ява

Используйте объект GoogleCredential для вызова API Google, выполнив следующие шаги:

  1. Создайте объект службы для API, который вы хотите вызвать, используя объект GoogleCredential . Например:
    SQLAdmin sqladmin =
        new SQLAdmin.Builder(httpTransport, JSON_FACTORY, credential).build();
  2. Отправляйте запросы к службе API, используя интерфейс, предоставляемый объектом службы . Например, чтобы перечислить экземпляры баз данных Cloud SQL в проекте capture-example-123:
    SQLAdmin.Instances.List instances =
        sqladmin.instances().list("exciting-example-123").execute();

Питон

Используйте авторизованный объект Credentials для вызова API Google, выполнив следующие шаги:

  1. Создайте объект службы для API, который вы хотите вызвать. Вы создаете объект службы, вызывая функцию build с именем и версией API и авторизованным объектом Credentials . Например, чтобы вызвать версию 1beta3 API администрирования Cloud SQL:
    import googleapiclient.discovery
    
    sqladmin = googleapiclient.discovery.build('sqladmin', 'v1beta3', credentials=credentials)
  2. Отправляйте запросы к службе API, используя интерфейс, предоставляемый объектом службы . Например, чтобы перечислить экземпляры баз данных Cloud SQL в проекте capture-example-123:
    response = sqladmin.instances().list(project='exciting-example-123').execute()

HTTP/REST

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

Срок действия токенов доступа истекает

Срок действия токенов доступа, выданных сервером авторизации Google OAuth 2.0, истекает по истечении срока, указанного в значении expires_in . По истечении срока действия токена доступа приложение должно сгенерировать еще один JWT, подписать его и запросить еще один токен доступа.

Коды ошибок JWT

поле error поле error_description Значение Как решить
unauthorized_client Unauthorized client or scope in request. Если вы пытаетесь использовать делегирование на уровне домена, учетная запись службы не авторизована в консоли администратора домена пользователя.

Убедитесь, что сервисный аккаунт авторизован на странице делегирования на уровне домена консоли администратора для пользователя в sub утверждении (поле).

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

unauthorized_client Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested. Сервисный аккаунт был авторизован с использованием адреса электронной почты клиента, а не идентификатора клиента (числового) в консоли администратора. На странице делегирования на уровне домена в консоли администратора удалите клиент и повторно добавьте его с числовым идентификатором.
access_denied (любое значение) Если вы используете делегирование на уровне домена, одна или несколько запрошенных областей не авторизованы в консоли администратора.

Убедитесь, что сервисный аккаунт авторизован на странице делегирования на уровне домена консоли администратора для пользователя во sub утверждении (поле) и что он включает все области, которые вы запрашиваете в утверждении scope вашего JWT.

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

admin_policy_enforced (любое значение) Аккаунт Google не может авторизовать одну или несколько запрошенных областей в соответствии с правилами администратора Google Workspace.

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

invalid_client (любое значение)

Клиент OAuth или токен JWT недействителен или неправильно настроен.

Подробности смотрите в описании ошибки.

Убедитесь, что токен JWT действителен и содержит правильные утверждения.

Убедитесь, что клиент OAuth и учетная запись службы настроены правильно и что вы используете правильный адрес электронной почты.

Убедитесь, что токен JWT правильный и был выдан для идентификатора клиента в запросе.

invalid_grant Not a valid email. Пользователь не существует. Убедитесь, что адрес электронной почты в sub претензии (поле) указан правильно.
invalid_grant

Invalid JWT: Token must be a short-lived token (60 minutes) and in a reasonable timeframe. Check your 'iat' and 'exp' values and use a clock with skew to account for clock differences between systems.

Обычно это означает, что локальное системное время неверно. Это также может произойти, если в будущем значение exp будет превышать значение iat более чем на 65 минут или значение exp будет ниже значения iat .

Убедитесь, что часы в системе, где генерируется JWT, правильные. При необходимости синхронизируйте свое время с Google NTP .

invalid_grant Invalid JWT Signature.

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

В качестве альтернативы утверждение JWT может быть закодировано неправильно — оно должно быть закодировано в Base64, без символов новой строки или заполнения знаков равенства.

Расшифруйте набор утверждений JWT и убедитесь, что ключ, подписавший утверждение, связан с учетной записью службы.

Попробуйте использовать библиотеку OAuth, предоставленную Google, чтобы убедиться, что JWT генерируется правильно.

invalid_scope Invalid OAuth scope or ID token audience provided. Никакие области не были запрошены (пустой список областей), или одна из запрошенных областей не существует (т. е. недействительна).

Убедитесь, что утверждение scope (поле) JWT заполнено, и сравните содержащиеся в нем области с документированными областями для API, которые вы хотите использовать, чтобы убедиться в отсутствии ошибок или опечаток.

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

disabled_client The OAuth client was disabled. Ключ, используемый для подписи утверждения JWT, отключен.

Перейти к Google API Consoleи в разделе IAM & Admin > Service Accounts включите учетную запись службы, которая содержит «идентификатор ключа», используемый для подписи утверждения.

org_internal This client is restricted to users within its organization. Идентификатор клиента OAuth в запросе является частью проекта, ограничивающего доступ к аккаунтам Google в конкретной организации Google Cloud .

Для аутентификации используйте сервисную учетную запись организации. Подтвердите конфигурацию типа пользователя для вашего приложения OAuth.

Приложение: авторизация сервисного аккаунта без OAuth

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

Если API, который вы хотите вызвать, имеет определение службы, опубликованное в репозитории Google API GitHub , вы можете выполнять авторизованные вызовы API, используя JWT вместо токена доступа. Для этого:

  1. Создайте учетную запись службы, как описано выше. Обязательно сохраните файл JSON, который вы получили при создании учетной записи.
  2. Используя любую стандартную библиотеку JWT, например, найденную в jwt.io , создайте JWT с заголовком и полезной нагрузкой, как показано в следующем примере:
    {
      "alg": "RS256",
      "typ": "JWT",
      "kid": "abcdef1234567890"
    }
    .
    {
      "iss": "123456-compute@developer.gserviceaccount.com",
      "sub": "123456-compute@developer.gserviceaccount.com",
      "aud": "https://firestore.googleapis.com/",
      "iat": 1511900000,
      "exp": 1511903600
    }
    • В поле kid в заголовке укажите идентификатор закрытого ключа вашего сервисного аккаунта. Это значение можно найти в поле private_key_id файла JSON вашего сервисного аккаунта.
    • В полях iss и sub укажите адрес электронной почты вашей учетной записи службы. Это значение можно найти в поле client_email JSON-файла вашего сервисного аккаунта.
    • В поле aud укажите конечную точку API. Например https:// SERVICE .googleapis.com/ .
    • Для поля iat укажите текущее время Unix, а для поля exp укажите время ровно через 3600 секунд, когда истечет срок действия JWT.

Подпишите JWT с помощью RSA-256, используя закрытый ключ, найденный в JSON-файле вашей сервисной учетной записи.

Например:

Ява

Использование google-api-java-client и java-jwt :

GoogleCredential credential =
        GoogleCredential.fromStream(new FileInputStream("MyProject-1234.json"));
PrivateKey privateKey = credential.getServiceAccountPrivateKey();
String privateKeyId = credential.getServiceAccountPrivateKeyId();

long now = System.currentTimeMillis();

try {
    Algorithm algorithm = Algorithm.RSA256(null, privateKey);
    String signedJwt = JWT.create()
        .withKeyId(privateKeyId)
        .withIssuer("123456-compute@developer.gserviceaccount.com")
        .withSubject("123456-compute@developer.gserviceaccount.com")
        .withAudience("https://firestore.googleapis.com/")
        .withIssuedAt(new Date(now))
        .withExpiresAt(new Date(now + 3600 * 1000L))
        .sign(algorithm);
} catch ...

Питон

Использование PyJWT :

iat = time.time()
exp = iat + 3600
payload = {'iss': '123456-compute@developer.gserviceaccount.com',
           'sub': '123456-compute@developer.gserviceaccount.com',
           'aud': 'https://firestore.googleapis.com/',
           'iat': iat,
           'exp': exp}
additional_headers = {'kid': PRIVATE_KEY_ID_FROM_JSON}
signed_jwt = jwt.encode(payload, PRIVATE_KEY_FROM_JSON, headers=additional_headers,
                       algorithm='RS256')
  1. Вызовите API, используя подписанный JWT в качестве токена-носителя:
    GET /v1/projects/abc/databases/123/indexes HTTP/1.1
    Authorization: Bearer SIGNED_JWT
    Host: firestore.googleapis.com

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

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

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