I have my WEB API's hosted in Docker. My Angular client will send a JWT token to access any of these API's. I wanted to make use of AWS API Gateway feature to add an Authorization check before calling the API client requested. From the docs I see that we can leverage the Lambda Authorizer concept to Achieve this. But then again I though why using Lambda Authorizer when I can come up with an DOT NET CORE API which can validate the user.
Does my Lambda Gateway makes sense for my case?
If it does, what would be the output of the lambda Authorizer? A simple true/false which says the the Token is valid or not?
I see that this is what the response should/might look like. How this should translate to in my case
{
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Resource": [
"arn:aws:execute-api:us-east-1:1234567:myapiId/staging/POST/*"
],
"Effect": "Allow"
}
]
},
"principalId": "Foo"
}
What should happen in API gateway after the Lambda Authorizer executed ? Who calls my actual API which is requested by the client?
If you are using a Lambda Authorizer, returning an Allow or Deny Policy is what you are looking for.
This essentially grants API Gateway permissions to invoke the underlying target. I know it sounds weird at a first glance, but that's how it works. Think of an Allow policy as a true return statement (credentials matched) kind of thing whilst a Deny policy is more of a false return statement (credentials didn't match / not enough permissions based on your rules, etc).
To get you off ground, you can simply copy/paste the code available at the docs and modify the authentication way to your liking (the docs show an example using a header with Allow or Deny values, which is definitely not what you want, that's just meant for the sake of an example).
So, back to your question by enumerating all the answers:
Yes, but it's called a Lambda Authorizer instead of a Lambda Gateway
Either an Allow or Deny policy for valid/invalid tokens respectively.
If the Lambda Authorizer responds with an Allow policy, it will then invoke the target (which can be a Lambda function, an SNS Topic, an HTTP endpoint - this is likely your case - and so on). The authorizer will just act as an interceptor and decide whether to proxy the call to the target or not.
From what I understood as per the question, you want to validate the user who are calling your API.
You can do it in all the ways you have already mentioned.
Using Lambda Authorizers, you will get a 200 or a 403 code not true false. You can follow the following link to set up your authorizer :
https://blog.codecentric.de/en/2018/04/aws-lambda-authorizer/
You can also use AWS Cognito for managing your users, it'll simplify your work a lot.
You just add your dotnet core api to "Integration Request" tab, Choose Integration type as HTTP and mention the dotnet core api in Endpoint URL field
Amazon Cognito lets you add user sign-up, sign-in, and access control to your web and mobile apps quickly and easily. Amazon Cognito scales to millions of users and supports sign-in with social identity providers, such as Facebook, Google, and Amazon, and enterprise identity providers via SAML 2.0.
Advantages for using Cognito:
Managed service, less components to implement/monitor/scale
Easily configurable via portal, CLI and templates
Supports multiple flows for authentication (client side, server side, OAuth2, custom)
Supports Lambda triggered functions on authentication/registration events
Uses JWT signed tokens which can be passed directly to clients in session cookies and used to verify requests and passed in related API calls so a single authentication/authorisation method can be used through your stack statelessly Group membership, supplied in access token can be used for authorisation (e.g. users in group “Admin” can perform admin functions)
Handles:
User group membership and attribute storage
Email/Phone verification
User invitation
Login/Signup UI forms (customisable)
Password reset
Disadvantages:
Less control over authentication/authorisation (limits to UI/flow customisation)
Potential for lock-in (cannot export users with passwords for migration)
Amazon Cognito has three type of authorizer
Amazon Cognito user pool - User pool authorizer.
Amazon Cognito federated identities - AWS IAM Authorization.
Custom Lambda identity providers - Custom Autorizer
Related
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.
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.
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.
Would appreciate any help.
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.
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.
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.
I have the following setup:
REST API Gateway containing one endpoint with a proxy Lambda integration and secured using the AWS_IAM authorizer. End users retrieve temporary AWS credentials from a Cognito Identity Pool in exchange for tokens retrieved from a Cognito User Pool and then call the API using these credentials.
When users call the API Gateway, I want the invoked lambda function to run with the same permissions as the invoking user. For example, if the user only has the permission to read items from DynamoDB with a PK of CUSTOMER1234, I want the lambda function to also only have that permission. Different users will have different permissions so I can't "hard-code" that into my Lambda permissions.
This seems like a fairly common use case, but I was unable to find any documentation on how to achieve something like this.
One possibility would be to send the AWS credentials (i.e. access key and secret key) of the user with every request and then construct a new session using these credentials in the Lambda, but this probably is a terrible idea (it also increases function runtime by 5x).
It doesn't seem like a good idea to send AWS credentials over the network, as there is the chance that they could be intercepted by an attacker and used to access your resources until they expired.
It could be possible to use the JWT from your Cognito User Pool to get the AWS credentials in the lambda function e.g. using:
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'IDENTITY_POOL_ID',
Logins: {
'cognito-idp.<region>.amazonaws.com/<YOUR_USER_POOL_ID>': 'ID_TOKEN'
}
});
This would mean that your lambda's API calls would run using the role mapped to in your identity pool, and you could use the LeadingKeys condition to ensure that your web identity could only access their own data:
"Condition": {
"ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": ["${cognito-identity.amazonaws.com:sub}"]
}
}
However, this would add processing time because you are making extra API calls (CognitoIdentityCredentials() makes 2 API calls behind the scenes) on every request to get the credentials. Plus, you couldn't use the AWS_IAM authorization on your API Gateway method because you won't have got the credentials yet. So you are then talking about having a two authorization setups. One to allow your web identity to call the API Gateway method, and a second for your Lambda function to run as your user.
So perhaps your choices are:
Make your API calls direct from your client
Instead of going via API Gateway, you could make the DynamoDB API calls directly from the client. This would work well with your Identity Pool setup, as you can make the CognitoIdentityCredentials() call just once when the user logs in to get the AWS credentials. The subsequent DynamoDB calls would then be made with your user permissions and you could use the LeadingKeys condition to ensure that your user only accesses their own data.
Accept that your lambda function will always run with the same execution role
If you want to use API Gateway backed with Lambda, then you could just accept that you will use Lambda or Cognito Authorizers to authorize your users have permissions to execute the API method, and then some further application-level authorization in the Lambda function to ensure they only call their own partition space. In fact, this way you might not need the Identity Pool, but rather just use the sub claim from the User Pool JWT token. After all, you will have validated the JWT containing the sub hasn't been tampered with, and that it was issued by the correct user pool and application client, so you can trust its contents.
You could integrate API Gateway directly with DynamoDB
Depending on your required logic, rather than have your API method backed by a lambda function you could integrate your API Gateway method directly with DynamoDB as shown in this amazon tutorial. Again, you would use the sub from the validated JWT as the partition key to enforce the correct data access.
I am using Spring Boot in my application. While searching for some IAM tools, I actually liked Auth0, but iam not affordable their pricing. So, I found another called AWS Cognito.
Below is Auth0 to restrict our custom access api
https://auth0.com/docs/api-auth/restrict-access-api
Currently, I am trying to restrict access API using AWS cognito, but I am not finding correct documentation to achieve this. Can anyone please tell me whether restricting api access can be possible using aws cognito.
It depends on how much fine-grained control you want over the access to your api.
Allow or Deny Scenario
In some cases, you either want to block someone completely, or give them access to all of your api. In this all or nothing scenario, the simplest route would be to use Cognito User Pools on their own to authorize your users. Cognito User Pools is just used to authenticate the user (are they who they say they are), and to provide tools to make sign up, and sign in easier.
If the user passes authentication, then you can pass one of the tokens returned by cognito user pools (the identity token) to API Gateway. As long as you have set up your api methods to have the Cognito User Pools authorizer in API Gateway, then this is enough for them to accept the identity token as authorization to access the methods.
Fine-Grained Access
However, in other cases, you need more fine-grained control. You may want all authenticated users to have access to a certain subset of your api methods, but only admins to have access to more restricted methods.
In this case, you will also need to use Cognito Identity Pools, to define user roles (e.g. UNAUTHENTICATED_USER, PAID_USER, ADMIN etc), and their associated IAM roles, which will have policies that give them access, or deny them access to various parts of your api.
You then set the authorizer for your api gateway resources to be AWS_IAM (instead of Cognito User Pools as in the all or nothing example above). And API Gateway will use the role credentials obtained from the Cognito Identity Pool to determine if the current user's role has the permissions to access the requested resource.
For example, perhaps your PAID_USER user role, will have the following IAM role attached:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow",
"Resource": [
"arn:aws:execute-api:*:*:fjfkdlsjflds/*"
]
},
{
"Action": "execute-api:Invoke",
"Effect": "Deny",
"Resource": [
"arn:aws:execute-api:*:*:fjfkdlsjflds/*/admin/*"
]
}
]
}
This gives them access to your api, apart from the api methods (resources) that you have set up under /admin/.
I highly recommend this AWS reinvent talk on Serverless Authentication and Authorization, which goes over these options with some good examples.
As I understand What you are trying to do is to resolve the authorization for your APIs. Cognito comes to picture in case of Authentication (Instead of your own database and user handling it supports everything). By using Cognito you can create a User pool and Identity pool to handle the User authentication and create the access token for the authorization for subsequent API calls. The following you need to do
1) Register with AWS Cognito and create a user management pool and Identitiy pool
2) Create a spring boot app (as you mentioned spring boot in your stack) for authentication.
3) Add spring security dependency
4) Register the appln as a resource app in COgnito
5) Do the authentication and return back the token you generated.
6) Create another application (for actual business you may have multiple microservices)
7) Register those spring boot app as resource server in cognito
8) Add the spring security dependency on the new app
9) Create a handler by extending WebSecurityConfigAdaper and override configure
10) Create a filter by extending OncePerRequestFilter
11) Authenticate the token by checking the claims
12) Restrict the API access to all calls in cofigure method of configurer
I'm quite new to AWS and I try to understand some basic concepts. In my Android app, I use:
Cognito User Pool and Identity Pool to allow my users to register and sign in, but also to use the app as guest users
API Gateway and AWS Lambda to create webservices that the app can call
My use case is very simple: I want some of the APIs I created in API Gateway to be available for my authenticated users and my guest users, and the other APIs available for my authenticated users only.
For the APIs available for my authenticated users only, I was thinking putting the users in a group of users (thanks to CognitoIdentityServiceProvider.adminAddUserToGroup()), that can have a common role with an IAM strategy attached to it, to allow them to access those APIs. I think it makes sense since I'll have different types of users, so I'll use a group for each type.
But for the APIs available for my authenticated users and my guest users, I'm note quite sure of what I'm supposed to do. Should the APIs be public, so they can be called by anyone including my guest users, or is it possible to make them only available for my authenticated users and my guest users, but without being public? What are the good practices and how can I achieve them?
Thanks for your help.
You should use "API Gateway Lambda Authorizers" for this. You configure the authorizer per method. So, only the endpoints reserved for authenticated users should have one set.
How do they work?
Every time a request hits an endpoint with an Authorizer configured, API Gateway will trigger it with the request information. The authorizer then checks if the request have the proper credentials. If it does, then an IAM policy is returned. The method execution call (another Lambda function for example) will consume this policy. Otherwise, the authorizer will return an error status code, say a 403 Access Denied.
In your case, since you are using Cognito, you can use a Cognito User Pool Authorizer. You can create it using Cognito's SDK or AWS cli. After you configure it the only thing you have to do is append the id or access token provided by Cognito after a user authenticates. It's usually served inside the Authorization header.
I hope it helps.
Here is how I did, using the console:
In API Gateway, click on the resource, then the method (GET, POST...)
Click on Method Request
For Authorization, choose AWS_IAM
In Cognito, choose Manage Identity Pools
Create (or edit) the identity pool you use with your Cognito User Pool
In the Unauthenticated identities block, check Enable access to unauthenticated identities
On the same page (at least if you edit the identity pool), you should also see the Authenticated role and the Unauthenticated role
Go to IAM, and in Roles, find those two roles
For each role, click on it, and in the Permissions tab, click on the policy attached to that role to view it (with the little arrow on the left)
Click on Edit policy, then the JSON tab, then add the following block (you can find the ARN by going to API Gateway, click on your API, click on your resource, click on your method: you'll find the ARN in the Method request block):
{
"Effect": "Allow",
"Action": [
"execute-api:Invoke"
],
"Resource": "<the_arn_of_your_resource_api>"
}
Click on Review policy, then Save changes
I have an existing Web API that I migrated to AWS using API Gateway and Lambda functions. However, I'm wondering how I can make use of AWS API Gateway's custom authorizer feature. My existing authorization framework is OAuth and I used ASP.Net Identity for user management. I generate bearer tokens and used the 'Authorized' attribute in my API Controllers for security. How can I do the same in AWS API gateway since I cannot change my framework cause I already have existing users. Thank you.
If you haven't already, check out the docs for custom authorizers: http://docs.aws.amazon.com/apigateway/latest/developerguide/use-custom-authorizer.html
You should be able to write the Lambda function to replicate the authorization logic used in your existing service. Then think about how you want to model permissions in terms of an IAM policy (which is the output of the authorizer). If you want simple allow|deny then you can return an IAM policy from the authorizer that says Allow * or Deny *. Or you can add fine grained permissions if you want.