I try to set up a test API with AWS API Gateway, Lambda and Cognito so secure the access. Since I am new to the AWS world, I am not sure how can I create a "logged in" post request to the AWS service with for example the request library
I guess on the client side I first have to log in via Cognito and the AWS Api and then use the informations I get to create a signed request like it is described here:http://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html ?
If I am on the wrong path, please tell me and point me in the right direction :)
Preface:
I will explain the flow of Google+ integration with Cognito, it goes almost the same for others. I will use node.js (javascript), after that you can have your users authenticated from Google and authorized by IAM and Cognito to access API Gateway.
Cognito is a federated login service, it offers you to sync "configuration" of your mobile/web app. First you have to setup cognito with an identity provider, say Google+ for example. for that:
Create a Google app in your Developer console
Create a pool in cognito and add google as a provider, configure your pool with the policies and the roles (services you want to give your users access to, in this case only API Gateway).
In your web/mobile app, show the user Google+ Signin button, after the user clicks on it, google will call back a callback url with code parameter code, use that token
Use this code to get a Cognito identity for your user, in this case We trust Google:
var params = {
IdentityPoolId: setting.POOL_ID,
Logins: {
'accounts.google.com': google_token // Coming from Google OAuth2
}
}
// Get Id from Cognito
cognitoIdentity.getId(params, resolverFunction);
Get IAM temporary credentials forthat Identity IdentityId, your Google authenticated user:
var params = {
IdentityId: IdentityId,
Logins: {
'accounts.google.com': google_token // Coming from Google OAuth2
}
}
cognitoIdentity.getCredentialsForIdentity(params, resolverFunction)
Your user is now authenticated with Google and have the authorization from the IAM service (through the roles/policies you attached to your Cognito pool).
In your API Gateway, activate the IAM authorization, and use the Credentials you got from point 7.
Use the Accesskey, secretKey and the token to sign every request you make for your API built on top of API Gateway, you can you use this lib: aws-v4-sign-small
Quick notes and headsup:
All of this is Asynchronous actions, so If you are in node js, it is way better to use Promises (ES6 or Bluebird).
Pay super attention to the roles you attached (acces to dynamodb document or S3 file, etc. read more about IAM it is suer helpful and you can do a fine grained authorizations)
Hope it is clear or at least it gives you a direction to start with.
One of the benefits of using API Gateway is that you can automatically generate SDKs for your API, which easily integrate with Cognito credentials. This saves you from the trouble of implementing SigV4 auth yourself.
Here are a couple of simple examples using Cognito credentials with a generated JavaScript SDK:
https://github.com/rpgreen/aws-recipes/blob/master/app/index.html
https://github.com/awslabs/api-gateway-secure-pet-store
Cheers,
Ryan
As Ryan mentioned, the best way to do this is via the API Gateway SDK. The downside to using this stack is that it becomes harder to integrate with off the shelf front-end tools. You can no longer make direct request to your REST end-points, you will want to go through the SDK.
You definitely lose some ease of development because you can't just slap something like ngResource on top of your endpoints and call it a day. You'll have to set up the calls to each of your AWS end points in a service layer yourself.
Related
So, I know the answer is "yes I can", but is this the right path?
I have a Cognito userpool within all my platform users, now we would like to open specific API Gateway endpoints to specific users, so I just thought about using a Cognito app client for each user who wants these special access.
If a user wants to use the API will need an app client setup with its id/secret keys, then I can create an authorizer in the API Gateway pointing to this userpool and add it to specific endpoints.
Then the user will use his/her id/secret keys to generate a token to be used against specific API endpoints.
This seems to be clean and neat, but there is a big problem: Cognito limits to 10k app client per userpool
We don't actually have so much users wanting to access our API but who knows in the future.
So is this still the right path?
I would like my client application to insert records in my dynamoDb instance using API gateway secured with Cognito user pools.
I have created my user pool and added it as an authorizer to my API gateway method call. Using AWS Cli I ran the following command which gave me my access token:
aws cognito-idp initiate-auth ...
My infrastructure seems to be working, now which direction do I need to go to pragmatically achieve signing-in as my user in the user pool, grabbing the token and calling my API method?
Well it's not difficult. You need to follow certain steps.
Create an user in Cognito user pool. Confirm it, by the means of activation message you have chosen. It can be sms or email as per the user pool settings.
After you confirm the user, you need to call the login API from Cognito SDK. Since I am comfortable in NodeJS, let me grab the method name - https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_InitiateAuth.html. Set AuthFLow to ADMIN_NO_SRP_AUTH .The response from this APi will have one idToken, one accessToken and one refreshToken. Since you need these credentials at your client, write an API in your preferred language, expose it your client and return the tokens.
Use the idToken to make API calls to your API Gateway Authorizer. This is how you pass the token using Postman -
You can replicate the same using any client. If you face any error, It'd be better if you show me your APIG authorizer configuration.
What I have done so far?
I have integrated Microsoft AD with AWS Cognito by adding Trust relationships and setting Cognito Identity provider. In this, I have set up an app domain prefix during Cognito set up. By using the following URL I am able to receive the token by logging in the AD login page. So when I decode this token I getting the required attributes and other information.
What I actually want?
So I don't want my users to be redirected to the AD login page and later to the application. I need just a REST API where I can provide the AD user credentials and this API will return the above JWT token which has all the information.
So in short, I want to get the Cognito JWT token by using the AD user credentials. These users are the part of AD groups which are linked to the AWS IAM by adding trust relationship using ADFS. So is there a way in which I don't need to use the Cognito hosted UI. Instead, my users will hit one API and get the credentials. I don't know what this API is.
Any help will be appreciated.
I am in the same dilemma myself, but I've found a very useful question (with answers) that might help you out.
It's been some time but maybe this will be useful for new people having the same problem nowadays.
What is the REST (or CLI) API for logging in to Amazon Cognito user pools
I think you'd want to enable the ClientCredentials flow in your cognito user pool.
You can read more on this flow here https://aws.amazon.com/blogs/mobile/understanding-amazon-cognito-user-pool-oauth-2-0-grants/ to make sure it is what you need (oauth flows and grants are the same thing).
I am building web application based on microservice architecture. At this moment I am considering few ways of user authentication flow. I predict following, example user roles:
admin - is able to create content, upload files etc (admin account can be created only by another admin)
unauthorized user - can view content
authorized user - can comment content
Here is, how I was thinking about authentication flow so far:
authentication service - have access to DB with users credentials and permissions
api gateway - retrieve requests from user, check if user is logged in (ie verifies OAuth2 access token with auth service) and transfer flow to other services based on user request (attaching JWT token with some basic user info)
another service - accept only requests from api gateway, and trusts user data from JWT token (does not need to connect with auth service to get information about user).
After deploying some stuff on AWS infrastructure my way of thinking have changed a little bit. As far as I understand AWS products (Lambda - serverless applications and API gateway), I should implement authentication flow as follows:
authentication service - gets request from user, retrieve data from dynamoDB and provide user cookie with JWT signed by private key
any other service - retrieves request with JWT token, verifies signature using public key, and perform some action.
And now the question comes:
How deos AWS Cognito fits here? Is it something useful for me? As far as I understand, Cognito simplifies flow of authenticating users via 3rd parties (facebook, twitter etc. etc.). Does AWS Cognito serves login page, separated from my application, or it is only background/webservices impelementation?
So far I am thinking about Cognito as a replacement for my authentication service - any of my services, should impelemnt Cognito authentication flow provided by SDK from amazon, and my static website would implement JavaScript SDK for user login/register. Am I right?
First of all AWS Cognito consists of two services.
AWS Cognito UserPools (Which is the Identity Provider) - This is the service where you can create the users and manage their credentials with other policies. It can also provide the login screen where we can customize the logo and look and feel so that it can become a plug and play Login service. Then it is also possible to configure the authentication flow (For example to make the service as an OpenIDConnect authentication provider so that it will return a JWT token once user logs in). It is also possible to connect Social Identities (Facebook, Google & etc.) and SAML.
AWS Cognito Federated Identities (Identity Federation to grant users access AWS Services) - This service is capable of accepting AWS Cognito UserPool Token or direct access from other providers where we can federate the access to AWS resources. For example, AWS Cognito Federated Identities can grant temporal access to a User, Authenticated from another provider (e.g; AWS Cognito UserPools) to upload files to S3.
For more details refer the article The Difference Between AWS Cognito UserPools and Federated Identities?.
So coming back to your questions,
So far I am thinking about Cognito as a replacement for my
authentication service?
you can use AWS Cognito UserPools authentication service to issue JWT tokens and validate the token in AWS Lambda Custom Authorizer at your other service endpoints. This is also the place where you can do Authorization.
My static website would implement JavaScript SDK for user
login/register. Am I right?
Not necessarily. If you use AWS Cognito UserPools Hosted UI, you will get Login, Signup, Password Change, Confirmation pages, by default along with auto redirection for Federated Identities (Based on the configurations) such as Facebook, Google or Corporate Credentials like Office365. Although the customization is limited, you should be able to add your own logo and change the background color of these screens. If you plan to implement this by your self, then you can use AWS SDKs to implement these screens.
For more details on the serverless architecture refer Full Stack Serverless Web Apps with AWS.
I am trying to make a serverless application (ReactJS, API Gateway, AWS Lambda, DynamoDB) with federated authentication. Below is the kind of architectire that I am envisioning ( Have not added STS for brevity. Also I don not think that I understand the flow fully):
I have created API Gateway endpoints that trigger lambda functions. I want to authenticate my users with google first and if they are successful then they should be able to use API endpoints.
First step is to authenticate the user with google using standard Outh2. I have created 2 unauthenticated endpoints /signin/google and /callback/google for the purpose . Once I get the successful authentication response from google in callback lambda function I have id_token (among others) that I can use.
At this point in time I have 2 approaches that I can use for authenticating APIs.
Build a custom authorizer that I can use for API endpoints. Here is the code (https://github.com/prabhatsharma/api-gateway-custom-authorizer/). This is pretty straightforward. I can use the same id_token provided by google to authenticate API endpoints. Custom authorizer will validate that id_token is good and grant access to the endpoint. It will also cache the result so that this verification is not needed everytime. (Is this a good approaach to reuse the id_token of google in this way?) You can use the authorizer with this (https://github.com/prabhatsharma/lambda-custom-auth)
I can use AWS cognito for authentication. For this I have created a federated identity pool and have set the google app client Id to cognito console. In my /callback/google lambda function I am using AWS SDK to get the identityId, sessionToken and accessKeyId. (Source code https://github.com/prabhatsharma/lambda-cognito-auth)
// Add the Google access token to the Cognito credentials login map.
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: config.COGNITO_IDENTITY_POOL_ID, //dauth
Logins: {
'accounts.google.com': JSON.parse(body).id_token
}
});
Now I can use the credentials to get Token using following code
// Obtain AWS credentials
AWS.config.credentials.get(function () {
// Access AWS resources here.
// Credentials will be available when this function is called.
var identityId = AWS.config.credentials.identityId;
var cognitoidentity = new AWS.CognitoIdentity();
var params = {
IdentityId: identityId, /* required */
Logins: {
'accounts.google.com': JSON.parse(body).id_token
}
};
cognitoidentity.getOpenIdToken(params, function (err, data) {
if (err) console.log(err, err.stack); // an error occurred
else {
console.log(data); // successful response
res.headers = {
location: config.APPLICAION_URL + '/auth/google?' + querystring.stringify(data)
}
callback(null, res); //redirect to front end application
}
});
I can now pass the identityId and Token to my front end reactJS application.
This is where I need little help understanding the concept. I can now use the AWS SDK in browser to access AWS resources. But hold on!!! Wasn't the purpose of creating standard RESTful APIs via Gateway was to use standard javascript without reliance on any specific library? Using AWS SDK directly looks like a more apt use case for android/ios/unity apps. I would like developers in my team to be able to use the standard javascript libraries of front end that they use for this situation too. Also I do not want to use the exported API SDK for my API endpoints and really want to keep the front end app clean of AWS SDK specifics. Signing every request with v4 signature manually is redundant work. Can't we have standard token based API endpoint access using Cognito?
What is the best practice for this kind of authentication? Am I thinking in right direction?
Disclaimer - Please do not use the code in the repos as yet. It is work in progress and not yet production ready.
You can use your Custom Authorizer function with the oauth2 token. Alternatively,
you can use Cognito with the corresponding IAM roles to manage user access to your AWS resources.
The second approach, which you describe in the flow picture, does not need of an auth lambda in front of every request and lets you control access to other AWS resources from your frontend (like IoT i.e.).
Once you get the secretKey, accessKey, sessionToken and region, you only need to sign every request to APIG. You don't need the AWS SDK to do this.
If you don't want to use the exported API, you have to sign the requests yourself. It's implemented in the sigV4Client.js so it's fairly easy to copy. You are going to need few dependencies anyway, so why not use the exported API? You're using React already which is so big.