aws / aws-sdk-js

AWS SDK for JavaScript in the browser and Node.js
https://aws.amazon.com/developer/language/javascript/
Apache License 2.0
7.59k stars 1.55k forks source link

[IOS mobile sdk] Forbidden error: iotdata.getThingShadow with cognito credentials #3993

Closed ggorlim closed 2 years ago

ggorlim commented 2 years ago

Confirm by changing [ ] to [x] below:

Describe the question

This issue already existed but it was closed as staled: "Forbidden error: iotdata.getThingShadow with cognito credentials #1312", only difference is i have same problem with IOS mobile SDK.

In short, for those who don't have time to read original thread, when i used Cognito user pool for authorized use of AWS IOT services i get response(s) for any awsClient(AWSIot) request(s), but all AWSIotData requests fail with the same: ForbiddenException.. In this sample code we have awsClient.listThings(IotRequest) which will pass and then get 1st thing shadow iotData.getThingShadow(IotDataRequest) which will fail...

I know this big chunk of code but everything is in one method with comments in places which are strange to me, plus there is log below which this code will produced so..

And yes i did attempt to use solution with attaching polices from previous thread, and that request do return success which can be seen in log but no use, on end getThingShadow always finish with: thingShadowResponse response nil, error Optional(Error Domain=com.amazonaws.AWSIoTDataErrorDomain Code=0 "null" UserInfo={NSLocalizedDescription=null, NSLocalizedFailureReason=ForbiddenException:})

So question is what i doing wrong?

