Gatsby + Cognito Authentication
I create videos that I don't want to upload to Youtube or Vimeo. There's nothing sketchy about my videos; I just don't want to share my family videos with the world, nor do I need Google's machine learning dissecting my kids into advertising data points. So, I want to self host.
Also, I want to easily share these videos with my extended family. And that needs to be easy for them. Not everyone in my family gets excited by technical challenges. So, I need some simple authentication layer. Finally, I really want my video hosting site to render differently for authenticated and unauthenticated users. This is because I want Open Graph meta headers to give information about a page on the site, even if the page does not render restricted content to unauthenticated users. In other words, I want links to my content that are posted in social media and messaging apps to be descriptive and interesting. Like so:
Finally, I wanted to ditch the traditional server approach that I have used in the past and try to leverage AWS services to build my video hosting site serverless.
- Use Gatsby to generate static pages for the site. I like React. I sorta like GraphQl. Gatsby seemed like a useful thing to learn.
- Use AWS Cognito to manage users and to allow users to login with Facebook.
- Somehow manage a list of allowed users. Anyone could attempt to login with Facebook, but only those users on the list would actually succeed.
- Use client-side security to show unauthenticated users a restricted version of the site. This is obviously not secure, so...
This proved to be possible with AWS and also quite a bit more complicated than I imagined. As I worked through the problem I discovered that AWS is both over-documented and under-documented. AWS services are always completely documented with respect to available API endpoints, parameters, allowed values, etc. But it is often the purpose of a service, or how it is intended to be incorporated into larger projects that is missing. I think AWS documentation is teleologically under described. ;)
But I eventually figured it out. I figured out the difference between AWS Cognito User Pools and Identity Pools. I figured out why one should secure content with signed cookies rather than with IAM permissions. I figured out how to use Cognito's various tokens. I figured out the separation of concerns between API Gateway, Lambda, Cognito, and CloudFront. And I figured out how to manage it all with CloudFormation - a good step for learning how to operate with infrastructure as code. You can see the whole project in GitHub.
I configured my Cognito User Pool to allow sign ups and logins from a 3rd party identity provider: Facebook. Additionally, because you can configure your user pool to connect to a Lambda function on certain triggers, I was able to use a Lambda function to ensure that the user is on my allow list.
My Gatsby content is stored in S3 and distributed through a CloudFront distribution. This distribution is public. I created a second CloudFront distribution to serve the video content. This distribution requires that requests include signed cookies. The user obtains those signed cookies by making a request to an API Gateway endpoint. That request must include an Authorization header containing the access token from Cognito. The API Gateway endpoint integrates with a Lambda function that creates the signed cookies.
And the whole thing is described in a CloudFormation template. The template includes 31 AWS resources! Many of these are access-related. Most resources need to be explicitly given access to the other resources with which they communicate. Access is granted by creating more resources.
I also used a handful of scripts to orchestrate creating the CloudFormation stack from the template and tearing it down. One particularly important function of the setup script is creating a key pair with OpenSSL, passing the private key to the signed cookie lambda function and the public key to the video CloudFront distribution.
Here are a couple diagrams:
UML Sequence Diagram
This was definitely not the simplest way to rig up a private video hosting site. But it worked and I learned a lot about Gatsby, AWS, and infrastructure as code.