Using the AWS SDK in the Browser

AppPack was designed with security front-and-center. One of the tenets we followed was that users should never have to provide us their AWS access keys. Almost everything that happens in the AppPack CLI and web dashboard involves direct communication between the user and their AWS account without proxying any information through AppPack itself.

Calling the AWS API from the command line is not all that novel, but doing it in the browser is interesting enough for a blog post 😄

The AppPack Dashboard

Our dashboard at https://dashboard.apppack.io is a static site served from an S3 bucket behind Cloudfront.

screenshot of AppPack's web dashboard

It uses the AWS SDK for JavaScript to allow the user to pull data directly from their AWS account via the browser.

Inspecting the Network

We can verify this is happening by inspecting the network calls that are happening via the browser's developer tools. Here's an example of what you might see when loading the AppPack Dashboard:

screenshot of web browser development tools showing network connections

If we lump all the AWS subdomains together, content is loaded from four hosts.

  • dashboard.appppack.io This is the static content for the site that is loaded from S3
  • auth.apppack.io This endpoint handles authentication (via Auth0). It returns a JWT that we will pass to AWS and exchange for temporary access keys (more on that later).
  • api.apppack.io This endpoint is hosted by AppPack and returns a list of applications and AWS roles associated with the user (identified via JWT). It's important to note that no authorization or access keys are handled at this step. That's all between the user and AWS.
  • *.amazonaws.com From here out, all the network calls happen directly between the user's browser and their AWS account.

Getting from JWT to AWS

JWT's get a lot of flack and rightfully so. They are often misused and complex enough that they are easy to parse incorrectly. For this scenario, however, they are a perfect fit. The user needs to authenticate and then prove their identity to a third-party (AWS). To minimize the chances of AppPack messing up token handling, we let Auth0 handle token generation and have AWS do the parsing and verification.

For api.apppack.io (responsible for listing the apps and roles for the user), we use a JWT authorizer attached to an API Gateway.

Once the user chooses an application to view, we can use the AWS API to trade the user's JWT for credentials scoped to that app's management role. AWS provides the AssumeRoleWithWebIdentityCommand API for this. That code looks something like this:

import { STSClient, AssumeRoleWithWebIdentityCommand } from "@aws-sdk/client-sts";
const client = new STSClient({ region: region });
const command = new AssumeRoleWithWebIdentityCommand({
RoleArn: roleArn,
RoleSessionName: arbitraryName,
WebIdentityToken: jwt,
});
const response = await client.send(command);
// this object contains your access keys
console.log(response.Credentials);

Authorization

If you're still following what's going on, you'll realize there is something missing. The user has been authenticated (authn), but how is authorization (authz) being handled? In other words, how do we know the user with this JWT is allowed to act on behalf of the role provided.

AWS handles this part for us too! During AppPack setup, an OpenID Connect (OIDC) identity provider (IdP) is created in AWS for AppPack. This allows us to create a trust policy on individual AWS IAM roles which gives access to specific users that have been authenticated via AppPack.

As I mentioned in the intro, AppPack was designed with security in mind. Keeping authz in the end-user's AWS account makes it fully auditable (via the AWS CLI or web console) and removes AppPack as a middle-man in that transaction. If AppPack were to have a horrible bug that started sending the wrong app role names to a user, they wouldn't be able to do anything with them because authz (via AWS IAM) would block that user from assuming the role.

Want a web dashboard without the hassle?

AppPack provides an easy-to-use dashboard for all your apps without any custom code

Rodney Dangerfield looking at confusing equations

Building this was a good example of the double-edged sword of AWS. It favors flexibility and composability at the expense of simplicity. I was shocked when I first learned this workflow was even possible 🤯. But with all that flexibility and configurability comes a lot of complexity.

From the outside, it's easy to feel like you're looking at a big box of LEGO and wondering how the heck you're going to build a replica Millenium Falcon from it.

lego millenium falcon

via funkblast1

With AppPack, we're trying to sort through that box for you, pre-assemble some of the complex pieces and provide building blocks that any developer can use without years of pain experience.