Optional authorization for API Gateway - security

I'm building an API served by a lambda through API Gateway. I already integrated with a Cognito user pool authorizer for some admin endpoints, which blocks non authorized requests, but now I would like to add some endpoints that can be called by both authenticated and unauthenticated users (and return different data depending on authorization). For instance you can imagine GET /users would only return basic profile info for the users if the request is not authenticated, and more details if it is.
What is the best way to set this up with API Gateway?

Option #1 (Prefer)
Please have 2 different endpoints
API X with GET https://api.xyz.com/users (Lambda User as back-end and Cognito as Authorizer)
API Y with GET https://api.xyz.com/public/users (Lambda User as back-end and without Cognito)
Lambda User needs to verify to see what response should be returned based on Header 'Authorization'. If 'authorized', return more detail, otherwise, return least detail.
Option #2
Please have 1 endpoint
API X with GET https://api.xyz.com/users (Lambda User as back-end without Cognito)
Lambda User needs to verify to see what response should be returned based on Header 'Authorization'. If 'authorized', return more detail, otherwise, return least detail.
The reason I prefer the first one because we can have a set of public APIs separately across our system for the long run. It also clear for all consumers. Easy for development and maintenance.

When using Cognito you have following possible options
1) Cognito authorization
2) Lambda authorization :
The advantage of using Lambda Function is that it can perform authorization processing other than verification of IdToken. For example, you can write processing according to your application, such as IP restrictions and allowing only specific user agents.
3) Inside your code write filter to intercept each request and response and manage your own role based authorization and return response as needed .

Related

Cognito authorization with two user pool

I have an API Gateway and two user pools, I want to implement Cognito on my endpoints supporting both user pools.
The idea I had was to create a lambda authorize which takes as input: the access_token and does a get on /oauth2/userInfo
of the two user pools, knowing that the request will only be satisfactory for only one user pool if the token is valid. And my lambda returned
For Get Ok -> "Effect": "Allow",
For Get Ko -> "Effect": "Deny"
like explain here:link
Questions
Is there another way to support authorization on two pools?
Is it normal to manage authorizations with access_token?
In case my lambda took as input the id_token instead of the access_token how can I verify that the id_token has been generated by one of my user pool
Thank you !
STANDARD BEHAVIOUR
There should be a single Authorization Server (AS) that your apps talk to. Partitioning users can be done for advanced purposes, such as multi-tenancy or storing Personally Identifiable Information (PII) in different databases per region.
Q1
A Cognito user pool is an entire Authorization Server - I would aim to use only one if possible, since this will keep the code in your apps simple.
If you have to proceed with a single app using multiple user pools, aim to get the API client to send you a hint that enables you to know which one is being used - perhaps via a value in a custom header during API requests:
x-mycompany-user-region: EU
x-mycompany-partner-id: ABC
This will enable you to implement a basic kind of content based routing. This type of header is only for routing and should be ot be used for security.
It is also possible to put a reverse proxy in front of Cognito to do this type of job - see this Curity article for details on this type of approach, where claims are used to route requests.
Q2
Yes - use access tokens containing or referencing scopes and claims. Note that Cognito may not allow you to customize access token JWT contents.
A way to get custom claims can be to send the access token to the user info endpoint, then cache results in the authorizer, as you seem to be doing. If it helps, see also this code of mine.
Q3
An ID token is a digitally verifiable assertion given to a UI as proof of authentication. It should not be used for authorization in APIs. It may be that Cognito has better support for customizing ID tokens - but it is not correct in OAuth terms (eg expiry is too long / no support for scopes).

Calling service B from service A for authentication and authorization

I have service Ticketing and a service Auth.
Not everyone is allowed to perform CRUD on tickets.
When a request comes to perform an action the ticket, I need to check if the user has proper right.
For that I need to communicate to service Auth which holds roles and endpoints of allowed permissions on the resource for a user.
How can I make this call?
Should I make sync call? i.e. Direct call from service A to B?
Use event bus
Or any other possible ways?
Also, there are some tables that is needed from Auth service to join with Ticketing service to fetch data.
How this issue can be solved?
Given the microservices tag, I assume Ticketing and Auth are separate microservices.
I am also assuming, based on description, that each has it's own API.
What's not quite clear, is how the user accessed the Ticketing service. Were they authenticated first (e.g. if a human user via a web application, did they login first, prior to being directed to Ticketing in some manner).
I assume a login was performed first.
An approach might be
At the point of user login (or caller if non-human) obtain the list of authorizations the user has access to via the Auth service.
Store the authorizations within the HTTP Authorization header
As each microservice is called, it then only need (synchronously) ask the Auth service to validate the list of authorizations is correct.
An appropriate technology approach is JWT.
In the scenario described above, when Auth is first called to authenticate the user/caller, the authorizations could be returned within the sub claim, or you could create your own claim to determine authorizations used.
As the user/caller moves through different microservices, a syncrhonous call to the Auth service need only be made to validate the user/caller is authorized.
Synchronous or asynchronous: regardless of direct call/event bus, if an authorization check is required, it needs to complete before the calling service takes action. Otherwise it may expose data / modify it, when the caller did not have authorization.
Joining tables: in a microservice pattern, this is not likely to be wise. Rather, obtain the data required from the Auth service via an API call and use that with the calling service (e.g. Ticketing).

