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

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

Related

How to restricted to access AWS API Gateway endpoint by Cognito user id

I am trying to implement an e-commerce application backend by using AWS Cognito, Api-Gateway, and Lambda.
To create a new Item, users must be logged in. Once created the item, the Logged used will be the creator of the item. I already created an Item-create endpoint and added the Cognito-JWT authorizer for the Apigateway endpoint.
Now I need to implement Item-update endpoint. The relevant Item update must be allowed only for the creator of the Item. API maybe like this.
/items/{item-id} PUT
body : { title, price... }
header { Authorization: Cognito-JWT-Token }
What is the best way to implement this kind of feature using the AWS ecosystem. Or any best practices to implement this kind of feature.
You would create a property owner for each item that is a user ID. Normally you'd use the sub claim from the token which is a unique ID. However, for various reasons, I wouldn't recommend using the subject value with cognito. You can use email just ensure safeguards are in place if someone deletes their account and a new one is created with the same email.
Whatever you choose, in your handler compare the owner with the validated token claim. Allow if they match, disallow if they don't.

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.

Changes to the scope of an API endpoint are not taken into account unless restarting WSO2. Product bug? If not, any way to configure this behaviour?

Starting with an API defined in WSO2 with no scopes associated to its endpoints...
... I get an access token and invoke them correctly.
Now I modify and publish my API, assigning a scope to one on the endpoints so that it requires the editor scope:
Now I invoke that editor scoped endpoint with the previous access_token and it works. This shouldn't happen since the token was given with the default scope, not the editor one.
Now I restart WSO2 and try again with the same token, getting the expected result of access denied:
(900910) - The access token does not allow you to access the requested resource</ams:description></ams:fault>%
I have needed to restart the platform so that scope changes are considered!! Is this a bug, a expected behaviour (it shouldn't...) of is there any way to force the refreshment of the endpoint requirements (appart from just publishing the changed API).?
If you generate a new access token after either modifying or deleting
a scope of an API resource that you had previously invoked, you will
not be able to access that particular resource of the API for a period
of 15 minutes, which is the default Gateway cache period, because the
WSO2 API Manager Gateway is designed to cache the details of the
resource on its side.
Ref: https://docs.wso2.com/display/AM250/Scope+Management+with+OAuth+Scopes
This is not a practical problem in a typical production environment, as API updates in a production environment are very rare.
However, if this is really a problem for you, you can override the default scope validator[1] by extending it to not to use this cache. This is configured in identity.xml.
[1] https://github.com/wso2-extensions/identity-inbound-auth-oauth/blob/master/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/JDBCScopeValidator.java#L92

How to check for Cognito permissions in API Gateway

Trying to understand how to use Cognito and API Gateway to secure an API.
Here is what I understand so far from AWS documentation and the Cognito user interface:
Clients
www-public - public facing website
www-admin - administrators website
Resource Servers
Prices - for this simple example the API will provide secured access to this resource.
Scopes
prices.read
prices.write
Again, very simple permissions on the API. Public www users can read prices, administrators can write them.
API Gateway
GET /prices - accessible to authenticated users that can read prices.
POST /prices - only accessible to administrators
Users
Administrators - can update prices via the POST method.
Non-administrators - cannot update prices.
Based on this...
Each client will request the scopes it is interested in. So for the public www site it will request prices.read and for the administration site both prices.read and prices.write.
The API Gateway will use two Cognito Authorisers, one for each HTTP Verb. So the GET method must check the user can read prices and the POST method that they can write prices.
The bit I don't see is how to put all of this together. I can make the clients request scopes but how do they now connect to user permissions?
When the token is generated, where is the functionality that says "Ok, you requested these scopes, now I'm going to check if this user has this permission and give you the right token?"
I understand that scopes ultimately related to the claims that will be returned in the token.For example, requesting the profile scope means that the token will contain certain claims e.g. email, surname etc.
I think based on this that my permissions will ultimately end up being claims that are returned when specific scopes are asked for. The fact that the two clients differ in what they request means that the prices write claim an never be returned to the public www client. It would never issue a token if the prices.write claim was requested.
What I can't see is where this fits in Cognito. There is the option to put users into groups but that is pretty much it. Likewise, there is nothing (that I could see) to relate scopes to claims.
I'm coming from a .Net and Identity Server background. Certainly in the last version of Identity Server I looked at there was a handler method where you would work out which claims to put into a token. I guess this would map into one of the custom handler lambda functions in Cognito. From there this would need to query Cognito and work out what claims to issue?
The final piece of the puzzle is how the API Gateway checks the claims. Can this be done in API Gateway or does the token need to be inspected in the Lambda function I will write to handle the API Gateway request?
Certainly using Identity Server and .Net there was a client library you would use in the API to inspect the claims and redact permissions accordingly. Guessing there is something similar in a Node JS Lambda function?
A few assumptions there as I'm basically in the dark. I think the basics are there but not sure how to connect everything together.
Hoping someone has figured this out.

Acessing user identity in AWS Lambda

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.