Acessing user identity in AWS Lambda - amazon-web-services

I want to use a lambda function that is only available to members of a Cognito user group. Within that function I want access the identity of the specific user that just called the function. So far, I did not get this to work.
I have created a Cognito group and have prepared an HTML-page with JS that can register, confirm and login users. Among other things, I can extract the identity token.
I also created an API in the API Gateway that is connected to a Lambda function. Setting the Authorization to the Cognito group works great and the function can only be called with a valid identity token.
However, I don't know how and if I can access the identity in the Lambda function this way. The flag "Invoke with caller credentials" can only be set with the Authorization AWS_IAM. This, in turn, requires a different Authorization in the HTTP request, but I don't know what I need to do there. Or am I moving into a completely wrong direction?
In other threads, e.g., https://forums.aws.amazon.com/thread.jspa?threadID=231032, and the documentation I found remarks about setting up a request mapping template, but I found no location in the GUI where I could do anything like that.
I'd appreciate any help on this! If any more information can help, just ask for it and I will provide as much as I can.

I finally got the identity of the caller via Mapping Templates.
To use Mapping Templates via JSON format, you need to add a template "application/json". You can then click on the new entry and provide a specific template. In my case, I used
{
"name": "$context.authorizer.claims.name",
"username": "$context.authorizer.claims['cognito:username']"
}
This adds the attributes name and username to the event object in the lambda call, which contain the content of the attribute name and the cognito username respectively.

