通过 OAuth 进行账号关联

OAuth 关联类型支持两种业界标准 OAuth 2.0 流程,即隐式和授权代码流程。

In the implicit code flow, Google opens your authorization endpoint in the user's browser. After successful sign in, you return a long-lived access token to Google. This access token is now included in every request sent from the Assistant to your Action.

In the authorization code flow, you need two endpoints:

  • The authorization endpoint, which is responsible for presenting the sign-in UI to your users that aren't already signed in and recording consent to the requested access in the form of a short-lived authorization code.
  • The token exchange endpoint, which is responsible for two types of exchanges:
    1. Exchanges an authorization code for a long-lived refresh token and a short-lived access token. This exchange happens when the user goes through the account linking flow.
    2. Exchanges a long-lived refresh token for a short-lived access token. This exchange happens when Google needs a new access token because the one it had expired.

Although the implicit code flow is simpler to implement, Google recommends that access tokens issued using the implicit flow never expire, because using token expiration with the implicit flow forces the user to link their account again. If you need token expiration for security reasons, you should strongly consider using the auth code flow instead.

实现 OAuth 账号关联

配置项目

如需将项目配置为使用 OAuth 关联,请按以下步骤操作:

  1. 打开 Actions 控制台,然后选择您要使用的项目。
  2. 点击开发标签页,然后选择帐号关联
  3. 启用帐号关联旁边的开关。
  4. 帐号创建部分,选择不,我只允许在我的网站上创建帐号
  5. 关联类型中,选择 OAuth授权代码

  6. 客户信息中:

    • 您的 Actions 向 Google 发出的客户端 ID 分配一个值,以标识来自 Google 的请求。
    • 记下 Google 向您的 Action 发放的 Client ID 的值;
    • 插入授权端点和令牌交换端点的网址。
  1. 点击保存

实现 OAuth 服务器

授权代码流程的 OAuth 2.0 服务器实现包含两个端点,您的服务通过 HTTPS 提供这些端点。第一个端点是授权端点,负责就数据访问征求用户意见或征求用户意见。授权端点会向尚未登录的用户呈现登录界面,并记录对于请求的访问权限。第二个端点是令牌交换端点,该端点用于获取加密字符串(称为令牌),用于授权 Action 用户访问您的服务。

当您的 Action 需要调用服务的某个 API 时,Google 会同时使用这些端点从用户处获取权限,以代表他们调用这些 API。

由 Google 发起的 OAuth 2.0 授权代码流程会话具有以下流程:

  1. Google 会在用户的浏览器中打开您的授权端点。如果针对 Action 的流程在纯语音设备上开始,Google 会将执行传输到手机。
  2. 用户登录(如果尚未登录),并向 Google 授予使用您的 API 访问其数据的权限(如果他们尚未授予权限)。

  3. 您的服务会创建授权代码,并将用户的浏览器重定向回 Google,并将授权代码附加到请求,从而将其返回给 Google。

  4. Google 会将授权代码发送到您的令牌交换端点,该端点会验证代码的真实性并返回访问令牌刷新令牌。访问令牌是短期有效的令牌,您的服务可接受该令牌作为访问 API 的凭据。刷新令牌是一个长期令牌,Google 可以存储该令牌,并在令牌过期时用它来获取新的访问令牌。

  5. 用户完成帐号关联流程后,从 Google 助理发送到您的执行方式网络钩子的每个后续请求都会包含访问令牌。

处理授权请求

当您的 Action 需要通过 OAuth 2.0 授权代码流程执行帐号关联时,Google 会通过包含以下参数的请求将用户发送到您的授权端点:

授权端点参数
client_id 您向 Google 注册的 Google 客户端 ID。
redirect_uri 您要将该请求的响应发送到的网址。
state 在重定向 URI 中原封不动地传回 Google 的簿记值。
scope 可选:一组用空格分隔的范围字符串,用于指定 Google 请求授权的数据。
response_type 字符串 code

例如,如果您的授权端点位于 https://myservice.example.com/auth,请求可能如下所示:

GET https://myservice.example.com/auth?client_id=GOOGLE_CLIENT_ID&redirect_uri=REDIRECT_URI&state=STATE_STRING&scope=REQUESTED_SCOPES&response_type=code

