Secure S3 Website Hosting with AWS Cloudfront and Cognito Authentication
Updated: Nov 5
Sometimes we need to protect our website (or part of it) from unauthorized access, this can be tricky because we need to think of a custom way of authentication module, or well, a third-party platform to integrate with our system, we also need to be concerned about the availability and performance of this service as it grows.
In this Teratip we will discover a new way of deploying our web static content to a high-availability service such as AWS S3, using Cloudfront as CDN that helps you to distribute your content quickly and reliably with high speed. As mentioned before, we need to protect from unauthorized access, so we will implement AWS Cognito as an authentication service, using JWT for session management via AWS Lambda.
High Availability Website
To begin, we will define if we want to host a new S3 website or use an existing website, we can deploy our static web content to an S3 private bucket and access it via Cloudfront using OAI, our Terraform module will allow you to set your domain and aliases and then will create the Cloudfront Distribution, S3 Bucket, and even SSL Certificates (using Amazon Certificate Manager).
Authentication Process
Good, we have our highly available website already running but we can notice that anyone can access it, but don’t worry because our module will create a new Authentication process that will be triggered by Cloudfront when a user request access to our website.
Lambda@Edge
Lambda@Edge lets you run AWS Lambda functions in an AWS location close to your customer in response to CloudFront events. Your code can be triggered by Amazon CloudFront events such as requests for content by viewers or requests from CloudFront to origin servers. You can use Lambda functions to change CloudFront requests and responses at the following points:
JWT Authentication
JSON Web Token (JWT) is a JSON-based open standard for creating access tokens that assert a series of claims as a JSON object.
There are several benefits to using Lambda@Edge for authorization operations. First, performance is improved by running the authorization function using Lambda@Edge closest to the viewer, reducing latency and response time to the viewer request. The load on your origin servers is also reduced by offloading CPU-intensive operations such as verification of JSON Web Token (JWT) signatures.
We will implement the JWT authentication process via Lambda functions using NodeJS 14.x in this case.
Amazon Cognito
Well, now we will talk about AWS Cognito, Cognito lets you add user sign-up, sign-in, and access control to your web and mobile apps quickly and easily. Also scales to millions of users and supports sign-in with social identity providers, such as Apple, Facebook, Google, and Amazon, and enterprise identity providers via SAML 2.0 and OpenID Connect. So using this service our authentication process will look as follows:
Our Terraform module will create the Cognito user pool for us and add the Cognito Login URL to the Cloudfront response in case we don’t are authenticated. Then, we can sign-up new users via Cognito login URL, or better, we can access to Cognito service in our AWS account and manage users, add new authentication providers, change password policies, and a lot of other things. Finally, our architecture will work as follows:
Access for the first time without an authenticated user:
Once we authenticate with a Cognito user:
Terraform module
This solution is easy to deploy because we build a Terraform module that, with a few variables, can deploy the entire infrastructure for us, below is an example that creates the website https://cloudfront-s3-cognauth.sandbox.teratest.net, a Cognito user pool, and finally uploads an index.html to the S3 bucket to check if after authentication we can access to the website.
module "cloudfront-s3-cognito" { source="git::git@github.com:teracloud-io/terraform_modules//services/web-cloudfront-s3-cognito" #Region which S3 website and Cognito services will be stored - default is us-east-1 region = "us-west-2" #Name of the service that will be used as a subdomain for your website service = "cloudfront-s3-cognauth" #Name of the domain that will be used in web URL (this domain must exist in Route53) domain = "sandbox.teratest.net" #The name of the Lambda function that will handle the JWT Authentication process lambda_name = "cognito-jwt-auth" #If you want to create an index.html for S3 website this variable must be untagged index_example = true #In order to add a logo to Cognito login page you need to set the path/filename logo = "logo.png" }
Conclusion
That's all you need!
With a few lines of Terraform, we've created a Frontend application in S3 with a CDN as Cloudflare, SSL certificates, and authentication mechanisms that protect it.
Protecting frontend and even backend code has never been easier, and doing so at an infrastructure level enables you to let your apps focus on just what they ought to.
References
AWS blog on validating JWTs with Lambda@Edge: https://aws.amazon.com/blogs/networking-and-content-delivery/authorizationedge-how-to-use-lambdaedge-and-json-web-tokens-to-enhance-web-application-security/
Sample code to decode and validate JWTs in python and typescript: https://github.com/awslabs/aws-support-tools/tree/master/Cognito/decode-verify-jwt
Authorization with Lambda@Edge and JSON Web Tokens (JWTs): https://github.com/aws-samples/authorization-lambda-at-edge/blob/master/
Accessing cookies in Lambda@Edge: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-examples.html
Nicolas Balmaceda
DevOps
Teracloud
 To learn more about cloud computing, visit our blog for first-hand insights from our team. If you need an AWS-certified team to deploy, scale, or provision your IT resources to the cloud seamlessly, send us a message here.