Why did I do this article?

I made this article because I find many articles available on medium and on internet allowing me to solve many problems. Nevertheless when I wanted to use the AWS stack for a personal project. I couldn’t find an easy way to communicate with AWS IoT with a Cognito authenticated user with his own MQTT topic. That’s why today, I propose the solution I found with a simple example hoping to help people facing the same problem as me.

Use Case

This short guide can be useful if you are building a SaaS application with AWS IoT as an MQTT broker and want your user to have his personal MQTT topic.

House managed by command through MQTT

To illustrate my point, I made a small use cases, of a user who would like to control his house from a web interface, the house will be represented by a SVG plan that will be updated when commands are published in MQTT brokers.

Setup

In this article I will use React and the amplify library, the goal is not to learn how to use React, I invite you to take the sources directly or to follow the instructions directly on your own project regardless of the framework used, amplify can be used on the different JS framework that exists.

If you want to follow the whole tutorial, you can download source here : https://github.com/alexisgardin/aws-iot-house-management-medium

Once you download the project run these two command :

npm install
npm start

The application is now available at : http://localhost:3000/
(note : you had to configure app before usage, just follow the tutorial)

AWS CLI is also highly recommended.

Authentication with a Cognito User Pool

As i said, i use the amplify library but i doesn’t use the amplify cli because this make too much magic for me. I like to understand what happen so we will create auth via the AWS Console. The documentation for Amplify Framework is available here : https://docs.amplify.aws/

We are going to use Cognito as identity provider, you can quickly see how the authentication with Cognito will work in our case with the image below.

https://aws.amazon.com/fr/blogs/mobile/building-fine-grained-authorization-using-amazon-cognito-user-pools-groups/

Create a Cognito UserPool

A user pool is a user directory in Amazon Cognito. With a user pool, your users can sign in to your web or mobile app through Amazon Cognito. Your users can also sign in through social identity providers like Google, Facebook, Amazon, or Apple, and through SAML identity providers. 

https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools.html

You had to create a Cognito UserPool in order to manage your users. This will allow your user to sign up and access to the AWS Ressources like your MQTT Broker thanks to Amazon Cognito Identity Pools.

You can choose review default and add an App client and then uncheck generate client secret like the screen below and let the default settings.

Create an Amazon Cognito Identity Pools

Amazon Cognito identity pools (federated identities) enable you to create unique identities for your users and federate them with identity providers. With an identity pool, you can obtain temporary, limited-privilege AWS credentials to access other AWS services.

https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-identity.html
Creation of an identity pool

In Cognito you can create Identity Pools which will be needed in order to access to the MQTT Broker. In the case of private access to AWS Resources do not check Unauthenticated identities this is all the interest of this guide.

In the authentication part, you will need the AppClientId in the App client settings section of your UserPool you just created and the UserPoolId you will find on the UserPool homepage.

PoolId of your UserPool
AppId of you application

Keep these two value for later we will need these value for the webapp configuration.

You can now create your identity pool like i do above and leave the default roles that we will modify later and then create your identity pool. The last value needed is the IdentityPoolsId, you can have it by executing the AWS CLI command below or in url when you are in amazon console in your federated identity management section of your Identity Pool.

aws cognito-identity list-identity-pools --max-results 20

result of cognito list-identity-pools command

Enable the authentication of the HouseManagementApp

In the HouseManagementApp source, you will find a ./src/aws-exports.js which is the necessary configuration needed by amplify in order to connect to your UserPool

const awsExports = {
  "Auth": {
    "identityPoolId": 'YOUR-IDENTITY-POOL-ID',
    "region": 'YOUR-REGION',
    "userPoolId": 'YOUR-USERPOOL-ID',
    "userPoolWebClientId": 'YOUR-APPID',
    },
  };

Once it’s done, launch the ReactApp with npm start and you can now connect and create an account on http://localhost:3000/.

Manage Policy

In order to communicate with the MQTT broker of AWS IoT you had to define a Policy, which allow your user to communicate with it, you will need your AccountId and your region (eu-west-2 for me) where you want to deploy your app for me.

You can get your account ID with this command or in profile section of AWS console.

aws sts get-caller-identity

This Policy below will be used for two things your AuthRole and for IoT Core in order to allow user to Connect, Publish and Subscribe to your MQTT broker.

