OpenID 连接

Google 的 OpenID Connect 端点已通过 OpenID 认证。

Google 的 OAuth 2.0 API 可用于身份验证和授权。本文档描述了我们用于身份验证的 OAuth 2.0 实现,它符合OpenID Connect规范,并通过了OpenID 认证使用 OAuth 2.0 访问 Google API中的文档也适用于该服务。如果您想以交互方式探索此协议,我们建议您使用Google OAuth 2.0 Playground 。要获得有关Stack Overflow的帮助,请使用“google-oauth”标记您的问题。

设置 OAuth 2.0

在您的应用程序可以使用 Google 的 OAuth 2.0 身份验证系统进行用户登录之前,您必须在 Google API Console 中设置一个项目以获取 OAuth 2.0 凭据,设置重定向 URI,并(可选)自定义您的用户在用户同意屏幕。您还可以使用 API Console 创建服务帐户、启用计费、设置过滤和执行其他任务。有关更多详细信息,请参阅Google API Console帮助

获取 OAuth 2.0 凭据

您需要 OAuth 2.0 凭据(包括客户端 ID 和客户端密码)来验证用户身份并获得对 Google API 的访问权限。

Om de client-ID en het clientgeheim voor een gegeven OAuth 2.0-referentie te bekijken, klikt u op de volgende tekst: Selecteer referentie . Kies in het geopende venster uw project en de gewenste referentie en klik vervolgens op Weergeven .

Of bekijk uw client-ID en clientgeheim vanaf de pagina Referenties op API Console :

  1. Go to the Credentials page.
  2. Klik op de naam van uw inloggegevens of op het potloodpictogram ( ). Uw klant-ID en geheim staan ​​bovenaan de pagina.

设置重定向 URI

您在 API Console 中设置的重定向 URI 确定 Google 将响应发送到您的身份验证请求的位置。

Ga als volgt te werk om de omleidings-URI's voor een bepaalde OAuth 2.0-referentie te maken, te bekijken of te bewerken:

  1. Go to the Credentials page.
  2. Klik in het gedeelte OAuth 2.0-client-ID's van de pagina op een referentie.
  3. Bekijk of bewerk de omleidings-URI's.

Als er geen sectie OAuth 2.0-client-ID's op de pagina Referenties is, heeft uw project geen OAuth-referenties. Klik op Inloggegevens maken om er een te maken.

自定义用户同意屏幕

对于您的用户,OAuth 2.0 身份验证体验包括一个同意屏幕,其中描述了用户发布的信息和适用的条款。例如,当用户登录时,他们可能会被要求授予您的应用访问他们的电子邮件地址和基本帐户信息的权限。您使用您的应用程序在其身份验证请求中包含的scope参数请求访问此信息。您还可以使用范围请求访问其他 Google API。

用户同意屏幕还显示品牌信息,例如您的产品名称、徽标和主页 URL。您可以控制 API Console中的品牌信息。

Om het toestemmingsscherm van uw project in te schakelen:

  1. Open de Consent Screen page in de Google API Console .
  2. If prompted, select a project, or create a new one.
  3. Vul het formulier in en klik op Opslaan .

以下同意对话框显示了当请求中同时存在 OAuth 2.0 和 Google Drive 范围时用户会看到什么。 (此通用对话框是使用Google OAuth 2.0 Playground生成的,因此它不包含将在 API Console中设置的品牌信息。)

同意页面截图

访问服务

Google 和第三方提供了库,您可以使用这些库来处理验证用户和获取对 Google API 的访问权限的许多实施细节。示例包括可用于各种平台的Google 登录Google 客户端库

如果您选择不使用库,请按照本文档其余部分中的说明进行操作,其中描述了作为可用库基础的 HTTP 请求流。

对用户进行身份验证

对用户进行身份验证涉及获取 ID 令牌并对其进行验证。 ID 令牌OpenID Connect的一项标准化功能,旨在用于在 Internet 上共享身份声明。