为了让授权端点能够处理登录请求,请执行以下步骤:

  1. 验证 client_id 与您向 Google 注册的 Google 客户端 ID 是否匹配,以及 redirect_uri 是否与 Google 为您的服务提供的重定向网址匹配。这些检查对于防止授予对意外或配置错误的客户端应用的访问权限非常重要。

    如果您支持多个 OAuth 2.0 流程,另请确认 response_typecode

  2. 检查用户是否登录了您的服务。如果用户没有登录,请完成服务的登录或注册流程。

  3. 生成 Google 用于访问您的 API 的授权代码。授权代码可以是任何字符串值,但它必须唯一地表示用户、令牌的客户端和代码的到期时间,并且必须不可猜测。您通常会签发大约 10 分钟后失效的授权代码。

  4. 确认 redirect_uri 参数指定的网址采用以下格式:

    https://oauth-redirect.googleusercontent.com/r/YOUR_PROJECT_ID
    YOUR_PROJECT_ID 是在 Actions 控制台的 Project settings 页面上找到的 ID。

  5. 将用户浏览器重定向到 redirect_uri 参数指定的网址。通过附加 codestate 参数进行重定向时,请添加您刚刚生成的授权代码以及未经修改的原始状态值。以下是生成的网址的示例:

    https://oauth-redirect.googleusercontent.com/r/YOUR_PROJECT_ID?code=AUTHORIZATION_CODE&state=STATE_STRING

处理令牌交换请求

您的服务的令牌交换端点负责两种令牌交换:

  • 将授权代码换成访问令牌和刷新令牌
  • 将刷新令牌交换为访问令牌

令牌交换请求包含以下参数:

令牌交换端点参数
client_id 将请求来源标识为 Google 的字符串。该字符串必须在您的系统中注册为 Google 的唯一标识符。
client_secret 您为服务向 Google 注册的密钥字符串。
grant_type 要交换的令牌的类型。可以选择 authorization_coderefresh_token
code 当为 grant_type=authorization_code 时,Google 从您的登录端点或令牌交换端点收到的代码。
redirect_uri 如果此参数为 grant_type=authorization_code,则此参数是在初始授权请求中使用的网址。
refresh_token 如果为 grant_type=refresh_token,则表示 Google 从令牌交换端点收到的刷新令牌。
将授权代码换成访问令牌和刷新令牌

用户登录后,您的授权端点向 Google 返回短期有效的授权代码后,Google 会向令牌交换端点发送请求,以用授权代码换取访问令牌和刷新令牌。

对于这些请求,grant_type 的值为 authorization_codecode 的值为您之前授予 Google 的授权代码的值。以下示例展示了用授权代码交换访问令牌和刷新令牌的请求:

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

client_id=GOOGLE_CLIENT_ID&client_secret=GOOGLE_CLIENT_SECRET&grant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=REDIRECT_URI

为了用访问令牌和刷新令牌交换授权代码,令牌交换端点会响应执行以下步骤的 POST 请求:

  1. 验证 client_id 是否将请求来源标识为已获授权的来源,以及 client_secret 是否与预期值匹配。
  2. 请验证以下内容:
    • 授权代码有效且未过期,并且请求中指定的客户端 ID 与授权代码关联的客户端 ID 一致。
    • redirect_uri 参数指定的网址与初始授权请求中使用的值相同。
  3. 如果您无法验证上述所有条件,则返回正文为 {"error": "invalid_grant"} 的 HTTP 400 Bad Request 错误。
  4. 否则,使用授权代码中的用户 ID 生成刷新令牌和访问令牌。这些令牌可以是任何字符串值,但必须唯一地代表用户和令牌所面向的客户端,并且不得被猜到。对于访问令牌,还要记录令牌的到期时间(通常在令牌颁发一小时后)。刷新令牌不会过期。
  5. 在 HTTPS 响应的正文中返回以下 JSON 对象:
    {
    "token_type": "Bearer",
    "access_token": "ACCESS_TOKEN",
    "refresh_token": "REFRESH_TOKEN",
    "expires_in": SECONDS_TO_EXPIRATION
    }
    

