How to use AWS IoT to send/receive messages to/from Web Browser
Asked Answered
R

4

37

We are trying to use Amazon Web Services Internet of Things (AWS IoT) to send messages from/to a Web Browser (e.g: . Given that the AWS IoT supports JavaScript we expect that this is possible ...

We have searched at the AWS IoT Documentation but only found server-side examples (which expose AWS secrets/keys...)

Are there any good working examples or tutorials for using AWS IoT to send/receive messages via WebSockets/MQTT in the browser (e.g: authenticating with AWS Cognito)? Thanks!

Recoup answered 16/2, 2016 at 17:55 Comment(0)
A
27

Here's a sample that uses a cognito identity pool in JS to connect, publish and react to a subscription.

// Configure Cognito identity pool
AWS.config.region = 'us-east-1';
var credentials = new AWS.CognitoIdentityCredentials({
    IdentityPoolId: 'us-east-1:your identity pool guid',
});

// Getting AWS creds from Cognito is async, so we need to drive the rest of the mqtt client initialization in a callback
credentials.get(function(err) {
    if(err) {
        console.log(err);
        return;
    }
    var requestUrl = SigV4Utils.getSignedUrl('wss', 'data.iot.us-east-1.amazonaws.com', '/mqtt',
        'iotdevicegateway', 'us-east-1',
        credentials.accessKeyId, credentials.secretAccessKey, credentials.sessionToken);
    initClient(requestUrl);
});

function init() {
  // do setup stuff
}

// Connect the client, subscribe to the drawing topic, and publish a "hey I connected" message
function initClient(requestUrl) {
    var clientId = String(Math.random()).replace('.', '');
    var client = new Paho.MQTT.Client(requestUrl, clientId);
    var connectOptions = {
        onSuccess: function () {
            console.log('connected');

            // subscribe to the drawing
            client.subscribe("your/mqtt/topic");

            // publish a lifecycle event
            message = new Paho.MQTT.Message('{"id":"' + credentials.identityId + '"}');
            message.destinationName = 'your/mqtt/topic';
            console.log(message);
            client.send(message);
        },
        useSSL: true,
        timeout: 3,
        mqttVersion: 4,
        onFailure: function () {
            console.error('connect failed');
        }
    };
    client.connect(connectOptions);

    client.onMessageArrived = function (message) {

        try {
            console.log("msg arrived: " +  message.payloadString);
        } catch (e) {
            console.log("error! " + e);
        }

    };
}

Documentation for the credentials.get call, here

Remember to authorize your IAM role for subscribing / publishing as well. Here's a sample:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iot:Connect"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "iot:Receive",
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": "iot:Subscribe",
            "Resource": [
                "arn:aws:iot:us-east-1::your/mqtt/topic"
            ]
        },
        {
            "Effect": "Allow",
            "Action": "iot:Publish",
            "Resource": [
                "arn:aws:iot:us-east-1::your/mqtt/topic"
            ]
        }
    ]
}
Aftermath answered 17/2, 2016 at 12:14 Comment(6)
the sigv4 function is here for reference.Aftermath
am getting the following error: aws-sdk-2.7.1.js:6834 Uncaught Error: moment is not defined(…)callListeners @ aws-sdk-2.7.1.js:6834emit @ aws-sdk-2.7.1.js:6810emit @ aws-sdk-2.7.1.js:4054transition @ aws-sdk-2.7.1.js:3831runTo @ aws-sdk-..... As i have updated the aws sdk also, but still getting the same error could you help me out or if possible share aws-sdk.js file which you are using.Cammi
you have used "iotdevicegateway", what are you passing as the parameter as "iotdevicegateway"? Either we have to pass resourceId or DeviceId?Cammi
Just add moment to your app: npm i moment --save. Also 'iotdevicegateway' stays as is in a string format.Helwig
Just out of curiosity, do you need to use something like this? I see new Paho.MQTT.Client in the code, but I don't see where that's coming fromSelves
If anyone is looking for shadow example, use this: gist.github.com/ma2shita/905971e4af2a0b9f73add70b75ee9fc3 Remember to authorize your IAM role for unauth users as instructed above.Schnauzer
A
2

In case anyone else is looking for a solution: here's a tutorial that demonstrates via a simple chat app how to get real-time updates into a ReactJS front-end using Serverless and Websockets on AWS IOT. The source code of the tutorial is available on Github.

Aeolotropic answered 29/6, 2017 at 18:57 Comment(1)
This example doesn't include Cognito support. It's not clear: does the example expose keys in the javascript source?Selves
K
1