验证用户和获取 ID 令牌的最常用方法称为“服务器”流程和“隐式”流程。服务器流程允许应用程序的后端服务器使用浏览器或移动设备验证人员的身份。当客户端应用程序(通常是在浏览器中运行的 JavaScript 应用程序)需要直接访问 API 而不是通过其后端服务器时,使用隐式流。

本文档描述了如何执行服务器流程来验证用户。由于在客户端处理和使用令牌的安全风险,隐式流程要复杂得多。如果您需要实现隐式流程,我们强烈建议您使用Google Sign-In

服务器流

确保您在 API Console中设置您的应用程序,以使其能够使用这些协议并对您的用户进行身份验证。当用户尝试使用 Google 登录时,您需要:

  1. 创建防伪状态令牌
  2. 向 Google 发送身份验证请求
  3. 确认防伪状态令牌
  4. 访问令牌和 ID 令牌的交换code
  5. 从 ID 令牌中获取用户信息
  6. 验证用户

1.创建防伪状态令牌

您必须通过防止请求伪造攻击来保护用户的安全。第一步是创建一个唯一的会话令牌,用于保存您的应用程序和用户客户端之间的状态。稍后,您将此唯一会话令牌与 Google OAuth 登录服务返回的身份验证响应相匹配,以验证用户发出请求而不是恶意攻击者。这些令牌通常称为跨站点请求伪造 ( CSRF ) 令牌。

状态令牌的一个不错的选择是使用高质量随机数生成器构造的 30 个左右字符的字符串。另一个是通过使用在后端保密的密钥对一些会话状态变量进行签名而生成的哈希。

以下代码演示了生成唯一会话令牌。

PHP

您必须下载用于 PHP 的 Google API 客户端库才能使用此示例。

// Create a state token to prevent request forgery.
// Store it in the session for later validation.
$state = bin2hex(random_bytes(128/8));
$app['session']->set('state', $state);
// Set the client ID, token state, and application name in the HTML while
// serving it.
return $app['twig']->render('index.html', array(
    'CLIENT_ID' => CLIENT_ID,
    'STATE' => $state,
    'APPLICATION_NAME' => APPLICATION_NAME
));

爪哇

您必须下载适用于 Java 的 Google API 客户端库才能使用此示例。

// Create a state token to prevent request forgery.
// Store it in the session for later validation.
String state = new BigInteger(130, new SecureRandom()).toString(32);
request.session().attribute("state", state);
// Read index.html into memory, and set the client ID,
// token state, and application name in the HTML before serving it.
return new Scanner(new File("index.html"), "UTF-8")
    .useDelimiter("\\A").next()
    .replaceAll("[{]{2}\\s*CLIENT_ID\\s*[}]{2}", CLIENT_ID)
    .replaceAll("[{]{2}\\s*STATE\\s*[}]{2}", state)
    .replaceAll("[{]{2}\\s*APPLICATION_NAME\\s*[}]{2}",
    APPLICATION_NAME);

Python

您必须下载适用于 Python 的 Google API 客户端库才能使用此示例。

# Create a state token to prevent request forgery.
# Store it in the session for later validation.
state = hashlib.sha256(os.urandom(1024)).hexdigest()
session['state'] = state
# Set the client ID, token state, and application name in the HTML while
# serving it.
response = make_response(
    render_template('index.html',
                    CLIENT_ID=CLIENT_ID,
                    STATE=state,
                    APPLICATION_NAME=APPLICATION_NAME))

2.向谷歌发送认证请求

下一步是使用适当的 URI 参数形成一个 HTTPS GET请求。请注意在此过程的所有步骤中使用 HTTPS 而不是 HTTP; HTTP 连接被拒绝。您应该使用authorization_endpoint元数据值从Discovery 文档中检索基本 URI。以下讨论假设基本 URI 是https://accounts.google.com/o/oauth2/v2/auth