Request mapping templates can be found in the GUI under Method -> Integration Response -> Mapping Templates.
A template like:
{"testHeader" : "$input.params().header.get('Authorization')"}
will pass the token to the lambda under 'testheader'.
It may also be worth checking out the context variable (https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html#context-variable-reference) to see if it holds the information you require, although I don't believe it will, unless you move to a Custom Authorizer method of authentication.

Related

No able to add custom scope in AWS Pre-Token Generation trigger

I have AWS Cognito user pool with one Allowed custom scopes for my app client i.e. admin-only. I have two kinds of users 1. Users in Admin Group 2. Non-Admins users. For my one of the AWS API Gateway Routes, I need to deny the access if user from non admin group is hitting the API Url, basically its allowed only for users which are part of Admin User group.
I can achieve it through adding an Authorization scope on API gateway route with this custom scope and then adding a scope manually when I request an Authorization token in Hosted UI popup. But in my app, I don't want to add this scope manually, rather want to add this scope when the token is generated. I explored Pre-Token Generation Trigger but not able to see the way to override or add scope attribute in it?
Question is, is there a way to add/override custom scope in pre-token generation trigger ? If yes then how ? But if there is no way, then how to solve my use case ?
You can use the claimsToAddOrOverride attribute in the response to replace the scopes value returned in the token with anything you want, BUT you cannot see the existing values in the lambda it seems so would need to know the full set of values up front

How do I expose a service to different frontends?

I want to create a microservice for "orders". The service will have typical actions like "get orders" or "create an order".
I would like to expose this service in two ways:
User frontend: If you call /orders, you will see your orders
Support frontend: if you call /orders, you will see all the orders of all the users
I would like to deploy one API (orders) that can be called from 2 API gateways (user and support). But, I don't know how to do it without duplicating code.
Is this the right approach?
I'm using AWS Apigateway + Lambda + Serverless.
In some way you're being able to differentiate the user that is making the request inside your lambda function, because you need to get only its orders. Based on that I'm considering that you're receiving some kind of token in your lambda where you can extract the correct user.
Considering that scenario, one standard solution to your problem is add something to your token that differentiate if the user is from the support group or not. Normally you add a claim to the token informing that he/she is part of the support group. Then inside your lambda you check this token and give a different answer based on your requirements. But for that solution, you'll need to have means to add new claims/manage your identity provider data (user information inside your service that provides user tokens).
But with that solution you will find a small problem: if a support user must get all the orders and in another moment only its orders you won't find an easy way to implement this. If your requirements demand that you provide both use cases for support users you will need another solution.
In that case another solution would be to provide two different endpoints (API Gateway API's) touching the same backend lambda. In the normal endpoint you forward the request to the backend and the lambda gets all the orders for the user. In the support endpoint you add something else to the request (can be a query parameter or a http header).
For a more secure solution, your support endpoint must not allow requests from people outside the support group. And if you go for a query parameter alternative, you must block this exact query param in the normal endpoint. Someone can abuse the normal api sending the query param for it and get all the orders if you just forward the query params downstream.
You will do all this different configuration in the integration request of AWS API Gateway. You can find how it works here.

Scope of returned resource highly depandant on user role encoded in JWT token - is it RESTful?

I'm working on API that tries to stay in RESTful principles, although one requirement keeps bugging me.
We use JWT based authentication. Inside JWT claims we store roles of the user. Now our main GET endpoint (let's call it ListAllOffers for simplicity) behaves differently depending on what role the user have:
if API recognizes admin via JWT it responds with full list of Offers
if API recognizes ordinary user via JWT it responds with narrowed list of Offers (depending on relation in DB)
My concerns is: is it ok according to REST principles or any unwritten REST practices? I am used to modify response object according to argument from url, params from querystring or alternatively via header values. Altering JSON response basing JWT seems not explicit enough that is feels some kinda strange.
Bonus question: is it against any of REST principles how should this requirement be implemented.
You'll find lots of APIs have resources where the value changes depending on the authenticated user (see GitHub's API).
When it comes to REST the endpoint must always reference the same resource. However, you may choose to represent that resource in any way you wish. Masking some of the offers because the user is not an admin has not changed the resource only the representation you are giving to that user.
While the REST specification doesn't have any specific examples of changing representations due to authorisation it is still a worthwhile read.
If you are concerned about changing the representation implicitly there are a few options available that would make it more explicit whilst still following RESTful standards.
You could add a query parameter that explicitly requests only the current user's offers: /offers?show=mine.
If the offers are 'owner' by a user you could also do something like: /users/{username}/offers. Here a user would be authorised only for their own offers. An admin would of course be authorised for any.
Overall the key point is this: an endpoint must always represent the same resource but how it represents it is up to you.

Serverless AWS - is it worth using custom authorizers (as a lambda)?

Hey I am getting started with the serverless framework, apigateway, lambdas and authorizers.
Here my questions:
In order to verify a proper JWT token (which seems nowadays the best solution for serverless authentication), I would need a client id. Since the authorizer lambda is not capable(?) of reaching any other parameters of the request into the lambda except for the token itself, this is a difficult task to do. How can I achieve this?
Since with every authenticated call, an additional lambda is called, my costs are doubled?! May be I am misunderstanding something here, but it seems to me like implementing a method for verifying my token without using the authorizer is cheaper and I don't need to implement the authorizer lambda itself.
The whole point of OAuth2 is that at the point of authorization you don't care who the bearer presenting you with a token is or which client they are using (web app, Postman, etc.), only that the token is valid for whatever the token bearer is trying to do. You trust that the authentication has happened because the token issuer has done the authentication.
When you talk about the aud(audience) (from comments) in JWTs, which may or may not be included dependent on the JWT issuer, this is intended to reflect the service or services that the JWT is valid for.
This could for example have a value of 'myAPIName' or 'myAPIName/test' or even ['myAPIName/test1', 'myAPIName/test2'].
If you need to validate an individual aud claim you have two choices, you can either have different Lambdas authorizing different api routes and methods with hardcoded aud variables or you can get the name of the api being called and map it back to something that would match an aud claim.
For example the method arn for the incoming request can be found in event.methodArn. This looks like arn:aws:execute-api:{regionId}:{accountId}:{apiId}/{stage}/{httpVerb}/ potentially with [{resource}/[{child-resources}]] dependent on your implementation. With a bit of string manipulation, you could map this back to the format of an audience claim (whatever that looks like for you).
If you would rather work an api name instead of an api name you can use the apigateway.getrestapi method for whatever sdk you are using. Documentation on this method for the JavaScript sdk can be found here.
The JWT may have a sub(subject claim), again dependent on implementation of the JWT issuer, this could relate to any of your users (or at least the users the JWT issuer knows about). Validating this beyond checking the signature of the JWT is pointless. The best you could do is check if that user exists and only if you have access to the same user database that the JWT issuer does. Although this can be used to ensure that UserA only has access to UserA's data. Again, you are trusting that the fact a bearer has a token is proof that they have authenticated (proved who they are).
I hope that answers part one of your question.
In regards to part 2, the advantage of using a Lambda Authorizer over doing the authorization in the target Lambda is caching.
So envisage I have a token valid for one hour and I call your API once every second for 30 minutes (1,800 calls). You have a Lambda authorizer with a response cache time of 10 minutes. That means you check the JWT 3 times for 1800 api calls.
But if you do the validation of that token in your target Lambda, you are doing that processing 1,800 times.
There is authentication with AWS Cognito which will allow you to use authentication outside your application and still being able to let your application divide the users into subgroups based on custom roles or similar.
If for some reason you cannot use AWS Cognito (Why not?) there is both the possibility of custom authentication (the same for each function with SLS) or even authenticating with custom code inside each lambda function.
I would recommend to use Cognito, and only custom if you know you need it.

Combine client_credentials and Custom authentication flow in Cognito?

Wondering if it's possible to combine using the client credentials OAuth flow in Cognito with a custom authentication flow as described here: https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-challenge.html
I know about custom authentication flows in Cognito, but I am not so well versed in OAuth itself. But as far as I understand it, the client credentials flow, is unrelated to a user? Because in that case, I would think it is impossible to use a custom authentication flow since the SDK documentation states the following (taken from the AWS node.js SDK):
The authentication parameters. These are inputs corresponding to the AuthFlow that you are invoking. The required values depend on the value of AuthFlow:
[...] For CUSTOM_AUTH: USERNAME (required)
If the value passed as USERNAME in the CUSTOM_AUTH flow is not a known user in Cognito, the lambda trigger for the custom flow will never be executed. And since it is never executed, it is not possible to use the USERNAME parameter to pass any other data to the lambda trigger (you can of course use custom authentication parameters, but you also need a valid USERNAME).