1

First I tried to use AWS Amplify library to use IOT Module to create AWS IOT Chat for my serverless application (I followed the steps given in the documentation), but it didn't work and gave 'Socket Already Closed' error.

Then I tried aws-iot-device-sdk and followed AWS documentation and code snippets to do so, but same error.

I have followed below steps:

  1. I have given "iot:*" rights to my Cognito pool.
  2. I have created one policy and attached my Cognito user identity to that policy.
  3. Then I tried connecting it using SDK and amplify library, and it was successfully connected, but then when I tried to publish/subscribe to a topic, it has thrown the same error.
Reza Mousavi
  • 4,420
  • 5
  • 31
  • 48

2 Answers2

1

I'm not entierly sure where your issue might be, but I struggled with the same issue when I first started using Amplify PubSub. For me it was a policy issue. So one of this things might help:

Connect your thing policy document to the user identity for the federated pool (not the identity from the user pool). My policy document looks like this:

 {
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "iot:Connect",
        "iot:AttachPrincipalPolicy",
        "iot:Publish",
        "iot:Subscribe",
        "iot:Receive",
        "iot:GetThingShadow",
        "iot:UpdateThingShadow",
        "iot:DeleteThingShadow"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}

To attach/update the the policy for all my users (not so many) I use a Lambda function to do it:

var AWS = require("aws-sdk");
const cognito = new AWS.CognitoIdentityServiceProvider({apiVersion: '2016-04-18'});
var cognitoidentity = new AWS.CognitoIdentity();
var iot = new AWS.Iot({apiVersion: '2015-05-28'});

exports.handler = (event, context, callback) => {
    var params = {
      IdentityPoolId: 'eu-central-1:xxxxxxxx-xxxx-xxxx-xxx-xxxxxxxx', /* change this */
      MaxResults: 50,
    };
    cognitoidentity.listIdentities(params, function(err, data) {
        if (err) console.log(err, err.stack);
        else {
            addPolicies(data.Identities);
        }
    });
    function addPolicies(users) {
        for (let i = 0; i<users.length;i++) {
            var params2 = {
              policyName: 'myIOTPolicy',
              principal: users[i].IdentityId
            };
            iot.attachPrincipalPolicy(params2, function(err, data) {
              if (err) console.log(err, err.stack);
              else     console.log(data);
            });
        }
    }
  callback(null, event);
};

For my authenticated role in the identity pool, I have the following policy attached to the role:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iot:AttachPrincipalPolicy",
                "iot:Connect",
                "iot:Publish",
                "iot:Subscribe",
                "iot:Receive",
                "iot:GetThingShadow",
                "iot:UpdateThingShadow",
                "iot:DeleteThingShadow"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

Hope this helps

Engam
  • 1,051
  • 8
  • 17
0

I know this is an old question, but I wanted to post my experiences getting the vague AMQJS0008I Socket closed (error nr 8) message from Amplify.

Related git issue: #2739

From my concern it always kicks you out because of a permission problem and the answers are a little bit spread around everywhere. From my point of view putting a "*" message in a policy is not the most secure thing to do. That's why I post my findings here:

First step

Make sure your IAM AuthRole from Amplify has the right permissions. The Authrole name is something like amplify-yourappname-dev-...-authRole (You can always find it in cloudformation console) and it must have following policies attached:

  • AWSIoTConfigAccess
  • AWSIoTDataAccess

From this point on your Amplify app can start accessing AWS IoT

Second step

This step needs to configure AWS IoT Console to allow certain Cognito User Id's to publish/subscribe to specific topics.

Create a policy with following contents:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:Connect",
      "Resource": "arn:aws:iot:eu-central-1:************:client/${iot:ClientId}"
    },
    {
      "Effect": "Allow",
      "Action": [
        "iot:Publish",
        "iot:Receive"
      ],
      "Resource": [
        "arn:aws:iot:eu-central-1:*************:topic/whatevertopic/*",
        "arn:aws:iot:eu-central-1:*************:topic/$aws/things/whateverthingname/shadow/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": "iot:Subscribe",
      "Resource": [
        "arn:aws:iot:eu-central-1:************:topicfilter/whatevertopic/*",
        "arn:aws:iot:eu-central-1:************:topicfilter/$aws/things/whateverthingname/shadow/*"
      ]
    }
  ]
}

Take not that:

  • Publish and Receive need the topic subject prefix
  • Subscribe needs the topicfilter prefix

This took me a long time to find out but it is correct.

Third step

You must allow specific cognito IDs to specific policy. You can connect a policy to a Cognito ID target. I created a Python Lambda function to do so, but can be done through the CLI for testing

aws iot attach-policy --policy-name 'myIoTPolicy' --target '<YOUR_COGNITO_IDENTITY_ID>'

The COGNITO_IDENTITY_ID is the output of my Vue Apps Amplify Auth module. See next step

Step Four

The last step is the app itself. This is the code which made it work for me:

import Amplify, { Auth, PubSub } from 'aws-amplify'
import { AWSIoTProvider } from '@aws-amplify/pubsub'

const region = 'eu-central-1'
const loginUserCredentials = await Auth.currentCredentials()

// This log shows the user ID somewhere in the object.
// It must be in following style: region:ID
console.log(loginUserCredentials)

Auth.essentialCredentials(loginUserCredentials),
   region: region
})

const awsiotprodvider = new AWSIoTProvider({
   aws_pubsub_region: region,
   aws_pubsub_endpoint: 'wss://*****************-ats.iot.eu-central-1.amazonaws.com/mqtt'
})

Amplify.addPluggable(awsiotprodvider)
// Can be interesting for testing:
Amplify.Logger.LOG_LEVEL = 'DEBUG'

PubSub.subscribe('$aws/things/whateverthingname/shadow/get/accepted').subscribe({
  next: (data: any) => console.log('Message received', data),
  error: (error:any) => console.error(error),
  complete: () => console.log('Done')
})

setTimeout(async () => {
  await PubSub.publish('$aws/things/whateverthingname/shadow/get', { msg: '' })
}, 5000)

This code works perfectly for me! Good luck!

Hacor

Interesting reads:

Hans Cornelis
  • 63
  • 1
  • 7