对于基本请求,请指定以下参数:

  • client_id ,您从 API ConsoleCredentials page获得。
  • response_type ,在基本授权码流请求中应该是code 。 (在response_type阅读更多内容。)
  • scope ,在基本请求中应该是openid email 。 (在scope阅读更多内容。)
  • redirect_uri应该是您服务器上的 HTTP 端点,它将接收来自 Google 的响应。该值必须与您在 API ConsoleCredentials page中配置的 OAuth 2.0 客户端的授权重定向 URI 之一完全匹配。如果此值与授权 URI 不匹配,则请求将失败并出现redirect_uri_mismatch错误。
  • state应该包括防伪唯一会话令牌的值,以及当用户返回到您的应用程序时恢复上下文所需的任何其他信息,例如起始 URL。 (阅读更多state 。)
  • nonce是您的应用程序生成的随机值,当存在​​时启用重放保护。
  • login_hint可以是用户的电子邮件地址,也可以是sub字符串,相当于用户的 Google ID。如果您未提供login_hint并且用户当前已登录,则同意屏幕会包含请求批准将用户的电子邮件地址发布到您的应用程序的请求。 (在login_hint阅读更多内容。)
  • 使用hd参数为特定 G Suite 域的用户优化 OpenID Connect 流程。 (阅读更多hd内容。)

下面是一个完整的 OpenID Connect 身份验证 URI 示例,带有换行符和空格以便于阅读:

https://accounts.google.com/o/oauth2/v2/auth?
 response_type=code&
 client_id=424911365001.apps.googleusercontent.com&
 scope=openid%20email&
 redirect_uri=https%3A//oauth2.example.com/code&
 state=security_token%3D138r5719ru3e1%26url%3Dhttps%3A%2F%2Foauth2-login-demo.example.com%2FmyHome&
 login_hint=jsmith@example.com&
 nonce=0394852-3190485-2490358&
 hd=example.com

如果您的应用程序请求有关他们的任何新信息,或者如果您的应用程序请求他们以前未批准的帐户访问权限,则用户必须给予同意。

3.确认防伪状态令牌

响应将发送到您在请求中指定的redirect_uri 。所有响应都在查询字符串中返回,如下所示:

https://oauth2.example.com/code?state=security_token%3D138r5719ru3e1%26url%3Dhttps%3A%2F%2Foa2cb.example.com%2FmyHome&code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&scope=openid%20email%20https://www.googleapis.com/auth/userinfo.email

在服务器上,您必须确认从 Google 收到的state与您在第 1 步中创建的会话令牌相匹配。这种往返验证有助于确保发出请求的是用户,而不是恶意脚本。

以下代码演示了确认您在步骤 1 中创建的会话令牌:

PHP

您必须下载用于 PHP 的 Google API 客户端库才能使用此示例。

// Ensure that there is no request forgery going on, and that the user
// sending us this connect request is the user that was supposed to.
if ($request->get('state') != ($app['session']->get('state'))) {
  return new Response('Invalid state parameter', 401);
}

爪哇

您必须下载适用于 Java 的 Google API 客户端库才能使用此示例。

// Ensure that there is no request forgery going on, and that the user
// sending us this connect request is the user that was supposed to.
if (!request.queryParams("state").equals(
    request.session().attribute("state"))) {
  response.status(401);
  return GSON.toJson("Invalid state parameter.");
}

Python

您必须下载适用于 Python 的 Google API 客户端库才能使用此示例。

# Ensure that the request is not a forgery and that the user sending
# this connect request is the expected user.
if request.args.get('state', '') != session['state']:
  response = make_response(json.dumps('Invalid state parameter.'), 401)
  response.headers['Content-Type'] = 'application/json'
  return response

4. access token 和 ID token 的兑换code

响应包括一个code参数,一个一次性授权代码,您的服务器可以用它来交换访问令牌和 ID 令牌。您的服务器通过发送 HTTPS POST请求进行此交换。 POST请求被发送到令牌端点,您应该使用token_endpoint元数据值从发现文档中检索该端点。以下讨论假设端点是https://oauth2.googleapis.com/token 。请求必须在POST正文中包含以下参数:

