You can learn a lot by kicking the tires on software. I work on identity systems, so I wanted to take AWS Cognito out for a spin. In this post, I’ll describe my experiment with Cognito to use G Suite SAML for ALB authentication, and how an encoding bug turned my joyride into a flat tire.
Cognito is two identity products: user pools and identity pools. User pools are a white-label user management system for people who don’t want to build one, like iOS developer implementing sign-in with Apple. You can accept identity providers like Apple using OpenID Connect (OIDC) or SAML and return a OIDC JWT to your application.
Identity pools are a “travel adapter” for AWS, allowing you to convert OIDC tokens, SAML assertions, and X.509 certificates on light bulbs into temporary IAM credentials.
While playing around with user pools, I ran across an open-source package called gsuite-saml-cognito on Twitter. It claims to “provision AWS Cognito resources for connecting to a G Suite instance SAML authentication”, which means it transmutes G Suite SAML assertions into Cognito OIDC JWT.
I thought to myself — “You know what would be cool? You could integrate this project with ALB authentication and have G Suite authenticated ALB-fronted services with minimal code.”
gsuite-saml-cognito is a terraform module, so I cloned the project and copied the /terraform-modules/gsuite-saml-cognito directory into a new project directory. I created a main.tf terraform file and G Suite app based on the instructions in the README. Finally, I ran terraform apply, and waited about 15 minutes for the Cognito resources to be created.
I wrote some terraform to create the ALB that uses this Cognito identity:
This adds a listener rule to the ALB that requires Cognito authentication. When I accessed the /auth subdirectory, I was redirected by the ALB to login with G Suite. After I logged in, the ALB included a JWT token in the x-amzn-oidc-data header I could use to verify the user’s identity in my application.
This is a pretty neat trick — typical custom SAML integrations require much more code! I was excited to build on it; I could pass these OIDC tokens along in subsequent request headers to enable authentication in downstream services. I could build access control in my app based on G Suite attributes by defining custom SAML attributes and connecting them with OAuth scopes.
But first, how do I use these tokens? From the AWS docs:
We recommend that you verify the signature before doing any authorization based on the claims. To get the public key, get the key ID from the JWT header and use it to look up the public key from a regional endpoint.
Unfortunately, when I attempted to run this code, I kept on getting a JsonWebTokenError with a message of “invalid token”. After a few hours of debugging, I was disappointed to find that the JWT in the x-amzn-oidc-data header was “sort of but not quite” Base64 URL (as opposed to Base64) encoded.
According to this Github issue comment, AWS moved to Base64 URL encoding, but left the Base64 padding (i.e. = characters at the end) which breaks the jws package’s validation that the jsonwebtoken package relies on. AWS was “deploying a fix” in a couple of weeks nearly a year ago, so it’s unclear if there’s a good way forward other than using custom JWT verification which is hard to get excited about introducing and maintaining in a code base.
So, while Cognito ALB authentication seems totally cool, to get things to work in a node environment with the default packages for JWT validation, we’re in a holding pattern until AWS fixes their JWTs to follow the spec.
Are you using Cognito for ALB authentication and see something I missed? Hit me up on Twitter at @alsmola.