How to connect cognito to IoT Core?
When developing an MQTT-based solution using AWS, it is common to use static AWS credentials for the initial proof of concept. However, as your project grows, you may need to restrict access and apply more granular permissions for each user. This is where Amazon Cognito becomes a powerful ally, allowing you to securely manage user authentication and authorization within the AWS IoT Core ecosystem.
What is Amazon Cognito?
Amazon Cognito is an authentication and authorization service that simplifies user management and access control for your applications. It enables developers to configure identities for end users, which is essential when securely connecting IoT devices.
User Pool vs. Identity Pool: What’s the Difference?
A User Pool in Amazon Cognito is a user directory that facilitates user authentication through various methods, such as traditional credentials or with other identity providers (e.g., Google or Facebook). On the other hand, an Identity Pool grants temporary AWS permissions to authenticated users, allowing controlled access to AWS resources.
In the context of connecting AWS IoT Core with Cognito, understanding how both elements work will enable you to implement a secure and scalable solution. Let's dive into how to achieve this integration effectively!
User Pool Configuration
The first step to integrate Cognito with IoT Core is to have a User Pool to manage the users. Create the User Pool from the

In the following steps, choose the password and security settings of your preference. Stop when you reach the "Custom attributes" section; this configuration will allow you to add additional fields for your users. For example, we will add the buildingId field to store the building number where the user lives. Custom attributes can be created as needed.

Finally, give the Pool and the App client a name. The App client represents the application in which the Cognito login will be used. Ensure all parameters are correct and finish the form.


Once created, go to the User Pool and copy the value of the User Pool ID.

Then go to the App integration tab; at the bottom, you will find the App Client we created, and within it, the Client ID. The User Pool ID and Client ID can be used to set up a login page using a Cognito library.

Identity Pool Configuration
With the User Pool, you can now create secure authentication for your users. However, our goal is to authorize users to connect to IoT Core via MQTT selectively. To achieve this, we will use the Identity Pool. With the Identity Pool, we can generate temporary AWS credentials, specifically AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN, which are unique per user and have a limited duration.
The Identity Pool is a separate service from the User Pool. While the User Pool manages application users, the Identity Pool manages identities.
Create the Identity Pool from the

Next, create a new role for the Identity Pool. This role has the default permission "cognito-identity:GetCredentialsForIdentity" which allows users to generate temporary credentials.

Select the User Pool and App Client we previously created.

At the end of the form, you will find the "Claim mapping" section. Since the Identity Pool is separate from the User Pool, mapping will allow you to associate attributes from the User Pool with the new identity in the Identity Pool. This way, you can configure policies based on variables, as we will see later.
Do you remember the buildingId field we added when creating the User Pool? We will add it to the claim mapping. To do this, enter an identifier as the Tag Key and the attribute name in the Claim. For custom attributes, prefix the word "custom", for example, custom:buildingId. If it’s not a custom attribute, do not add the word "custom".

Finally, give the Identity Pool a name and do not enable basic authentication (unless needed) to simplify the process.

Enabling Claim Mapping Tags
In the previous section, we added an Attribute map. In the future, if you need to add more mappings, you can do it by entering the created Identity Pool, going to the User access section, and selecting Identity provider. Open the provider, and at the bottom, you will be able to edit the mappings.

To finish enabling the use of claim mapping tags, open the Identity Pool Role.

In the role, under the Trust relationships section, edit the policy and add the sts:TagSession action. This action allows us to configure policies based on the mapped attributes.

Configuring IAM Policies for the Identity Provider
To connect to IoT Core using Cognito, we need to configure two policies: an IAM policy and an IoT Policy. The IAM policy will grant the identity provider permissions to connect to IoT Core services. A simple policy that allows connection to any MQTT topic might look like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ConnectClient",
"Effect": "Allow",
"Action": [
"iot:Connect"
],
"Resource": "*"
},
{
"Sid": "SubscribeToTopics",
"Effect": "Allow",
"Action": [
"iot:Subscribe"
],
"Resource": "*"
},
{
"Sid": "ReceiveMessages",
"Effect": "Allow",
"Action": [
"iot:Receive",
"iot:Publish"
],
"Resource": "*"
}
]
}
This policy is fine in a testing environment, but in a production environment, users should be able to interact with a limited number of topics. To achieve this, we can use
${variableName}
To access a tag variable exclusively, use the prefix aws:PrincipalTag/ followed by the tag name given during claim mapping:
${aws:PrincipalTag/tag_name}
As an example, we will create a policy that allows users to subscribe and publish only to topics belonging to their building, using the building_id tag we created earlier.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ConnectClient",
"Effect": "Allow",
"Action": [
"iot:Connect"
],
"Resource": "*"
},
{
"Sid": "SubscribeToTopics",
"Effect": "Allow",
"Action": [
"iot:Subscribe"
],
"Resource": "arn:aws:iot:eu-central-1:123456789012:topicfilter/building/${aws:PrincipalTag/building_id}"
},
{
"Sid": "ReceiveMessages",
"Effect": "Allow",
"Action": [
"iot:Receive",
"iot:Publish"
],
"Resource": "arn:aws:iot:eu-central-1:123456789012:topic/${aws:PrincipalTag/building_id}"
}
]
}
In the policy above, the topicfilter keyword is typically used with subscriptions, and the topic keyword is used for publishing. Once the policy is created (TestIamPolicyBuilding), it should be attached to the role associated with the Identity Pool (TestIdentityPoolRole).