字段
code初始请求返回的授权代码。
client_id您从 API ConsoleCredentials page获取的客户端 ID,如获取 OAuth 2.0 凭据中所述。
client_secret您从 API ConsoleCredentials page获取的客户端密码,如获取 OAuth 2.0 凭据中所述。
redirect_uri在 API ConsoleCredentials page中指定的给定client_id的授权重定向 URI,如设置重定向 URI中所述。
grant_type如 OAuth 2.0 规范中所定义,该字段必须包含一个值authorization_code

实际请求可能类似于以下示例:

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

code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=your-client-id&
client_secret=your-client-secret&
redirect_uri=https%3A//oauth2.example.com/code&
grant_type=authorization_code

对此请求的成功响应在 JSON 数组中包含以下字段:

字段
access_token可以发送到 Google API 的令牌。
expires_in访问令牌的剩余生命周期(以秒为单位)。
id_token包含由 Google 数字签名的用户身份信息的JWT
scope access_token授予的访问范围,以空格分隔、区分大小写的字符串列表表示。
token_type标识返回的令牌类型。此时,该字段的值始终为Bearer
refresh_token (选修的)

仅当在身份验证请求中将access_type参数设置为offline时,此字段才存在。有关详细信息,请参阅刷新令牌

5.从ID token中获取用户信息

ID Token 是JWT (JSON Web Token),即加密签名的 Base64 编码的 JSON 对象。通常,在使用ID 令牌之前验证它至关重要,但由于您通过无中介的 HTTPS 通道直接与 Google 通信,并使用您的客户端密码向 Google 验证您自己,因此您可以确信您使用的令牌是接收确实来自谷歌并且是有效的。如果您的服务器将 ID 令牌传递给应用程序的其他组件,那么其他组件在使用令牌之前验证令牌非常重要。

由于大多数 API 库将验证与解码 base64url 编码值和解析其中的 JSON 的工作结合在一起,因此您可能最终会在访问 ID 令牌中的声明时验证令牌。

ID 令牌的有效负载

ID 令牌是包含一组名称/值对的 JSON 对象。这是一个示例,为便于阅读而格式化:

{
  "iss": "https://accounts.google.com",
  "azp": "1234987819200.apps.googleusercontent.com",
  "aud": "1234987819200.apps.googleusercontent.com",
  "sub": "10769150350006150715113082367",
  "at_hash": "HK6E_P6Dh8Y93mRNtsDB1Q",
  "hd": "example.com",
  "email": "jsmith@example.com",
  "email_verified": "true",
  "iat": 1353601026,
  "exp": 1353604926,
  "nonce": "0394852-3190485-2490358"
}

Google ID 令牌可能包含以下字段(称为声明):