`func awsCognitoInit() { print("awsCognitoInit..")

    let IoT_KEY = "SampleIotKey"
    let USER_POOL_KEY = "SampleUserPool"
    //all const start with AWS_* are set using AWS dashboard and tested using other apps/ways

    let AWS_IoT_ENDPOINT = "https://************-ats.iot.us-east-1.amazonaws.com" //work in unauthorised flow
    let AWS_SHADOW_NAME = "s*****" //this named shadow exists and work in unauthorised flow

    let AWS_COGNITO_IDENTITY_POOL_ID = "us-east-1:********-****-****-****-d************" //my AWSIoT (Federated Identity)
    let AWS_USER_POOL_ID = "us-east-1_********"

    let AWS_CLIENT_ID = "4**"
    let AWS_CLIENT_SECRET = "4**"
    let AWS_REGION = AWSRegionType.USEast1

    AWSDDLog.sharedInstance.logLevel = .info
    AWSDDLog.add(AWSDDTTYLogger.sharedInstance)

    let serviceConfiguratin = AWSServiceConfiguration(region: AWS_REGION, credentialsProvider: nil)
    AWSServiceManager.default().defaultServiceConfiguration = serviceConfiguratin

    let awsCognitoIdentityUserPoolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: AWS_CLIENT_ID, clientSecret: AWS_CLIENT_SECRET, poolId: AWS_USER_POOL_ID)
    AWSCognitoIdentityUserPool.registerCognitoIdentityUserPool(with:  awsCognitoIdentityUserPoolConfiguration, forKey: USER_POOL_KEY)

    let userPool = AWSCognitoIdentityUserPool(forKey: USER_POOL_KEY)

    let awsCognitoCreditalsProvider = AWSCognitoCredentialsProvider(regionType: AWS_REGION,
                                                                    identityPoolId: AWS_COGNITO_IDENTITY_POOL_ID,
                                                                    //if i try this i get: message=Basic (classic) flow is not enabled, please use enhanced flow
                                                                    // and it stay same way even if i enable basic flow in AWS dashboard...
                                                                    //unauthRoleArn: nil,
                                                                    //authRoleArn: "arn:aws:iot:us-east-1:*policy/*_policy",
                                                                    identityProviderManager: userPool)
    let configuration = AWSServiceConfiguration(region: AWS_REGION, credentialsProvider: awsCognitoCreditalsProvider)
    // this do nothing, h file say this property can be set only once and other set calls will be ignored
    //AWSServiceManager.default().defaultServiceConfiguration = configuration
    AWSIoT.register(with: configuration!, forKey: IoT_KEY)
    let awsClient = AWSIoT.init(forKey: IoT_KEY)

    let iotEndPoint = AWSEndpoint(urlString: AWS_IoT_ENDPOINT)//this end point work when i use unauthorised flow
    let config = AWSServiceConfiguration(region: AWS_REGION,
                                         endpoint: iotEndPoint,
                                         credentialsProvider: awsCognitoCreditalsProvider)
    // this do nothing, h file say this property can be set only once and other set calls will be ignored
    // AWSServiceManager.default().defaultServiceConfiguration = config
    AWSIoTData.register(with:  config!, forKey: IoT_KEY) // we also try other value for key, change nothing
    let iotData = AWSIoTData.init(forKey: IoT_KEY)

    let USER_EMAIL = "v**"
    let USER_PASS = "****"
    let authDetails = AWSCognitoIdentityPasswordAuthenticationDetails(username: USER_EMAIL, password: USER_PASS )
    self.passwordAuthenticationCompletion?.set(result: authDetails)

    let user = userPool!.currentUser()
    //let details = user?.getDetails()//some ppl say it not work whituot this i see no diffrence

    print("user.getSession..")
    user?.getSession(USER_EMAIL, password: USER_PASS, validationData: nil, clientMetaData: nil)
        .continueWith(executor: AWSExecutor.mainThread(), block: { (taskSession:AWSTask!) -> AnyObject? in

        if taskSession.error == nil {
            let session = taskSession.result! as AWSCognitoIdentityUserSession
            if let tokenString = session.idToken?.tokenString{
                print("user.getSession, success, token \(tokenString)")
            }
            print("getIdentityId..")
            awsCognitoCreditalsProvider.getIdentityId().continueWith(block:  {
                (taskGetIdentity) -> AnyObject? in
                if ((taskGetIdentity.error) != nil) {
                    print("Error: " + taskGetIdentity.error!.localizedDescription)
                    return taskGetIdentity
                } else {
                    let cognitoId = taskGetIdentity.result!
                    print("Cognito id: \(cognitoId)")
                    awsCognitoCreditalsProvider.credentials().continueWith(block: {
                        (taskCredentials) -> AnyObject? in
                        if ((taskCredentials.error) != nil) {
                            print("awsCognitoCreditalsProvider?.credentials() Error: " + taskGetIdentity.error!.localizedDescription)
                        } else {
                            print("awsCognitoCreditalsProvider?.credentials result \(String(describing: taskCredentials.result))")

                            let policyName = "VK_iOS_policy" //IOT policy which alow getThingshadow
                            guard let attachPolicyRequest = AWSIoTAttachPrincipalPolicyRequest() else {
                                print("attachPrincipalPolicyRequest unexpectedly nil")
                                return nil
                            }
                            attachPolicyRequest.policyName = policyName //  name of the policy "in IoT policies"
                            attachPolicyRequest.principal = cognitoId as String //also try COGNITO_IDENTITY_POOL_ID
                            print("attachPrincipalPolicy..")
                            awsClient.attachPrincipalPolicy(attachPolicyRequest).continueWith {
                                taskAttachPolicy -> AnyObject? in
                                if let error = taskAttachPolicy.error {
                                    print("attachPrincipalPolicy failed: [\(error)]")
                                    return nil
                                }
                                print("attachPolicyRequest successful: [\(String(describing: taskAttachPolicy.result))]")

                                print("listThingsRequest..")
                                let listThingsRequest = AWSIoTListThingsRequest()
                                awsClient.listThings(listThingsRequest!) { response, error in
                                    print("response listThings \(String(describing: response)), error \(String(describing: error))")

                                    if let things = response?.things{
                                        if(things.count>0){
                                            if let thingName = things[0].thingName{
                                                print("First thing found, name: \(thingName), attempt to get thing shadow..")

                                                let thingShadowResponse = AWSIoTDataGetThingShadowRequest()
                                                thingShadowResponse?.thingName = thingName
                                                thingShadowResponse?.shadowName = AWS_SHADOW_NAME

                                                print("thingShadowRequest..")
                                                iotData.getThingShadow(thingShadowResponse!) { response, error in
                                                    print("thingShadowResponse response \(String(describing: response)), error \(String(describing: error))")
                                                }
                                            }
                                        }
                                    }
                                }
                                return nil
                            }
                        }
                        return taskCredentials
                    })
                }
                return taskGetIdentity;

            })
        } else {
            print("LOGIN FAIL!")
        }
        return nil
    })
}`

Log:

aws_ios_iot_log.txt

ggorlim commented 2 years ago

OK guys we can close this issue problem was with polices, code work fine.

Our first IOT policy not have "iot:AttachPolicy","iot:AttachPrincipalPolicy", and that not work.

Then we make new policy where we added this actions but when we created new policy we paste action from browser and it add additional quotes (") to action that was not detected by us or AWS dash so we worked we bad policies(basically action had double quotes) until today when we found that extra quotes after we fixed it all work fine, sry..

Original IOT policies, THIS not work: { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "iot:Connect", "iot:ListThing", "iot:GetThingSadow" ], "Resource": "*" } ] }

New IOT policies, this one work: { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "iot:Connect", "iot:AttachPolicy", "iot:AttachThingPrincipal", "iot:AttachPrincipalPolicy", "iot:ListAttachedPolicies", "iot:GetThingShadow", "iot:ListThings" ], "Resource": "*" } ] }

IAM policies always had "iot:AttachPolicy", "iot:AttachThingPrincipal" so that is not problem..

I think it will be wise to add some check when polices are edited to warn user if action not exist( not standard action) if that is possible..

ajredniwja commented 2 years ago

Glad you were able to solve the issue. Pease reach out if you need any other help.