通过 OAuth 和“使用 Google 账号登录”简化关联流程

概览

基于 OAuth 的“使用 Google 账号登录”简化关联功能OAuth 关联的基础上添加了“使用 Google 账号登录”功能。这为 Google 用户提供了顺畅的关联体验,并且还支持创建账号,让用户可以使用自己的 Google 账号在您的服务中创建新账号。

如需使用 OAuth 和“使用 Google 账号登录”功能执行账号关联,请按以下一般步骤操作:

  1. 首先,要求用户同意访问其 Google 个人资料。
  2. 使用其个人资料中的信息检查用户账号是否存在。
  3. 对于现有用户,关联账号。
  4. 如果您在身份验证系统中找不到与 Google 用户匹配的账号,请验证从 Google 收到的 ID 令牌。然后,您可以根据 ID 令牌中包含的个人资料信息创建用户。
此图显示了用户使用简化的关联流程关联其 Google 账号的步骤。第一张屏幕截图显示了用户如何选择您的应用以进行关联。第二个屏幕截图可让用户确认他们是否在您的服务中拥有现有账号。第三张屏幕截图显示了用户可以选择要关联的 Google 账号。第四张屏幕截图显示了将用户的 Google 账号与您的应用相关联的确认信息。第五张屏幕截图显示了 Google 应用中成功关联的用户账号。

图 1. 在用户的手机上使用简化关联功能进行账号关联

简化关联的要求

实现 OAuth 服务器

令牌交换 端点必须支持 checkcreateget intent。请按照以下步骤完成账号关联流程,并了解何时使用不同的 intent:

  1. 用户在您的身份验证系统中是否有账号?(用户通过选择“是”或“否”来决定)
    1. 是:用户是否使用与其 Google 账号关联的电子邮件地址登录您的平台?(用户通过选择“是”或“否”来决定)
      1. 是:用户在您的身份验证系统中是否有匹配的账号?(check intent is called to confirm)
        1. 是:系统会调用 get intent,如果 get intent 成功返回,则账号会关联。
        2. 否:创建新账号?(用户通过选择“是”或“否”来决定)
          1. 是:系统会调用 create intent,如果 create intent 成功返回,则账号会关联。
          2. 否:系统会触发 Web OAuth 流程,用户会被定向到其浏览器,并且用户可以选择使用其他电子邮件地址进行关联。
      2. 否:系统会触发 Web OAuth 流程,用户会被定向到其浏览器,并且用户可以选择使用其他电子邮件地址进行关联。
    2. 否:用户在您的身份验证系统中是否有匹配的账号?(check intent is called to confirm)
      1. 是:系统会调用 get intent,如果 get intent 成功返回,则账号会关联。
      2. 否:系统会调用 create intent,如果 create intent 成功返回,则账号会关联。

检查现有用户账号(检查 intent)

在用户同意访问其 Google 个人资料后,Google 会发送 请求,其中包含 Google 用户身份的已签名断言。通过 断言包含的信息包括用户的 Google 账号 ID、 姓名和电子邮件地址为您的 Google Cloud 控制台配置的令牌交换端点 项目处理该请求。

如果您的身份验证中已有相应的 Google 账号 系统时,您的令牌交换端点会返回 account_found=true。如果 Google 账号与现有用户不匹配,您的令牌交换端点 返回“HTTP 404 Not Found”错误以及 account_found=false

请求的格式如下:

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

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&intent=check&assertion=JWT&scope=SCOPES&client_id=GOOGLE_CLIENT_ID&client_secret=GOOGLE_CLIENT_SECRET

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

令牌端点参数
intent 对于这些请求,此参数的值为 check
grant_type 所交换的令牌的类型。对于这类请求 参数的值为 urn:ietf:params:oauth:grant-type:jwt-bearer
assertion 一个 JSON Web 令牌 (JWT),提供 Google 用户身份。JWT 包含的信息包括用户 Google 账号 ID、姓名和电子邮件地址。
client_id 您分配给 Google 的客户 ID。
client_secret 您分配给 Google 的客户端密钥。

如需响应 check intent 请求,您的令牌交换端点必须执行以下步骤:

  • 验证和解码 JWT 断言。
  • 检查您的身份验证系统中是否已存在该 Google 账号。
验证和解码 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 不 建议使用权威凭据和密码或其他验证方法进行验证 用户。email_verified 可能为 true,因为 Google 最初验证了 创建 Google 账号后,该用户会获得第三方的所有权, 后,电子邮件账号可能已更改。

