Skip to content

Latest commit

 

History

History
149 lines (109 loc) · 5.5 KB

README.md

File metadata and controls

149 lines (109 loc) · 5.5 KB

MOPED - Cognito Authentication

This folder contains two directories, one containing the pre-token hook that generates the JTW token for Hasura, called cognito-pre-token-hook. And another that gets deployed to CloudFront to validate the cookie generated by AWS amplify, it is called cloudfront-cognito-at-edge.

Cognito Pre-Token Hook

To connect Hasura with AWS Cognito via JWT, we followed the guidelines as provided by the Hasura team in this page:

https://hasura.io/docs/1.0/graphql/core/guides/integrations/aws-cognito.html

How does a cognito trigger work?

There are several factors to be considered, among them:

  • Set up user pools and hosted web UI. In here we create a pool of username and passwords. They also provide an option for a hosted UI to log in.

  • Create a lambda function to add claims to the JWT. AWS provides a way to customize the behavior of the authentication process, here they create a "trigger hooks" which is basically a lambda function that executes whenever a token is being generated. This token contains "claims" which is just a json document that we can customize.

  • Configure Cognito to trigger the lambda function. Here the pool is configured to use the trigger they create to customize the claims for Hasura's consumption.

  • Test the Cognito login and generate sample JWTs for testing. Here they demonstrate how to get a JWT token from the hosted UI. This is not necessary for us, we can get the token from logging in to Moped.

  • Configure Hasura to use Cognito keys. Here they demonstrate how to configure Hasura to retrieve the certificate for a user pool in order to decrypt the JWT claims it will receive.

  • Add access control rules via the Hasura console. Here they demonstrate how to set up access control rules in Hasura.

  • Sync users from Cognito. Here they demonstrate how to sync users from Cognito into Hasura using another trigger hook.

Development

Currently, we do not support branches or PRs for cognito triggers, there are GitHub actions set up that listen for any changes to PRODUCTION and MAIN, if the current branch is neither, then the changes will not be deployed.

The MAIN branch serves as the staging environment, if you want to experiment with cognito triggers, for now just create a branch and merge your code to MAIN. Do not merge code to PRODUCTION unless you know that the code has been tested.

Where do I get help?

This is a living document, and it is being fleshed out as we work in this authentication mechanism. Feel free to reach out to any of the developers for help if you do not understand how it works.

Lambda@Edge

We use lambda@edge to protect content. Lambda-at-edge functions are basically leaner lambda functions with stricter and size restrictions, but they are far more scalable, and they run in CloudFront's CDN. Also, they allow you to integrate and extend request events using javascript or python, etc.

How does it work?

This is simplified illustration of how we integrate Cognito to CloudFront using Lambda@edge:

In short, the mission of the function in lambda at edge is to validate the JWT token contained in our Cognito session. This session exists either in the application local memory (default: local, PRs), or it exists in a Cookie (by configuration: test, staging, prod).

Thankfully, there are now libraries recently published that simplify the validation of the token, it used to be that we had to write your own validator in javascript or python.

The library we use is called cognito-at-edge, published by awslabs. It can be found here.

All it requires is to pass the properties of your cognito instance, like so:

const { Authenticator } = require('cognito-at-edge');

const authenticator = new Authenticator({
  // Replace these parameter values with those of your own environment
  region: 'us-east-1', // user pool region
  userPoolId: 'us-east-1_tyo1a1FHH', // user pool ID
  userPoolAppId: '63gcbm2jmskokurt5ku9fhejc6', // user pool app client ID
  userPoolDomain: 'domain.auth.us-east-1.amazoncognito.com', // user pool domain
});

exports.handler = async (request) => authenticator.handle(request);

How do we make changes?

Given its simplicity, there should not be any need to make changes to the code moving forward. If anything it may only be necessary to make updates as the library gains maturity.

To make changes, go to the cloudfront-cognito-at-edge folder, and you will see one file for each stage that makes use of this library. One for test, one for staging and another for production.

Simply update the file, and there is a GitHub action whose purpose is to update the lambda function.

What about local and PRs?

Local and PRs do not use cookies at this moment, they simply use an unprotected bucket directed to staging. Any files in staging are not considered to be protected.

The way this is accomplished is by establishing two CloudFront distributions, both pointed to the same staging bucket. The staging distribution has the protection enabled (for testing purposes), while the other does not, and that is intentional as to provide local environment and PRs access to the files and images.

Important Note

After the successful deployment to lambda, be sure to publish the changes manually from the AWS console. All there is to do is to find the lambda function to update, then click Actions -> Deploy to Lambda-at-Edge, then click "Use existing CloudFront trigger on this function" and click "Deploy".