使用 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 进行确认)
        1. 是:如果获取 intent 成功返回,则调用 get intent 并关联帐号。
        2. 否 :创建新帐号?(用户选择“是”或“否”)
          1. 是:如果创建 intent 成功返回,则调用 create intent 并关联帐号。
          2. 否 :网络 OAuth 流程被触发,用户被定向到浏览器,并且用户可以选择使用其他电子邮件地址进行关联。
      2. 否:系统会触发 Web OAuth 流程,将用户定向到他们的浏览器,并向用户提供使用其他电子邮件地址的选项。
    2. 否 :用户在身份验证系统中是否有匹配的帐号?(调用 check intent 进行确认)
      1. 是:如果获取 intent 成功返回,则调用 get intent 并关联帐号。
      2. 否:如果创建意图成功返回,则会调用 create intent 并关联帐号。

Check for an existing user account (check intent)

After the user gives consent to access their Google profile, Google sends a request that contains a signed assertion of the Google user's identity. The assertion contains information that includes the user's Google Account ID, name, and email address. The token exchange endpoint configured for your project handles that request.

If the corresponding Google account is already present in your authentication system, your token exchange endpoint responds with account_found=true. If the Google account doesn't match an existing user, your token exchange endpoint returns an HTTP 404 Not Found error with account_found=false.

The request has the following form:

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

Your token exchange endpoint must be able to handle the following parameters:

Token endpoint parameters
intent For these requests, the value of this parameter is check.
grant_type The type of token being exchanged. For these requests, this parameter has the value urn:ietf:params:oauth:grant-type:jwt-bearer.
assertion A JSON Web Token (JWT) that provides a signed assertion of the Google user's identity. The JWT contains information that includes the user's Google Account ID, name, and email address.
client_id The client ID you assigned to Google.
client_secret The client secret you assigned to Google.

To respond to the check intent requests, your token exchange endpoint must perform the following steps:

  • Validate and decode the JWT assertion.
  • Check if the Google account is already present in your authentication system.
Validate and decode the JWT assertion

You can validate and decode the JWT assertion by using a JWT-decoding library for your language. Use Google's public keys, available in JWK or PEM formats, to verify the token's signature.

When decoded, the JWT assertion looks like the following example:

{
  "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
}

In addition to verifying the token's signature, verify that the assertion's issuer (iss field) is https://accounts.google.com, that the audience (aud field) is your assigned client ID, and that the token has not expired (exp field).

Using the email, email_verified and hd fields you can determine if Google hosts and is authoritative for an email address. In cases where Google is authoritative the user is currently known to be the legitimate account owner and you may skip password or other challenges methods. Otherwise, these methods can be used to verify the account prior to linking.

Cases where Google is authoritative:

  • email has a @gmail.com suffix, this is a Gmail account.
  • email_verified is true and hd is set, this is a G Suite account.

Users may register for Google Accounts without using Gmail or G Suite. When email does not contain a @gmail.com suffix and hd is absent Google is not authoritative and password or other challenge methods are recommended to verify the user. email_verfied can also be true as Google initially verified the user when the Google account was created, however ownership of the third party email account may have since changed.

Check if the Google account is already present in your authentication system

Check whether either of the following conditions are true:

  • The Google Account ID, found in the assertion's sub field, is in your user database.
  • The email address in the assertion matches a user in your user database.

If either condition is true, the user has already signed up. In that case, return a response like the following:

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

{
  "account_found":"true",
}

If neither the Google Account ID nor the email address specified in the assertion matches a user in your database, the user hasn't signed up yet. In this case, your token exchange endpoint needs to reply with a HTTP 404 error that specifies "account_found": "false", as in the following example:

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

{
  "account_found":"false",
}

处理自动链接(获取 intent)

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

如果您的身份验证系统中已存在相应的 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 帐号。
Validate and decode the JWT assertion

You can validate and decode the JWT assertion by using a JWT-decoding library for your language. Use Google's public keys, available in JWK or PEM formats, to verify the token's signature.

When decoded, the JWT assertion looks like the following example:

{
  "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
}

In addition to verifying the token's signature, verify that the assertion's issuer (iss field) is https://accounts.google.com, that the audience (aud field) is your assigned client ID, and that the token has not expired (exp field).

Using the email, email_verified and hd fields you can determine if Google hosts and is authoritative for an email address. In cases where Google is authoritative the user is currently known to be the legitimate account owner and you may skip password or other challenges methods. Otherwise, these methods can be used to verify the account prior to linking.

Cases where Google is authoritative:

  • email has a @gmail.com suffix, this is a Gmail account.
  • email_verified is true and hd is set, this is a G Suite account.

Users may register for Google Accounts without using Gmail or G Suite. When email does not contain a @gmail.com suffix and hd is absent Google is not authoritative and password or other challenge methods are recommended to verify the user. email_verfied can also be true as Google initially verified the user when the Google account was created, however ownership of the third party email account may have since changed.

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

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

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

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

