简化了与OAuth和Google登录的链接

概述

基于OAuth的Google登录精简链接OAuth链接之上添加了Google登录。这为Google用户提供了无缝的链接体验,还为使用非Google身份注册到您的服务的用户启用了帐户链接。

要执行OAuth和Google登录的帐户链接,请遵循以下常规步骤:

  1. 首先,请用户同意访问其Google个人资料。
  2. 使用他们的个人资料中的信息来检查用户帐户是否存在。
  3. 对于现有用户,请链接帐户。
  4. 如果在身份验证系统中找不到与Google用户匹配的内容,请验证从Google收到的ID令牌。然后,您可以基于ID令牌中包含的配置文件信息创建用户。

图3 。使用简化的链接在用户电话上进行帐户链接

帐户使用行业标准的OAuth 2.0隐式授权代码流进行链接。您的服务必须支持符合OAuth 2.0的授权令牌交换端点。此外,令牌交换端点应支持JSON Web令牌(JWT)断言,并实现checkcreateget intent。

获取您的Google API客户端ID和机密

在完成OAuth链接步骤时,您需要使用创建的项目来获取API客户端ID和密码。为此,请完成以下步骤:

  1. 打开Google API控制台的“凭据”页面。
  2. 创建或选择一个Google API项目。

    如果您的项目没有Web应用程序类型的客户端ID,请单击创建凭据> OAuth客户端ID创建一个。确保在“授权JavaScript来源”框中包含您网站的域。在执行本地测试或开发时,必须将http://localhosthttp://localhost:<port_number>Authorized JavaScript origins字段。

实施您的OAuth服务器

检查现有的用户帐户

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

如果您的身份验证系统中已经存在相应的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

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

令牌端点参数
intent对于这些请求,此参数的值为check
grant_type交换令牌的类型。对于这些请求,此参数的值为urn:ietf:params:oauth:grant-type:jwt-bearer
assertion JSON Web令牌(JWT),提供对Google用户身份的签名断言。 JWT包含的信息包括用户的Google帐户ID,名称和电子邮件地址。

当您的令牌交换端点接收到check请求时,它需要验证和解码JWT断言。

验证并解码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,但是此后第三方电子邮件帐户的所有权可能已更改。

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

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

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

如果任一条件为真,则用户已经注册。在这种情况下,返回如下响应:

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

{
  "account_found":"true",
}

然后,Google向用户显示一个链接同意对话框,并请求同意所需的范围,以便继续进行链接。 Google征得用户同意后,Google get向您的令牌端点发送一个get请求,以继续进行链接。

如果断言中指定的Google帐户ID或电子邮件地址都不匹配您数据库中的用户,则该用户尚未注册。在这种情况下,令牌交换端点需要使用HTTP 404错误进行响应,该错误指定为"account_found": "false" ,如以下示例所示:

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

{
  "account_found":"false",
}
当Google收到带有"account_found": "false"错误的404错误响应时,Google会向用户显示一个对话框,以请求同意创建新帐户并访问所需的范围。 Google征得用户同意后,Google会使用设置为intent参数的值调用您的令牌交换端点,以create该令牌,并包括一个ID令牌,该令牌包含请求中的用户个人资料信息。

处理自动链接

用户同意访问其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

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

令牌端点参数
intent对于这些请求,此参数的值为get
grant_type交换令牌的类型。对于这些请求,此参数的值为urn:ietf:params:oauth:grant-type:jwt-bearer
assertion JSON Web令牌(JWT),提供对Google用户身份的签名断言。 JWT包含的信息包括用户的Google帐户ID,名称和电子邮件地址。
scope可选:您已配置Google来向用户请求的所有范围。

当您的令牌交换端点接收到链接请求时,它需要验证并解码JWT断言。

验证并解码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,但是此后第三方电子邮件帐户的所有权可能已更改。

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

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

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

在某些情况下,对于用户而言,基于ID令牌的帐户链接可能会失败。如果出于某种原因这样做,则令牌交换端点需要使用HTTP 401错误进行响应,该错误指定error=linking_error ,如以下示例所示:

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会在请求中使用以下内容调用您的令牌交换端点:

  • 设置要createintent参数
  • 具有ID令牌和用户个人资料信息的JWT

通过Google登录处理帐户创建

当用户需要在您的服务上创建帐户时,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[&NEW_ACCOUNT_INFO]

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

令牌端点参数
intent对于这些请求,此参数的值为create
grant_type交换令牌的类型。对于这些请求,此参数的值为urn:ietf:params:oauth:grant-type:jwt-bearer
assertion JSON Web令牌(JWT),提供对Google用户身份的签名断言。 JWT包含的信息包括用户的Google帐户ID,名称和电子邮件地址。

assertion参数中的JWT包含用户的Google帐户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,但是此后第三方电子邮件帐户的所有权可能已更改。

验证用户信息并创建新帐户

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

  • 在断言的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",

  "expires_in": SECONDS_TO_EXPIRATION
}

验证您的实施

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

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

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

您可以使用Google帐户链接演示工具来验证您的实施。

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

  1. 点击使用Google登录按钮。
  2. 选择您要链接的帐户。
  3. 输入服务ID。
  4. (可选)输入一个或多个您将请求访问的作用域。
  5. 单击开始演示
  6. 出现提示时,请确认您同意并拒绝链接请求。
  7. 确认您已重定向到平台。