Deploying changes to your Auth0 accounts with GitHub Actions

SD

Sandrino Di Mattia / November 12, 2020

7 min read––– views

Introduction#

As you build applications which use Auth0 it's a best practice to create a dedicated account per environment. This allows you to isolate your production userbase from your other environments and you'll also be able to configure different administrators per environment (eg: engineers might not have access to the production environment).

Auth0 environments

But managing multiple environments does come at a cost and you'll want to avoid manual changes to keep environments up-to-date. In this article we'll explore how the Auth0 Deploy CLI can be used to export the configuration of your environment and then import this same configuration on your other environments. The configuration will be managed in GitHub and will be deployed to the environments using GitHub Actions.

This is how the final setup will look like once we're done:

Final setup

Configuring your Auth0 accounts#

In this scenario we'll have 3 different Auth0 accounts:

  • zerohr-dev.auth0.com: The account used by developers. This is where manual changes are happening.
  • zerohr-staging.auth0.com: The account used by the Product Managers and QA team.
  • zerohr-prod.auth0.com: The account used by end users/customers.

For each of these accounts we'll need to create a Machine to Machine client which represents the Auth0 Deploy CLI. It will need the client_id and client_secret to get access to the Management API.

Create a machine to machine client for the Auth0 Deploy CLI

When creating the client we'll also need to enable it for the Management API and enable all of the scopes:

Enable the Auth0 Deploy CLI for the Management API

And that's all there is to it. As a next step we can start by creating whatever resource that is needed in the development account. In this example I've created an API, some a clients, a rule ...

Initial Export#

Once the development account is configured we can use the Auth0 Deploy CLI to perform an initial export. Let's start by creating a config file which contains the client_id and client_secret for the M2M client in the development account. We can also use the EXCLUDED_PROPS setting to avoid exporting any secrets:

./config.json
{
  "AUTH0_DOMAIN": "zerohr-dev.us.auth0.com",
  "AUTH0_CLIENT_ID": "qOY47ME3LaPWkYOwQj8o62lVUNiUKw01",
  "AUTH0_CLIENT_SECRET": "...",
  "EXCLUDED_PROPS": {
    "clients": ["client_secret"],
    "connections": ["options.client_secret"]
  }
}

And now to run the export:

npm i -g auth0-deploy-cli
a0deploy export -c config.json --strip --format yaml --output_folder .

This command will go over all of the resources in our Auth0 account and export them to a yaml file (+ some files for any rule/hook/... in the account).

Initial Export

The following is excerpt of what such an export looks like:

rules:
  - name: custom-claims
    script: ./rules/custom-claims.js
    stage: login_success
    enabled: true
    order: 1
resourceServers:
  - name: ZeroHR API
    identifier: 'https://api.dev.zerohr.app/'
    allow_offline_access: false
    signing_alg: RS256
    skip_consent_for_verifiable_first_party_clients: true
    token_lifetime: 86400
    token_lifetime_for_web: 7200

Moving to GitHub#

A sample repository with the workflows and a sample export can be found here.

We now have everything we need to create a GitHub repository containing the following:

  • The configuration for dev/staging/production
  • The yaml file and the other files (*.js files for all of your rules and hooks)
  • The GitHub Action workflows

Let's start by creating a local directory for the repository and move all of the exported files in it:

mkdir github-actions-auth0-deploy
git init

We'll also create a dedicated config folder containing all of the configuration files:

./config/dev.json
./config/prod.json
./config/staging.json

Each of these configuration files will contain the domain and the client_id. Note that the client_secret should not be added here. We'll manage that using GitHub Secrets, not directly in source control.

{
  "AUTH0_DOMAIN": "zerohr-staging.us.auth0.com",
  "AUTH0_CLIENT_ID": "Y13eh0EhojdF1Bti06FLJJ1jZx0Pb0hJ",
  "AUTH0_ALLOW_DELETE": true,
  "AUTH0_KEYWORD_REPLACE_MAPPINGS": {
    "APP_CALLBACKS": ["https://www.staging.zerohr.app", "https://staging.zerohr.app"],
    "API_IDENTIFIER": "https://api.staging.zerohr.app/"
  },
  "EXCLUDED_PROPS": {
    "clients": ["client_secret"],
    "connections": ["options.client_secret"]
  }
}