检查您的身份验证系统中是否已存在该 Google 账号

请检查以下任一条件是否成立:

  • Google 账号 ID(可在断言的 sub 字段中找到)位于您的用户中 数据库。
  • 断言中的电子邮件地址与用户数据库中的用户匹配。

如果满足上述任一条件,则表明用户已注册。在这种情况下 返回如下所示的响应:

HTTP/1.1 200 Success
Content-Type: application/json;charset=UTF-8

{
  "account_found":"true",
}

如果 Google 账号 ID 和 断言与您的数据库中的用户匹配,该用户尚未注册。在 在这种情况下,您的令牌交换端点需要返回 HTTP 404 错误 指定 "account_found": "false",如以下示例所示:

HTTP/1.1 404 Not found
Content-Type: application/json;charset=UTF-8

{
  "account_found":"false",
}

处理自动链接(获取 intent)

在用户同意访问其 Google 个人资料后,Google 会发送 请求,其中包含 Google 用户身份的已签名断言。通过 断言包含的信息包括用户的 Google 账号 ID、 姓名和电子邮件地址为您的 Google Cloud 控制台配置的令牌交换端点 项目处理该请求。

如果您的身份验证中已有相应的 Google 账号 系统,您的令牌交换端点将为用户返回一个令牌。如果 Google 账号与现有用户不匹配,您的令牌交换端点 返回 linking_error 错误和可选的 login_hint

请求的格式如下:

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

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&intent=get&assertion=JWT&scope=SCOPES&client_id=GOOGLE_CLIENT_ID&client_secret=GOOGLE_CLIENT_SECRET

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

令牌端点参数
intent 对于这些请求,此参数的值为 get
grant_type 所交换的令牌的类型。对于这类请求 参数的值为 urn:ietf:params:oauth:grant-type:jwt-bearer
assertion 一个 JSON Web 令牌 (JWT),提供 Google 用户身份。JWT 包含的信息包括用户 Google 账号 ID、姓名和电子邮件地址。
scope 可选:您已将 Google 配置为向其请求访问权限的任何范围 用户。
client_id 您分配给 Google 的客户 ID。
client_secret 您分配给 Google 的客户端密钥。

如需响应 get intent 请求,您的令牌交换端点必须执行以下步骤:

  • 验证和解码 JWT 断言。
  • 检查您的身份验证系统中是否已存在该 Google 账号。
验证和解码 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 不 建议使用权威凭据和密码或其他验证方法进行验证 用户。email_verified 可能为 true,因为 Google 最初验证了 创建 Google 账号后,该用户会获得第三方的所有权, 后,电子邮件账号可能已更改。

检查您的身份验证系统中是否已存在该 Google 账号

请检查以下任一条件是否成立:

  • Google 账号 ID(可在断言的 sub 字段中找到)位于您的用户中 数据库。
  • 断言中的电子邮件地址与用户数据库中的用户匹配。

如果找到了用户的账号,请发出访问令牌,并在 HTTPS 响应正文的 JSON 对象中返回相应值,如以下示例所示:

{
  "token_type": "Bearer",
  "access_token": "ACCESS_TOKEN",
  "refresh_token": "REFRESH_TOKEN",
  "expires_in": SECONDS_TO_EXPIRATION
}

在某些情况下,基于 ID 令牌的账号关联可能会失败。如果 因为任何原因,您的令牌交换端点都需要以 HTTP 响应 指定 error=linking_error 的 401 错误,如以下示例所示:

HTTP/1.1 401 Unauthorized
Content-Type: application/json;charset=UTF-8

{
  "error":"linking_error",
  "login_hint":"foo@bar.com"
}

当 Google 收到包含 linking_error 的 401 错误响应时,会发送 使用 login_hint 作为参数将用户发送到您的授权端点。通过 用户在浏览器中使用 OAuth 关联流程完成账号关联。

使用“使用 Google 账号登录”处理账号创建(创建 intent)

当用户需要在您的服务中创建账号时,Google 会向您的令牌交换端点发出请求,其中指定了 intent=create

该请求采用以下形式:

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

response_type=token&grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&scope=SCOPES&intent=create&assertion=JWT&client_id=GOOGLE_CLIENT_ID&client_secret=GOOGLE_CLIENT_SECRET

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

