Amazon Cognito: How to pass credentials to server-side application - amazon-iam

I have a client side application (developed in Java, not Android) that authenticate a user with an Amazon Cognito User Pool. To make things clear: that application display a username/password entry dialog, then authenticate with the Cognito user pool service using the SRP method; potential challenges are handled in that dialog (device id, password must be changed, two factors, etc). In the end, I have a series of tokens that allow the program to use AWS services with the user's credentials.
Now, I need the client application to communicate with a custom server side application. The client will have to prove its identity to the server application, which will then communicate with more AWS services. Here, I have two distinct use case:
1) The server only needs to know who the client-authenticated user is (in a safe manners, but without impersonating the user).
2) The client needs to delegate some or all of privileges of the user to the server; the server will then perform some actions on AWS services under that users behalf.
The server side application will most likely be developed in Java, running on an EC2 machine. I'm only interested in user authentification through the Cognito user pool source (that is, I have no interest in Facebook/Google/OpenID-based authentication flows).
It seems rather easy to achieve both objectives in very unsafe manners: just have the client application send to the server all the tokens it has been granted. But this is obviously not the way to go.
Yet, I'm having a hard time figuring out from Cognito User Pool/Cognito Identity Pool/IAM/STS documentation how this can be correctly achieved. I would expect for example the possibility for the client application to be able to generate some kind of "delegation token", which can be passed to the server; the server should then be able to validate that token and extract identity information from it (satisfying #1), or to impersonate the identity corresponding to the token in order to perform calls to AWS services (satisfying #2). Or maybe I'm thinking this incorrectly?

What you are pointing at is OAuth 2.0 code grant flow which is not currently supported by Amazon Cognito.
The current available safe way to do this is to pass the id token from the client to the server and then validate that token and extract identity information from it. This proves the identity of the caller as you can validate the signature of the id token.

Related

Token exchange grant support in AWS Cognito