Link a third party application account in Cognito

I need to link a third party application credentials to the one the user uses to connect to my application through Cognito. In other words: user connects to application A (my application), then application A asks user for credentials to connect to application B. Application A then interact with application B with the credentials provided by the user. I would like to link those identities but let the user connect to application A only with credentials I provided him (and not from the ones he uses for B).
AWS has AdminLinkProviderForUser in Cognito which doesn't satisfy the last requirement.
Application B offers multiple authentications: Form, Basic HTTP, SSO, OAuth2 and OpenID.
Is there any way to link the two identities in the way described above and store it in Cognito for future use?
If I am understanding you correctly - what you need is for the user to log into your application with credentials they have for another application and then store the login they have for that application and provide them with new login details where those login details link them to your application and the that application.
Have a look here: https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-identity-federation.html
However taking that approach the users will still login with application B details. But Cognito provides that out of the box.
If for some reason you do not want to take that approach - and you want to have a force change on the password you must do the link yourself.
A suggested approach would be as follows (Making use of Lambda Triggers in Cognito User Pools):
Pre-signup lambda trigger:
Get the user credentials
Use the credentials to make the call in that lambda function to
application B.
Get the authentication response from that call
Pass this as a param to your Cognito user attributes
Make a call using AdminCreateUser(https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminCreateUser.html)
Now you have the user stored with the auth data from application B as a custom attribute.
This is not the best way to do it though because auth data usually needs to get refreshed which will add extra complexity. You could do that refresh in a Pre-Authentication Trigger that updates that custom attribute for the user.
Honestly, if application B is custom, I think the best approach for you will be to use OIDC Provider approach https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-oidc-idp.html

Multiple oauth clients for the same api

I'm trying to accomplish the following scenario.
I have and API at the moment and one web app. I have also create a new oauth client on my auth server (keycloak), which follows the implicit grant. I also used jwks on my nodejs api to do the token verification.
Now I want to create an SDK that will target the same API.
The questions is how do I get the SDK to retrieve an access token from the auth server. The first thought is that I will have to create a new client oauth client on the authserver and then use the client credential flow to get the access token. However, I dont know what should the behaviour of my API be like. At the moment it used jwks against a single audience. How should it be configured to verify access tokens from multiple clients (potentially thousand of them)
If you want multiple clients to call your API they should all use the same audience, and your first level of security will work.
The audience in access tokens represents the API(s) the token can be used against.
You will then need to use something else to authorize API requests, depending on the type of client and what they are allowed to do.
Configure each type of client in your auth server so that you are in control and know who is calling the API.
You may have 1000 clients but only 4 levels of privilege - in which case only configuring 4 OAuth clients may make sense.
One OAuth option you can use is give different clients different scopes. Scopes can represent high level privileges.
If a particular client calls an addOrder operation but does not have an Orders scope you could return a 403 response.
Often though API authorization needs to go beyond OAuth checks and apply custom rules based on the end user privileges.
If you can provide more info on your scenario I could provide a more complete answer.

AWS - Cognito Identity with nodejs - What to do with tokens

So I'm trying to use Cognito Identity in my nodejs API. My goal in using Cognity Identity is to be able to give users a secure way to create a user account and log in. I'd like to use my API to make aws cognito calls to verify users by identifying them with their email address. My API will then give users access based on who they are, which is based on their email.
I was able to create a user, verify the user's email, and log in to get an AccessToken, IDToken, and RefreshToken. That's great, but at this point I'm not entirely sure what to do with these tokens. I'd imagine I can somehow use them to verify a user every time they make a call to my API, but I'm uncertain how to do that.
I'm imagining authentication flow going something like this:
User logs in with their password -> My API makes a call to aws to get tokens -> My API passes those tokens back to the user's mobile device -> Their mobile device stores these tokens -> AccessToken is used to verify all API calls until it expires -> RefreshToken is used to get a new set of tokens if AccessToken expires -> If RefreshToken is expired user must log in with username/password again.
Is that incorrect or an improper way of doing this? If I have the right idea, then how do I use the tokens to accomplish this? I wasn't able to find any documentation on the authentication process once a user gets their tokens. The only thing I can find that seems it might be able to accomplish this is here: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityServiceProvider.html#initiateAuth-property
Your next step depends on what service you use and how you use it.
For example, for an API-driven application with Lambda / API Gateway, you'd use Amazon Cognito User Pools for your API resource methods and send the ID token as an Authorization header with your API call. (Yes, the name is misleading. It should be Authentication since the authorization logic is actually implemented in your Lambda function)
Then, your Lambda function can access the identity claim properties from the user pool using the context object (when you enable Lambda proxy integration) as:
const email = context.authorizer.claims.email;
or
const cognitoGroups = context.authorizer.claims['cognito:groups'];
If you haven't enabled Lambda proxy integration, you should make sure to pass those values in your body-mapping template.
You can find code examples here and here.
There is a good reference github application that demonstrates various aspects of cognito and how integrates with lambda, API Gateway, DynamoDB and other AWS services.
Here's a link:
https://github.com/awslabs/aws-serverless-auth-reference-app

Resources