How to Deploy Static Website with OAuth 2.0 Authorization, CloudFront CDN in 20 Minutes?

(Illustration: Afternoon-tea with Grilled Halloumi Cheese. Not-so-related to this article, I know XDD. When writing such a long post, you know :p Image source: by Ernest, in London.)

0. Origin

This month Pahud invited us - a group of AWS Hero and AWS Builders to use AWS CDK to code around CloudFront Extensions (CloudFront Lambda@Edge). From my long todo list, I found a topic related to OAuth 2.0 that I have always wanted to do. It’s a perfect timing to try and see how to implement a CloudFront Extension solution in AWS CDK (Cloud Development Kit) with .env environment variable settings, so that you can easily set your favorite IdP (Identity Provider), and then set the parameters generated by the IdP into .env file. You can use this CloudFront Extension CDK solution to complete the deployment. After practicing, it should be completed within 20 minutes.

This article is organized in order for future education and training material, and is divided into three parts:

  1. At the beginning, we will introduce use cases, architecture, OAuth 2.0 protocol flow, and Grant Type: Authorization Code process
  2. Then select and configure a IdP you like (this part is expected to be expanded and updated in the future)
  3. Finally, “CloudFront Extension OAuth2 Getting Started” takes everyone to actually operate this CDK


1. Use Case

In recent years, the use of static site generators has gradually become a mainstream tool for setting up blogs or document sites. The generated static files, in addition to putting GitHub Pages, another good place is Amazon S3.

Putting Amazon CloudFront as a CDN in front of Amazon S3 is also a common choice.

In some usage scenarios, users are expected to log in before they can view the content of the website or document. In this case, you can request CloudFront Lambda@Edge to help us execute a Lambda program, log in and OAuth 2.0 authorization process with the IdP (Identity Provider) specified by ourselves.

In this case, we will use “Viewer request”, one of the four trigger points of CloudFront Lambda@Edge to trigger the execution of the Lambda program we specify to communicate with the IdP. If the OAuth2.0 Authorization Code is successfully authorized, the user will be allowed to watch the origin - the content of website or document.

Then look at two demo videos (corresponding to different IdPs, but both use standard OAuth 2.0 and OpenID) to quickly understand the use case that this article wants to implement:

  • The following figure uses Auth0 (IdP) + OAuth2 Authentication (CloudFront Extension) implemented in this article
  • The following figure uses self-built KeyCloak (IdP) + OAuth2 Authentication (CloudFront Extension) implemented in this article
    • Also use CDK to quickly deploy KeyCloak

2. Amazon CloudFront Extensions

Amazon CloudFront Extensions is a combination of using Lambda@Edge to extend CloudFront to implement various rich features. Originally implemented using AWS CloudFormation, but this time Pahud invited us to use the AWS CDK method to implement it.

The git repo we implemented this time is placed in Pahud’s home: https://github.com/pahud/cdk-cloudfront-plus

The name of the extension I implemented this time is “OAuth2 Authentication”, project number “SO8131”, directory name “cf-authentication-by-oauth2”.

The goal of implementation is to create a universal package (Lambda@Edge), which can be set to correspond to various parameters provided by IdP (Identity Provider) by setting environment variables, such as client id, client secret, callback URL, etc. And hope to reduce package dependencies and use TypeScript throughout.

3. OAuth 2.0 Grant Type: Authorization Code

RFC6749 defines The OAuth 2.0 Authorization Framework, OpenID implemented OAuth 2.0 and integrated account information. Simply understand that OAuth 2.0 is a theory, and OpenID is one of the implementation methods based on this theory.

OAuth 2.0 defines four roles, six steps, Three round trips. Refer to the following Protocol Flow:

  • Client: Authorization Request –> Resource Owner: Authorization Grant
  • Client: Authorization Grant –> Authorization Server: Access Token
  • Client: Access Token –> Resource Server: Protected Resource

Quick summary: three simple steps: first take a code –> take the code for a (access) token –> take the token for resources.


OAuth 2.0 defines four Authorization Grant Types:

  • Authorization Code
    • Authorization Code
    • Authorization Code with PKCE
  • Implicit
  • Resource Owner Password Credentials
  • Client Credentials

Quick summary: The context in this article is suitable for Authorization Code.


After establishing the process structure and narrowing down the scope of using the Authorization Code, then set up the IdP you choose, create a client in the IdP environment, and obtain various environmental variables of the IdP and the client.

