Friday, 19 October 2018

API Gateway Authentication with Amazon Cognito


Amazon API Gateway is a fully managed service that makes it easy for developers to create, publish, maintain, monitor, and secure APIs at any scale. With a few clicks in the AWS Management Console, you can create an API that acts as a “front door” for applications to access data, business logic, or functionality from your back-end services, such as workloads running on Amazon Elastic Compute Cloud (Amazon EC2), code running on AWS Lambda, or any web application.

Amazon API Gateway handles all the tasks involved in accepting and processing up to hundreds of thousands of concurrent API calls, including traffic management, authorization, and access control, monitoring, and API version management. Amazon API Gateway has no minimum fees or startup costs. You pay only for the API calls you receive and the amount of data transferred out.


Different authentication methods for API Gateway

There are multiple ways to authenticate the requests an API Gateway. These are:


  1. Resource policies let you create resource-based policies to allow or deny access to your APIs and methods from specified source IP addresses or VPC endpoints.
  2. Standard AWS IAM roles and policies offer flexible and robust access controls that can be applied to an entire API or individual methods.
  3. Cross-origin resource sharing (CORS) lets you control how your API responds to cross-domain resource requests.
  4. Lambda authorizers are Lambda functions that control access to your API methods using bearer token authentication as well as the information described by headers, paths, query strings, stage variables, or context variables request parameters.
  5. Amazon Cognito user pools let you create customizable authentication and authorization solutions.
  6. Client-side SSL certificates can be used to verify that HTTP requests to your backend system are from API Gateway.
  7. Usage plans let you provide API keys to your customers — and then track and limit usage of your API stages and methods for each API key.


Cognito

What is Cognito and Cognito User Pool?

Amazon Cognito lets you add user sign-up, sign-in, and access control to your web and mobile apps quickly and easily.

Amazon Cognito User Pools provide a secure user directory that scales to hundreds of millions of users. As a fully managed service, User Pools are easy to set up without any worries about standing up server infrastructure.


Creating a Cognito User Pool?

To create a User Pool go to the Cognito User Pool console and follow these steps.


1. Set the name of the user pool
2. Click on review defaults and then on App clients.
3.On the App Clients page click on Add an app client.
4. Enter the name of the client. Uncheck the Generate client secret, we won't be needing it. Check the last option - Enable username-password (non-SRP) flow for app-based authentication (USER_PASSWORD_AUTH).  Click on Create Client App.



5. Click on return to pool details and then click on Create Pool.
6. Now once the user pool is created. Go to App Client settings under App Integration.
7. Check the Cognito User Pool checkbox. Put your call back URLs. These are the URLs that Cognito will redirect to after sign in/up.
8. Under Allowed OAuth Flows check Authorization code grant and Implicit grant.
9. Under Allowed OAuth Scopes check email and openid.
10. Click on Save Changes.



11. Now go to Domain Name under App Integration.
12. In Domain prefix, enter the prefix of the domain you want for sign in/up. After entering a prefix which is available click on save changes.
13. At this point, you would have a sign in/up page where users can sign in or register. The URL for this page will be of the following format :

https://your-domain-prefix.auth.us-east-1.amazoncognito.com/login?response_type=token&client_id=asd233wde232da23&redirect_uri=https://google.com

Replace your-domain-prefix with the domain prefix you set up in step 12, client_id with the client id of the client you created in step 3, redirect_uri with the Callback URL(s) you set in step 7.





14. Once you login or sign up using this you will be redirected to your call back URL. In the URL generated for redirecting you will see the Cognito has added some key-value pairs. The id_token is the token you would need to authenticate your request with API Gateway. The token is in JWT format which is explained below.



Enabling Authentication in API Gateway

1. Go to your API in API Gateway. Make sure CORS is enabled.
2. Under your API, go to Authorizers, and click on Create New Authorizer.
3. Enter the name of your Authorizer.
4. In type select Cognito.
5. Under Cognito User Pools, select the user pool you created.
6. In Token Source, write Authorization, and click on create.



7. Now go to your API -> Resources ->  method -> Method Request.
8. Under settings, click on the pencil next to Authorization, from the drop-down that comes select the Authorizer that you just created and save the settings. If you don't see your authorizer try refreshing the page. 
9. Deploy the API.
10. At this point, your API would only accept requests which have the id token. If you want to know more the format in which the token is and how to store it you can read the following parts about JWT.


JSON Web Tokens (JWT) 

JSON web Tokens or JWT and OAuth 2.0 are the most common ways to authenticate the APIs. 

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties. They are a great authentication mechanism. They give you a structured and stateless way to declare a user and what they can access. They can be cryptographically signed and encrypted to prevent tampering on the client side.

Visualizing/decoding JWT data

The token you get from Cognito is in JWT format and you cant really see what is the data in it. To see the data that it contains go to the following website and paste your token. 
https://jwt.io/

Where to Store your JWTs – Cookies vs local storage

Once you are authenticated with Cognito, you would get the token in the URL. You need to store it safely so that you can access it in every API call. 
There are two ways in which you can easily store the JWT. The first one is localStorage and second is cookies. Both have their advantages and disadvantages. Let's see what are the advantages and disadvantages or each of them. 

Local Storage:

Web Storage (localStorage/sessionStorage) is accessible through JavaScript on the same domain. This means that any JavaScript running on your site will have access to web storage, and because of this can be vulnerable to cross-site scripting (XSS) attacks. We import multiple JS files on our website, even if one of them is compromised our website could be susceptible to XSS. On Local storage, we can't put restrictions like HTTPS only or prevent javascript from using it. 

Cookie Storage

Cookies, when used with the HttpOnly cookie flag, are not accessible through JavaScript, and are immune to XSS. You can also set the Secure cookie flag to guarantee the cookie is only sent over HTTPS. This is one of the main reasons that cookies have been leveraged in the past to store tokens or session data.  The HttpOnly flag is set by the server and not by the Javascript in front-end. This becomes a problem for us as we need to access cookies to store the token we get. 

Cookies are vulnerable to a different type of attack: cross-site request forgery (CSRF). A CSRF attack is a type of attack that occurs when a malicious web site, email, or blog causes a user’s web browser to perform an unwanted action on a trusted site on which the user is currently authenticated. This is an exploit of how the browser handles cookies. A cookie can only be sent to the domains in which it is allowed. CSRF can be prevented easily so this should not be a big problem.

Few things to keep in mind:

Things you store in cookies are sent to the server on every call(if the server and the place setting the cookies are on the same domain). So if you want to store something that is not required by the server you should save it in the local storage otherwise you would be wasting bandwidth by sending extra data in every request.

You may need to share cookies between subdomains if you are setting cookies and sending a request in different subdomains.

The id token you get from Cognito has a short lifespan. If the id token has expired the request will fail on which you can ask the user to log in again. You can also set the expiry in cookies.



Resources: