Situation:

  1. I have a ReactJS web application that is deployed through AWS Amplify and uses Amplify Studio backend to handle authentication through Cognito services and aws-amplify sdk (sign in, sign up).

  2. The web application also implements the use of react native aws-amplify sdk pubsub to subscribe to an MQTT topic in IoT Core and retrieve messages but only to authenticated users.

  3. As per step 2 in the pubsub documentation: Attach your policy to your Amazon Cognito Identity.

If I use the AWS CLI to attach the IoT policy to the user and then I sign in with that user through the web application I am able to successfully subscribe and receive MQTT messages -- it works perectly!

Problem:

The application allows Cognito self-service user sign up (Self-registration) and expects to have many users.

  1. I implemented a post authentication lambda trigger function in the Cognito user pool created by the Amplify service.

  2. The lambda function runs the following sample from AWS documentation with two (2) additional console logs:

exports.handler = (event, context, callback) => {

    // Send post authentication data to Cloudwatch logs
    console.log ("Authentication successful");
    console.log ("Trigger function =", event.triggerSource);
    console.log ("User pool = ", event.userPoolId);
    console.log ("App client ID = ", event.callerContext.clientId);
    console.log ("User ID = ", event.userName);

*** console.log("Event = ", event);
    console.log("Context = ", context);***

    // Return to Amazon Cognito
    callback(null, event);
};
  1. I then authenticate with the user again through the application and go to CloudWatch logs for that Lambda function.

  2. This is the information I get from logging Event and Context after post authentication trigger:

Event log:

{
  version: '1',
  region: 'us-east-1',
  userPoolId: 'us-east-1_*********',
  userName: '4eea4a48-92b6-45da-b26e-*********',
  callerContext: {
    awsSdkVersion: 'aws-sdk-unknown-unknown',
    clientId: '*********'
  },
  triggerSource: 'PostAuthentication_Authentication',
  request: {
    userAttributes: {
      sub: '4eea4a48-92b6-45da-b26e-*********',
      'cognito:email_alias': '*********.com',
      'cognito:user_status': 'CONFIRMED',
      email_verified: 'true',
      name: 'asdfasdf',
      email: '*********.com'
    },
    newDeviceUsed: false
  },
  response: {}
}

Context log:

{
  callbackWaitsForEmptyEventLoop: [Getter/Setter],
  succeed: [Function (anonymous)],
  fail: [Function (anonymous)],
  done: [Function (anonymous)],
  functionVersion: '$LATEST',
  functionName: 'userAccess_iotCore_attachPolicyToCognitoIdentityID',
  memoryLimitInMB: '128',
  logGroupName: '/aws/lambda/userAccess_iotCore_attachPolicyToCognitoIdentityID',
  logStreamName: '2023/01/13/[$LATEST]4eb4287aa4db4dd8a6b6efd810a7***',
  clientContext: undefined,
  identity: undefined,
  invokedFunctionArn: 'arn:aws:lambda:us-east-1:*********:function:userAccess_iotCore_attachPolicyToCognitoIdentityID',
  awsRequestId: 'bf6afd1c-117c-4a9e-9d3b-*********',
  getRemainingTimeInMillis: [Function: getRemainingTimeInMillis]
}
  1. The big issue here is that context.identity is undefined so I am not able to get that authenticated user's Amazon Cognito Identity Id to attach the required IoT policy for PubSub to work through the application.

Questions:

  1. How can I get the Amazon Cognito Identity Id after post authentication trigger to then attach an IoT policy?

  2. From the web application using the aws-amplify sdk I am able to get this Id after sign in. Is there any API I can use from the application to attach this policy?

Thanks.

1

There are 1 best solutions below

0
On

I answered the matter of attaching IoT policies to a Cognito Identity ID in a recent post. But to answer your questions precisely:

  1. You can not retrieve this information in the lambda function input argument. The call is initiated by AWS, not the Cognito client. Your lambda can however make calls to list the available Cognito Identity IDs and make sure each of them have the IoT Policy attached...

  2. Yes. Everything you do in the web-console (or CLI) goes through an API. But I would not do it: for that, you would need to give iot:attachPolicy rights to the Cognito assumed Authenticated Role, and there is no way to restrict the iot:attachPolicy action to the identity ID of the initiator. This means, any authenticated user will then be able to attach the IoT policy to any Thing he wants. This is over permissive, as mentionned here.

My take on this: The best way to go, while ensuring security and least privilege principles, is to define a lambda function which is triggered after authentication. The lambda function attaches the policy to the Cognito Identity ID, which is accessible through the context argument given that the call was made by a Cognito User.