如何使用从 Alexa Smart Home Skill Directive 收到的 BearerToken 使用 AWS Lambda 和 NodeJS 识别用户电子邮件和个人资料?

How to use BearerToken received from Alexa Smart Home Skill Directive to identify the user email and profile using AWS Lambda with NodeJS?

一些背景知识: 我正在使用 Amazon Alexa 智能家居技能来控制一些物联网设备,这些设备使用以 NodeJS 编写的 AWS Lambda 函数作为默认端点。我已经将 Alexa Skill 与 lambda 函数相关联,创建了一个 Login With Amazon 安全配置文件,并为 alexa skill 启用了帐户链接。现在,我已经关闭了“发送 Alexa 事件”权限,因为这在响应事件方面增加了自身的复杂性。目前我的技能处于 Beta 测试阶段。

我的用例:我有 3 个不同的客户,他们有不同的姓名、不同的电子邮件 ID 和不同的亚马逊账户。所有 3 位客户都在他们的 alexa 应用程序中下载了我的 Alexa 技能,并将其链接到他们的亚马逊账户。所有这些客户家里都有 2 台设备(灯和风扇),它们连接到我的云。我已将他们的设备信息与他们的电子邮件 ID 存储在我的云数据库中,如下所示:

  1. bob@gmail.com,灯光 -> 打开,风扇 -> 关闭
  2. jhonny@gmail.com,灯光 -> 关闭,风扇 -> 关闭
  3. alice@gmail.com,灯光 -> 关闭,风扇 -> 关闭

现在假设其中一位顾客 Jhonny 想要开灯。因此,当 Jhonny 要求 Alexa 打开灯时,我的 lambda 函数将收到以下指令:

{
    "directive": {
        "header": {
            "namespace": "Alexa.PowerController",
            "name": "TurnOn",
            "payloadVersion": "3",
            "messageId": "003edc9f-68ad-4618-a982-f73fc37d25be",
            "correlationToken": "AAAAAAAAAQDaOl/vVaX+GlN2m9SvqFri/AEAAAAAAACdKWHpFqLjNmk4Y6DeNDRoGjdMODTZW6kkXKZoe3Ya289F45koL55JJmcv05BVBFceJH9FXOb/YJcwQH+xk4yx1KMa92zoBTl7jNayw8r4Pzfvd8oO486Fx3q3g35xAeNfNPbHalpV7ftYw86qXsurSfRkk2vgqWQu9CsYH013/fqY3ojnQySOIu31BAaiGWI4Pur4x/2zg3HOBKQkzguIMVmWxZP7de+VCoD5cqEQYOoE7ACQi6NAGPJHbS5cLY/2FBO163wLeDGoJZ8sfEcroQYpqGCkQ+KTLolZ2SDle2VvubB7Ntc0Rzpfg45dGFj6T2Mb8mP/PgxH+mfgTzjTUgTq9N6wfA/zAvXWnpCkC6/3nFUS3NzsYqaa+ff/Zm7smI645BJU6BJu19f6oRi4sjK+mqQLJDax9orIjrZ2Yd6ASq2Z31lZthgDFTyqe5b+JTP1Sp4j6S9uayIxyGj59eYxB1YMCxrm3clMJRKBphwiNrGewcGWZ2Qb2saB3Ctmy+fxPmasqFfbxnf4LYBib0VQrOpmvbRR8u3CT14ltCTubEA/iw2krMfqlM6xvzukFyRj++8oOfXNMGefGfe11GlcbDWXVDoJURbOjbaGGqks6jdZ0TOD/TxepHHnlBfWgHA9pfibikqBsB8NXvp"
        },
        "endpoint": {
            "scope": {
                "type": "BearerToken",
                "token": "Atza|IwEBIAdKRpKJ5emckzWUGPRR-O9_Pg2mWV0BDxgfUCJbcqNNWSb8zfl4ueaG8eu-1YZOyA3qTyJVnn9X9JtYOfzcJClEROo1bDoGMs_VEeA-7aTZK5RKMWIHIbz8BIdmt6Ncr6bF8WkZnhNjS4q-qim4ICRfatrIaD2C0KXykXNJnYco11aSR7tGkhcwKm27jjPoewap2k07BqMhmaaB2ie_-v_2ojbDWmKW95MuCeYMoZmYTmNh4o4A5YH_UlFO9atUTjr9oA4ROwL_3R02Yi_VYRf8iZJCF4FmxiXRGGMqwEMF1KNeV6zcUFAjBIvSORAOpSO6iRySn9lZeAazywrdCIYBc8LFnDtGQIeYdKXSW39qYFbfC-Hy3RCJwuVPQPzS9jX99pyYZ0q1ZuRRUg"
            },
            "endpointId": "Light",
            "cookie": {}
        },
        "payload": {}
    }
}

因此,为了打开 Jhonny 家的灯,我必须以某种方式使用来自 Alexa 指令的 BearerToken 从 Amazon OAuth 提供商服务中检索 Jhonny 的电子邮件地址或用户 ID。那是对的吗?否则,我不知道要更改数据库中的哪个条目。

现在我的问题是:我如何使用这个BearerToken 来获取用户ID 和其他信息?为此,我需要 NodeJS 中的一些示例代码,我可以在我的 Lambda 函数中实现这些代码。

我们可以通过使用不记名令牌向主机发送 HTTP GET 请求来获取用户配置文件。下面 link 提供了一种通过提供不记名令牌来验证用户个人资料的漂亮且非常简单的方法,并提供基本的理解。

https://reqbin.com/req/5k564bhv/get-request-bearer-token-authorization-header-example

