Testing your authentication flows locally during development is a surprisingly tricky business, often involving more boilerplate than actual application code.

Let’s get a local Auth0 setup running so you can test your app without hitting the actual Auth0 service. We’ll use Docker for this.

First, you need a docker-compose.yml file. This will define our Auth0-like service.

version: '3.7'

services:
  auth0-emulator:
    image: auth0/auth0-emulator:latest
    ports:
      - "9080:80" # Map local port 9080 to the container's port 80
    environment:
      # These are the critical settings for your local Auth0
      - AUTH0_DOMAIN=auth0-emulator:80 # Internal domain for the emulator
      - AUTH0_CLIENT_ID=local-client-id # Your app's client ID
      - AUTH0_CLIENT_SECRET=local-client-secret # Your app's client secret
      - AUTH0_ISSUER=http://auth0-emulator:80 # The issuer URL
      - AUTH0_AUDIENCE=http://localhost:3000 # Your app's API audience (if applicable)
    volumes:
      - ./auth0-emulator-data:/data # Persist emulator data
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

You’ll also need a directory for the emulator’s data: mkdir auth0-emulator-data.

Now, let’s start the emulator:

docker-compose up -d auth0-emulator

This command pulls the auth0/auth0-emulator:latest image and starts it in detached mode. It maps local port 9080 to the container’s port 80. The environment variables are crucial:

  • AUTH0_DOMAIN: This is the domain your application will use to communicate with the emulator. We’re using auth0-emulator:80 because that’s how services on the same Docker network resolve each other.
  • AUTH0_CLIENT_ID and AUTH0_CLIENT_SECRET: These are dummy values your application will use. You’ll configure these in your app’s environment variables.
  • AUTH0_ISSUER: This is the URL Auth0 uses to identify itself.
  • AUTH0_AUDIENCE: If your application acts as an API, this is the identifier for that API.

Your application’s configuration will need to point to http://localhost:9080 for the Auth0 domain.

Here’s a snippet of what your application’s authentication configuration might look like (using Node.js and auth0-react as an example):

// authConfig.js
import { Auth0Provider } from '@auth0/auth0-react';

const auth0Domain = process.env.REACT_APP_AUTH0_DOMAIN || 'localhost:9080'; // Point to your local emulator
const auth0ClientId = process.env.REACT_APP_AUTH0_CLIENT_ID || 'local-client-id'; // Match emulator's client ID
const auth0Audience = process.env.REACT_APP_AUTH0_AUDIENCE || 'http://localhost:3000'; // Match emulator's audience

const Auth0ProviderWithHistory = ({ children }) => {
  return (
    <Auth0Provider
      domain={auth0Domain}
      clientId={auth0ClientId}
      audience={auth0Audience}
      redirectUri={window.location.origin}
      scope="read:current_user openid profile email" // Example scopes
    >
      {children}
    </Auth0Provider>
  );
};

export default Auth0ProviderWithHistory;

And in your index.js or App.js:

// index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import Auth0ProviderWithHistory from './authConfig';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Auth0ProviderWithHistory>
      <App />
    </Auth0ProviderWithHistory>
  </React.StrictMode>
);

When you run your application, it will attempt to authenticate against http://localhost:9080. The auth0-emulator service will intercept these requests. You can then interact with your local Auth0 by visiting http://localhost:9080 in your browser. This is where you’ll create users, configure applications, and manage rules.

The auth0-emulator is essentially a simplified, self-contained version of Auth0. It simulates token issuance, user authentication, and basic user management. It’s not a full-fledged Auth0 replica; it’s designed to provide the core authentication and authorization functionalities needed for local development and testing. The emulator uses a local SQLite database to store user information and configurations, which is persisted via the auth0-emulator-data volume. This means your local user data and settings will survive container restarts.

A common point of confusion is the AUTH0_DOMAIN setting in the docker-compose.yml. When your application runs on your local machine and tries to reach localhost:9080, it’s using your host machine’s network. However, the auth0-emulator container, when it needs to refer to itself internally (e.g., when issuing tokens), uses its internal Docker network name, which is auth0-emulator. The auth0-emulator:80 syntax tells the emulator to use its own service name within the Docker network. When your app makes the external request to localhost:9080, Docker’s port mapping handles the translation.

When you’re done, stop the emulator with docker-compose down.

The next step is to explore how to simulate different user scenarios, like expired tokens or invalid credentials, using the emulator’s administrative interface.

Want structured learning?

Take the full Auth0 course →