How to restrict AWS Cognito users from taking certain actions? - amazon-web-services

Help is required in the following problem we're facing 😔
Any tip would be much appreciated!
Details and environment:
A multi-tenant application that aims to provide a dedicated tenant per customer (organization), in order to achieve full separation.
AWS Cognito user pool as my users' datastore and authentication provider.
an "AWS Cognito user pool" per customer (org).
Role management - based on the built-in user pool groups. Group per role and the server-side verifies that a user's access token includes a group name in it's embedded group's list.
So far so good and everything is working as expected, using AWS Amplify's SDK, for the client side's implementation. Amplify performs well and allows me to do whatever I want. The server verifies group belonging etc.
The problem:
I want to restrict non-admin users (that doesn't belong to the "admin" group) from performing certain Cognito actions via Amplify.
2 Examples:
I want to disable non-admin users' ability to modify a specific attribute's value, via Amplify.
I want to disable non-admin users' ability to modify MFA settings for themselves, via Amplify.
The actual problem started when I wanted administrators to be able to set MFA (enable/disable) for other users, but in Cognito (as I understand it) only a user can set his own MFA settings.
What I saw and already tried:
Set read/write permissions for user attributes. So the specific attribute I want to protect is modifiable only via API calls with developer credentials. That way, admins can call my server to ask for attribute modification. The server verifies the role by a group belonging according to the access token and calls Cognito API. The problem with that solution is that it covers only the attribute modification scenario.
Create an AWS Cognito identity pool for each of the user pools. For every group in every user pool, create an AWS IAM role with a policy that would restrict or allow the wanted behavior. The could actually work. The problem with that solution is that it feels like a super-duper overkill, plus it requires me to create an extra identity pool and an IAM role for each user pool. It means that every new customer that joins the service, would require (1) user pool, (2) Cognito client application, (3) identity pool and (4) IAM Role (instead of just a user pool and Cognito client app). Essentially, implementing this solution.
The real question:
Can I restrict users in a certain group from performing actions on themselves, such as disabling the MFA (even that the user-pool's MFA is set to "Optional")?
Thank you all so much! any help would be appreciated!

Well... After long research, we have come to the understanding that there is no proper right way. Every possible solution has its own pros and cons. A consultant meeting with AWS's experts taught us that:
Options Overview:
[Server Side Only] - Solution #1 that I proposed is exactly as described. Drawbacks are the same. It could work, and access to user-attributes will be restricted. Any other action that another client would make will not be blocked.
[Identity Pools] - Solution #2 that I proposed is the most accurate one. Yet I described it with one big mistake: one identity-pool can serve multiple user-pools! So essentially, we could create only one IAM role and one identity-pool per app's role. Then we match every user-pool we want to that same identity-pool and when introducing a new role to the app - just create a new group in the user-pool and match it to the IAM role. This solution is not as complicated as thought, and it would definitely do the trick. As a bonus, you'll get the ability to control and allow access to different AWS services. That being said, it still requires management and effort.
[Post-Auth Lambda] - Solution #3 that was not mentioned here, and I started to work on a day after posting this post. I blocked the write permissions of a new boolean custom attribute called "MFA". It indicates the desired MFA configuration for the user. Only a server could edit its value (and users with the admin role will have access to the server's API endpoint that can modify it). We've deployed a lambda function that would be triggered after successful authentication (post auth trigger in Cognito user-pool). It would verify a match between the desired and current MFA configurations for the authenticated user. If there is a mismatch, throw the user out because he did something that is not allowed.
*To be exact, we created one more custom attribute called "mfa_status" and it is set to true after the user has set it's MFA configurations. The lambda checks if both MFA and mfa_status are true and the real current MFA Configurations are false. if this is the case - the user is thrown out.
The Chosen One:
The solution we picked eventually is #3 (Post-Auth lambda) as it is the most detached solution. It does not require any mix with our server or client's code, any special configurations that are specific to a user pool and it still allows us to keep working with the Cognito's Amplify SDK as a client.
Thank you all for your time, I hope this post would help someone in the future.

Related

Multi level authentication/authorization or approvals for access/delete resource in AWS

I want to implement a security measure that enables any authenticated user to delete any AWS resource only on approval from any other member.
The main objective is to make sure that, if the creds are compromised, the user should not be able to delete any resource without approval.
Looking forward to get some expert advise and discussion on this.
I understand, the least access privilege and MFA, but still it is fulfilling our requirements.
Since this is not directly supported by AWS, you will have to set up a custom solution for this.
This is one possible solution:
Create a role with delete permissions.
Create a Lambda to use that role to delete a resource.
Implement the Lambda to only processes the delete request if the input includes at least two approvals. (This part depends on your org authentication mechanism. Could be LDAP, short term access tokens, etc)
This fulfills the two requirements you mentioned:
a security measure that enables any authenticated user to delete any AWS resource only on approval from any other member.
The Lambda will only process the request if there is at least 2 approvals.
if the creds are compromised, the user should not be able to delete any resource without approval.
In this design there are no credentials. The role you created can only be assumed by the Lambda. You can further improve the security by only allowing certain Users, Roles or Groups to be able to execute the Lambda.

Different access tokens for users using aws Cognito for authorisation

I am trying to provide differential access to services behind an aws api-gateway, lets say simplistically split into 2 user groups user-a and user-b that each can access /routeA or /routeB but not the other.
User Authentication is via Cognito User Pool with 2 user groups defined. Front End is React and Amplify.
I am going round in circles with this having tried a few approaches. Running this decision tree select-auth-method points to using Cognito AuthZ which is fine in itself as I am using Cognito for AuthN.
So far none of the examples/tutorials/articles/videos have demonstrated how to block some users while enabling others. I have tried:
Cognito User Pool Authorizer - identity token based but seems to just AuthZ any logged in user. I could possibly attach IAM Roles to the user groups ?
Custom Lambda Authorizer - Works well on checking for the valid user-group in the Access Token and dynamically creating the required permissions but, some additional latency from a λ and no check that the access token is current. Starts to feel like I am writing code for a system that exists somewhere else?
Cognito OAuth - Spent some time trying to understand this. So I create a Resource Server attached to the Cognito app client and create some custom scopes that I can then reference in the Api-g Authorizers. Fine, but how to I create two different access tokens for the users that have different scopes? So far my understanding seems to suggest that it is another binary AuthZ approach, but articles suggest (but don't demonstrate!) it is capable of granular permissions? I must be missing something.
I also saw this 2017 answer but things may have moved on with better approaches?
I am minded to go OAuth route as it seems more of an industry standard approach and promises much but need a bit of insight as to how to enable different access tokens for different users.
So the answer is that Cognito does not implement the OAuth2 functionality that I need but others do. Given the following excerpt from AuthO docs, this feels like a better solution path...
It would be very useful if we could just restrict endpoints on the cognito:groups that are in the tokens,but this functionality isn't there.
There are a few options that might suit you, one is just put the users in different user pools and create different authorizers for those pools and assign them the routes.
Another is OAuth scopes, you can create two app clients each with different scopes then configure the scopes in APIGW. Note that in this case you need to pass the access token to APIGW not the ID token.
In either case if you are using Hosted UI you will have a problem as you will now have two Hosted UIs and it may not be straightforward knowing which one to display to a user until they have entered their email address.
BTW if you are not using Hosted UI you will be unable to use OAuth scopes.
You can also assign IAM roles to the cognito:groups and allow these access to the APIGW resources you want. This is probably the easiest method although it is not as simple as you would probably like.
The final way I can think of is writing a custom authorizer, I would avoid this if you can, I have done it a few times now and it has never been enjoyable.
Your requirement seems to be to authorise users depending on their permissions, as opposed to needing different types of access token.
I would recommend sending the Cognito access token to the API and receiving it like this in the API:
Level 1: Validate the access token first to check it is not expired etc
Level 2: Consider checking a scope to ensure that the token is for your API
Level 3: Check finer grained claims such as group membership or anything else that makes sense for your scenario. Your API can get groups by sending the access token to the Cognito user info endpoint.
DESIGN PATTERN
This blog post of mine describes the pattern and use of claims caching.
Here is some lambda authorizer sample code, though the same behaviour can be implemented in your API directly and my blog has some examples of this:
Cognito Claims Based Authorization

AWS Cognito User Management

For the last few years, I've built a PHP and MySQL based website where users can submit reprographics and IT requests. I'm hoping to make this cloud-based rather than running it from a local webserver. My initial idea was to have an EC2 instance running as a web server for each company which uses my system, but as the system is PHP session based I'm assuming the security would not be great so I think I need to move towards more of an AWS system using Cognito for user management and the API Gateway with Lambda to do the job of getting the data from the databases. My question is, my current system has an admin console where the admin user can access the lists of users, and assign them permissions (session variables) which allows them access to specific pages. How would I make a webpage where users can manage the users in a Cognito user pool without giving them access to the AWS console.
Implement a Cognito AdminAddUserToGroup operation in your Lambda function for admin users to manage what Cognito Groups your users belong to. Your admins will be the only ones that are able to invoke the API call to the Lambda function because they'll be included in the Cognito Admin Group with appropriate permissions to invoke the Lambda function that you specified as the developer.
Specify permissions of what each Congito Group has access to by assigning roles for each Cognito Group.
You can also decode the jwt on the backend to determine what Cognito Group the user belongs to that made the request and use Amplify on the FrontEnd to manage the FrontEnd's display of content based on the Group (links, etc). More info about that can be found in this thread: How do I access the group for a Cognito User account?
I do not have enough points to comment on the CLI update-user-pool suggestion, but wanted you to know that wouldn't work because 1.) It would impact the entire user pool and affect ALL users in the user pool and 2.) It would make no difference in regards to what your users were able to have permission to access in your application.

AWS cognito: allowing only a certain users (identities) to get credentials

My production environment uses AWS cognito federated identities (AKA: identity pool) for allowing users to sign in with their Google/FB accounts. This is working fine.
However, for my staging environment I want to offer a similar sign in functionality (that is: users sign in in via Google) but I want to restrict the ability to sign in only to a specific whitelist (of, say, #gmail.com accounts). This is because the staging environment should only be accessed by team members and a bunch of trusted users.
My question is this: is it possible to define a Cognito identity pool that allows only the whitelisted users to sign in? if the answer is "yes", then how?
I know that I can do the whitelist checking at my backend (that is: at the backend of my staging env. I will reject every request not coming from these whitelisted users) but this is a bit unclean. In particular, I want the rejection to happen as early and possible and, ideally, to be carried out by cognito itself, and not by my backend.
Clarifications:
I mentioned a list of #gmail.com accounts as the whitelist. I am open to any other mechanism for defining the whitelist.
In a way I am asking whether I can have user directory functionality (in particular: determining if a user is in a certain group of users) with an cognito identity pool.
The purpose of the Identity pool is to provide access to authenticated identities. Therefore, this resource cannot manage users (ie: white listing a user group).
There are two ways to do this:
Link 3rd Party Identity into a User Pool and white list by User Group. Your user group will have a Roles and permission it will assume that will allow your test users to have the functionality they need without allowing other users who log to have that functionality.
Manage users from the 3rd Party Identity - I am not an expert at Google authentication.. but some OpenID providers allow you to create user groups with different access roles. This is an example for Google.
You can invoke a trigger and execute a lambda function when the user submits their information to accept or deny the sign in request.

Can I have dynamic User specific permissions using AWS IAM / Cognito?

I'm attempting to develop an application architecture almost exclusively on top of AWS services.
This application has both User and Organization "entities". As one might except, a User may be an admin, role-x or role-y of one or more organizations. (role-x and role-y are just placeholders for some role with some set of specific permissions. A User may also be standalone (that is, not have a role on any Organization).
Our current thinking is to use DynamoDB to store organization and user specific data. For users this may include some basic information (address, phone number, whatever), and for organizations it may include fields like "mission statement", "business address" and so on.
An admin of an organization would be able to edit all organization fields, whereas a role-x might only be able to update "mission statement" while reading all other fields.
Since I mentioned that a single user may have roles on many different organizations, that might look something like:
user1:
organizations:
123: 'admin'
456: 'role-x'
789: 'admin'
It's also worth noting that these role assignments are modifiable. New or existing users may be invited to take on a specific role for an organization, and an organization may remove a user from a role.
This is a fairly straightforward type of layout, but I wanted to be very clear about the many-to-many nature of the user, org and roles.
I've been reading IAM and Cognito documentation, as well as how it relates to fine-grained control over DynamoDB items or S3 buckets - but many of the examples focus on a single user accessing their own data rather than a many-to-many role style layout.
How might one go about implementing this type of permission system on AWS?
(If policy definitions need to be updated with specific Identities (say, for an Organization), can that reliably be done in a programatic way - or is it ill-advised to modify policies on the fly like that?)
The above answer is outdated.
AWS has added Cognito-Groups recently. That provides more flexibility
You can use technique described in the article to achieve that:
https://aws.amazon.com/blogs/aws/new-amazon-cognito-groups-and-fine-grained-role-based-access-control-2/
Unfortunately the kind of permission system you are trying to implement is not possible with Cognito at the moment. With Cognito you can currently create unique identities for your users in an identity pool. Users can authenticate using any external provider such as Facebook, Amazon, Google, Twitter/Digits or any OpenId Connect Provider. Users can also authenticate through your own backend authentication process. After the user authenticates, Cognito creates a unique identity for that user. There’s a concept of an identity, but there’s no concept of groups. All users/identities within a one identity pool can get credentials from roles associated with that identity pool. Currently you can specify two roles: One role for authenticated identity and one role for unauthenticated identity. There’s no such feature at the moment where you can specify multiple groups for each identity and specify role on that group.
For more information on Cognito, you can refer to
https://aws.amazon.com/cognito/faqs/
http://docs.aws.amazon.com/cognito/devguide/getting-started/