宣称假如描述
aud总是此 ID 令牌的目标受众。它必须是您的应用程序的 OAuth 2.0 客户端 ID 之一。
exp总是不得接受 ID 令牌的过期时间或之后。以 Unix 时间(整数秒)表示。
iat总是颁发 ID 令牌的时间。以 Unix 时间(整数秒)表示。
iss总是响应的颁发者的颁发者标识符。对于 Google ID 令牌,始终使用https://accounts.google.comaccounts.google.com
sub总是用户的标识符,在所有 Google 帐户中唯一且从未重复使用。一个 Google 帐户可以在不同的时间点拥有多个电子邮件地址,但sub值永远不会改变。在您的应用程序中使用sub作为用户的唯一标识符键。最大长度为 255 个区分大小写的 ASCII 字符。
at_hash访问令牌哈希。提供访问令牌与身份令牌绑定的验证。如果在服务器流中使用access_token值颁发 ID 令牌,则始终包含此声明。此声明可用作防止跨站点请求伪造攻击的替代机制,但如果您遵循步骤 1步骤 3 ,则无需验证访问令牌。
azp授权演示者的client_id 。仅当请求 ID 令牌的一方与 ID 令牌的受众不同时才需要此声明。在 Google 的混合应用程序中可能就是这种情况,其中 Web 应用程序和 Android 应用程序具有不同的 OAuth 2.0 client_id但共享相同的 Google API 项目。
email用户的电子邮件地址。此值可能不是该用户唯一的,不适合用作主键。仅当您的范围包含email范围值时才提供。
email_verified如果用户的电子邮件地址已经过验证,则为真;否则为假。
family_name用户的姓氏或姓氏。存在name声明时可能会提供。
given_name用户的名字或名字。存在name声明时可能会提供。
hd用户的托管 G Suite 域。仅当用户属于托管域时提供。
locale用户的区域设置,由BCP 47语言标记表示。存在name声明时可能会提供。
name用户的全名,以可显示的形式。可能在以下情况下提供:
  • 请求范围包括字符串“profile”
  • ID 令牌从令牌刷新中返回

当存在name声明时,您可以使用它们来更新应用程序的用户记录。请注意,永远不能保证此声明存在。

nonce您的应用在身份验证请求中提供的nonce值。您应该通过确保仅显示一次来强制防止重放攻击。
picture用户个人资料图片的 URL。可能在以下情况下提供:
  • 请求范围包括字符串“profile”
  • ID 令牌从令牌刷新中返回

当存在picture声明时,您可以使用它们来更新应用的用户记录。请注意,永远不能保证此声明存在。

profile用户个人资料页面的 URL。可能在以下情况下提供:
  • 请求范围包括字符串“profile”
  • ID 令牌从令牌刷新中返回

当存在profile声明时,您可以使用它们来更新应用程序的用户记录。请注意,永远不能保证此声明存在。

6. 验证用户

从 ID 令牌中获取用户信息后,您应该查询应用的用户数据库。如果该用户已存在于您的数据库中,那么如果 Google API 响应满足所有登录要求,您应该为该用户启动一个应用程序会话。

如果您的用户数据库中不存在该用户,您应该将用户重定向到您的新用户注册流程。您可以根据从 Google 收到的信息自动注册用户,或者至少您可以预先填写注册表单上所需的许多字段。除了 ID 令牌中的信息之外,您还可以在我们的用户配置文件端点获取其他用户配置文件信息

高级主题

以下部分更详细地描述了 Google OAuth 2.0 API。此信息适用于对身份验证和授权有高级要求的开发人员。

访问其他 Google API

使用 OAuth 2.0 进行身份验证的优势之一是,在您对用户进行身份验证的同时,您的应用程序可以获得代表用户使用其他 Google API(例如 YouTube、Google Drive、日历或联系人)的权限。为此,请在发送给 Google 的身份验证请求中包含您需要的其他范围。例如,要将用户的年龄组添加到您的身份验证请求中,请传递openid email https://www.googleapis.com/auth/profile.agerange.read的范围参数。在同意屏幕上适当地提示用户。您从 Google 收到的访问令牌允许您访问与您请求并被授予的访问范围相关的所有 API。

刷新令牌

在您的 API 访问请求中,您可以请求在code交换期间返回一个刷新令牌。当用户不在您的应用程序中时,刷新令牌可让您的应用程序持续访问 Google API。要请求刷新令牌,请在您的身份验证请求中添加将access_type参数设置为offline

注意事项:

  • 请务必安全且永久地存储刷新令牌,因为您只能在第一次执行代码交换流程时获得刷新令牌。
  • 颁发的刷新令牌的数量有限制:每个客户端/用户组合有一个限制,所有客户端的每个用户都有另一个限制。如果您的应用程序请求太多刷新令牌,它可能会遇到这些限制,在这种情况下,旧的刷新令牌将停止工作。

有关详细信息,请参阅刷新访问令牌(离线访问)