Setting up IoT Policies for the Identity Provider
To connect to MQTT using temporary credentials from the Identity Provider, the IAM policy alone is not enough; you must also create an IoT policy, which will be attached to the Identity as we will show later. The IoT policy has the same format as the IAM policy but must be created in the security section of IoT Core.

Just like with the IAM policy, you can also use policy variables in the IoT policy. Since we mapped building_id in the claim mapping for the Identity Pool, we also have access to those tags in the IoT policy.
Create Cognito Identities
So far, we have configured all the necessary services to connect Cognito with IoT Core. Now, let's dive deeper into Cognito identities and their permissions.
A Cognito identity is created after a user logs in through the User Pool and then invokes the
{
"AccessToken": "eyJra........",
"ExpiresIn": 3600,
"IdToken": "eyJra........",
"RefreshToken": "eyJjd........",
"TokenType": "Bearer"
}
With the idToken and the
import { fromCognitoIdentityPool } from "@aws-sdk/credential-providers";
...
const loginProvider = `cognito-idp.${awsRegionName}.amazonaws.com/${userPoolId}`;
const credentials = await fromCognitoIdentityPool({
identityPoolId: 'identityPoolId',
clientConfig: { region: 'aws region' },
logins: {
[loginProvider]: idToken
}
})();
For the code above, you need to know the identityPoolId, which corresponds to the ID of the Identity Pool. It can be found by accessing the previously created Pool. Keep in mind that it is different from the User Pool ID.

After invoking the GetCredentialsForIdentity endpoint, either directly or using the SDK, a unique Identity will be created in the Identity Pool for the user, and temporary AWS credentials (accessKeyId, secretAccessKey, and sessionToken) will be generated.
{
"accessKeyId": "string",
"expiration": number,
"secretAccessKey": "string",
"sessionToken": "string"
"identityId": "string"
}
The identities created in the previous step are listed within the Identity Pool. These identities correspond to a unique identifier (identityId) per user from the User Pool who completes the previous steps. However, it is not possible to determine which identity belongs to a specific user through the AWS console.

The temporary credentials generated can be used to access AWS services authorized by the role associated with the Identity Pool. However, to use them to connect to IoT Core via MQTT, an additional step is required.
Attaching Policies to the Cognito Identity
To connect to IoT Core via MQTT, the IoT policy we created in earlier steps must be attached to the Identity. There are a few options for doing this, and depending on your application's model, you should choose the most appropriate one. In any case, the process involves using the

To simplify the process, we'll take the easiest option, though this option should only be used in a testing environment. It involves editing the policy (TestIamPolicyBuilding) associated with the Identity Pool role (TestIdentityPoolRole) and adding the AttachPolicy permission.
This permission will allow the use of temporary credentials to attach the IoT policy to the Cognito Identity. The following code uses the
import { IoTClient, AttachPolicyCommand } from "@aws-sdk/client-iot";
…
const iotClient = new IoTClient({ region: config.region, credentials });
const attachPolicyCommand = new AttachPolicyCommand({
policyName: 'TestIotPolicyBuilding', // IoT policy name
target: credentials.identityId, // identity id
});
const result = await iotClient.send(attachPolicyCommand);
To verify that the policy was attached to the Identity, go to the IoT policy created in the

Once this is done, you can use the generated temporary credentials to connect to IoT Core via MQTT, for example, using WebSockets:
import { iot, mqtt } from "aws-iot-device-sdk-v2";
const builder = iot.AwsIotMqttConnectionConfigBuilder.new_builder_for_websocket()
.with_credentials(
'aws-region',
credentials.accessKeyId,
credentials.secretAccessKey,
credentials.sessionToken
)
.with_client_id('client-id')
.with_endpoint('endpoint')
.build();
const connection = new mqtt.MqttClient().new_connection(builder);
connection.connect();
As mentioned earlier, this approach is valid for a development environment. However, in production, it is not recommended to add the AttachPolicy permission to the Identity Pool role, as any user could attach policies to other users. In such cases, you can delegate the task to a Lambda function executed by the user, which will exclusively handle attaching the policy to the Identity included in the context.
The best solution will depend on your business model and we can support you in achieving your goals. Schedule a call with us here: