Public API how to discriminate authenticated user vs anonymous user - amazon-web-services

Suppose I have an AWS Lambda and a related API Gateway endpoint API (e.g. /user/{userid}/activities).
This API can be invoked from both authenticated and unauthenticated users, and based on this, acts differently.
Authenticated users are users that owns a valid JWT. Unauthenticated users, haven't a JWT or their JWT is expired or it's invalid.
This JWT is validated by a Custom Authorizer who make validation based on token's signature public key and other parameters (expire date, issuer and so on).
The lambda related to /user/{userid}/activities needs to know if the custom authorizer had validated (or not) the related call and so can choose what behaviour should be used.
How can I do that?

Approach depends on the way you distinguish anonymous and authenticated users.
Given that you want to identify anonymous users when no authorization header is specified, AWS APIGateway authorizers will always deny access since the header is missing.
You could consider following options:
Always use a token (for both anonymous/authenticated users).
Move authorization logic to a lambda wrapper (so you won't be using custom authorizer for these endpoints).
Proxy through a router so you get an opportunity to add authorization header if missing (with this no client changes, server add a predefined token for anonymous users).
Either way you will need to change the endpoint signature to something like: /user/activities and infer userId from the context, context can be updated with a proper userId post verification by the authorizer.

Related

AWS API Gateway Authorizer - Allow Cognito UserPool auth (via headers) and public access

I have a single REST API endpoint which handles multiple operations based on the body of the request. Some of these operations are 'admin-only', so I added a JWT authorizer linked to my Cognito User Pool to allow me to check if the user is an admin. However, now EVERY request must have a JWT supplied via the Authorization header - unauthenticated requests do not work and are met with a {"message":"Unauthorized"} error.
Is there any way to stop the authorizer from blocking all unauthenticated traffic?
I resolved this issue by removing the authorizer and just implementing the JWT decoding/verification in the function itself using a modified version of this.
I learned that the purpose of an authorizer is to protect the function from unauthenticated access, not just to decode and get the claims from the JWT. In my use case, the authorizer was completely unnecessary.

AWS authenticate multiple apigateway

I have a AWS Apigateway which has two api OrderApi and ItemApi. These api uses Lambda authorizer for athorization. React App gets the AWS jwt token on Login. This token is sent as a header to the ApiGateway which will be validated by the authorizer. I have another Apigateway with one api userApi which provides the information about the user. OrderApi and ItemApi call this api to get the user data. UserApi may or may not be directly called by any client. Now my question is
what type of authentication and authorization is required for UserApi. Is it ok to pass the same Jwt token to the UserApi.
Since UserApi is mostly called by other Api, is authentication required for it.
Is this the right approach for common UserApi. I have other Api which will call to get User data.
It completely depends on your system. If user service is not accessible from outside and you have already authorize the user on 1st gateway then then there is no such need. So, you can avoid the same.

AWS Cognito + API Gateway, Authorize based on username

I have an API Gateway HTTP API. For some of the routes I've attached a Cognito User Pool authorizer which works fine. However now I want to add a restriction to the authorization so that only the user with the correct username can access a certain API.
For example, if the route is PUT /users/{username}, I only want the user with the corresponding username to be able to edit his/her profile information. For other users, it should be unauthorized.
This seems like a common use case, how do I do this? I've looked into scopes but that doesn't seem to be the solution. Would I have to write a custom Lambda authorizer for this, instead of using the Cognito authorizer? If so could you provide an example, since I have no experience writing the Lambda authorizer.
You can use a Mapping Template to structure a request in API Gateway, and use the information stored in a Cognito token from there https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html#context-variable-reference.
For your specific request to /users/{username}, you could use an if to only provide the desired information if the user matches How to use IF condition in AWS Api gateway mapping templates. can i use if condition without using foreach?.
Alternatively, you may be able to simplify the path to PUT to /users, then pull the username from the cognito token and use it in the mapping template How can I access the Cognito username of the caller in a Lamda function?.

Use custom authorization logic with AWS Cognito authentication

We have a Cognito User Pool which contains the users we would like to allow access to, to our API. Our API uses Lambda functions to service the endpoints. The Cognito pool is configured with a custom field roles which is essentially a comma-separated list of roles that user possesses.
Now, first, we want the Cognito authentication to take place (to determine whether the user does belong to our pool and the credentials are valid). Then, we somehow want to run our custom logic to run which will look at the roles field which it will receive through the claims, and then allow or deny the request based on internal business logic.
We tried using a custom authorizer to implement this logic and set it as the authorizer for our endpoints. Then, we enabled Cognito authorization for this authorizer function. The problem is that since Cognito protects API endpoints and not lambda functions per se, the Cognito authorization simply does not run when an API endpoint is hit and the custom authorizer is called.
How do we achieve our objective of using custom logic with Cognito authorization? Any help in this regard would be highly appreciated.
All of the claims in the users' token are available in the context that can be passed to your lambda function if you are using cognito authorizers under $context.authorizer.claims.property Would mapping that claim into your lambda function and checking that the roles is present at the beginning of your lambda work for you?

Is it safe to authenticate a Cognito User through API Gateway to Lambda using a custom property?

I'm currently using a Cognito User Pool as an authorizer for an API Gateway endpoint, through to a Lambda function.
Can I pass the Integrated Request on to Lambda and SECURELY allow or deny from inside Lambda based on a custom attribute?
Mapping:
"administrator" : "$context.authorizer.claims['custom:administrator']",
Lambda handler:
boolean isAdmin = Boolean.parseBoolean(request.getContext().get("administrator"));
if(isAdmin) etc...
To be clear, a user that is NOT an administrator should not have access to the same API endpoints that and Administrator does..
Do I need to do anything else before/after this point?
I am sending the initial request to the API Gateway with Javascript after the user has logged into Cognito, by including the Authorization: JWToken header.
Do I need to verify the signature of the token in the Lambda function? I presume that API Gateway has already done that.
Is there a better way to manage this in terms of security?
Ideally I would like to be able to limit access to the API Endpoint based on GROUPS within the User Pool, however I don't think this is possible.
The Groups documentation talks about limiting access/permissions via AWS Identity and Access Management. If I go down this path, how do I make a request to the API Gateway? Do I still use the JWToken Authorization header, and use Cognito as the Authorizer in API Gateway?
The custom attribute/claim support included with Cognito user pools is secure and can be used for use cases such as this when used correctly. There are a couple of caveats.
First, ensure that users aren't able to modify the custom attribute themselves. When adding the customer attribute, do not mark the attribute as mutable. Also, custom attributes can be can be marked as readable or writable for each application. For this use case, you'll want to set the attribute as readable for the application the users have access to. Details about custom attributes can be found here.
The other caveat is to ensure that your request body can never by-pass your mapping template which could allow an attacker a way to directly set the administrator attribute being passed to your Lambda function. To do this, edit your integration request and set "Request body passthrough" to "Never".
There are other alternatives you could use for this use case. The cleanest approach is to provide a completely separate API for your administrators. Then you can use a separate Cognito user pool for your administrators, or you could use IAM users or groups.