通过Cognito Identity Pool

时间:2017-02-28 18:21:58

标签: javascript amazon-web-services mqtt aws-sdk

很快:我有一个联合身份池,它具有未经身份验证和身份验证的访问权限。我对未经身份验证的访问没有任何问题。但是,当涉及到经过身份验证的访问时,用户登录就可以了,但我对经过身份验证的用户的角色实际上并未实际应用。

我有一个s3存储桶,其中包含简单的index.html和index.js文件,这些文件通过MQTT进行通信。

对于经过身份验证和未经身份验证的用户,这两种策略现在看起来完全相同,并且相当宽松(当然,这不是生产方式,但我只是试图让它以任何方式工作到目前为止)。因此,这两项政策如下:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "*",
            "Resource": "*"
        }
    ]
}

在index.js中,我可以将MQTT连接建立为未经身份验证的用户,如下所示:

var region = 'eu-west-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
  IdentityPoolId: 'my-identity-pool-id',
});
AWS.config.credentials.clearCachedId();
AWS.config.credentials.get(function(err) {
  console.log('accessKeyId:', AWS.config.credentials.accessKeyId);
  if(err) {
    console.log(err);
    return;
  }
  var requestUrl = SigV4Utils.getSignedUrl(
    'wss',
    'data.iot.' + region + '.amazonaws.com',
    '/mqtt',
    'iotdevicegateway',
    region,
    AWS.config.credentials.accessKeyId,
    AWS.config.credentials.secretAccessKey,
    AWS.config.credentials.sessionToken
  );

  initClient(requestUrl);
});

initClient()只是通过Paho

建立MQTT连接
function initClient(requestUrl) {
  var clientId = String(Math.random()).replace('.', '');
  var rpcId = "smart_heater_" + String(Math.random()).replace('.', '');
  var client = new Paho.MQTT.Client(requestUrl, clientId);
  var connectOptions = {
    onSuccess: function () {
      console.log('connected');
      // Now I can call client.subscribe(...) or client.send(...)
    },
    useSSL: true,
    timeout: 3,
    mqttVersion: 4,
    onFailure: function (err) {
      console.error('connect failed', err);
    }
  };
  client.connect(connectOptions);

  client.onMessageArrived = function (message) {
    console.log("msg arrived: " +  message);
  };
}

它运行得很好:connected被打印到控制台,我实际上可以发送/订阅。

现在,我正在尝试对经过身份验证的用户执行相同的操作。为此,我添加了Cognito用户池,在那里创建了一个用户,创建了一个“应用程序”,将身份验证提供程序“Cognito”添加到我的身份池(具有相应的用户池ID和应用程序客户端ID),并且身份验证本身工作正常:用户登录。代码如下所示:

var region = 'eu-west-1';
var poolData = {
  UserPoolId: 'eu-west-1_XXXXXXXXX',
  ClientId: 'ZZZZZZZZZZZZZZZZZZZZZZZZZ',
};
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);

var authenticationData = {
  Username: 'myusername',
  Password: 'mypassword',
};
var authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
var userData = {
  Username: 'myusername',
  Pool: userPool
};
var cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
  onSuccess: function (result) {
    console.log('result:', result);
    console.log('access token: ' + result.getAccessToken().getJwtToken());

    var myUserPoolId = 'eu-west-1_XXXXXXXXX';
    console.log('You are now logged in.');

    // Add the User's Id Token to the Cognito credentials login map.
    var logins = {};
    logins['cognito-idp.' + region + '.amazonaws.com/' + myUserPoolId] = result.getIdToken().getJwtToken();

    AWS.config.credentials.params.Logins = logins;
    // finally, expire the credentials so we refresh on the next request
    AWS.config.credentials.expired = true;

    //call refresh method in order to authenticate user and get new temp credentials
    AWS.config.credentials.refresh((error) => {
      if (error) {
        console.error(error);
      } else {
        console.log('Successfully logged!');
        console.log('accessKeyId:', AWS.config.credentials.accessKeyId);

        var requestUrl = SigV4Utils.getSignedUrl(
          'wss',
          'data.iot.' + region + '.amazonaws.com',
          '/mqtt',
          'iotdevicegateway',
          region,
          AWS.config.credentials.accessKeyId,
          AWS.config.credentials.secretAccessKey,
          AWS.config.credentials.sessionToken
        );

        initClient(requestUrl);
      }
    });
  },

  onFailure: function (err) {
    alert(err);
  },

  newPasswordRequired: function(userAttributes, requiredAttributes) {
    // User was signed up by an admin and must provide new 
    // password and required attributes, if any, to complete 
    // authentication.

    // the api doesn't accept this field back
    delete userAttributes.email_verified;

    var newPassword = prompt('Enter new password ', '');
    // Get these details and call 
    cognitoUser.completeNewPasswordChallenge(newPassword, userAttributes, this);
  },
});

登录部分并不容易上班,但现在它工作正常:在控制台中,我看到:

You are now logged in.
Successfully logged!
accessKeyId: ASIAIRV4HOMOH6DXFTWA

然后,在连接时,我收到以下错误:

connect failed: {invocationContext: undefined, errorCode: 8, errorMessage: "AMQJS0008I Socket closed."}

我认为这是因为实际应用于经过身份验证的用户的角色不允许操作iot:Connect;我相信是因为我可以为未经身份验证的用户重现完全相同的错误,如果我将以下内容放入未经身份验证的用户的策略中:

{
    "Action": [
        "iot:Connect"
    ],
    "Resource": "*",
    "Effect": "Deny"
},

然后,未经身份验证的用户在尝试连接时会收到相同的错误AMQJS0008I Socket closed

因此,看起来我对经过身份验证的用户的策略实际上并未应用于经过身份验证的用户。

在写这个问题之前我做了很多实验。目前,在我的身份池设置中,在“经过身份验证的角色选择”中,我只使用“使用默认角色”,这应该确实选择我的身份验证角色,即:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "*",
            "Resource": "*"
        }
    ]
}

但我也尝试选择“选择带规则的角色”并编写一条规则,根据某些匹配条款设置我的角色。

我还尝试在我的用户池中创建一个组,在那里使用相同的角色,将我的用户添加到该组,并在Identity Pool设置中设置“从令牌中选择角色”。

没有任何帮助。我一直为经过身份验证的用户收到此errorMessage: "AMQJS0008I Socket closed."消息,而对于未经身份验证的用户,一切正常,即使策略完全相同。

感谢任何帮助。

1 个答案:

答案 0 :(得分:3)

问题在于,对于经过身份验证的Cognito用户,将IAM策略附加到身份池是不够的:除此之外,还必须为每个身份附加 IoT策略(不是IAM策略) (基本上,每个用户),像这样:

$ aws iot attach-principal-policy \
    --policy-name Some-Policy \
    --principal us-east-1:0390875e-98ef-420d-a52d-f4188ce3cf06

同时检查此帖子https://forums.aws.amazon.com/thread.jspa?messageID=726121