In the process, we will encounter various IdP hidden magic logic, oh no, I mean IdP, in order to strengthen the verification process, there will be different details that need to be paid attention to. E.g

  • The Auth0 document does not mention but the authorize parameter combination must be response_type=code with scope=openid to get the JWT token. (reference source)
  • KeyCloak stipulates which URL the Client comes from at the beginning, and the subsequent specified redirect_uri must be exactly the same URL before releasing, otherwise KeyCloak will keep spraying 400 Bad Request with a mysterious and unknown error log: [0m11:57:34,772 WARN [org.keycloak.events] (default task-97) type=CODE_TO_TOKEN_ERROR, realmId=RealmDemoCDK, clientId=demo-cloudfront-plus, userId=2ae58a4c-9b3e-4f0e-b58d-b9462e73105a, ipAddress=10.0.9.157, error=invalid_code, grant_type=authorization_code, code_id=fbb08df8-1af1-4960-a9ac-ab80e0b3d377, client_auth_method=client-secret. (reference source)

4.1 IdP Example Configuration: Auth0

Final .env

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
CLIENT_DOMAIN="your-domain-here.example.auth0.com"
CLIENT_ID="yourClientId"
CLIENT_SECRET="yourClientSecret"
CLIENT_PUBLIC_KEY="-----BEGIN CERTIFICATE-----\nPlaceYourKeyHere\n-----END CERTIFICATE-----"

AUTHORIZE_URL="https://your-domain.here/authorize"
AUTHORIZE_PARAMS="?response_type=code&scope=openid"
AUTHORIZE_REDIRECTURI_SHOULD_MATCH=false

CALLBACK_PATH="/callback"

JWT_ARGORITHM="RS256"
JWT_TOKEN_PATH="/oauth/token"

DEBUG_ENABLE=false

4.2 IdP Example Configuration: KeyCloak

After implementing the Auth0 example, I am planning to connect a few more IdPs to verify that the OAuth 2.0 Authorization Code processes of each provider are consistent. I happened to chat with Pahud about his cdk-keycloak construct, it’s such a coincidence, so I started the integration journey of Keycloak (aka trip to pit).

First of all, since the cdk-keycloak construct is already available, I only need to assemble a CDK app and execute it, so I made a cdk-keycloak-demo, please refer to repo README.md, there will be a chance to organize it into note articles in the future. For now, I will focus on how to set it up so that it can work with CloudFront Extension OAuth2.

Create Realm

It is recommended not to include blank characters in the name, preferably consecutive alphanumeric characters, with no difference in capitalization.

Then the Realm name will become part of the URL. In this example, it is called RealmDemoCDK.

Create Key

Create a set of keys to generate JWT token in RS256. In this example, it is called demo-cdk-rsa-key-pair. Please put the content of pressing the button “Public Key” on the right into the CLIENT_PUBLIC_KEY field of the CloudFront Extension OAuth2 .env file.

For example:

CLIENT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQABAQUAA4GNADDCiQKBgQ8EpDw0PDfYYqAwWFnzVFkfC7+diZTkVJob3WwtaeIsY9ygFCjGhOSWgpJkB8pqGFu0kYT8Nz8ZBOetvMwFUAGBBPrOVa6TSocss8fwMmmkQvFxpC2UmzRZY1Oj+0eMGI0KUPJ6I7wTuPdnC0mip+RJdvKxJw7UhOld4yAp1JSQewIAAQAB\n-----END PUBLIC KEY-----"

Create Client

In this example, it is called demo-cloudfront-plus. Please put the content in the CLIENT_ID field of the CloudFront Extension OAuth2 .env file.

Setup Client

Please refer to the figure below to select the corresponding on/off switch, and set the endpoint URL of the static website deployed by the CloudFront Extension OAuth2 demo to Root URL, Valid Redirect URLs, Admin URL and Web Origins, and finally set * to Web Origins.

Get Client Secret

You can find Client Secret in the “Credentials” tab of Client settings. Please put the content in the CLIENT_SECRET field of the CloudFront Extension OAuth2 .env file.

Create Client Role

In this example it is called User. If the Client Role is not established and several subsequent associated actions, it will cause a 401 Unauthorized error.

Create User

This step is very flexible, you can use your favorite Username, Email, Last Name and First Name.

Setup User

For the convenience of testing, I turned on User Enabled and Email Verified.

Setup User Credentials

For the convenience of testing, directly assign a set of passwords to this new User, turn off Temporary, and then press Reset Password. The picture below is the screen after the password has been set.

Create Group

In this example it is called ClientUsers.

Setup Group Role Mapping

Edit the newly created Group, use the Role Mappings tab, set the Client Roles, select the Client demo-cloudfront-plus we created, and select the Client Role User just created.

Setup User Group

Back to the User edit screen, use the Groups tab to classify this user into the Group ClientUsers just created.

Final .env

In order to enable CloudFront Extension OAuth2 to correctly connect and interact with KeyCloack, please set AUTHORIZE_REDIRECTURI_SHOULD_MATCH=true in .env.

Example format of CloudFront Extension OAuth2 .env file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
CLIENT_DOMAIN="your-demo-keycloak-elb-endpoint.example.com"
CLIENT_ID="demo-cloudfront-plus"
CLIENT_SECRET="12341234-1234-1234-1234-1234567890ab"
CLIENT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQABAQUAA4GNADDCiQKBgQ8EpDw0PDfYYqAwWFnzVFkfC7+diZTkVJob3WwtaeIsY9ygFCjGhOSWgpJkB8pqGFu0kYT8Nz8ZBOetvMwFUAGBBPrOVa6TSocss8fwMmmkQvFxpC2UmzRZY1Oj+0eMGI0KUPJ6I7wTuPdnC0mip+RJdvKxJw7UhOld4yAp1JSQewIAAQAB\n-----END PUBLIC KEY-----"

AUTHORIZE_URL="https://your-demo-keycloak-elb-endpoint.example.com/auth/realms/RealmDemoCDK/protocol/openid-connect/auth"
AUTHORIZE_PARAMS="?response_type=code&scope=openid"
AUTHORIZE_REDIRECTURI_SHOULD_MATCH=true

CALLBACK_PATH="/callback"

JWT_ARGORITHM="RS256"
JWT_TOKEN_PATH="/auth/realms/RealmDemoCDK/protocol/openid-connect/token"

DEBUG_ENABLE=false

5. CloudFront Extension OAuth2 Getting Started

To sum up, we have the process concept of Lambda@Edge, OAuth 2.0 Authorization Code grant type, and set up an IdP of our own choosing. Next, let’s run the CloudFront Extension OAuth2 demo and have a try. After the demo is successfully deployed, there will be a CloudFront distribution endpoint URL can be set back to the IdP Client, let IdP know this set of URLs.

The CloudFront Extension OAuth2 demo is placed in https://github.com/pahud/cdk-cloudfront-plus/tree/main/src/demo/cf-authentication-by-oauth2, you can clone the repo and operate directly in the project root directory. Remember to set up your AWS CLI environment, AWS profile, and CDK environment first.

Setup .env

Duplicate dotenv/cf-authentication-by-oauth2/.env-example to dotenv/cf-authentication-by-oauth2/.env, and then set .env according to your IdP environment.

cp dotenv/cf-authentication-by-oauth2/.env-example dotenv/cf-authentication-by-oauth2/.env

yarn

Open two terminals. One for yarn watch, and the other for cdk.

On the first terminal:

yarn install

cd lambda-assets/extensions/cf-authentication-by-oauth2

yarn install

cd ../../..

yarn watch

cdk deploy

On the second terminal:

AWS_REGION=us-east-1 cdk --app lib/demo/cf-authentication-by-oauth2/index.js bootstrap

AWS_REGION=us-east-1 cdk --app lib/demo/cf-authentication-by-oauth2/index.js diff

AWS_REGION=us-east-1 cdk --app lib/demo/cf-authentication-by-oauth2/index.js deploy

Output

Once you deploy successfully, the CDK script will output a CloudFront distribution endpoint URL. Please combine with the callback path you assigned in the .env file to configurate callback URL at your IdP application/client setting.

Login

On deploy completed, open the CloudFront URL with

https://<CLOUDFRONT_DOMAIN>

If an error occurs during the process, you can go to the AWS Management Console and use the CloudWatch Logs function to track down. Remember to select the AWS Region that occurred to find the corresponding Lambda@Edge execution result, and remember to enable DEBUG_ENABLE=true in .env.

Origin S3 (Private content)

If everything goes well, you will see the following content :) Replace it with your own web content or document to fit your case.

Hello CloudFront Extension with CDK!!!
You have logged in. Enjoy your private content.

6. Reference

7. Bottom line

The beginning of this article introduces the usage scenarios, architecture, OAuth 2.0 principle, Grant Type: Authorization Code process, and then takes you to set up your own IdP Client/Application, and finally takes you to actually operate this CDK rapid deployment through “CloudFront Extension OAuth2 Getting Started”.

I have found a few friends for actual testing, and the deployment of CloudFront Extension OAuth2 CDK can be completed in 20 minutes without having issue when setting the IdP.

I hope this CloudFront Extension OAuth2 can save everyone’s time and let everyone focus more on product design and content output. You can try it out :)

If you like this kind of fully implemented articles, please use the form below to subscribe to my newsletter.

Peace out!

Loading comments…