Cognito Groups with IAM Permissions
Asked Answered
T

2

12

What I want to implement:

I have a Cognito User-Pool and I have some Users and some Groups. I want that certain Users have access to API Gateway functions, some Users can access some functions and others have no access.

What I did:

I created three groups and assigned the Users to each of the groups. I gave each of the groups an IAM role and gave each roled spezific policies. The permission for the group for all users looks like this:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "execute-api:*",
            "Resource": "*"
        }
     ]
}

I created Lambda functions and API Gateway Resources through the Serverless framework. I set the authorizer to a Cognito User-Pool authorizer.

(I tried a couple different things like using federated identities but that didnt seem to work as well)

What is my result:

All Users have full access to the API Gateway. The given permissions do not seem to make any difference to the access of each user.

Help: What did I do wrong? How can I achieve my goal?

Temperament answered 24/7, 2018 at 15:44 Comment(1)
hope this post helps you aws.amazon.com/blogs/mobile/… Regarding the policy that you posted is allowing everything to be executed on the all apis, you need to make sure that you are only allowing the things that you wantHypertension
P
14

The roles attached to a user pool group only come into picture when you generate credentials for the user using Cognito Federated Identity. Adding groups to a user pool

IAM roles and their permissions are tied to the temporary AWS credentials that Amazon Cognito identity pools provide for authenticated users. Users in a group are automatically assigned the IAM role for the group when AWS credentials are provided by Amazon Cognito Federated Identities using the Choose role from token option.

So basically

  1. create an identity pool attached to your user pool.
  2. change authorization for API gateway to IAM
  3. after login to user pool, user id_token to generate the federated identity
  4. use this identity (secret key + access key + token) for authorization with API gateway.

Now your roles should be honored. But mind you - you will be required to generate AWS SigV4 credentials on your own as for some reason this is not provided out of the box. I ended up using aws-sign-web for use in browser.

PS: your role seems to give blanket access to API gateway. you will need to fix that as well. e.g. sample role I used to limit access to one API endpoint

{
"Version": "2012-10-17",
"Statement": [
    {
        "Action": "execute-api:Invoke",
        "Resource": [
            "arn:aws:execute-api:us-east-2:<aws account id>:<API id>/*/*/acc/*"
        ],
        "Effect": "Allow"
    }
]
}

Sample code to generate federated identity

function getAccessToken(idToken, idenPoolId, userPool) {
        let region = idenPoolId.split(":")[0];
        let provider = "cognito-idp." + region + ".amazonaws.com/" + userPool;
        let login = {};

        login[provider] = idToken;

        // Add the User's Id Token to the Cognito credentials login map.
        let credentials = new AWS.CognitoIdentityCredentials({
            IdentityPoolId: idenPoolId,
            Logins: login
        });

        //call refresh method in order to authenticate user and get new temp credentials
        credentials.get((error) => {
            if (error) {
                console.error(error);

                //let response = {
                //  statusCode: 500,
                //  body: JSON.stringify(error)
                //};

                return null;

            } else {
                console.log('Successfully logged!');
                console.log('AKI:'+ credentials.accessKeyId);
                console.log('AKS:'+ credentials.secretAccessKey);
                console.log('token:' + credentials.sessionToken);

                let response = JSON.stringify({
                    'AKI': credentials.accessKeyId,
                    'AKS': credentials.secretAccessKey,
                    'token': credentials.sessionToken
                });

                return response;
            }
        });
    }
Precocity answered 29/7, 2018 at 20:8 Comment(7)
thanks for the answer. You mentioned in step 4 that the User needs a secret key for authentification. 1) Do you mean the "App client secret"? If I understood it correctly only IAM Users have a secret key. 2) So for every user the Admin has to give the user these credentials, right? I want Users to be able to create themselves and have restricted access to the API. 3.) Would it make sense to just define the "Admin-methods" in an IAM Role and give access to the remaining methods to everybody in the User Pool.Temperament
The federated identity returned in step 3 contains the (secret key + access key + token). These are short lived (minimum 1 hour) tokens. Edited answer to include sample code on how to generate federated identity in JS.Precocity
Hi, so I generated the federated identity in Python and this worked for me .Temperament
One thing is still not 100% clear for me. It would be great if you can shed some light onto this for me. If a user is assigned a group he will get the role of the group once I have "Choose role from token" selected. Is this correct? And what would I put into the "Authenticated role" and "Authenticated role" they also assign the User a role. Would I put e.g. an admin role into the group and a low permission role into the "authenticated role"?Temperament
I am not really clear on that as explanation seems too short - docs.aws.amazon.com/cognitoidentity/latest/APIReference/…. But I use the same role (the one I used in group) as an authenticated role. This is only needed as I have set AmbiguousRoleResolution to AuthenticatedRole. I believe you set it to Deny you do not need to set anything.Precocity
Basically, if you want to control permissions from roles assigned to user groups, you can do that. For that, set AmbiguousRoleResolution to Deny and leave Authenticated Role and Unauthenticated Role to empty.Precocity
@Precocity Could you please help me with this #63914423Roulade
F
-2

I have a much better solution, and you don't need the IAM.

Simply save the pair of {username, serviceName} in a S3 or DB. So every time, you get the request for a service:

  1. Check if the user is authorized (from Cognito).
  2. Check if the user is authorized for the service (S3, MySQL, RDS, etc.).

Why I think it is better

Because adding/removing users from services, you don't need to login as an admin to IAM. And hopefully later on, you can create a dashboard for management.

Work Flow

  1. UserA sends a request to your securityApi.
  2. SecurityApi checks the token is authorized (user is valid or not).

  3. If the UserA is valid, the securityApi, sends the username of the user (can get it from the payload of the token) and the service name to a DB, to see if the user has access to the user. For example for Mysql (use RDS for this):

    SELECT username from ServiceX LIMIT 1 WHERE username = "xyz"; 
    
  4. If the second or third steps passed the user is 1. valid user and 2. has the right to use the service. If the user is failed in step 2 or 3, the user is not authorized to use the service.

Fireboat answered 26/7, 2018 at 10:54 Comment(5)
Thanks @Dave! What you are saying makes sense. I am not sure if I understood the implementation 100% correctly. A user signs-up and is created in Cognito. When he now requests a service, it is checked in the DB if he has the rights to access the service. How do I implement it in AWS that the Method uses this way of access control in the API gateway? Or do I set the authorization to "NONE" and check this in the Lambda function?Temperament
@Temperament updated the answer. In terms of long term, I think it is better to use some additional persistence layer here, so if later you have more services, you just add a table, or add a row to the DB (depends how you want to design the DB).Fireboat
this clears this up quite good. For step 4 I thought about creating for my securityApi an IAM User and using his access credentials to perform the calls to the AWS API Gateway. Thank you for your help.Temperament
Why skip IAM so that you can make every request dependent on an SQL lookup? Sounds slow to me.Gharry
This is externalising the approach to permissions. It could be good if you have a service large enough for it, or if deciding the permissions need a complex branching approach. Something I don't see here, to be honest.Perform

© 2022 - 2024 — McMap. All rights reserved.