I am working on a cloud native microservices based application deployed in AWS. This application should use a OIDC based IdP (preferably AWS Cognito). The authentication and authorization flow are as follows. Once the user logs in using authorization code flow, the IdP generates one ID token and access token. The access token is sent to the backend services for the backend calls. The backend service can fetch the user information through /userinfo endpoint if needed. Additionally, the access token has the required group claim and the scopes to determine if the right access is present.
Now if the backend service needs to call downstream backend internal microservices on behalf of the logged in user, then should I create a
new token with client credentials grant with a reduced scope needed for the service call? or
Should I send the initial access token of the user only?
In the first case the user context is lost in the new access token and if the downstream service requires the user context, then how will the downstream service get the user information?
In the second case the downstream service is not called with the exact specific scope needed for that service and is not security best practice.
However, there is also one more grant call exchange grant (https://www.rfc-editor.org/rfc/rfc8693.html) which supports creating a new access token with the user context from the initial token without relogging in the user (delegation mode in https://www.rfc-editor.org/rfc/rfc8693.html). Is it supported in AWS Cognito? If not, then how can I achieve the same functionality if I use AWS Cognito?
It kind of depends on the level of trust between microservices. By default, if they are part of the same unit, aim for this type of setup:
Orders microservice, requires an orders scope
Shipping microservice, requires a shipping scope
Customer website, uses scopes orders and shipping
You can then flow the user identity securely, if Orders calls Shipping, by forwarding the access token. Meanwhile each API verifies the JWT on every request and checks for the scopes it needs.
Each API should also check for an audience claim has the expected value, representing a set of related APIs, such as api.example.com. I believe currently though, AWS Cognito does not issue an audience claim to access tokens.
If Shipping is a less trusted subdivision of your company, token exchange would be more appropriate. Eg if you have concerns about a Shipping service abusing Orders privileges.
Avoid over-reliance on scopes, or too many of then. Use claims for the main authorization. In Cognito you can look up these values after verifying the JWT but before your API creates its claims principal
Cognito only has quite limited support for more advanced token behaviour, such as issuing custom claims, reference tokens, or token sharing techniques. If you need them, the preferred option is to choose an authorization server that supports the standards.

What is the best way to authorize an AWS API call for a logged in user?

I have a spring boot based web application which already authenticates the user. I would like to create some AWS APIs using AWS api gateway and a springboot rest app deployed on EC2. The user will log into the existing application and call the new AWS APIs from the browser ajax calls (like an SPA I guess but I have a server and can use client credentials/secrets if needed). What is the best way to handle security for the APIs. My first thought was to generate a JWT using a shared server side secret for hash and verify it with an AWS lambda. But this seems a bit non standard. Oauth2 might be better but might also be over kill. Not sure the performance would be good. Few requirements
token passed to the API should be a user specific token and have an expiration (and hence a refresh token)
The user is already logged into my web app and calling the api should not issue a login challenge.
AWS API Gateway should be able to verify the token before passing it to the application
Any token passed to the API should probably be generated on the logged in web application. Server knows the user is authenticated and should generate the user access token on behalf of the user. The AWS api should be able to figure out what privileges the user has based on the user principle or scopes in the token
I've thought about using Cognito AWS but I dont want to require the users to preexist in a user pool. Cognito seems geared more for the authentication rather than authorization and I would not be using the cognito login functionality. I dont know if its possible to use the oauth2/token endpoint alone with cognito.

How to authentic/authorize a backend API for non browser clients in AWS Cognito service?

I am working on a NodeJS application which exposes some APIs. This APIs can be accessed by different vendors (Without any browser). Now I want to know what is the best way to Authentic/Authorize these vendors using AWS Cognito service?
Should I give a client ID (Username) and API_KEY (Password) to my vendors and Validate it through cognito for every subsequent requests. (As of now it is already implemented from the Database)
[OR]
Should I generate a access and Id token for the vendor with MAX validity and give it to the vendor. (I understand this isn't the best practice)
So what should be the best way to achieve this?
Define app clients in your user pool, one for each of the "foreign" servers. When you create them Cognito will generate an app client id and a secret (if you check that box...and you should check it). In "App client settings" (AWS Console for Cognito) you tell it the client will use "Client credentials." Finally, define whatever custom scopes you think you can leverage to provide finer control of access. These can be different for each client.
Now the foreign server can authenticate/get-authorization by passing in the app client id and the secret, etc., etc. Usual Cognito stuff.

EC2 and RDS Authentication?

I have an EC2 instance running a Node.js Express backend that controls CRUD operations to an RDS instance. I am doing a mobile application client (which I am authenticating users through cognito with the client sdk). What would be the best way to authenticate my mobile app users so that only authenticated users can access my Node.js Express functions running in EC2? Basically looking for something like IAM Lambda authentication (but for this server application, rather than a serverless architecture).
What would be the best way to authenticate my mobile app users so that only authenticated users can access my Node.js Express functions running in EC2
Authenticating with Cognito you shoud be able to get an access token (id token and access token) once the user is authenticated.
The token is to be fetched by the mobile client after the user is authenticated, see https://docs.aws.amazon.com/cognitoidentity/latest/APIReference/API_GetOpenIdToken.html . This token can be sent along every request to the nodejs services. Please note the token has an expiration time and the client needs to get another one when the token is expired.
The token contains user identity, issuer, expiration time and (Cognito) user groups
The services must validate the token (issuer, expiration, signature) and trust the token (or not) based on the validation.
Validation example: https://github.com/kjur/jsrsasign/wiki/Tutorial-for-JWT-verification) in this example the public key is read from a certificate. Amazon provides only the public key properties (e,n) https://aws.amazon.com/premiumsupport/knowledge-center/decode-verify-cognito-json-token/, so you will have to complete the public key yourself, example https://github.com/rzcoder/node-rsa
Edit: more detailed clarifications

Confused on use/ need of cognito

So since parse is shutting down we are moving our website / mobile app that we've been developing to AWS. We are primarily going to use the following services:
SNS, SES, Dynamo, S3, Lambda.
Now I am still a bit confused on:
what cognito is used for? Do we really need cognito to authenticate users and use DynamoDB, S3, SNS ? Or can we just use specific APIs for each of these services and connect directly (using Js SDK)?
If we do have to use cognito how do we save local data i.e logged in user/ identity? is that what cognito sync is for or do we have to use cookies ?
In summary why do I need cognito when I can directly connect to DynamoDB using the JavaScript SDK?!
Thank you in Advance.
Amazon Cognito can be decomposed in two sub-services: Amazon Cognito Identity and Amazon Cognito Sync.
Think of the former as an authentication service and a credentials provider. The latter is just a service to store user data and keep it synchronized between multiple devices.
What is the purpose of Amazon Cognito Identity?
Suppose that you have a table in DynamoDB. Let's say that you have a web application that will store an item on that table.
You can create an user in IAM, embed the credential information on the web application, and then put the item on the table using the AWS SDK.
There are three things going on here:
The credentials are embedded in the application
The credentials do not expire.
Every user in your application has the same access rights on your table
This may be fine for some applications, but Amazon Cognito Identity offers a solution to these common problems.
Let me explain Cognito Identity's workflow:
An user registers an account on your application, sending all the information (username, password, other data...) to your server.
The server stores the user in some back-end database (it could be a DynamoDB table) and creates a new identity on the Cognito service. This identity is then mapped to this user.
The user can now login into your application. The user logins and sends username and password to your server. (This process could be done automatically after account registration)
The server checks the username and password against your back-end database. If everything is right, then the server makes a request to Amazon Cognito for a temporary access token.
The web application receives the token and makes a request to Amazon Cognito (using that access token) to get the user credentials. These credentials are basically a temporary IAM user that was created specifically for this user. It will have an expiration (usually an hour).
The web application uses these credentials to make operations on AWS, such as putting an item on a DynamoDB table, or calling a Lambda.
When the credentials expire, the user must re-login into the application. This might be done automatically or not, depending on your application's requirements.
On the Amazon Cognito dashboard, you can configure roles and policies for your "identities" (an user in Cognito). This way you can specify which services it can access. It even allows you to create access roles for your users (Admin users may be able to access some services that normal users should not).
I should also note that Amazon Cognito can be easily adapted to support Facebook / Google+ / Amazon accounts, which will be mapped to the same identity, so the user can login via multiple sources.
What is the purpose of Amazon Cognito Sync?
Consider it like a DynamoDB table where you store information for a specific user. These information is shared between multiple devices and is always synchronized. This means that when a web application updates an user value, then the mobile application will automatically reflect this change.
There is a limit on how much user data you can store (I don't remember now), so it's not something you would use to persist information (such as an user password), but rather a mean to share information.