CloudFront with JWT authentication
Tags: aws, cloudfront, s3, jwt
Published: 2022-08-25 04:10:00

In this post, I will explain how CloudFront provides JWT authentication, which I applied successfully in the project. Hopefully, it will be helpful for you.

**1. The requirement:**
My customer provides LMS (Learning Management System) for language training on AWS.  Their clients should buy training course to learn from recorded videos (Video on Demand). Videos are stored in S3 bucket as origin. After the client logins the web portal successfully, he will get a [JWT](https://jwt.io/) (JSON Web Token) from webapp and then use the token to get video from origin via CloudFront.

**2. Solution:**
CloudFront authenticates using CloudFront Function the client's JWT and then do the following actions:

- If the token is valid or the expiry time hasn't passed, CloudFront distribution checks its cache. In case of cache hit, CloudFront Edge delivers the video to client. Otherwise, CloudFront distribution request origin (S3 bucket) for the video.
- If the token is invalid or the expiry time has passed or the token is not included in the client request, CloudFront Edge will send a response with error code 401 (Unauthorized) to the client.

The below shows the solution concept:

![CloudFront with S3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gkyoucr0zs9qtei6vj37.png)
 
The components are:

- CloudFront: CloudFront Functions runs JWT authentication source code. S3 bucket, which stores training videos, will be an origin of CloudFront distribution with [Origin Access Identity (OAI)](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html) to restrict access to the bucket only through CloudFront.
- S3 bucket: storing training videos. S3 bucket policy should be configured to allow only CloudFront distribution to access the bucket.

**3. Introduction to CloudFront and CloudFront Functions:**
**3.1. CloudFront:**
CloudFront is AWS CDN (Content Delivery Network), securely delivers content with low latency and high transfer speeds. It consists of a global network of CloudFront Edge locations that are distributed across the globe and use AWS Shield Standard in default to defend against DDoS attacks at no additional charge. Please check the below for more information:

- [Official CloudFront document ](https://aws.amazon.com/cloudfront/?nc1=h_ls)
- [CloudFront infrastructure information](https://aws.amazon.com/cloudfront/features/?whats-new-cloudfront.sort-by=item.additionalFields.postDateTime&whats-new-cloudfront.sort-order=desc#Global_Edge_Network) 

**3.2. CloudFront Functions:**
[CloudFront Functions](https://aws.amazon.com/blogs/aws/introducing-cloudfront-functions-run-your-code-at-the-edge-with-low-latency-at-any-scale/) are ideal for lightweight computation tasks on web requests. Some popular use cases are:

- **HTTP header manipulation**: View, add, modify, or delete any of the request or response headers. For example, add HTTP Strict Transport Security (HSTS) headers to your response or copy the client IP address into a new HTTP header (like True-Client-IP) to forward this IP to the origin with the request.

- **URL rewrites and redirects** : Generate a response from within CloudFront Functions to redirect requests to a different URL. For example, redirect a non-authenticated user from a restricted page to a paywall. You could also use URL rewrites for A/B testing a website.

- **Cache key manipulations and normalization** : Transform HTTP request attributes (URL, headers, cookies, query strings) to construct the [CloudFront cache key](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/understanding-the-cache-key.html) that is used for determining cache hits on future requests. By transforming the request attributes, you can normalize multiple requests to a single cache key, leading to an improved cache hit ratio.

- **Access authorization**: Implement access control and authorization for the content delivered through CloudFront by creating and validating user-generated tokens, such as HMAC tokens or JSON web tokens (JWT), to allow or deny requests.

At the time I write this post, CloudFront Functions only supports JavaScript runtime. For more information, kindly refer to the [link](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-functions.html).

I referred to [this AWS GitHub repository](https://github.com/aws-samples/amazon-cloudfront-functions/tree/main/verify-jwt) for JWT authentication source code. You can write the JWT authentication approach by yourself. It's up to you!

Here are some configurations in CloudFront:

a) CloudFront Functions after published in live deployment:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8pz0xzo9zu0d0pbeu8ff.png)

CloudFront Functions written in JavaScript to authenticate client request with JWT string parameter. It will decode JWT value using pre-defined key (in my case, it is AWS secret key). If JWT value is validated successfully by CloudFront Functions or the expiry time hasn't passed, the client request will be served. Otherwise, the response message “401 - Unauthorized” will be sent to the client.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/szd36axsuzz95hewbise.png)

CloudFront function can be also published in development environment for testing. Please remember that only in production environment, the function will be associated with CloudFront distribution.

b) In my case, the client request contains a JWT value in a query string parameter named jwt. CloudFront cache policy should be configured with cache key setting like this:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i0wgo8639zdp62kxn7we.png)
**Note:** _query strings value may be different in your case. It depends on the parameter string which is defined in your backend code. In some cases, the parameter string is "token=xxxyyyzzz". So, query string value should be "token"._

c) After creating CloudFront Functions, it should integrate with CloudFront behavior, in order to make the function in use:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a7ziptjzl8w23569q9qq.png)

**Note:** CloudFront Functions can be integrated with Viewer request and Viewer response. For Origin request and Origin response, only [Lamda@Edge](https://aws.amazon.com/lambda/edge/) can be integrated.

After above steps done, I tested the function with Postman application before applying it into the project. A video was uploaded into S3 bucket (origin). An API request with valid JWT string parameter was passed from CloudFront’s authentication and received the response from CloudFront. Otherwise, the response message “401 - Unauthorized” was sent back. Here is the result:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bi7ibhz423io54gv66m4.png)

_The API with valid JWT parameter string. The response code was 200 OK._

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7eue0gf1q22z6ovbmj18.png)

_The API request without JWT parameter string. The response code is 401 Unauthorized._

**4.Conclusion:**

In this post, I demonstrated how CloudFront can authenticate JWT from the client request to serve the video stored in S3 bucket. The solution may be helpful for who has a VoD application.

P/s: AWS document also provides the tutorial for hosting on-demand streaming video with S3, CloudFront and Route 53. If you are interested in this scenario, kindly refer to [this link](https://docs.aws.amazon.com/AmazonS3/latest/userguide/tutorial-s3-cloudfront-route53-video-streaming.html). 

Thank you for your reading! I look forward to hearing any comments from you!