令牌端点参数
intent 对于这些请求,此参数的值为 create
grant_type 要交换的令牌的类型。对于这些请求,此 参数的值为 urn:ietf:params:oauth:grant-type:jwt-bearer
assertion JSON 网络令牌 (JWT),用于提供 Google 用户身份的签名断言。JWT 包含的信息包括用户的 Google 账号 ID、姓名和电子邮件地址。
client_id 您分配给 Google 的客户端 ID。
client_secret 您分配给 Google 的客户端密钥。

assertion 参数中的 JWT 包含用户的 Google 账号 ID、姓名和电子邮件地址,您可以使用这些信息在您的服务中创建新账号。

如需响应 create intent 请求,您的令牌交换端点必须执行以下步骤:

  • 验证 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 不 建议使用权威凭据和密码或其他验证方法进行验证 用户。email_verified 可能为 true,因为 Google 最初验证了 创建 Google 账号后,该用户会获得第三方的所有权, 后,电子邮件账号可能已更改。

验证用户信息并创建新账号

检查是否满足以下任一条件:

  • 断言的 sub 字段中找到的 Google 账号 ID 位于您的用户数据库中。
  • 断言中的电子邮件地址与用户数据库中的用户匹配。

如果满足任一条件,请提示用户将其现有账号与其 Google 账号相关联。为此,请使用 HTTP 401 错误响应请求,该错误指定了 error=linking_error 并将用户的电子邮件地址作为 login_hint。以下是示例响应:

HTTP/1.1 401 Unauthorized
Content-Type: application/json;charset=UTF-8

{
  "error":"linking_error",
  "login_hint":"foo@bar.com"
}

当 Google 收到包含 linking_error 的 401 错误响应时,Google 会将用户发送到您的授权端点,并将 login_hint 作为参数。用户在其浏览器中使用 OAuth 关联流程完成账号关联。

如果这两个条件都不满足,请使用 JWT 中提供的信息创建新的用户账号。新账号通常没有设置密码。建议您将“使用 Google 账号登录”添加到其他平台,以便用户可以在应用的所有界面上使用 Google 账号登录。或者,您也可以通过电子邮件向用户发送一个链接,该链接会启动密码恢复流程,让用户设置密码以便在其他平台上登录。

创建完成后,请发出访问令牌 和刷新令牌 ,并在 HTTPS 响应正文中的 JSON 对象内返回这些值,如以下示例所示:

{
  "token_type": "Bearer",
  "access_token": "ACCESS_TOKEN",
  "refresh_token": "REFRESH_TOKEN",
  "expires_in": SECONDS_TO_EXPIRATION
}

获取 Google API 客户端 ID

在账号关联 注册 过程中,您需要提供 Google API 客户端 ID。

如需使用您在完成 OAuth 关联 步骤时创建的项目获取 API 客户端 ID,请完成以下步骤:

  1. 前往“客户端”页面。
  2. 创建或选择 Google API 项目。

    如果您的项目没有 Web 应用类型的客户端 ID,请点击创建客户端 以创建一个。请务必在已获授权的 JavaScript 来源 框中添加您网站的网域。在执行 本地测试或开发时,您必须将 http://localhosthttp://localhost:<port_number> 都添加到 已获授权的 JavaScript 来源 字段中。

验证您的实现效果

您可以使用 OAuth 2.0 Playground 工具验证您的实现。

在该工具中,执行以下步骤:

  1. 点击配置 以打开“OAuth 2.0 配置”窗口。
  2. OAuth flow(OAuth 流程)字段中,选择 Client-side(客户端)。
  3. OAuth Endpoints 字段中,选择 Custom
  4. 在相应字段中指定您的 OAuth 2.0 端点以及您分配给 Google 的客户端 ID。
  5. 第 1 步部分中,请勿选择任何 Google 范围。请将此字段留空,或输入适用于您服务器的范围(如果您不使用 OAuth 范围,则输入任意字符串)。完成后,点击 Authorize APIs
  6. 第 2 步第 3 步部分中,完成 OAuth 2.0 流程,并验证每个步骤是否按预期运行。

您可以使用 Google 账号关联演示工具验证您的实现。

在该工具中,执行以下步骤:

  1. 点击使用 Google 账号登录按钮。
  2. 选择您要关联的账号。
  3. 输入服务 ID。
  4. (可选)输入您将请求访问的一个或多个范围。
  5. 点击开始演示
  6. 当系统提示时,请确认您可以同意或拒绝关联请求。
  7. 确认您已重定向到相应平台。