{  
    "Version": "2012-10-17",  
    "Statement": [  
        {  
            "Action": [  
                "iot:Connect"  
            ],  
            "Resource": [  
                "arn:aws:iot:eu-west-2:**<ACCOUNT_ID>**:client/${cognito-identity.amazonaws.com:sub}"  
            ],  
            "Effect": "Allow"  
        },  
        {  
            "Action": [  
                "iot:Publish"  
            ],  
            "Resource": [  
                "arn:aws:iot:eu-west-2:**<ACCOUNT_ID>**:topic/${cognito-identity.amazonaws.com:sub}"  
            ],  
            "Effect": "Allow"  
        },  
        {  
            "Action": [  
                "iot:Subscribe"  
            ],  
            "Resource": [  
                "arn:aws:iot:eu-west-2:**<ACCOUNT_ID>**:topicfilter/${cognito-identity.amazonaws.com:sub}/*"  
            ],  
            "Effect": "Allow"  
        },  
        {  
            "Action": [  
                "iot:Receive"  
            ],  
            "Resource": [  
                "arn:aws:iot:eu-west-2:**<ACCOUNT_ID>**:topic/${cognito-identity.amazonaws.com:sub}/house"  
            ],  
            "Effect": "Allow"  
        }  
    ]  
}

You can find more explanation of every policy action here : https://docs.aws.amazon.com/iot/latest/developerguide/iot-policy-actions.html

Add policy to IoT Core

In AWS IoT Core you can go to Security > Policies and then create a new policy and enable advanced mode for copy and past the policy, don’t forget to add AccountId.

Creation of an IoT Policy

IAM Policy for Authenticated User

Same things for the Authenticated Role, Go to IAM and create a new Policy for your roles and copy and paste the same policy.

Creation of an inline policy

Attach Policy to your user

The last action you need to do is to attach the IoT policy to your Cognito User, In order to retrieve the Cognito Identity Id you can use Amplify (it’s already displayed if you use the HouseManagementApp).

This code use amplify to allow you to retrieve your current user identityId

Auth.currentCredentials().then(user => { console.log(user.identityId);
});

Then you have to use aws cli in order to attach the policy.

aws iot attach-principal-policy --policy-name 'HouseManagementIoT' --principal ''

Cognito Identity attached to HouseManagement policy

Subscribe and Publish with Amplify

During the last part if you get an error similar to this : errorCode: 8, errorMessage: AMQJS0008I Socket closed. Check that all steps of management policy are done.

https://docs.amplify.aws/lib/pubsub/getting-started/q/platform/js#create-iam-policies-for-aws-iot

Configure Amplify for your Broker

The code above used in ./src/HouseManagement.js configure the connection information for amplify in order to connect to the MQTT Broker with websocket.

Auth.currentCredentials().then(user => {
  Amplify.addPluggable(
    new AWSIoTProvider({
      aws_pubsub_region: "eu-west-2",
      aws_pubsub_endpoint:
      "wss://-ats.iot.eu-west-2.amazonaws.com/mqtt",
      clientId: user.identityId
    })
  );
});

You can see we use our user identityId for clientId because we specify in policy that only authenticated user can connect to the broker. You also need your Pub/Sub endpoint to establish the connection between your client and the broker. Use this command to retrieve your IoT endpoint.

aws iot describe-endpoint

This command will give you the endpoint address, don’t forget to add the
“-ats” because without the ats you will get a certificate error. You can now change it in ./src/HouseManagement.js

Subscribe with Amplify

PubSub.subscribe(user.identityId+'/house').subscribe(v => {

  console.log(v.value.message);  

});

The code for subscribing is pretty simple, like the connection we use the user IdentityId as mqtt topics then we add “/house” because we set this topic in the receive action when we create the policies. He is already present in ./src/HouseManagement.js. In AWS console, you can test it in AWS IoT Core in the “test” section that allows you to publish messages or subscribe on the MQTT Broker.

Available command : on / off.
Available rooms : sundeck, fdr_bedroom, er_bedroom, entrance_hall, kitchen, sec_bedroom, living_room.

Publish with Amplify

PubSub.publish(this.user.identityId+'/house', {'message': this.state.command});

The code to publish is just as simple as the subscription code, you put the right topic to publish, for us CognitoIdentityId + ‘/house’. The code is available in the file ./src/HouseManagement.js in the onClick() function.

Catégorisé: