#Grant IOT permissions to new users of app without having to manually attach them. (PubSub Library)

20 messages · Page 1 of 1 (latest)

fierce scarab
#

TLDR: I want grant Full IOT permissions to all users of a specified identity pool without having to manually attach the specified IOT policy to their Cognito ID as directed in the PubSub library post: aws iot attach-policy --policy-name 'myIoTPolicy' --target '<YOUR_COGNITO_IDENTITY_ID>'
https://docs.amplify.aws/lib/pubsub/getting-started/q/platform/js/

I am building a demo application in which I want to grant Unauthorized users IOT access priviledges (publish / subscribe to topic / etc.) I have a groupless Cognito userpool which connects to an identity pool that has a role for unauthorized users of the app. I have taken the following steps to (successfully) grant a specific unauthorized Cognito user IOT privileges.

Attach AWSIoTDataAccess and AWSIoTConfigAccess roles directly to Unauthorized identity pool role.
Attach my own IOT policy to the specific Cognito ID with the command aws iot attach-policy --policy-name 'myIoTPolicy' --target '<YOUR_COGNITO_IDENTITY_ID>'

This solution works, 100% of the time for the specified user. BUT, when a new user visits my app they will not have IOT access because "myIotPolicy" hasn't been explicitly attached to their Cognito ID. What's the workaround? In the future I will implement cognito User pool groups to specify guest / admin rights but I don't think that will help me here. Is there a way to programmatically attach the existing IOT policy to the cognito ID when the user visits the app for the first time? Any solution would be greatly appreciated!!!

Also - I am deploying these resources via serverless cloudformation (attached photo). I am using AWS:IoT::PolicyPrincipalAttachment to manually attach to the Cognito user id (Principal). Wondering if I can use some kind of !Ref to Cognito User ID.

cursive heath
cursive heath
#

@fierce scarab Can confirm I set up an automated method for attaching the IOT policy to the cognito ID based off these examples.

The example is a little dated as one of the IoT sdk methods it uses is deprecated. Here you go:

Front end uses Amplify Hub's 'auth' listener callback in App.tsx:

...
Hub.listen('auth', authListener) // Monitor and manage the authentication state
export const authListener = async (data: any) => {
    switch (data.payload.event) {
        case 'signIn':
            logger.info('user signed in')

            // Attach IoT connect policy to Cognito ID
            Auth.currentCredentials()
                .then(info => {
                    attachIotConnectPolicyToCognitoId(info.identityId)
                })
                .catch(err => console.error(err))
            break
        case 'autoSignIn':
            logger.info('auto sign in successful')
            break
        case 'signOut':
            logger.info('user signed out')
            break
    }
}

On every login, attach the policy by fetching the cognito Id and making a call to my back-end (nodeJS) api. I'm using RTK query which is why the call is wrapped in store.dispatch and seems wonky.

This is overkill since it only needs to happen once. A cleaner method would be to user the post-registration lambda trigger in cognito. However, the cognito id isn't available in in the post-registration lambda trigger's event body, so you'd have to add additional queries to fetch that before you could attach the policy. However, this was faster for me and works.

import store from '../../store/store'
import { iotAPI } from '../../services/iot/endpoints'

const attachIotConnectPolicyToCognitoId = async (cognitoIdentityId: string) => {
    store.dispatch(
        iotAPI.endpoints.attachIotConnectPolicy.initiate({
            cognitoIdentityId,
        })
    )
}

export default attachIotConnectPolicyToCognitoId

Back-end handler

import { IoTClient, AttachPolicyCommand } from '@aws-sdk/client-iot'
import { StatusCodes } from 'http-status-codes'

export const attachIoTPolicies = async (req, res) => {
    const { cognitoIdentityId } = req.body
    const policyName = '<<<YOUR-POLICY-NAME-HERE>>>'

    const client = new IoTClient({
        accessKeyId: process.env.AWS_ACCESS_KEY_ID,
        secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
        region: 'us-east-2'
    })

    const input = {
        policyName: policyName,
        target: cognitoIdentityId,
    }
    const command = new AttachPolicyCommand(input)
    try {
        const response = await client.send(command)
        // const response = await client.attachPrincipalPolicy(input).promise()
        res.status(StatusCodes.OK).json({ success: true, response })
    } catch (error) {
        res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
            success: false,
            error,
        })
    }
}
fierce scarab
#

@cursive heath Wow, thank you so much for taking the time to help me. I will give this all a go!! 🙂

cursive heath
#

Some of the above is specify to my setup which doesn't use Amplify CLI to manage my back-end infrastructure. For instance, I use a custom nodeJS express API on top of MongoDB instead of graphql and dynamodb.

#

If you're using Amplify to handle your API, I think the simplest thing might be to make a custom lambda function that attaches the policy which you can call from your API.

fierce scarab
#

I am using serverless graphql appsync plugin

cursive heath
#

Yep. I'm using that on another project.

fierce scarab
#

For serverless*

#

I could create that lambda function and call it only during the user registration part of the app (when I add sign-in functionality)

cursive heath
#

Yep.

#

I think you can create the function via the CLI then add it to your appsync graphql api via the @function directive

#

Just need to make sure it has the right permissions to attach an IOT policy

fierce scarab
#

Sweet, Thanks big time

cursive heath
#

even simpler, if you're using cognito for authorization, you could use one of the lambda triggers:
https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html

You can specify those from the AWS console and attach a custom one for a post-sign up event / log in event.

The challenge there is the auth event context (payload) that gets sent to the lambda function has a fixed schema and doesn't include the cognitoId you need for the user. So you'd have to make a separate call to cognito within that handler to fetch the cognitoId using the info provided, then call the iot attach command.

#

anyways good luck and let me know if you have questions