It is hard to find good tutorials for integrating AWS IoT in browser.

Basically you need to have some authentication method (Facebook, Google, AWS Cognito, your own SSO service with SAML support) and then you need to do following steps:

  1. Configure Cognito User Pool or Federated Identity Pool with your authentication method.
  2. In browser you have to implement Extended Flow (https://aws.amazon.com/blogs/mobile/understanding-amazon-cognito-authentication-part-4-enhanced-flow/)
  3. You need to AttachPolicy in IoT for your user's Cognito identityId - it will be used as principal (it the same way as devices are using certificates).
  4. You need to create MQTT client using https://github.com/aws/aws-iot-device-sdk-js and providing your temporary accessKeyId, secretKey and sessionToken received from Extended Flow authentication.
Kingbolt answered 7/12, 2018 at 12:14 Comment(0)
M
1

It's very important to understand the difference between IAM policy and AWS IOT policy here. Suppose you use cognito user pool as your identity provider.

First you need to set up a cognito identity pool, link the identity pool to your user pool and assign a role(attach IAM policy to this role) to this identity pool.

Second, in your app, you first login so that you get the user pool credential, then you call

    AWS.config.credentials = new AWS.CognitoIdentityCredentials({
      IdentityPoolId: 'us-west-2:b8d2b32b-cbab-4ae3-9d47-1624d09c9350',
      Logins: {
        'cognito-idp.us-west-2.amazonaws.com/${userPoolIdentity}': userPoolCredential.getIdToken().getJwtToken(),
      }
    });

to exchange your user pool credential with aws temporary access credential:

    AWS.config.getCredentials(e => {
      if(e) console.log("Get credential failed", e);
      this.device = AwsIot.device({//AwsIot is the aws-iot-sdk package
        clientId: clientID,//clientId is just random string
        host: '*-ats.iot.us-west-2.amazonaws.com',//replace * with your own host
        protocol: 'wss',
        accessKeyId: AWS.config.credentials.accessKeyId,
        secretKey: AWS.config.credentials.secretAccessKey,
        sessionToken: AWS.config.credentials.sessionToken
      });
      this.device.on('connect', function() {
        console.log("DEVICE CONNECTED");
      });
      this.device.subscribe('test');
      this.device
      .on('message', function(topic, payload) {
        console.log(`TOPIC IS ${topic}\nMESSAGE IS ${payload.toString()}`);
      });
    });

But the above code will not work!!! Here is the tricky part: the credentials you get by exchanging your user pool credential is just a temporary credential that represent the AWS IAM policy you just attached to your identity pool! When it requested to connect with your IOT, AWS will check if it's allowed to request and if it's allowed to do what the user requested. You already got the IAM policy so you are allowed to request, but it will check the AWS IOT policy attached to this identity. Since you haven't done this, you are not allowed to do what you really requested(namely connection). So at the first time you want to connect, you should attach a IOT policy to this identity. You can do this either by command line or

  (<AWS.CognitoIdentityCredentials>AWS.config.credentials).refresh(e => {
    if(e) console.log('error', e);
    const principal = (<AWS.CognitoIdentityCredentials>AWS.config.credentials).identityId;
    console.log(`IdentityId: ${principal}`);
    this.attachPrincipalPolicy("test-delete-after-use", principal);
  });
  attachPrincipalPolicy(policyName, principal) {
    new AWS.Iot().attachPrincipalPolicy({ 
      policyName: policyName, // you need to create policy beforehand in iot
      principal: principal 
    }, err => {
      err ? console.log('ATTACH PRINCIPAL POLICY ERROR', err) : console.log('ATTACH PRINCIPAL POLICY SUCCESS');
    });
  }

Now, when the identity tries to connect with IOT, IOT will find a IOT policy attached to this identity, and approve this connection.

Sorry about the bad wording. In summary, you need to clarify the difference between IAM policy and IOT policy. This is just what I understand, it may have incorrect places. If you find it, please leave a comment or edit my answer.

IMPORTANT Just found the relation between these two policies from official doc: https://docs.aws.amazon.com/iot/latest/developerguide/pub-sub-policy.html Look at section Policies for HTTP and WebSocket Clients

Monovalent answered 4/3, 2019 at 17:22 Comment(2)
I really would like to see your full solution to this if it could be possible?! This is exactly the problem I am facing at the moment.Antisocial
@AshkanHovold how it is going on? Did you resolve that scenario? I'm facing the same thing and I'm looking for more information since I found the AWS docs hard to follow...Fibrovascular

© 2022 - 2024 — McMap. All rights reserved.