AWS API Gateway: Authorize Cognito user groups

Viewed 1140

I am new to the AWS world of API Gateway, and am trying to limit access to my APIs by user group. To clarify I can already run authenticated API, the question is around authorization (limit a group of users to API-1, and another group to API-2). I am using HTTP-API and I do not wish to use others (to save cost).

I have created a Cognito user-pool and created two groups called 'regular' and 'admin'. I have also setup an app-client.

user-pool

Next I have two API routes, that map to two different Lambda functions (just hello world). These work perfectly without authentication, and also with authentication. I am using JWT-auth with Cognito, and for multiple reasons, this is the right approach for my app.

API Auth

The trouble is how do I restrict access to the API, for the relevant group of users. Hence, only the users in the admin-group should be able to use the admin-api. I believe the section in red can help, but I cant seem to find the right documentation. I read that I can also create a lambda function to authorize users, but that seems like a waste, why pay for another lambda function, if the restrictions can be applied here.

authorize users

Would appreciate any help.

3 Answers

Scopes are not the right thing to use here. Scopes work well if you have different app clients (actual applications using your API Gateway) to limit the scope of what endpoints they can access. They don't work for role based access.

As you say, you already use Cognito User Pool and have some user groups setup. A simple apporach would be to use a post confirmation Lambda trigger. This Lambda is triggered whenever a new user has been confirmed. It even gets triggered when you use an external identity provider and a user logs in the first time with Cognito hosted UI for instance. In the simplest case you add a user to your 'regular' group with the post confirmation Lambda.

For authentication and authorization on your API Gateway routes, you replace JWT authorizer with a custom authorizer. This means, you will use a Lambda function to do the authentication and authorization. Within the Lambda function you must verify the JWT token. If the JWT token or the request itself is invalid you throw an exception with the message "Unauthorized". API Gateway will translate this to a 401 "Unauthorized" response. If the JWT token is valid, you decode it and get the cognito:groups claim out of it. You can decode your token and look at the claims on jwt.io for testing. In you Lambda function, you then check in what groups your user is and if the group has access to a specific route. The route the user tries to access is also part of the event triggering your Lambda function.

# dummy python code
if route.startswith('/adminuser') and 'admin' in cognito_groups:
   return { 'isAuthorized': True, 'context': { 'custom_key': 'custom_value' } }
# respond with 403 "Forbidden"
return { 'isAuthorized': False }

The payload for the Lambda input and output can be found in the HHTP API Lambda Authorizer documentation.

For a deeper dive, have a look into "building fine-grained authorization using Amazon Cognito, API Gateway, and IAM". For a more advanced look into authorization options, I recommend this video from the re:invent 2017.

Application scopes are not what are looking for (they define application access, not user access).

This is broadly referred to as Role-based Access Control (RBAC). The built in authorizer doesn't have any support for it so you'll have to either check the role/group in you handler or opt for a custom authorizer.

One idea is to create a different Cognito Userpool for each group. Then within each pool, define a Resource Server and put the group name as a scope. This way the group is available in the scope. Then, make a combined Cognito authorizer as follows.

  securitySchemes:
    CognitoAuth:
      type: apiKey
      name: Authorization
      in: header
      x-amazon-apigateway-authtype: cognito_user_pools
      x-amazon-apigateway-authorizer:
        type: cognito_user_pools
        providerARNs:
          - 'arn:aws:cognito-idp:{region}:{account}:userpool/{pool1}'
          - 'arn:aws:cognito-idp:{region}:{account}:userpool/{pool2}'
          - 'arn:aws:cognito-idp:{region}:{account}:userpool/{pool3}'

You are then free to put the group names (as scopes) globally or in any path.

Furthermore, you will have more control over the policies (e.g., custom attributes, MFA, sign-up options) for each group by virtue of having used different pools.

Related