您可以通过在您的身份验证请求中将prompt参数设置为consent来提示用户重新授权您的应用程序。当包含prompt=consent时,每次您的应用请求授权访问范围时都会显示同意屏幕,即使所有范围之前都已授予您的 Google API 项目。因此,仅在必要时才包含prompt=consent

有关prompt参数的更多信息,请参阅身份验证 URI 参数表中的prompt

身份验证 URI 参数

下表对 Google 的 OAuth 2.0 身份验证 API 接受的参数进行了更完整的描述。

范围必需的描述
client_id (必需的)您从 API ConsoleCredentials page获取的客户端 ID 字符串,如获取 OAuth 2.0 凭据中所述。
nonce (必需的)由您的应用生成的随机值,可启用重放保护。
response_type (必需的)如果值为code ,则启动基本授权代码流,需要向令牌端点发送POST以获取令牌。如果值为token id_tokenid_token token ,则启动隐式流,要求在重定向 URI 处使用 JavaScript 从URI #fragment identifier中检索令牌。
redirect_uri (必需的)确定发送响应的位置。此参数的值必须与您在 API ConsoleCredentials page 中设置的授权重定向值之一完全匹配(包括 HTTP 或 HTTPS 方案、大小写和结尾的“/”,如果有)。
scope (必需的)

范围参数必须以openid值开头,然后包括profile值、 email值或两者。

如果存在profile范围值,则 ID 令牌可能(但不保证)包含用户的默认profile声明。

如果存在email范围值,则 ID 令牌包括emailemail_verified声明。

除了这些特定于 OpenID 的范围之外,您的范围参数还可以包含其他范围值。所有范围值必须用空格分隔。例如,如果您希望按文件访问用户的 Google Drive,则您的范围参数可能是openid profile email https://www.googleapis.com/auth/drive.file

有关可用范围的信息,请参阅 Google API 的OAuth 2.0 范围或您要使用的 Google API 的文档。

state (可选,但强烈推荐)

在协议中往返的不透明字符串;也就是说,它在 Basic 流中作为 URI 参数返回,在 Implicit 流中作为 URI #fragment标识符返回。

state对于关联请求和响应很有用。因为您的redirect_uri可以被猜到,所以使用state值可以增加您对传入连接是您的应用程序发起的身份验证请求的结果的保证。如果您在此state变量中生成随机字符串或编码某些客户端状态(例如 cookie)的散列,则可以验证响应以另外确保请求和响应源自同一浏览器。这提供了针对诸如跨站点请求伪造之类的攻击的保护。

access_type (选修的)允许的值是offlineonline 。效果记录在Offline Access中;如果正在请求访问令牌,则客户端不会收到刷新令牌,除非指定了offline值。
display (选修的)一个 ASCII 字符串值,用于指定授权服务器如何显示身份验证和同意用户界面页面。 Google 服务器指定并接受以下值,但对其行为没有任何影响: pagepopuptouchwap
hd (选修的)

hd (托管域)参数简化了 G Suite 托管帐户的登录过程。通过包含 G Suite 用户的域(例如mycollege.edu ),您可以指示应针对该域中的帐户优化帐户选择 UI。要优化 G Suite 帐户而不是仅针对一个域,请设置星号 ( * ) 的值: hd=*

不要依赖此 UI 优化来控制谁可以访问您的应用程序,因为可以修改客户端请求。请务必验证返回的 ID 令牌是否具有符合您期望的hd声明值(例如mycolledge.edu )。与请求参数不同,ID 令牌hd声明包含在来自 Google 的安全令牌中,因此可以信任该值。

include_granted_scopes (选修的)如果此参数的值为true ,并且授权请求被授予,则授权将包括之前授予此用户/应用程序组合的其他范围的任何授权;请参阅增量授权

请注意,您不能对已安装的应用程序流程进行增量授权。

