Continuous Integration and Deployment of Auth0 Rules using Github Actions
Tags: actions, auth0, javascript, cicd
Published: 2020-08-24 11:01:08




Auth0 Rules are the mechanism that Auth0 provides to extend and customise the authentication pipeline. They run after the user has proven their identity by providing their password or entering a code/link sent to their email address or phone number. They are a really powerful tool which enable you the modify the payload of the JWT, get users to complete an MFA challenge or redirect out to interstitial pages. The default way to work with Rules code is via the AUth0 console. I'm not a big fan of editing code in consoles. It's too hard to test and there is no clear way to track changes to the code. Either what is running doesn't match what is in source control or there is nothing in source control.
In this post I'm going to go through setting up Continuous Integration and Continuous Delivery for Auth0 Rules using the Auth0 Deploy CLI and Github Actions. Code for everything in this post (including this post) can be found in [this Github repository](https://github.com/KleeUT/Auth0-rules-cicd-example-2020).

# Setup Auth0

The first thing to do is to configure Auth0 to accept code pushed to it.

Auth0 has an extension that will set this up, that's the easiest way to get the Auth0 part of the CI/CD working.
To install this

- Navigate to the extensions window
- Click on the deploy cli extension
- Agree to install it and allow it access to the required scopes.

This will create a new Auth0 Application called `auth0-deploy-cli-extension` that will have access to modify the whole tenant using the `Auth0 Management API` that is set up by default on the tenant.

# The Rule code

Each rule consists of exactly one function which is run within the Auth0 web task environment (NodeJS 12). The out of the ordinary thing is that it has to be just a single function. When a sample rule is created in the console it looks like this:

```javascript
function (user, context, callback) {
  // Ok there is more code here in the sample but you get the idea.
}
```

Even outside of a writing in a console this is still hard to write unit tests for. What I want to do is write unit tests that can exercise the code paths and make me more comfortable about continuously delivering to production.

To make this work with a test framework like Jest the function needs to be exported. The Web Task environment is rather specific about how this works. It does not work with es modules, nor does it expose the global `module` property. If you try and run `module.exports = rule` or `export rule` in the Auth0 environment it will throw an error and users wont be able to log in.

The work around is to wrap the code in an anonymous immediately executed function that returns the rule function if `module` doesn't exist and exports the function if it does. This way when running within the Jest test runner module exists and the code is exported but in the Auth0 environment just the rule is returned and the code can function.

It looks a bit like this:

```javascript
(() => {
  function rule(user, context, callback) {
    // TODO: implement your rule
  }
  if (module) {
    module.exports = rule;
  } else {
    return rule;
  }
})() // prettier-ignore
```

In this block it's worth noting that the last line has no semicolon. Having a semicolon here causes the Auth0 rule to throw an error. Thats why `// prettier-ignore` is there, Prettier keeps adding the semi colon every time I save.

With this in place the code can be imported in to a Jest test so that I can be more confident that the code running as part of the authentication flow actually works.

# Using the Auth0 Deploy CLI

Auth0 deploy CLI is a tool that will interact with the Auth0 Management API on your behalf. The Auth0 Deploy CLI is an NPM package and can be run through installing it locally, globally, or by using npx. I prefer to avoid running anything globally if I can avoid it. I've installed the Deploy CLI into the project and am running it from npm scripts.

## Setup the config

The config can be put together as a `json` file. The minimum needed is the `AUTH0_DOMAIN`, `AUTH0_CLIENT_SECRET`, and `AUTH0_CLIENT_ID`. I've added the `AUTH0_ALLOW_DELETE` property and set it to true so that it will remove any rules stored in Auth0 that aren't present in the code.

In my file I've used this `##variable##` notation that allows me to pass in the values for the parameters as environment variables. This makes it easier to integrate with Github Actions and helps me to avoid accidentally committing secrets. You can find more info on replacement mappings [in the Auth0 documentation](https://auth0.com/docs/extensions/deploy-cli-tool/environment-variables-and-keyword-mappings).

```json
{
  "AUTH0_DOMAIN": "##AUTH0_DOMAIN##",
  "AUTH0_CLIENT_SECRET": "##AUTH0_CLIENT_SECRET##",
  "AUTH0_CLIENT_ID": "##AUTH0_CLIENT_ID##",
  "AUTH0_ALLOW_DELETE": true
}
```

