每个 smart home Action 都必须包含用户身份验证机制。
通过身份验证,你可以将用户的 Google 帐号与用户在你的身份验证系统中的帐号相关联。这样,您就可以在您的执行方式接收到智能家居 intent 时识别用户。Google 智能家居仅支持包含授权代码流程的 OAuth。
本页介绍如何设置 OAuth 2.0 服务器,以使其可与 smart home Action 搭配使用。
授权代码流程
授权代码流程的 OAuth 2.0 服务器实现包含两个端点,您的服务可通过 HTTPS 提供这两个端点。第一个端点是授权端点,负责查找数据或征得用户同意来访问数据。授权端点会向尚未登录的用户显示登录界面,并记录是否同意请求的访问权限。第二个端点是令牌交换端点,用于获取加密令牌(称为令牌),这些令牌可授权用户访问您的服务。
当 Google 应用需要调用您的某个服务 API 时,Google 会结合使用这些端点来向用户请求代表他们调用这些 API 的权限。
由 Google 发起的 OAuth 2.0 授权代码流程会话具有以下流程:
- Google 会在用户的浏览器中打开您的授权端点。如果 Action 在纯语音设备上启动了相应 Action,Google 会将执行操作传输到手机。
- 用户登录(如果尚未登录),并授予 Google 使用您的 API 访问其数据的权限(如果他们尚未授予权限)。
- 您的服务会创建一个授权代码并将其返回给 Google。为此,请使用附加到请求中的授权代码将用户浏览器重定向回 Google。
- Google 将授权代码发送到您的令牌交换端点,该端点会验证代码的真实性并返回访问令牌和刷新令牌。访问令牌是服务接受的用于访问 API 的短期令牌。刷新令牌是长期令牌,Google 可以存储该令牌并用于在到期时获取新的访问令牌。
- 在用户完成帐号关联流程后,Google 发送的每个后续请求都包含一个访问令牌。
处理授权请求
当您需要使用 OAuth 2.0 授权代码流执行帐号关联时,Google 会将用户发送到您的授权端点,其中包含以下参数:
授权端点参数 | |
---|---|
client_id |
您分配给 Google 的客户端 ID。 |
redirect_uri |
您向此请求发送响应的网址。 |
state |
在重定向 URI 中传递回 Google 的簿记值保持不变。 |
scope |
可选:以空格分隔的范围字符串集,用于指定 Google 请求授权的数据。 |
response_type |
在响应中返回的值的类型。对于 OAuth 2.0 授权代码流,响应类型始终为 code 。 |
user_locale |
采用 RFC5646 格式的 Google 帐号语言设置,用于以用户的首选语言本地化内容。 |
例如,如果您的授权端点位于 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&user_locale=LOCALE
为了让授权端点能够处理登录请求,请执行以下步骤:
- 验证
client_id
与您为 Google 分配的客户端 ID 是否一致,以及redirect_uri
是否与 Google 为您的服务提供的重定向网址匹配。这些检查非常重要,可防止授予对意外或配置错误的客户端应用的访问权限。如果您支持多个 OAuth 2.0 流程,还应确认response_type
为code
。 - 检查用户是否已登录您的服务。如果用户未登录,请完成服务的登录或注册流程。
- 生成授权代码,供 Google 用于访问您的 API。授权代码可以是任何字符串值,但必须以唯一方式表示用户、令牌的客户端和代码的到期时间,并且不可猜测。您通常会颁发在大约 10 分钟后过期的授权代码。
- 确认
redirect_uri
参数指定的网址采用以下格式:https://oauth-redirect.googleusercontent.com/r/YOUR_PROJECT_ID https://oauth-redirect-sandbox.googleusercontent.com/r/YOUR_PROJECT_ID
- 将用户的浏览器重定向到由
redirect_uri
参数指定的网址。通过附加code
和state
参数进行重定向时,请包含您刚刚生成的授权代码以及未经修改的原始状态值。以下是生成的网址的示例: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_code 或 refresh_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_code
,code
的值为您之前授予 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
请求:
- 验证
client_id
是否将请求来源标识为已获授权的来源,以及client_secret
是否与预期值匹配。 - 验证授权代码有效且未过期,请求中指定的客户端 ID 与授权代码关联的客户端 ID 一致。
- 确认
redirect_uri
参数指定的网址与初始授权请求中使用的值完全相同。 - 如果您无法验证上述所有条件,则返回正文为
{"error": "invalid_grant"}
的 HTTP 400 Bad Request 错误。 - 否则,请使用授权代码中的 User-ID 生成刷新令牌和访问令牌。这些令牌可以是任何字符串值,但必须以唯一性表示令牌和令牌对应的用户,且不得猜测。对于访问令牌,还应记录令牌的过期时间,通常是在您发出令牌后一小时。刷新令牌不会过期。
- 在 HTTPS 响应的正文中返回以下 JSON 对象:
{ "token_type": "Bearer", "access_token": "ACCESS_TOKEN", "refresh_token": "REFRESH_TOKEN", "expires_in": SECONDS_TO_EXPIRATION }
Google 会为用户存储访问令牌和刷新令牌,并记录访问令牌的有效期。访问令牌到期后,Google 会使用刷新令牌从令牌交换端点获取新的访问令牌。
使用刷新令牌交换访问令牌
访问令牌到期后,Google 会向令牌交换端点发送请求,以刷新令牌换取新的访问令牌。
对于这些请求,grant_type
的值为 refresh_token
,refresh_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
请求:
- 验证
client_id
是否将请求来源标识为 Google,以及client_secret
是否与预期值匹配。 - 验证刷新令牌是否有效,以及请求中指定的客户端 ID 是否与刷新令牌关联的客户端 ID 一致。
- 如果您无法验证上述所有条件,则返回正文为
{"error": "invalid_grant"}
的 HTTP 400 Bad Request 错误。 - 否则,请使用刷新令牌中的用户 ID 生成访问令牌。这些令牌可以是任何字符串值,但必须唯一表示令牌的用户和客户端,并且不得猜测。对于访问令牌,还应记录令牌的到期时间,通常是在您发出令牌后一小时。
- 在 HTTPS 响应的正文中返回以下 JSON 对象:
{ “token_type”: “Bearer”、 “access_token”: “ACCESS_TOKEN”; “expires_in”: SECONDS_TO_EXPIRATION }
处理 userinfo 请求
userinfo 端点是受 OAuth 2.0 保护的资源,会返回有关关联用户的声明。实现和托管 userinfo 端点是可选的,但以下用例除外:
成功从您的令牌端点检索到访问令牌后,Google 向您的 userinfo 端点发送请求,以检索关于关联用户的基本个人资料信息。
userinfo 端点请求标头 | |
---|---|
Authorization header |
Bearer 类型的访问令牌。 |
例如,如果您的 userinfo 端点在 https://myservice.example.com/userinfo
处可用,则请求可能如下所示:
GET /userinfo HTTP/1.1 Host: myservice.example.com Authorization: Bearer ACCESS_TOKEN
如需让您的 userinfo 端点处理请求,请执行以下步骤:
- 从授权标头中提取访问令牌,并返回与访问令牌相关联的用户的信息。
- 如果访问令牌无效,使用
WWW-Authenticate
响应标头会返回 HTTP 401 Unauthorized 错误。下面是 userinfo 错误响应的示例:HTTP/1.1 401 Unauthorized WWW-Authenticate: error="invalid_token", error_description="The Access Token expired"
如果在关联过程中返回 401 Unauthorized 或任何其他失败的错误响应,该错误将无法恢复,检索到的令牌将会被舍弃,用户需要再次启动关联流程。 如果访问令牌有效,请在 HTTPS 响应的正文中返回以下 JSON 对象并相应地返回 HTTP 200 响应:
{ "sub": "USER_UUID", "email": "EMAIL_ADDRESS", "given_name": "FIRST_NAME", "family_name": "LAST_NAME", "name": "FULL_NAME", "picture": "PROFILE_PICTURE", }
如果您的 userinfo 端点返回 HTTP 200 成功响应,则系统会将检索到的令牌和声明注册到用户的 Google 帐号。userinfo 端点响应 sub
用于识别系统中用户的唯一 ID。 email
用户的电子邮件地址。 given_name
可选:用户的名字。 family_name
可选:用户的姓氏。 name
可选:用户的全名。 picture
可选:用户的个人资料照片。