{
  "token_type": "Bearer",
  "access_token": "ACCESS_TOKEN",

  "refresh_token": "REFRESH_TOKEN",

  "expires_in": SECONDS_TO_EXPIRATION
}

在某些情况下,基于 ID 令牌的帐号关联可能会为用户失败。如果出现任何此类情况,您的令牌交换端点都需要使用返回 error=linking_error 的 HTTP 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 关联流程完成帐号关联。

Handle account creation via Google Sign-In (create intent)

When a user needs to create an account on your service, Google makes a request to your token exchange endpoint that specifies intent=create.

The request has the following form:

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

Your token exchange endpoint must able to handle the following parameters:

Token endpoint parameters
intent For these requests, the value of this parameter is create.
grant_type The type of token being exchanged. For these requests, this parameter has the value urn:ietf:params:oauth:grant-type:jwt-bearer.
assertion A JSON Web Token (JWT) that provides a signed assertion of the Google user's identity. The JWT contains information that includes the user's Google Account ID, name, and email address.
client_id The client ID you assigned to Google.
client_secret The client secret you assigned to Google.

The JWT within the assertion parameter contains the user's Google Account ID, name, and email address, which you can use to create a new account on your service.

To respond to the create intent requests, your token exchange endpoint must perform the following steps:

  • Validate and decode the JWT assertion.
  • Validate user information and create new account.
Validate and decode the JWT assertion

You can validate and decode the JWT assertion by using a JWT-decoding library for your language. Use Google's public keys, available in JWK or PEM formats, to verify the token's signature.

When decoded, the JWT assertion looks like the following example:

{
  "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
}

In addition to verifying the token's signature, verify that the assertion's issuer (iss field) is https://accounts.google.com, that the audience (aud field) is your assigned client ID, and that the token has not expired (exp field).

Using the email, email_verified and hd fields you can determine if Google hosts and is authoritative for an email address. In cases where Google is authoritative the user is currently known to be the legitimate account owner and you may skip password or other challenges methods. Otherwise, these methods can be used to verify the account prior to linking.

Cases where Google is authoritative:

  • email has a @gmail.com suffix, this is a Gmail account.
  • email_verified is true and hd is set, this is a G Suite account.

Users may register for Google Accounts without using Gmail or G Suite. When email does not contain a @gmail.com suffix and hd is absent Google is not authoritative and password or other challenge methods are recommended to verify the user. email_verfied can also be true as Google initially verified the user when the Google account was created, however ownership of the third party email account may have since changed.

Validate user information and create new account

Check whether either of the following conditions are true:

  • The Google Account ID, found in the assertion's sub field, is in your user database.
  • The email address in the assertion matches a user in your user database.

If either condition is true, prompt the user to link their existing account with their Google Account. To do so, respond to the request with an HTTP 401 error that specifies error=linking_error and gives the user's email address as the login_hint. The following is a sample response:

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

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

When Google receives a 401 error response with linking_error, Google sends the user to your authorization endpoint with login_hint as a parameter. The user completes account linking using the OAuth linking flow in their browser.

If neither condition is true, create a new user account with the information provided in the JWT. New accounts don't typically have a password set. It's recommended that you add Google Sign-In to other platforms to enable users to log in with Google across the surfaces of your application. Alternatively, you can email the user a link that starts your password recovery flow to allow the user to set a password to sign in on other platforms.

When the creation is completed, issue an access token and refresh token and return the values in a JSON object in the body of your HTTPS response, like in the following example:

{
  "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. 打开 Google API 控制台凭据页面。
  2. 创建或选择 Google API 项目。

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

验证您的实现

您可以通过使用验证实现的OAuth 2.0游乐场工具。

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

  1. 单击配置打开的OAuth 2.0配置窗口。
  2. OAuth流场中,选择客户端
  3. OAuth端点字段中,选择自定义
  4. 在相应字段中指定您的 OAuth 2.0 端点和您分配给 Google 的客户端 ID。
  5. 步骤1部分,不要选择任何谷歌范围。相反,将此字段留空或键入对您的服务器有效的范围(如果不使用 OAuth 范围,则输入任意字符串)。当您完成后,单击授权的API。
  6. 步骤2步骤3段,完成OAuth 2.0流程和验证每个步骤按预期工作。

您可以通过验证您的实现谷歌帐户链接演示工具。

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

  1. 点击登录在与谷歌按钮。
  2. 选择您要关联的帐户。
  3. 输入服务标识。
  4. (可选)输入您将请求访问的一个或多个范围。
  5. 单击开始演示
  6. 出现提示时,确认您可以同意并拒绝链接请求。
  7. 确认您被重定向到您的平台。