适用于 Google 助理的 Google 登录功能为用户和开发者提供最简单、最便捷的用户体验,从而实现帐号关联和帐号创建。您的 Action 可以在对话期间请求访问用户的 Google 个人资料,包括用户的姓名、电子邮件地址和个人资料照片。
个人资料信息可用于在您的 Action 中打造个性化用户体验。如果您的应用在其他平台上运行,并且这些应用使用 Google 登录机制,您还可以查找并关联到现有用户的帐号、创建新帐号,并建立与用户直接沟通的渠道。
如需通过 Google 登录功能执行帐号关联,您需要请求用户同意访问其 Google 个人资料。然后,您可以使用其个人资料中的信息(例如其电子邮件地址)来识别您系统中的用户。
实现 Google 登录账号关联
请按照以下部分中的步骤将 Google 登录帐号关联添加到您的 Action。
配置项目
要将您的项目配置为使用 Google 登录账号关联功能,请按以下步骤操作:
- 打开 Actions 控制台并选择一个项目。
- 点击开发标签页,然后选择帐号关联。
- 启用帐号关联旁边的开关。
- 在帐号创建部分,选择是。
在关联类型中,选择 Google 登录。
打开 Client Information,并记下 Google 向您的 Action 签发的 Client ID 的值。
点击保存。
为身份验证流程设计语音界面
检查用户是否已通过验证,并启动账号关联流程
- 在 Actions 控制台中打开您的 Actions Builder 项目。
- 创建一个新场景,以开始在您的 Action 中关联帐号:
- 点击 Scenes。
- 点击添加 (+) 图标以添加新场景。
- 在新创建的场景中,点击条件对应的添加 add 图标。
- 添加一个条件,用于检查与对话关联的用户是否为经过验证的用户。如果检查失败,您的 Action 无法在对话期间执行帐号关联,而应回退以提供对不需要帐号关联的功能的访问权限。
- 在条件下的
Enter new expression
字段中,输入以下逻辑:user.verificationStatus != "VERIFIED"
- 在 Transition 下,选择不需要帐号关联的场景,或选择访客专享功能的入口点。
- 在条件下的
- 点击条件对应的添加 add 图标。
- 添加一个条件,以便在用户没有关联的身份时触发帐号关联流程。
- 在条件下的
Enter new expression
字段中,输入以下逻辑:user.verificationStatus == "VERIFIED"
- 在转换下,选择帐号关联系统场景。
- 点击保存。
- 在条件下的
保存后,系统会将一个名为 <SceneName>_AccountLinking
的新帐号关联系统场景添加到您的项目中。
自定义账号关联场景
- 在 Scenes 下,选择帐号关联系统场景。
- 点击发送提示并添加一小句话,向用户说明该 Action 为何需要访问其身份(例如“保存您的偏好设置”)。
- 点击保存。
- 在条件下,点击如果用户成功完成帐号关联。
- 配置当用户同意关联其账号时该流程应如何处理。 例如,调用网络钩子以处理所需的任何自定义业务逻辑,并转换回原始场景。
- 点击保存。
- 在条件下方,点击如果用户取消或忽略帐号关联。
- 配置如果用户不同意关联其帐号,相应流程应如何处理。例如,发送确认消息并重定向到提供无需进行帐号关联的功能的场景。
- 点击保存。
- 在条件下方,点击如果出现系统或网络错误。
- 配置帐号关联流程因系统或网络错误而无法完成时应采取的流程。例如,发送确认消息并重定向到提供无需进行帐号关联的功能的场景。
- 点击保存。
在后端访问个人资料信息
在用户授权您的操作访问他们的 Google 个人资料后,您将收到一个 Google ID 令牌,该令牌包含用户在您操作的每个后续请求中获得的 Google 个人资料信息。
如需访问用户的个人资料信息,您需要先执行以下操作来验证和解码令牌:
- 使用适用于您的语言的 JWT 解码库来解码令牌,并使用 Google 的公钥(提供 JWK 或 PEM 格式)来验证令牌的签名。
- 验证令牌的颁发者(已解码令牌中的
iss
字段)是否为https://accounts.google.com
,以及目标对象(已解码令牌中的aud
字段)是否为 Google 为您的 Action 颁发的客户端 ID 的值,该值已在 Actions 控制台中分配给您的项目。
以下是已解码令牌的示例:
{ "sub": 1234567890, // The unique ID of the user's Google Account "iss": "https://accounts.google.com", // The token's issuer "aud": "123-abc.apps.googleusercontent.com", // Client ID assigned to your Actions project "iat": 233366400, // Unix timestamp of the token's creation time "exp": 233370000, // Unix timestamp of the token's expiration time "name": "Jan Jansen", "given_name": "Jan", "family_name": "Jansen", "email": "jan@gmail.com", // If present, the user's email address "locale": "en_US" }
如果您使用 Node.js 版 Actions on Google Fulfillment 库,该库将负责验证和解码令牌,并为您提供对配置文件内容的访问权限,如以下代码段所示。
... const app = conversation({ // REPLACE THE PLACEHOLDER WITH THE CLIENT_ID OF YOUR ACTIONS PROJECT clientId: CLIENT_ID, }); ... // Invoked on successful completion of account linking flow, check if we need to // create a Firebase user. app.handle('linkAccount', async conv => { let payload = conv.headers.authorization; if (payload) { // Get UID for Firebase auth user using the email of the user const email = payload.email; if (!conv.user.params.uid && email) { try { conv.user.params.uid = (await auth.getUserByEmail(email)).uid; } catch (e) { if (e.code !== 'auth/user-not-found') { throw e; } // If the user is not found, create a new Firebase auth user // using the email obtained from Google Assistant conv.user.params.uid = (await auth.createUser({email})).uid; } } } });
处理数据访问请求
如需处理数据访问请求,只需验证由 Google ID 令牌声明的用户是否已存在于您的数据库中。以下代码段举例说明了如何检查 Firestore 数据库中是否已存在用户的订单:
... app.handle('Place_Order', async conv => { const order = conv.session.params.order; const userDoc = dbs.user.doc(conv.user.params.uid); const orderHistory = userDoc.collection("orderHistory"); if (orderHistory) { // Order history exists, so the user already placed an order. // Update counter for order type. await orderHistory.doc(order).update({ count: admin.firestore.FieldValue.increment(1)}); } else { // First order they place await orderHistory.doc(order).set({ option: order, count: 1}); options.forEach(opt => { if (opt != order) { orderHistory.doc(opt).set({ option: opt, count: 0}); } }); } return conv.add(`Your ${order} has been placed. ` + 'Thanks for using Boba Bonanza, see you soon!'); });