通过 Cognito 身份验证用户使用 Web 套接字连接到 AWS IoT

connect to AWS IoT using web socket with Cognito authenticated users

我正在尝试使用浏览器中的网络套接字连接到 AWS IoT。

我试过这个例子: https://github.com/awslabs/aws-iot-examples/tree/master/mqttSample

另一个稍微修改了一点,因此它可以与 Cognito Identity 池登录用户一起使用。 https://github.com/dwyl/learn-aws-iot/blob/master/src/js/utils/request.js#L27

如果我使用具有有效 IoT 策略的 IAM 用户,我可以成功连接,但如果我使用用户凭据,我会收到“101 切换协议”响应,但随后它会关闭。

与经过身份验证的用户关联的 IAM 角色是正确的,我可以签署请求并执行其他私有操作,例如调用 APIG 端点。套接字连接也没有响应 403。所以这可能不是权限问题。

还能是什么?

对于未经身份验证的 Cognito 身份,"Identity pool anauthenticated" 角色足以允许连接到 IoT MQTT 代理。然而,对于经过身份验证的 Cognito 身份,需要两件事:

  1. "Identity pool authenticated" 角色必须允许访问您需要的 IoT 操作(例如连接、发布等)。

  2. 您必须使用 AttachPrincipalPolicy API

  3. 将 IoT 策略(与附加到您设备的策略完全相同)附加到认知身份

第 2 步是我今天早些时候卡住的地方,因为在任何地方都不是特别清楚这是必需的。

据我所知,无法从任何 AWS 网站将 IoT 策略附加到 Cognito 用户。但是,如果您的计算机上安装了 AWS 命令​​行界面,则可以从那里进行操作。该命令如下所示:

aws iot attach-principal-policy --policy-name <iot-policy-name> --principal <cognito-identity-id>

可以使用 Federated Identities > Your Pool > Identity browser 找到 Cognito 身份 ID,或者您也可以在对 CognitoIdentityCredentials.get 电话的回复中找到它。看起来像这样 us-east-1:ba7cef62-f3eb-5be2-87e5-fffbdeed2824

对于生产系统,您显然希望自动附加此策略,可能在用户注册时使用 lambda 函数。

可以在 this page:

上找到讨论需要附加 IoT 策略的文档部分

For an authenticated Amazon Cognito identity to publish MQTT messages over HTTP on topic1 in your AWS account, you must specify two policies, as outlined here. The first policy must be attached to an Amazon Cognito identity pool role and allow identities from that pool to make a publish call. The second policy is attached to an Amazon Cognito user using the AWS IoT AttachPrincipalPolicy API and allows the specified Amazon Cognito user access to the topic1 topic.

这是一个代码示例,用于从 Lambda (NodeJS) 函数将 IoT 策略附加到 Cognito 用户 ID。

function attachPrincipalPolicy(device_id, cognito_user_id) {
    const iotMgmt = new AWS.Iot();
    return new Promise(function(resolve, reject) {
        let params = {
            policyName: device_id + '_policy',
            principal: cognito_user_id
        };
        console.log("Attaching IoT policy to Cognito principal")
        iotMgmt.attachPrincipalPolicy(params, (err, res) => {
            if (err) {
                console.error(err);
                reject(err);
            } else {
                resolve();
            }
        });
    });
}

为了在前端实现 Caleb 的回答,我不得不做几件事:

  1. 通过转到 IoT 控制台 > 安全 > 策略并将 AWSIoTDataAccess 策略内容复制并粘贴到其中来创建 IoT 策略(名为 "default")
  2. 将以下内联策略添加到我的 Cognito 身份池的身份验证角色:{"Effect": "Allow", "Action": ["iot:AttachPrincipalPolicy"], "Resource": ["*"]

然后我将前端代码更新为:

AWS.config.region = process.env.AWS_REGION;
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
  IdentityPoolId: process.env.AWS_IDENTITY_POOL,
  Logins: {
    'graph.facebook.com': FACEBOOK_ACCESS_TOKEN
  }
});
AWS.config.credentials.get(() => {
  const IoT = new AWS.Iot();
  IoT.attachPrincipalPolicy({
    policyName: 'default',
    principal: AWS.config.credentials.identityId
  }, (err, res) => {
    if (err) console.error(err);
    // Connect to AWS IoT MQTT
  });
});

我参考了 Caleb 和 senornestor 的回答,以下实现对我有用:

AWS.config.credentials = new AWS.CognitoIdentityCredentials({
  IdentityPoolId: AWSConfiguration.poolId,
  Logins: {
     'accounts.google.com': user.Zi.id_token
  }
});

var cognitoIdentity = new AWS.CognitoIdentity();

AWS.config.credentials.get(function(err, data) {
  if (!err) {
     console.log('retrieved identity: ' + AWS.config.credentials.identityId);

     var params = {
        IdentityId: AWS.config.credentials.identityId,
        Logins: {
           "accounts.google.com": user.Zi.id_token
        }
     };
     cognitoIdentity.getCredentialsForIdentity(params, function(err, data) {
        if (!err) {
           console.log('retrieved credentials');
           const IoT = new AWS.Iot();
           IoT.attachPrincipalPolicy({
              policyName: 'exampleIoTPolicy',
              principal: AWS.config.credentials.identityId
           }, (err, res) => {
              if (err) console.error(err);
           });  // Change the "policyName" to match your IoT Policy
        } else {
           console.log('error retrieving credentials: ' + err);
           alert('error retrieving credentials: ' + err);
        }
     });
  } else {
     console.log('error retrieving identity:' + err);
     alert('error retrieving identity: ' + err);
  }
});

这是一个示例应用程序,应该有助于演示如何使用 Cognito 对 IoT 进行身份验证:

https://github.com/awslabs/aws-iot-chat-example

关于明确的说明,您可以阅读:

https://github.com/awslabs/aws-iot-chat-example/blob/master/docs/authentication.md

事实证明,即使在 2021 年,也有必要创建一个专用的 Lambda 函数来执行 AttachPolicy(而不是 AttachPrincipalPolicy,因为它是过时的)。如the official Docs所述:

To attach an AWS IoT Core policy to a Amazon Cognito Identity, you must define a Lambda function that calls AttachPolicy.

其他答案展示了如何实现 Lambda。