关联的帐号登录

通过 Google 账号关联,Google 账号持有人可以快速、顺畅、安全地连接到您的服务并与 Google 共享数据。

如果用户的 Google 帐号已与您的服务相关联,则“关联的帐号登录”功能可为已将其 Google 帐号的用户启用一键登录 Google 功能。这提升了用户体验,因为用户只需点击一下,即可登录,而无需重新输入用户名和密码。这还可以降低用户在您的服务上重复创建帐号的几率。

要求

要实现关联的帐号登录,您必须满足以下要求:

  • 您的 Google 帐号 OAuth 关联实现支持 OAuth 2.0 授权代码流程。您的 OAuth 实现必须包含以下端点:
    • 授权端点来处理授权请求。
    • token 端点,用于处理访问令牌和刷新令牌的请求。
    • userinfo 端点,以检索在关联的帐号登录过程中向用户显示的关联用户的基本帐号信息。
  • 您有一个 Android 应用。

运作方式

必备条件 :用户之前已将其 Google 帐号与您的服务帐号相关联。

  1. 您可以在“一键登录”流程中选择显示关联的账号。
  2. 系统会向用户显示“一键登录”提示,以便用户使用关联的帐号登录您的服务。
  3. 如果用户选择继续使用关联的帐号,Google 会向您的令牌端点发送请求以保存授权代码。请求包含您的服务签发的用户访问令牌以及 Google 授权代码。
  4. 您需要使用 Google 授权代码来换取包含用户 Google 帐号相关信息的 Google ID 令牌。
  5. 流程结束后,您的应用还会收到一个 ID 令牌,您需要将此令牌与您的服务器收到的 ID 令牌中的用户标识符进行匹配,以便让用户登录您的应用。
关联的帐号登录。
图 1.关联的帐号登录流程。如果用户的设备上有多个已登录的帐号,则可能会看到帐号选择器,并且只有在选择关联的帐号时,才会转到“关联的帐号登录”视图。

在您的 Android 应用中实现关联的帐号登录

若要在您的 Android 应用中支持关联的帐号登录,请按照 Android 实现指南中的说明操作。

处理来自 Google 的授权代码请求

Google 会向您的令牌端点发出 POST 请求,以保存授权代码,您可以使用该代码来换取用户的 ID 令牌。该请求包含用户的访问令牌和 Google 签发的 OAuth2 授权代码。

在保存授权代码之前,您必须先验证访问令牌是否属于您授予 Google(由 client_id 标识)。

HTTP 请求

示例请求

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

code=GOOGLE_AUTHORIZATION_CODE
&grant_type=urn:ietf:params:oauth:grant-type:reciprocal
&client_id=CLIENT_ID
&client_secret=CLIENT_SECRET
&access_token=ACCESS_TOKEN

您的令牌交换端点必须能够处理以下请求参数:

令牌端点参数
code Google OAuth2 授权代码必需
client_id 必需您发送给 Google 的客户端 ID
client_secret 您发送给 Google 的必需客户端密钥
access_token 必需。您发送给 Google 的访问令牌。您将使用该函数来获取用户的上下文
grant_type 必需值必须设置为 urn:ietf:params:oauth:grant-type:reciprocal

您的令牌交换端点应通过执行以下操作来响应 POST 请求:

  • 确认 access_token 已授予 client_id 标识的 Google。
  • 如果请求有效且授权代码成功交换 Google ID 令牌,则返回 HTTP 200 (OK) 响应;如果请求无效,则返回 HTTP 错误代码。

HTTP 响应

成功

返回 HTTP 状态代码 200 OK

成功响应示例
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{}

错误数

如果 HTTP 请求无效,请在响应时提供以下 HTTP 错误代码之一:

HTTP 状态代码 Body 说明
400 {"error": "invalid_request"} 该请求缺少参数,因此服务器无法继续进行该请求。如果请求包含不受支持的参数或重复某个参数,也可能返回此代码
401 {"error": "invalid_request"} 客户端身份验证失败,例如请求中包含无效的客户端 ID 或密钥
401 {"error": "invalid_token"}

在响应标头中添加“WWW-Authentication: Bearer”身份验证质询

合作伙伴访问令牌无效。
403 {"error": "insufficient_permission"}

在响应标头中添加“WWW-Authentication: Bearer”身份验证质询

合作伙伴访问令牌未包含执行双向 OAuth 所需的范围
500 {"error": "internal_error"} 服务器错误

错误响应应包含以下字段:

错误响应字段
error 必填错误字符串
error_description 直观易懂的错误说明
error_uri 提供错误相关详细信息的 URI
错误 400 响应示例
HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
  "error": "invalid_request",
  "error_description": "Request was missing the 'access_token' parameter."
}

交换 ID 令牌的授权代码

您需要将收到的授权代码换成包含用户 Google 帐号相关信息的 Google ID 令牌。

如需将授权代码换成 Google ID 令牌,请调用 https://oauth2.googleapis.com/token 端点并设置以下参数:

请求字段
client_id 必填。从 API 控制台的“凭据”页面获取的客户端 ID。这通常是名为 New Actions on Google App 的凭据
client_secret 必需:从 API 控制台的“凭据”页面获取的客户端密钥
code 必需:初始请求中发送的授权代码
grant_type 必需 如 OAuth 2.0 规范中所定义,此字段的值必须设置为 authorization_code
示例请求
POST /oauth2/v4/token HTTP/1.1
Host: www.googleapis.com
Content-Type: application/x-www-form-urlencoded

code=GOOGLE_AUTHORIZATION_CODE
&grant_type=authorization_code
&client_id=GOOGLE_CLIENT_ID
&client_secret=GOOGLE_CLIENT_SECRET

Google 通过返回一个 JSON 对象来响应此请求,该对象包含一个短期有效的访问令牌和一个刷新令牌。

响应包含以下字段:

响应字段
access_token 您的应用发送的 Google 签发的访问令牌,用于授权 Google API 请求
id_token ID 令牌包含用户的 Google 帐号信息。“验证响应”部分详细介绍了如何解码和验证 ID 令牌响应
expires_in 访问令牌的剩余生命周期(以秒为单位)
refresh_token 可用于获取新访问令牌的令牌。刷新令牌在用户撤消访问权限之前一直有效
scope 对于“已关联的帐号登录”用例,此字段的值始终设为 openid
token_type 返回的令牌类型。目前,此字段的值始终设置为 Bearer
响应示例
HTTP/1.1 200 OK
Content-type: application/json; charset=utf-8

{
  "access_token": "Google-access-token",
  "id_token": "Google-ID-token",
  "expires_in": 3599,
  "token_type": "Bearer",
  "scope": "openid",
  "refresh_token": "Google-refresh-token"
}


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

code=Google authorization code
&grant_type=authorization_code
&client_id=Google client id
&client_secret=Google client secret

验证 ID 令牌响应

验证并解码JWT断言

您可以使用针对您的语言JWT解码库来验证和解码JWT断言。使用Google的JWKPEM格式的公钥来验证令牌的签名。

解码后,JWT断言类似于以下示例:

{
  "sub": "1234567890",      // The unique ID of the user's Google Account
  "iss": "https://accounts.google.com",        // The assertion's issuer
  "aud": "123-abc.apps.googleusercontent.com", // Your server's client ID
  "iat": 233366400,         // Unix timestamp of the assertion's creation time
  "exp": 233370000,         // Unix timestamp of the assertion's expiration time
  "name": "Jan Jansen",
  "given_name": "Jan",
  "family_name": "Jansen",
  "email": "jan@gmail.com", // If present, the user's email address
  "email_verified": true,   // true, if Google has verified the email address
  "hd": "example.com",      // If present, the host domain of the user's GSuite email address
                            // If present, a URL to user's profile picture
  "picture": "https://lh3.googleusercontent.com/a-/AOh14GjlTnZKHAeb94A-FmEbwZv7uJD986VOF1mJGb2YYQ",
  "locale": "en_US"         // User's locale, from browser or phone settings
}

除了验证令牌的签名外,还要验证断言的颁发者( iss字段)为https://accounts.google.com ,受众( aud字段)是您分配的客户端ID,并且令牌尚未过期( exp场地)。

使用emailemail_verifiedhd字段,您可以确定Google是否托管电子邮件地址并对其具有权威性。如果Google具有权威性,则当前已知该用户为合法帐户所有者,您可以跳过密码或其他挑战方法。否则,可以使用这些方法在链接之前验证帐户。

Google具有权威性的情况:

  • email后缀为@gmail.com ,这是一个Gmail帐户。
  • email_verified为true并且设置了hd ,这是一个G Suite帐户。

用户可以在不使用Gmail或G Suite的情况下注册Google帐户。如果email不包含@gmail.com后缀,并且没有hd则Google并不具有权威性,建议您使用密码或其他验证方法来验证用户。当Google在创建Google帐户时最初验证了用户时, email_verfied也可能为true,但是此后第三方电子邮件帐户的所有权可能已更改。