login_hint (选修的)当您的应用程序知道它正在尝试验证哪个用户时,它可以将此参数作为提示提供给验证服务器。传递此提示会抑制帐户选择器并预先填写登录表单上的电子邮件框,或选择正确的会话(如果用户使用多重登录),这可以帮助您避免应用程序出现的问题登录错误的用户帐户。该值可以是电子邮件地址或sub字符串,相当于用户的 Google ID。
prompt (选修的)以空格分隔的字符串值列表,指定授权服务器是否提示用户重新进行身份验证和同意。可能的值是:
  • none

    授权服务器不显示任何身份验证或用户同意屏幕;如果用户尚未经过身份验证并且尚未为请求的范围预先配置同意,它将返回错误。您可以使用none检查现有的身份验证和/或同意。

  • consent

    授权服务器在向客户端返回信息之前提示用户同意。

  • select_account

    授权服务器提示用户选择用户帐户。这允许在授权服务器上拥有多个帐户的用户在他们可能具有当前会话的多个帐户中进行选择。

如果未指定任何值且用户之前未授权访问,则向用户显示同意屏幕。

验证 ID 令牌

您需要验证服务器上的所有 ID 令牌,除非您知道它们直接来自 Google。例如,您的服务器必须验证它从您的客户端应用程序接收到的任何 ID 令牌都是真实的。

以下是您可能会将 ID 令牌发送到服务器的常见情况:

  • 发送带有需要验证的请求的 ID 令牌。 ID 令牌告诉您发出请求的特定用户以及为哪个客户端授予了该 ID 令牌。

ID 令牌很敏感,如果被拦截,可能会被滥用。您必须确保仅通过 HTTPS 并且仅通过 POST 数据或在请求标头中传输这些令牌来安全地处理这些令牌。如果您在服务器上存储 ID 令牌,则还必须安全地存储它们。

使 ID 令牌有用的一件事是,您可以在应用程序的不同组件中传递它们。这些组件可以使用 ID 令牌作为对应用程序和用户进行身份验证的轻量级身份验证机制。但在您可以使用 ID 令牌中的信息或将其作为用户已通过身份验证的断言之前,您必须对其进行验证。

验证 ID 令牌需要几个步骤:

  1. 验证 ID 令牌是否由发行者正确签名。 Google 颁发的令牌使用在Discovery 文档jwks_uri元数据值中指定的 URI 中找到的证书之一进行签名。
  2. 验证 ID 令牌中iss声明的值是否等于https://accounts.google.comaccounts.google.com
  3. 验证 ID 令牌中aud声明的值是否等于您应用的客户端 ID。
  4. 验证 ID 令牌的到期时间( exp声明)尚未过去。
  5. 如果您在请求中指定了hd 参数值,请验证 ID 令牌具有与接受的 G Suite 托管域匹配的hd声明。

步骤 2 到 5 仅涉及非常简单的字符串和日期比较,因此我们不会在此处详细说明。

第一步更复杂,涉及加密签名检查。出于调试目的,您可以使用 Google 的tokeninfo端点与您的服务器或设备上实现的本地处理进行比较。假设您的 ID 令牌的值为XYZ123 。然后,您将取消引用 URI https://oauth2.googleapis.com/tokeninfo?id_token= XYZ123 。如果令牌签名有效,则响应将是其解码的 JSON 对象形式的 JWT 有效负载。

tokeninfo端点对于调试很有用,但对于生产目的,从密钥端点检索 Google 的公钥并在本地执行验证。您应该使用jwks_uri元数据值从发现文档中检索密钥 URI。对调试端点的请求可能会受到限制或以其他方式受到间歇性错误的影响。

由于 Google 很少更改其公钥,因此您可以使用 HTTP 响应的缓存指令缓存它们,并且在绝大多数情况下,执行本地验证比使用tokeninfo端点更有效。此验证需要检索和解析证书,并进行适当的加密调用以检查签名。幸运的是,有多种语言提供的经过良好调试的库可以完成此任务(请参阅jwt.io )。

获取用户配置文件信息

