Skip to content
This repository has been archived by the owner on Dec 9, 2021. It is now read-only.

Use Webpack plugin #9

Closed
dferber90 opened this issue Dec 1, 2020 · 0 comments
Closed

Use Webpack plugin #9

dferber90 opened this issue Dec 1, 2020 · 0 comments

Comments

@dferber90
Copy link
Owner

dferber90 commented Dec 1, 2020

Instead of having a script to prepare the plugins, we could have a Next.js Webpack plugin. This has the benefit of easier integration into Next.js.

The plugin would roughly be this

"use strict";

const write = require("write");
const fetch = require("node-fetch");
const jwkToPem = require("jwk-to-pem");

const region = process.env.AUTH_AWS_COGNITO_REGION;
const userPoolId = process.env.AUTH_AWS_COGNITO_USER_POOL_ID;

const red = (str) => `\x1b[31m${str}\x1b[0m`;

async function fetchJwks() {
  const url = `https://cognito-idp.${region}.amazonaws.com/${userPoolId}/.well-known/jwks.json`;

  const jwks = await fetch(url)
    .then((res) => res.json())
    .catch((error) => {
      console.error(
        red("AwsCognitoPemPlugin"),
        `Could not fetch jwks.json from Cognito`
      );
      console.error(`Tried "${url}", but it does not exist.`);
      console.error(``);
      console.error(
        `Maybe the provided region (${region}) or userPoolId (${userPoolId}) are incorrect?`
      );
      return process.exit(1);
    });

  if (jwks.message) {
    console.error(red("AwsCognitoPemPlugin"), jwks.message);
    return process.exit(1);
  }

  if (!Array.isArray(jwks.keys) || jwks.keys.length === 0) {
    console.error(red("AwsCognitoPemPlugin"), "No keys present in response");
    console.log(jwks);
    return process.exit(1);
  }

  return jwks;
}

// map public-keys to pems, so the client/server don't need to do it
// on every request
function mapJwksToPems(jwks) {
  return {
    ...jwks,
    keys: jwks.keys.map((key) => ({
      ...key,
      pem: jwkToPem(key),
    })),
  };
}

module.exports = class AwsCognitoPemPlugin {
  constructor(options) {
    if (!options) {
      throw new Error(
        `Please provide 'options' for the AwsCognitoPemPlugin config`
      );
    }

    // these are the webpack options provided by Next.js in
    //   webpack: (config, options) => {}
    // which the callees forward to us
    this.options = options;

    // cache to avoid fetching multiple times
    this.pems = null;
  }

  apply(compiler) {
    // only run on the server to avoid fetching the pems twice (client & server)
    if (!this.options.isServer) return;

    compiler.hooks.emit.tapPromise(
      "AwsCognitoPemPlugin",
      async (compilation) => {
        if (this.pems) return

        const jwks = await fetchJwks();
        this.pems = mapJwksToPems(jwks);
        write.sync("./pems.json", JSON.stringify(this.pems, null, 2));
      }
    );
  }
};

And it would be used like that

  webpack: (config, options) => {
    config.plugins.push(new AwsCognitoPemPlugin(options));
    return config;
  },

The plugin would fetch the JWKs from the Cognito user pool, add a pem entry to each key and write them out to pems.json.

You'd also need to provide environment variables, e.g. in .env.local like this:

AUTH_AWS_COGNITO_REGION="eu-central-1"
AUTH_AWS_COGNITO_USER_POOL_ID="eu-central-1_xxxxx"

The reason we start with AUTH_ is that Vercel and other hosting solutions reserve env vars starting with AWS_, e.g. Vercel's reserved env vars list

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant