Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 108 additions & 16 deletions packages/auth0/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Auth0 simulator

Read about this simulator on our blog: [Simplified Local Development and Testing with Auth0 Simulation](https://frontside.com/blog/2022-01-13-auth0-simulator/).
Read about this simulator on our blog: [Simplified Local Development and Testing with Auth0
Simulation](https://frontside.com/blog/2022-01-13-auth0-simulator/).

## Table of Contents

Expand All @@ -11,22 +12,33 @@ Read about this simulator on our blog: [Simplified Local Development and Testing
- [Code](#code)
- [Example](#example)
- [Configuration](#configuration)
- [CLI Flags](#cli-flags)
- [JSON Config Files](#json-config-files)
- [Environment Variables](#environment-variables)
- [Programmatic Options](#programmatic-options)
- [Options](#options)
- [Rules](#rules)
- [Endpoints](#endpoints)

Please read the [main README](../../README.md) for more background on simulacrum.

The auth0 simulator has been initially written to mimic the responses of a real auth0 server that is called from auth0 client libraries like [auth0/react](https://auth0.com/docs/quickstart/spa/react/01-login) and [auth0-spa-js](https://github.com/auth0/auth0-spa-js) that use the OpenID [authorization code flow](https://developer.okta.com/docs/concepts/oauth-openid/).
The auth0 simulator has been initially written to mimic the responses of a real auth0 server that is
called from auth0 client libraries like
[auth0/react](https://auth0.com/docs/quickstart/spa/react/01-login) and
[auth0-spa-js](https://github.com/auth0/auth0-spa-js) that use the OpenID [authorization code
flow](https://developer.okta.com/docs/concepts/oauth-openid/).

If this does not meet your needs then please create a github issue to start a conversation about adding new OpenID flows.
If this does not meet your needs then please create a github issue to start a conversation about
adding new OpenID flows.

## Quick Start

This quick start assumes you have your own app with Auth0.

> [!IMPORTANT]
> The Auth0 clients expect the server to be served as `https`, and will throw an error if it is served as `http`. Currently, we rely on a certificate available in the home directory. On first run, you will see instructions on how to set up this certificate through `mkcert`.
> [!IMPORTANT]
> The Auth0 clients expect the server to be served as `https`, and will throw an error if it is
> served as `http`. Currently, we rely on a certificate available in the home directory. On first
> run, you will see instructions on how to set up this certificate through `mkcert`.

### Using Default User

Expand All @@ -36,12 +48,13 @@ You may start a server directly from the command line.
npx @simulacrum/auth0-simulator # this will start a simulation server at http://localhost:4400
```

Given no further input, it will use the default values as below. This will point your app at the simulation instead of the Auth0 endpoint.
Given no further input, it will use the default values as below. This will point your app at the
simulation instead of the Auth0 endpoint.

```json
{
"domain": "https://localhost:4400",
"clientId": "00000000000000000000000000000000",
"clientID": "00000000000000000000000000000000",
"audience": "https://thefrontside.auth0.com/api/v1/"
}
```
Expand All @@ -63,29 +76,108 @@ By passing an `initialState`, you may control the initial users in the store.

### Example

The folks at Auth0 maintain many samples such as [github.com/auth0-samples/auth0-react-samples](https://github.com/auth0-samples/auth0-react-samples). Follow the instructions to run the sample, set the configuration in `auth_config.json` to match the defaults as noted above, and run the Auth0 simulation server with `npx auth0-simulator`.
The folks at Auth0 maintain many samples such as
[github.com/auth0-samples/auth0-react-samples](https://github.com/auth0-samples/auth0-react-samples).
Follow the instructions to run the sample, set the configuration in `auth_config.json` to match the
defaults as noted above, and run the Auth0 simulation server with `npx auth0-simulator`.

## Configuration

The Auth0 Simulator uses [cosmiconfig](https://github.com/cosmiconfig/cosmiconfig) to load the configuration options. This provides many options in where to place your configuration. Using the module name, `auth0Simulator`, you could, for example, set your configuration in a `.auth0Simulatorrc.json` file.
The Auth0 Simulator uses [configliere](https://github.com/thefrontside/configliere) to parse
configuration from CLI flags, environment variables, JSON config files, and programmatic options.

### Options
### CLI Flags

When running from the command line, you can pass configuration as flags:

```bash
npx @simulacrum/auth0-simulator --port 5000 --audience https://myapp.com/api
```

The CLI also accepts an explicit `start` command, though it remains the default:

```bash
npx @simulacrum/auth0-simulator start --port 5000
```

Run with `--help` to see all available flags:

```bash
npx @simulacrum/auth0-simulator --help
```

### JSON Config Files

The `options` field supports the [auth0 configuration fields](https://auth0.com/docs/quickstart/spa/vanillajs#configure-auth0). The option fields should match the fields in the client application that is calling the auth0 server.
You can stage configuration through a JSON file with `-c` and then override values with CLI flags:

The `scope` also accepts an array of objects containing `clientId`, `scope` and optionally `audience` to enable dynamic scopes from a single simulator. This should allow multiple clients to all use the same simulator. Additionally, setting the `clientId: "default"` will enable a default fallback scope so every client does not need to be included.
```bash
npx @simulacrum/auth0-simulator -c auth0-simulator.json --port 5000
```

Values from CLI flags still take precedence over environment variables and config file values.

### Environment Variables

Configuration can also be set via environment variables. Field names are mapped to
`UPPER_SNAKE_CASE`:

```bash
PORT=5000 AUDIENCE=https://myapp.com/api npx @simulacrum/auth0-simulator
```

### Programmatic Options

When using the simulator as a library, pass options directly:

```js
import { simulation } from "@simulacrum/auth0-simulator";

const app = simulation({
options: {
port: 5000,
audience: "https://myapp.com/api",
},
});
```

### Options

An optional [`rulesDirectory` field](#rules) can specify a directory of [auth0 rules](https://auth0.com/docs/rules) code files, more on this [below](#rules).
The `options` field supports the [auth0 configuration
fields](https://auth0.com/docs/quickstart/spa/vanillajs#configure-auth0). The option fields should
match the fields in the client application that is calling the auth0 server.

| Option | CLI Flag | Env Var | Description |
| ---------------- | ------------------- | ----------------- | -------------------------------- |
| `port` | `--port`, `-p` | `PORT` | Port to listen on |
| `domain` | `--domain` | `DOMAIN` | Server domain |
| `audience` | `--audience` | `AUDIENCE` | Auth0 audience |
| `clientID` | `--client-id` | `CLIENT_ID` | Auth0 client ID |
| `scope` | `--scope` | `SCOPE` | Auth0 scope |
| `clientSecret` | `--client-secret` | `CLIENT_SECRET` | Client secret |
| `rulesDirectory` | `--rules-directory` | `RULES_DIRECTORY` | Directory containing auth0 rules |
| `connection` | `--connection` | `CONNECTION` | Auth0 connection |
| `protocol` | `--protocol` | `PROTOCOL` | Server protocol (https/http) |

The `scope` also accepts an array of objects containing `clientID`, `scope` and optionally
`audience` to enable dynamic scopes from a single simulator (programmatic usage only). This should
allow multiple clients to all use the same simulator. Additionally, setting the `clientID:
"default"` will enable a default fallback scope so every client does not need to be included.

An optional [`rulesDirectory` field](#rules) can specify a directory of [auth0
rules](https://auth0.com/docs/rules) code files, more on this [below](#rules).

### Rules

It is possible to run [auth0 rules](https://auth0.com/docs/rules) if the compiled code files are on disk and all located in the same directory.
It is possible to run [auth0 rules](https://auth0.com/docs/rules) if the compiled code files are on
disk and all located in the same directory.

Set the `rulesDirectory` of the [options field](#options) to a path relative to your current working directory.
Set the `rulesDirectory` of the [options field](#options) to a path relative to your current working
directory.

For example, a [sample rules directory](./test/rules) is in the auth0 package for testing.

If we want to run these rules files then we would add the `rulesDirectory` field to the [options object](#options).
If we want to run these rules files then we would add the `rulesDirectory` field to the [options
object](#options).

## Endpoints

Expand Down
85 changes: 73 additions & 12 deletions packages/auth0/bin/start.cjs
Original file line number Diff line number Diff line change
@@ -1,13 +1,74 @@
#!/usr/bin/env node
const auth0APIsimulator = require("../dist/index.cjs");

const app = auth0APIsimulator.simulation();
app.listen(4400, () =>
console.log(
`Auth0 simulation server started at https://localhost:4400\n` +
`Visit the root route to view all available routes.\n\n` +
`Point your configuration at this simulation server and use the default user below.\n` +
`Email: ${auth0APIsimulator.defaultUser.email}\nPassword: ${auth0APIsimulator.defaultUser.password}\n` +
`\nPress Ctrl+C to stop the server`,
),
);
const { createContext } = require("configliere");
const { auth0Program, readJsonConfig, simulation, defaultUser } = require("../dist/index.cjs");

const args = process.argv.slice(2);
const envs = [{ name: "env", value: /** @type {Record<string, string>} */ (process.env) }];

async function main() {
let parser = auth0Program.parse({ args, envs });

if (!parser.ok) {
throw parser.error;
}

if (parser.value.help) {
console.log(auth0Program.help({ args }));
return;
}

if (parser.value.version) {
console.log(parser.value.version);
return;
}

const command = parser.value.config;

if (command.help) {
console.log(command.text);
return;
}

switch (command.name) {
case "start": {
let configPath = command.config.config;
let values = configPath ? [{ name: configPath, value: readJsonConfig(configPath) }] : [];
let configParser = command.config.next(values[0]?.value ?? {});
let input = {
args: parser.remainder.args ?? [],
envs: parser.remainder.envs ?? envs,
values,
};
let result = configParser.parse(input, createContext(input));

if (!result.ok) {
throw result.error;
}

const app = simulation({ config: result.value });

/** @param {{ server: any; port: any }} listening */
const { server, port } = await app.listen();
const info = server.address();
const host =
typeof info === "object" && info?.address && !["::", "0.0.0.0"].includes(info.address)
? info.address
: "localhost";
console.log(
`Auth0 simulation server started at https://${host}:${port}\n` +
`Visit the root route to view all available routes.\n\n` +
`Point your configuration at this simulation server and use the default user below.\n` +
`Email: ${defaultUser.email}\nPassword: ${defaultUser.password}\n` +
`\nPress Ctrl+C to stop the server`,
);
return;
}
default:
throw new TypeError(`Unknown command ${command.name}`);
}
}

main().catch((error) => {
console.error(error instanceof Error ? error.message : String(error));
process.exitCode = 1;
});
16 changes: 12 additions & 4 deletions packages/auth0/example/index.mts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,16 @@ let app = simulation({
},
},
});
app.listen(undefined, () =>

app.listen().then(({ server, port }) => {
const info = server.address();
const host =
typeof info === "object" && info?.address && !["::", "0.0.0.0"].includes(info.address)
? info.address
: "localhost";
console.log(
`auth0 simulation server started at https://localhost:4400\nusername: default@example.com\npassword: 12345\n`,
),
);
`Auth0 simulation server started at https://${host}:${port}\n` +
`username: default@example.com\n` +
`password: 12345\n`,
);
});
2 changes: 1 addition & 1 deletion packages/auth0/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@
"@simulacrum/server": "workspace:^",
"assert-ts": "^0.3.4",
"base64-url": "^2.3.3",
"configliere": "^0.4.0",
"cookie-session": "^2.1.0",
"cors": "^2.8.6",
"cosmiconfig": "^9.0.0",
"express": "^5.2.1",
"html-entities": "^2.5.2",
"jsesc": "^3.1.0",
Expand Down
Loading
Loading