要获取有关用户的其他配置文件信息,您可以使用访问令牌(您的应用程序在身份验证流程中收到)和OpenID Connect标准:

  1. 要符合 OpenID,您必须在身份验证请求中包含openid profile范围值。

    如果您希望包含用户的电子邮件地址,您可以指定email的附加范围值。要同时指定profileemail ,您可以在身份验证请求 URI 中包含以下参数:

    scope=openid%20profile%20email
  2. 将您的访问令牌添加到授权标头并向 userinfo 端点发出 HTTPS GET请求,您应该使用userinfo_endpoint元数据值从Discovery 文档中检索该请求。 userinfo 响应包括有关用户的信息,如OpenID Connect Standard Claims和 Discovery 文档的claims_supported元数据值中所述。用户或其组织可能会选择提供或保留某些字段,因此您可能无法获得授权访问范围内每个字段的信息。

发现文件

OpenID Connect 协议需要使用多个端点来对用户进行身份验证,以及请求包括令牌、用户信息和公钥在内的资源。

为了简化实现并提高灵活性,OpenID Connect 允许使用“发现文档”,这是一个位于众所周知的位置的 JSON 文档,其中包含提供有关 OpenID Connect 提供者配置的详细信息的键值对,包括授权的 URI 、令牌、撤销、用户信息和公钥端点。 Google OpenID Connect 服务的 Discovery 文档可以从以下位置检索:

https://accounts.google.com/.well-known/openid-configuration

要使用 Google 的 OpenID Connect 服务,您应该将 Discovery-document URI ( https://accounts.google.com/.well-known/openid-configuration ) 硬编码到您的应用程序中。您的应用程序获取文档,在响应中应用缓存规则,然后根据需要从中检索端点 URI。例如,要对用户进行身份authorization_endpoint ,您的代码将检索 authentication_endpoint 元数据值(以下示例中的https://accounts.google.com/o/oauth2/v2/auth )作为发送到的身份验证请求的基本 URI谷歌。

这是此类文档的示例;字段名称是OpenID Connect Discovery 1.0中指定的名称(请参阅该文档以了解其含义)。这些值纯粹是说明性的,可能会发生变化,尽管它们是从实际 Google Discovery 文档的最新版本中复制而来的:

{
  "issuer": "https://accounts.google.com",
  "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
  "device_authorization_endpoint": "https://oauth2.googleapis.com/device/code",
  "token_endpoint": "https://oauth2.googleapis.com/token",
  "userinfo_endpoint": "https://openidconnect.googleapis.com/v1/userinfo",
  "revocation_endpoint": "https://oauth2.googleapis.com/revoke",
  "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",
  "response_types_supported": [
    "code",
    "token",
    "id_token",
    "code token",
    "code id_token",
    "token id_token",
    "code token id_token",
    "none"
  ],
  "subject_types_supported": [
    "public"
  ],
  "id_token_signing_alg_values_supported": [
    "RS256"
  ],
  "scopes_supported": [
    "openid",
    "email",
    "profile"
  ],
  "token_endpoint_auth_methods_supported": [
    "client_secret_post",
    "client_secret_basic"
  ],
  "claims_supported": [
    "aud",
    "email",
    "email_verified",
    "exp",
    "family_name",
    "given_name",
    "iat",
    "iss",
    "locale",
    "name",
    "picture",
    "sub"
  ],
  "code_challenge_methods_supported": [
    "plain",
    "S256"
  ]
}

您可以通过缓存 Discovery 文档中的值来避免 HTTP 往返。使用标准 HTTP 缓存标头,应予以尊重。

客户端库

以下客户端库通过与流行框架集成,使 OAuth 2.0 的实现更加简单:

OpenID Connect 合规性

Google 的 OAuth 2.0 身份验证系统支持OpenID Connect Core规范所需的功能。任何设计用于 OpenID Connect 的客户端都应与此服务互操作( OpenID 请求对象除外)。