如果您使用 Login With Amazon 作为您的 OAuth2.0 身份提供商,那么只需在上面提及以下项目 link:

  • 主持人URL:https://api.amazon.com/user/profile
  • Select授权
  • Select 不记名令牌
  • 复制从 alexa 指令接收到的承载令牌并将其粘贴到框中(通常以 Atza|xxxyyyzzz 开头)
  • 点击发送
  • 如果您输入了正确的主机 URL、路径和不记名令牌,并且您的不记名令牌尚未过期,那么您应该会看到 200 响应代码以及用户配置文件详细信息,例如 user_id 、姓名、电子邮件等
  • 如果您的持有者令牌已过期,您只需让 Alexa 操作其中一台设备,并获取新的持有者令牌以从 AWS CloudWatch 日志中进行验证。
  • Bearer Token 通常在 3600 秒后过期。

现在回答我最初的问题,这是一个使用 NodeJS 的 AWS Lambda 的简单代码片段,它对我有用:

const https = require('https');

const handlerInput = {
    "directive": {
        "header": {
            "namespace": "Alexa.PowerController",
            "name": "TurnOn",
            "payloadVersion": "3",
            "messageId": "f8107929-cc45-4bfe-b7ae-eb9b61c09d5c",
            "correlationToken": "AAAAAAAAAQDaOl/vVaX+GlN2m9SvqFri/AEAAAAAAADsiP98Ew8DnuYv7JWgKtb4W/WFyycSA33aKocZD0AOnl5+PmKQuqgx2p/CT5Efln0lbORsb9jSB/zB9s3RLgO2dCG3B5/b6jzVzch98o3ULz61HgoAPz/ZsZIFWXyfqUhLrdwyggsnInsCOeUOHpRZ15VLJ6oEyW7zNE6MCfcH+SfPB2BMr7ex1wP3Ghz7fPNIwTeMhV/ZstF/mF7K74gH1psCLgGLzdc4YYtRtz7KxHCS3I8eUd2UQZ4NyCf9gVktphgTzXu6ezBp5qoexsmgkHQ0duK0zF2HoKvY8jhVD8MH2unJQLbcn5VQyMololRkQ3E1orZXy86t5Ls1ILC6qbtT1tKbeTqVChTBiCh5jczOuSW5mES+1fmhbBxx1HbE8OXhfSNcXcE2VAY49LTBafnUQ7NftkDTRRFJKgL+IIdBq283SgZNFWmJxwGBN5OXC5vKQGD1QzHUlhb91I9xQ1bmx9jKPP8tBj0ydkY/nD++34zQfxDObbX7cmVs4/4nTKNhm2o2RGZmVyMr6PzhvRzq777Vi+keJM+qXQxSRO0NzdSFWdBDBHCL46nAvwlOjNIvHsOoaODEVNK5HzT6a+H4PsbDazZgfXB4QFjIb9FvFZ6wmlGM1iOU/sN40ro3pQ12IE+Dn+tweemgRaRucn/mT0/E5IRgnliR"
        },
        "endpoint": {
            "scope": {
                "type": "BearerToken",
                "token": "Atza|IwEBINNVA3FmduJsc_iA2Jpo2hBfl2XKPCzujkVWgix2TychTLTuAbnchakWuNiNGnpimsAx2dwoJCoUbarJXRv5a1ECrYoVi-SnAt6xxVvxi4CX4zd13rCq1wddl90Lp71bJDq9qfvlS-d9KillCwRYh4BBY0GegU16hM4RC07nBMvra07Dfe_kq9PIzu4yOeSaLwobiEAhNex3Qyo4n0jW6iyB5YzKUe2UAu0pQZmkeNjRmhPVpy0L4Pv539O87yXBT7iQ8pkhZBKeZWcuFltCN2_v1SfhMNQbnLlt_CMFry-eQg0t9oSaweCJOvrAYVKmg0cd-gNd27ttqZkmD0rMGERmTNkij_rTQJ9iiQKjEW1Xx_7TWVniqda1Hp_o1wA5p4aT6BYQKkoFrRm4pSa3iUA"
            },
            "endpointId": "Light",
            "cookie": {}
        },
        "payload": {}
    }
};

exports.handler = async (event) => {
    // TODO implement
    const accessToken = handlerInput.directive.endpoint.scope.token;
    console.log(accessToken);
    const info = await getUserInfo(accessToken);
    console.log(`info: ${JSON.stringify(info)}`);
    const response = {
        user_ID: info.user_id,
        name: info.name,
        email: info.email,
    };
    return response;
};

async function getUserInfo(accessToken) {
    return new Promise((resolve, reject) => {
        const options = {
            "method": "GET",
            "hostname": "api.amazon.com",
            "path": "/user/profile",
            "headers": {
                "Authorization": `Bearer ${accessToken}`
            }
        };
        let req = https.request(options, (response) => {
            let returnData = '';

            response.on('data', (chunk) => {
                returnData += chunk;
            });

            response.on('end', () => {
                resolve(JSON.parse(returnData));
            });

            response.on("error", (error) => {
                reject(error);
            });
        });
        req.end();
    });
}

确保用最新的副本替换上面代码中的 handlerInput。您可以从日志 -> 日志组下的 AWS CloudWatch 获取它。

下面的 YouTube 视频也提供了有关创建技能、帐户 link 和通过访问令牌识别用户的整个过程的非常有用的信息:

https://www.youtube.com/watch?v=NrBBM9XhzG0

遗憾的是,视频没有解释如何实现智能家居技能,尽管它非常相似。