2

I'm using AWS Cognito to implement the user sign-up and sign-in in my iOS APP (Swift).

When a user signed up, I want to add the user into a specified user pool group which has already been created in my cognito user pool.

I have tried the swift code bellow, but it doesn't seem to work.

After the user signing up, he or she push the [addToGroup] button to join the user pool group named [GroupA].

@IBAction func addToGroupButton(_ sender: Any) {
        let request = AWSCognitoIdentityProviderAdminAddUserToGroupRequest()
        request?.groupName = "GroupA"
        request?.userPoolId = "ap-northeast-1_8H0k*****"
        request?.username = eMailAdd

        let identityProvider = AWSCognitoIdentityProvider()
        identityProvider.adminAddUser(toGroup: request!).continueWith { (task) -> Any? in
            DispatchQueue.main.async(execute: {
                if let error = task.error {
                    print("\(error.localizedDescription)")
                }
            })
        }
    }

There is no problem for the user to sign-up, and I can confirm the information the user signed up. BUT, the user name just doesn't appear in the GroupA.

Could anybody tell me what's wrong with my code?? THANK YOU!!

keishinzzz
  • 113
  • 1
  • 10

1 Answers1

4

I assume you get some error like AWSCognitoIdentityProviderErrorNotAuthorized. As this is a 'admin' API call which 'Requires developer credentials' as suggested here [0].

I would suggest you to hardcode your credential to test if this API call works first

AWSStaticCredentialsProvider *credentialsProvider = [AWSStaticCredentialsProvider credentialsWithAccessKey:"your-access-key" secretKey:"your-secret-key"];
AWSServiceConfiguration *configuration = [AWSServiceConfiguration configurationWithRegion:AWSRegionUSEast1 credentialsProvider:credentialsProvider];
[AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;

Please note do not hardcode your credential in your production environment.

21/07/2019 update:

let staticCredentialProvider = AWSStaticCredentialsProvider.init(accessKey: "yourAccessKey", secretKey: "yourSecretKey")
let configuration = AWSServiceConfiguration.init(region: .APSoutheast2, credentialsProvider: staticCredentialProvider)
AWSServiceManager.default()?.defaultServiceConfiguration = configuration

let request = AWSCognitoIdentityProviderAdminAddUserToGroupRequest()
request?.groupName = "GroupA"
request?.userPoolId = "ap-southeast-2_xxxxxxxxx"
request?.username = "yourUserName"


    AWSCognitoIdentityProvider.default().adminAddUser(toGroup: request!).continueWith { (task) -> Any? in
        DispatchQueue.main.async(execute: {
            if let error = task.error {
                print("\(error.localizedDescription)")
            }
        })
    }

I have tested, the above code is working in my test environment. I list it here for your reference. The next step will be trying to remove hardcoded credential from the codebase. Instead of using AWSStaticCredentialsProvider, temp credentials can be acquired using Cognito identity pool. I reckon with adequate permission, this flow can work without developer credential.

26/07/2019 update:

// using Cognito userpool with identity pool, to provider credential to AWSServiceManager
let serviceConfiguration = AWSServiceConfiguration(region: .APSoutheast2, credentialsProvider: nil)
let userPoolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: "YourUserPoolClientId", clientSecret: "YourUserPoolClientSecret", poolId: "YourUserPoolId")
AWSCognitoIdentityUserPool.register(with: serviceConfiguration, userPoolConfiguration: userPoolConfiguration, forKey: "RandomStringForIdentifyingYourPoolWithinThisApp")
let pool = AWSCognitoIdentityUserPool(forKey: "RandomStringForIdentifyingYourPoolWithinThisApp")
let credentialsProvider = AWSCognitoCredentialsProvider(regionType: .APSoutheast2, identityPoolId: "YourIdentityPoolId", identityProviderManager:pool)

let configuration = AWSServiceConfiguration.init(region: .APSoutheast2, credentialsProvider: credentialsProvider)
AWSServiceManager.default()?.defaultServiceConfiguration = configuration