## The deploying to Auth0

There is more than just code that is required to configure the rules. The following YAML file configures the `rules/sampleRule.js` to be run as the first rule after a user has successfully logged in and configures a secret that will be passed through as an environment variable. This YAML file can include as much or as little of the tenants configuration as needed. In this case I'm keeping this deployment to only updating the rules as they have their own change cycle that is separate to the rest of the tenants configuration.

```yaml
rules:
  - name: sampleRule
    script: ./rules/sampleRule.js
    stage: login_success
    enabled: true
    order: 1
rulesConfigs:
  - key: "ItsASecret"
    value: "##SECRET_IN_ENV_VARIABLES##"
```

Import the rules into the Tenant

## Test the deploy

The config that I have set up above uses the `##` notation to inject environment variables into the configuration so to run that command some values need to be Auth0 console. Grab the config values for the `auth0-deploy-cli` application that the Auth0 extension created. And set the as environment variables named `AUTH0_DOMAIN`, `AUTH0_CLIENT_SECRET`, and `AUTH0_CLIENT_ID`.
![The image of the Application]()

Add the config into the environment variables and run the import statement e.g. `a0deploy import -c ./config.json -i ./src/tenant.yaml`.

I tested that this was working by reviewing the code in the Auth0 console to see that it's the same code that was deployed.

With this complete I have the ability to deploy code to the rules without having to copy it into the console. It's a good step forward. The next thing to do is get this happening automatically when the code is pushed into version control.

## Run in Github Actions

To do automated Continuous Integration and Continuous Deployment I used Github Actions. I've split the action into two jobs. One that runs tests that will run on every push and one that actually deploys the code to Auth0. This second one runs only when code is committed to the `main` branch, allowing me to do development on feature branches and only deploy to the live environment when code is complete.

The first job doesn't have much relevance to the Auth0 deploy so I wont got through the code. If you're interested it can be found [here](https://github.com/KleeUT/Auth0-rules-cicd-example-2020/blob/main/.github/workflows/deployAuth0Rules.yml#L5-L16)..

The the second job is more relevant. It starts out by setting up that it's going to run on Ubuntu but only after the `build` job has finished and the only on the `main` branch.

```yaml
deploy:
  runs-on: ubuntu-latest
  if: github.ref == 'refs/heads/main'
  needs:
    - build
```

The steps it's going to take begin by checking out the code and setting NodeJS 12 and installing the projects dependencies. In this case those dependencies include the Auth0 Deploy CLI.

```yaml
steps:
  - uses: actions/checkout@v2
  - uses: actions/setup-node@v2-beta
    with:
      node-version: "12"
  - name: NPM Install
    run: npm install
```

The next step does the actual deploying to Auth0. In this step it sets the environment variables that we need from secrets that have been uploaded into the Github console. Once those are set it runs `npm run deploy` which is an NPM script that runs `a0deploy import -c ./config.json -i ./src/tenant.yaml`.

```yaml
- name: Push to Auth0
  env:
    AUTH0_DOMAIN: ${{secrets.AUTH0_DOMAIN}}
    AUTH0_CLIENT_SECRET: ${{secrets.AUTH0_CLIENT_SECRET}}
    AUTH0_CLIENT_ID: ${{secrets.AUTH0_CLIENT_ID}}
  run: npm run deploy
```

# Finally

With that it's done. I can automatically deploy one or more Rules to Auth0 after running unit tests on them. This approach makes me much more confident in the code that I deploy and is vital when working in teams of any size.

> As a final note the Auth0 Deploy CLI can be used to deploy pretty much all of your Auth0 configuration. If you are using Auth0 for production workloads I'd strongly suggest using a configuration as code approach to keep track of changes to your environment and allow testing changes in staging environments.  
> Different sections of the config (rules, custom login pages, applications, hooks, etc) can be stored in different repositories to reflect it's rate of and reasons to change.

---
Cover photo by [hannah grace](https://unsplash.com/photos/j9JoYpaJH3A) from [unsplash](https://unsplash.com/)