You'll also notice the AUTH0_KEYWORD_REPLACE_MAPPINGS which allows us to have environment specific settings which will be injected in the yaml file. A use case here is that the applications in your environments have different URLs, and these URLs can be managed here and then injected in the template as we deploy to a specific environment.

For this to work we do need to modify the yaml file and replace the actual values of the URLs with variables. The ##var## format will be replaced with a string literal, the @@var@@ format with a JSON representation of the variable (in this case an actual array).

./tenant.yaml
resourceServers:
  - name: ZeroHR API
identifier: '##API_IDENTIFIER##'
allow_offline_access: false signing_alg: RS256 skip_consent_for_verifiable_first_party_clients: true token_lifetime: 86400 token_lifetime_for_web: 7200 clients: - name: ZeroHR App app_type: spa
callbacks: @@APP_CALLBACKS@@
client_aliases: [] cross_origin_auth: false custom_login_page_on: true grant_types: - authorization_code - refresh_token is_first_party: true

GitHub Actions & Continuous Delivery#

As a final step we'll create 2 workflows:

  • .github/workflows/auth0-staging.yml which will deploy to staging when we push a change to the staging branch
  • .github/workflows/auth0-prod.yml which will deploy to production when we merge a change to the main branch

GitHub recently decided to use main as the name of the default branch instead of master. If you still have a repository which uses master you'll need to change the workflow below.

The workflows are very simply, they just install and run the Auth0 Deploy CLI:

.github/workflows/auth0-prod.yml
name: Deploy to Auth0 production account
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Download files from the current repository
        uses: actions/checkout@v2
      - name: Install Node.js
        uses: actions/setup-node@v1
        with:
          node-version: '14.x'
      - name: Install the auth0-deploy-cli
        run: npm install
      - name: Import changes to the Auth0 production account
        env:
          AUTH0_CLIENT_SECRET: ${{ secrets.PROD_AUTH0_CLIENT_SECRET }}
        run: npm run import:prod

To make local testing easier we can move all of the commands to a package.json file:

package.json
"scripts": {
  "export:dev": "a0deploy export --strip --format yaml --output_folder . --config_file ./config/dev.json --secret $DEV_AUTH0_CLIENT_SECRET",
  "import:staging": "a0deploy import --input_file tenant.yaml --config_file ./config/staging.json",
  "import:prod": "a0deploy import --input_file tenant.yaml --config_file ./config/prod.json"
}

This is why you'll see that the final line of the workflow uses npm run import:prod.

The Auth0 Deploy CLI will need to use the client_id and client_secret for the given environment to access the Management API, which is used to create/update/delete resources. The client_secret is a secret and should not be stored in source control. So for each environment we'll create a Repository Secret in GitHub:

Action Secrets

When the import step runs as part of the job it will load the secret and expose it as an environment variable to the Auth0 Deploy CLI:

- name: Import changes to the Auth0 production account
  env:
AUTH0_CLIENT_SECRET: ${{ secrets.PROD_AUTH0_CLIENT_SECRET }}
run: npm run import:prod

Deployment#

The jobs will run whenever there is a push to the main or staging branch. As a simple test we can just modify a setting one one of our APIs (Resource Server) and push the change to the staging branch.

git checkout -b staging
git add tenant.yaml
git commit -m "Changed token lifetime for the main API"
git push -u origin staging

Immediately after pushing the change we can see that a new workflow is started:

New Workflow

By opening the workflow you to inspect every step of the job. If the job fails you'll find the reason in the Import changes to the Auth0 account step:

New Workflow

Next Steps#

If any other changes need to happen to your Auth0 environments we can make the manually in the development account and then perform a new export:

DEV_AUTH0_CLIENT_SECRET=... npm run export:dev

Running the export will then make changes to your tenant.yaml file which can be reviewed in your IDE. This will allow us to pick the changes we need to commit but it's also an opportunity to replace environment specific values (like https://api.zerohr.app) with a variable defined in AUTH0_KEYWORD_REPLACE_MAPPINGS.

Export

✅ Success!#

And we're done. We've put a process in place where changes can be automated across different Auth0 accounts and which leverages all of the benefits that GitHub offers: an audit trail, PR reviews, protected branches, access control, ...

This solution can also be applied to GitLab CI/CD, Bitbucket Pipelines, Azure DevOps, Jenkins, ...

Discuss on Twitter