// sign in a user
pool.getUser("UserNameOfAUserInYourPool").getSession("UserNameOfAUserInYourPool", password: "PasswordOfAUserInYourPool", validationData: nil).continueWith { (task) -> Any? in
    if let error = task.error {
        print("user sign in error: \(error.localizedDescription)")
    } else {
        print("user session is: \(String(describing: task.result))")
    }

    // add a user to GroupA
    let request = AWSCognitoIdentityProviderAdminAddUserToGroupRequest()
    request?.groupName = "GroupA"
    request?.userPoolId = "YourUserPoolId"
    request?.username = "UserNameOfAUserInYourPool"

    return AWSCognitoIdentityProvider.default().adminAddUser(toGroup: request!)
    }.continueWith { (task) -> Any? in
        if let error = task.error {
            print("cannot add user to group \(error.localizedDescription)")
        }
}

Like I suggested, this is the solution without hardcoded AWS credential. Cognito Identity pool provides temperature AWS credential after user sign in to Cognito user pool [3], which enables the 'adminAddToGroup' API call.

Along with the above code, you also need to setup your Cognito identity pool on AWS console, or using AWS CLI. You can find the details in the screenshot.

configuring_identity_pool

You also need to create an auth IAM role for authenticated user to assume. Here is the policy example.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "cognito-idp:AdminRemoveUserFromGroup",
                "cognito-idp:AdminAddUserToGroup"
            ],
            "Resource": "Your_userpool_ARN"
        }
    ]
}

Reference: [0]https://aws-amplify.github.io/aws-sdk-ios/docs/reference/Classes/AWSCognitoIdentityProvider.html#//api/name/adminAddUserToGroup: [2]https://aws.amazon.com/blogs/mobile/how-amazon-cognito-keeps-mobile-app-users-data-safe/ [3]https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-integrating-user-pools-with-identity-pools.html

Sida
  • 66
  • 5
  • Thank you for your answer. It could be a key to this problem. But right now, I can not even print the error, because this part of code seem to have something wrong. The error message is [Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '`- init` is not a valid initializer. Use `+ defaultCognitoIdentityProvider` or `+ CognitoIdentityProviderForKey:` instead.']. I think I did print the "\(error.localizedDescription)" yesterday with the same code. But today when I run the program, the message above showed up. So confused...Do you have any idea? – keishinzzz Jul 21 '19 at 08:02
  • @keishinzzz no worries, I have updated my answer. The reason you get the exception is because AWSCognitoIdentityProvider cannot be created using normal init() method. You should use the singleton of it instead. Like I suggested yesterday, you need to config AWSServiceConfiguration to sign this admin API call. I may update the answer once I have time to get rid of hardcoded developer credential. – Sida Jul 21 '19 at 10:37
  • It worked! Thank you sooo much! Without your help, I don't know how much more time I would spent on this issue. The next step is to get rid of the hardcoded developer credential part. Before that, I realized that the [request?.username] should be the UUID in the user pool. But right now, I'm using eMail to sign up in my app, so I should find a way how to call the UUID in the program. I just started learning the AWS for a couple of mouthes all by myself. I'm so glad you helped me. Thank you so much . – keishinzzz Jul 22 '19 at 13:21
  • I tried again, and the code just worked perfectly. It seems I can add a user either by his username or by his UUID(sub). I don't have absolute confidence, but it seems to work in either way... – keishinzzz Jul 24 '19 at 00:15
  • 1
    @keishinzzz You are welcome :) ultimately you will want to get rid of AWSStaticCredentialsProvider, using AWSCognitoCredentialsProvider instead. In that case sign-in users can fetch a temp credential from identity pool, which in theory enable them to add themselves to a group. The auth role in identity pool should have sufficient permission to invoke the admin API call. Quite busy this week, will try to test it once I have time. – Sida Jul 24 '19 at 12:52
  • 1
    @keishinzzz As promised, I added new solution without hardcoded AWS credential. To use with AWS services in iOS, I would suggest you to check out the latest Amplify iOS SDK (https://aws-amplify.github.io/docs/ios/start). It has better document and provides better development experience. "AdminAddToGroup" is not supported out-of-box, but I think it can work with my above solution with some tweaks. I may provide an example when I have time. – Sida Jul 26 '19 at 05:26
  • 1
    @keishinzzz no worries :) I know you won't disturb me, but asking question here can benefit others with same problem right? You can always ask question here, and ping me, I will do my best to answer when I am available. – Sida Jul 28 '19 at 04:24