I am creating an app on AWS using API gateway, Lambda and DynamoDb which will be used as Helpdesk ticket tool. The tool should support thousands of users, hundreds of organizations. One of the requirements would be for an administrative user to create their own sets of roles (groups) that would then be assigned to users in the app. The roles will, of course, determine whether a user can access a url resource on the API gateway (or rather the Lambda function behind it). I think I've worked out that Cognito user pools are probably the best way to manage users themselves but there are some quotas on user pools that won't work so well for what I'm attempting using Cognita's implementation of groups (max 10k user groups which is less of a problem and max 100 user groups for a given user which is more of a problem). I already have a plan for this which seems ok: I can create a dynamodb table to hold group definitions (name, desc, list of roles) and a separate dynamodb table which maps users to groups.
I need to have an easily configurable way to restrict a user from being able to access a resource they don't have access to. Since I don't feel like I can use Cognito groups, then what I had intended to do was (part 1) just use the AWS IAM authorizer to authorize the user to hit the resource (of course this lets ANY authenticated user hit it), (part 2) to create a lambda function that gets triggered on PreTokenGeneration and inject a custom claim into the token which list the roles the user had access to. Once that is working, (part 3) then in order to utilize this custom claim, I'd intended to add a check on each resource to decode the token and check to see if the appropriate claim was present (so, for instance, if it is the lambda that gets triggered for POST on /tickets then the claim I would check for in the token would be to see if 'appUserRoles' includes 'TICKETS_CREATE'). This would just method call to a JWT utils function to decode the id token to check to see if the claim is present. If the token has the appropriate claim, then the Lambda would perform the put item on Dyanamo and return some result, if the token doesn't have the appropriate claim, the Lambda would return a 403.
I already have most of this working and I'm pretty confident I can get it all working but I'm less sure on whether this is the best or even a good approach. Is there a better / easier / more efficient / (maybe more secure) way to accomplish this?
Related
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.
I am new to the AWS ecosystem, coming over from Firebase. I needed a step up in configuration, and especially permissions. This might be an obvious question, but I'm not sure how to reason about it...
My Problem
I am trying to build an app that will have dozens, perhaps around 100 different permissions to perform different actions. I plan on implementing these with IAM policies on AWS Cognito Identity pools, since a maximum of 25 attributes and 25 groups won't be enough for the number of distinct permissions that I will be dealing with. How could I go about letting the front-end UI know what permissions any given user has?
If, for example, I had all of the permissions listed in the attributes, then that would be easy; I would just check the user's JWT and read out the attributes and update the UI to hide the actions user's don't have access to. Same thing goes for groups. But I am not sure on how this could be done for IAM policies?
I am aware that an option would be to just query a backend endpoint when the user tries to perform an action, and if permission is denied, then warn the user...but that is, in my opinion, a terrible UX.
I can also imagine that you could have some kind of endpoint, let's call it getUserPermissions, where when you call, a lambda function retrieves the IAM policies, parses them, and returns some permissions' describing object back to the front end. But doing this feels awkward, and might lead to outdated permissions if one forgets to query the endpoint. Is this the best/only option?
Any tips would be much appreciated.
Please note I also purposefully didn't mention what front-end (web, native, etc.) or backend I am using, because I would like to know in the general case how this works
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
is there a way to limit the number of users that are allowed to signup in Cognito per User Pool? The idea is to create a tier based plan that allows an organization to only be able to create a certain number of users (eg. basic plan - 3 users, pro - 10 users, etc...). I'm thinking of tackling this with the presignup trigger but I'm not sure if that's the way to go or if there's a better way to handle this.
From the Pre Sign-up docs:
The pre sign-up Lambda function is triggered just before Amazon
Cognito signs up a new user. It allows you to perform custom
validation to accept or deny the registration request as part of the
sign-up process
So far so good. But you may have challenges counting the number of users in the pool. The best I found was the ability to ListUsers and that could get expensive if you've got a large user pool.
In that case I would still use the pre-sign up Lambda but store the count of users somewhere. Perhaps a DynamoDB table or updating the user pool with a tag that has the number of users.
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.