Google 会存储用户的访问令牌和刷新令牌,并记录访问令牌的失效时间。访问令牌到期后,Google 会使用刷新令牌从您的令牌交换端点获取新的访问令牌。

将刷新令牌交换为访问令牌

访问令牌到期后,Google 会向令牌交换端点发送请求,以用刷新令牌换取新的访问令牌。

对于这些请求,grant_type 的值为 refresh_tokenrefresh_token 的值为您之前授予 Google 的刷新令牌的值。以下示例展示了将刷新令牌交换为访问令牌的请求:

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

client_id=GOOGLE_CLIENT_ID&client_secret=GOOGLE_CLIENT_SECRET&grant_type=refresh_token&refresh_token=REFRESH_TOKEN

为了用刷新令牌换取访问令牌,令牌交换端点会响应执行以下步骤的 POST 请求:

  1. 验证 client_id 是否将请求来源标识为 Google,以及 client_secret 是否与预期值匹配。
  2. 验证刷新令牌是否有效,以及请求中指定的客户端 ID 是否与与刷新令牌关联的客户端 ID 匹配。
  3. 如果您无法验证上述所有条件,则返回正文为 {"error": "invalid_grant"} 的 HTTP 400 Bad Request 错误。
  4. 否则,请使用刷新令牌中的用户 ID 生成访问令牌。这些令牌可以是任何字符串值,但必须唯一地代表用户和令牌所面向的客户端,并且不得被猜到。对于访问令牌,还要记录令牌的到期时间(通常在令牌颁发一小时后)。
  5. 在 HTTPS 响应的正文中返回以下 JSON 对象:
    {
    "token_type": "Bearer",
    "access_token": "ACCESS_TOKEN",
    "expires_in": SECONDS_TO_EXPIRATION
    }

为身份验证流程设计语音界面

检查用户是否已通过验证,并启动账号关联流程

  1. Actions 控制台中打开您的 Actions Builder 项目。
  2. 创建一个新场景,以开始在您的 Action 中关联帐号:
    1. 点击 Scenes
    2. 点击添加 (+) 图标以添加新场景。
  3. 在新创建的场景中,点击条件对应的添加 图标。
  4. 添加一个条件,用于检查与对话关联的用户是否为经过验证的用户。如果检查失败,您的 Action 无法在对话期间执行帐号关联,而应回退以提供对不需要帐号关联的功能的访问权限。
    1. 条件下的 Enter new expression 字段中,输入以下逻辑:user.verificationStatus != "VERIFIED"
    2. Transition 下,选择不需要帐号关联的场景,或选择访客专享功能的入口点。

  1. 点击条件对应的添加 图标。
  2. 添加一个条件,以便在用户没有关联的身份时触发帐号关联流程。
    1. 条件下的 Enter new expression 字段中,输入以下逻辑:user.verificationStatus == "VERIFIED"
    2. 转换下,选择帐号关联系统场景。
    3. 点击保存

保存后,系统会将一个名为 <SceneName>_AccountLinking 的新帐号关联系统场景添加到您的项目中。

自定义账号关联场景

  1. Scenes 下,选择帐号关联系统场景。
  2. 点击发送提示并添加一小句话,向用户说明该 Action 为何需要访问其身份(例如“保存您的偏好设置”)。
  3. 点击保存

  1. 条件下,点击如果用户成功完成帐号关联
  2. 配置当用户同意关联其账号时该流程应如何处理。 例如,调用网络钩子以处理所需的任何自定义业务逻辑,并转换回原始场景。
  3. 点击保存

  1. 条件下方,点击如果用户取消或忽略帐号关联
  2. 配置如果用户不同意关联其帐号,相应流程应如何处理。例如,发送确认消息并重定向到提供无需进行帐号关联的功能的场景。
  3. 点击保存

  1. 条件下方,点击如果出现系统或网络错误
  2. 配置帐号关联流程因系统或网络错误而无法完成时应采取的流程。例如,发送确认消息并重定向到提供无需进行帐号关联的功能的场景。
  3. 点击保存

处理数据访问请求

如果 Google 助理请求包含访问令牌,请先检查访问令牌是否有效(且未过期),然后从数据库中检索关联的用户帐号。