Get #Amazon #Prime for this #holiday #amazonprime #christmas #2019

How to set up #aws #iot #pubsub using #AwsAmplify sdk?

If you are using aws Amplify library (I use 0.4.8 at the moment) in an Angular project and trying to use PubSub module via aws IoT, you may want to check out this article on how to configure for PubSub module.

There are a few open issues regarding PubSub documentation on Amplify GitHub:
1) Issue 684: Documentation about Pub-Sub Module with AWS IoT
2) Issue 781: AWSIoTProvider not connecting
3) Issue 749: PubSub & IoT Policy: client connection not authorized if using aws_cognito_identity_pool_id

If you follow steps on Amplify PubSub document, according to the above issues, there are some additional steps and a typescript type checking issue (that I encountered):

To check if your front-end does subscribe to an IoT topic, you can
right click > inspect > on browser DevTool, go to Network tab >  filter for WS (websockets)
then, you should see a websocket connection like the following:

aws-iot mqtt connection

Issues that I faced:

Issue 1:

Argument of type '{ aws_pubsub_region: string; aws_pubsub_endpoint: string; }' is not assignable to parameter of type 'MqttProvidertOptions'. Object literal may only specify known properties, and 'aws_pubsub_region' does not exist in type 'MqttProvidertOptions'.

This is the code to configure the region and endpoint of AWSIoTProvider

Amplify.addPluggable(new AWSIoTProvider({
     aws_pubsub_region: '<YOUR-AWS-REGION>',
     aws_pubsub_endpoint: 'wss://xxxxxxxxxxxxx.iot.<YOUR-AWS-REGION>.amazonaws.com/mqtt',
}));

If you see the implementation of MqttOverWSProvider class, MqttOverWSProvider class can be constructed with additional properties not listed in the type MqttProvidertOptions. Strict type checking will complain as aws_pubsub_region and aws_pubsub_endpoint properties can't be found. 

constructor(options: MqttProvidertOptions = {}) {

super({ ...options, clientId: options.clientId || uuid(), });
}

Solution:
in node_modules/aws-amplify/lib/PubSub/Providers/MqttOverWSProvider.d.ts (the file in your node_modules folder)

export interface MqttProvidertOptions {
    clientId?: string;
    url?: string;
    [key: string]: any;
    // aws_pubsub_region?: string;
    // aws_pubsub_endpoint?: string;
}
Reference:
Can you declare a object literal type that allows unknown properties in typescript? StackOverflow thread

Issue 2:


Error: Uncaught (in promise): Object: {"errorCode":7,"errorMessage":"AMQJS0007E Socket error:undefined."}

Error: Uncaught (in promise): Object: {"errorCode":8,"errorMessage":"AMQJS0008I Socket closed."}

Searching around, I arrived at Amplify issue 781.

i) According to GitHub user johnrjj in that thread, you need to include IoT permissions to your Identity Pool users' roles (unauthenticated or / and authenticated roles depending on how you use it). You can then add iot permission to your identity pool roles. With some trials and errors, I found that these are possibly the minimum permissions that you need to subscribe to a topic on your front-end. You can try and let me know if these are enough. You can further find-tune the policy to fit your need.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "iot:Receive",
                "iot:UpdateStream",
                "iot:CreateStream",
                "iot:DeleteStream",
                "iot:Subscribe",
                "iot:Connect",
                "iot:DescribeStream"
            ],
            "Resource": "*"
        }
    ]
}

I noticed I only needed to attach IoT Permission to my identity pool principals (users, I think) like what Amplify PubSub document suggests:
aws iot attach-principal-policy --policy-name 'myIOTPolicy' --principal '<YOUR_COGNITO_IDENTITY_ID>'

To double check, you may find these aws iot commands useful. To use these commands, you can this aws cli document to set up

aws iot attach-policy --policy-name 'convertIoT' --target YOUR_IDENTITY_POOL_ID
aws iot list-attached-policies --target YOUR_IDENTITY_POOL_ID
aws iot detach-policy --policy-name YOUR_IOT_POLICY_NAME --target YOUR_IDENTITY_POOL_ID

aws iot attach-principal-policy --policy-name YOUR_IOT_POLICY_NAME --pincipal YOUR_COGNITO_IDENTITY_ID
aws iot list-principal-policies --principal YOUR_COGNITO_IDENTITY_ID
aws iot detach-principal-policy --policy-name YOUR_IOT_POLICY_NAME --principal YOUR_COGNITO_IDENTITY_ID

Edit:
Note that list-principal-policies is deprecated. You can use list-attached-policies instead.
Check out aws list-principal-policies doc and aws cli list-principal-policies doc

* All capital case words are placeholders.
* YOUR_COGNITO_IDENTITY_ID is a value you can find by opening up your local storage. It is the value for key 'aws.cognito.identity-id.YOUR_IDENTITY_POOL_ID' or you can look it up using the code snippet below (source: Amplify PubSub doc)

Auth.currentCredentials().then((info) => {
  const cognitoIdentityId = info._identityId;
});

Additional:
Most probably, you prefer not to attach an IoT policy to your identity pool principals one by one manually.

i) You can either write a lambda and have it attach an IoT policy before pubsub is executed. You can then cache the IoT policy attachment record locally so that you don't have to keep invoking the lambda.

ii) You can refer this code snippet of having the IoT policy attachment code run automatically in the front-end by pveller in issue #684.

Issue 3:

I noticed it wouldn't work if you attach a policy that allows for resources iot:topic/* to your principals.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iot:*",
      "Resource": "arn:aws:iot:us-east-1:aws_acc_num:*"
    }
  ]
}

I think it is because cognito identities aren't resources allowed as they are not topics. If you do it this way -> "Resource": "arn:aws:iot:us-east-1:aws_acc_num:*", it would work.
or you can create another policy and attach it to cognito identities.

Issue 4:

Issue 749: PubSub & IoT Policy: client connection not authorized if using aws_cognito_identity_pool_id
The thread talks about how to configure authorization(IoT permissions) for users.
Source: 3rd post in the thread by GitHub user - leantide who was in touch with aws support:

Answer from AWS Support:

Currently, you need to create a policy for each authenticated Cognito principal, or let all users share the unauthenticated Cognito access (with access defined in your pool policy).

Notes:
i) MQTT
From Wiki, MQTT (Message Queuing Telemetry Transport) is an ISO standard (ISO/IEC PRF 20922) publish-subscribe-based messaging protocol. It works on top of the TCP/IP protocol.

ii) If you use IoTDataPlane boto3 client in Python backend / lambda, you may notice this warning:
/var/runtime/botocore/handlers.py:697: UnsupportedTLSVersionWarning: Currently installed openssl version: OpenSSL 1.0.0-fips 29 Mar 2010 does not support TLS 1.2, which is required for use of iot-data. Please use python installed with openssl version 1.0.1 or higher.
Apparently, this is a known issue by aws lambda team. Check out this aws boto 3 issue 1559

Additionally, please check out my Amazon Alexa post which I will keep updating after I try out with more Alexa functions. Thanks

Thanks for reading!
Jun
Support me on Amazon