10
votes

Help is required in the following problem we're facing ????

Any tip would be much appreciated!

Details and environment:

  1. A multi-tenant application that aims to provide a dedicated tenant per customer (organization), in order to achieve full separation.
  2. AWS Cognito user pool as my users' datastore and authentication provider.
  3. an "AWS Cognito user pool" per customer (org).
  4. 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:

  1. I want to disable non-admin users' ability to modify a specific attribute's value, via Amplify.
  2. 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:

  1. 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.
  2. 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!

1

1 Answers

11
votes

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:

  1. [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.
  2. [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.
  3. [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.