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
531 changes: 531 additions & 0 deletions docs/_includes/plugin-flow.html

Large diffs are not rendered by default.

1,908 changes: 1,908 additions & 0 deletions docs/assets/js/plugin-flow.js

Large diffs are not rendered by default.

98 changes: 98 additions & 0 deletions docs/plugins/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
---
title: Architecture
parent: Plugins
nav_order: 5
layout: default
---

## What this page covers

This page explains the parts of the plugin system, what happens when you install and run a plugin, and which files the CLI writes on your machine. Use it when you want to know where a plugin's data lives or why a run stopped at a particular check.

## The diagram

In the diagram below, pick a scenario from the dropdown, then use Next, Previous or Play. Each step highlights the component that acts and explains what happens and why. The flows cover install and update with the consent screen, running a command with scope enforcement, creating, validating and linking a plugin, the release template, uninstalling, agent mode and CI.

{% include plugin-flow.html %}

## Components

| Component | What it does |
| --- | --- |
| Auth0 CLI | Registers the plugin commands, runs the checks in order, asks for consent, and starts the plugin. |
| Registry client | Updates the signed registry metadata, checks it against the root built into the CLI, checks the index against it, and keeps the verified copy. |
| Store | Downloads archives from allowed hosts, checks their hashes, unpacks them, writes the lockfile, and checks the files before each run. |
| Approvals | Shows the consent screen, records approvals per tenant, and reads CI pins. |
| Runner | Builds the plugin environment, removes CLI flags from the arguments, and starts and stops the plugin process. |
| Loopback proxy | Serves HTTPS on `127.0.0.1`, an address that only this machine can reach, enforces scopes, strips secret fields, and forwards calls with the real token. |
| Dev links | Records local builds that an author runs with `auth0 plugin dev link`. |
| Scaffold | Writes the starter projects for `auth0 plugin dev create`. |
| Analytics | Sends the plugin usage events, unless analytics are turned off. |
| Plugin | Any language, built by its author. A native executable with a `plugin.json` next to it. It calls the Management API through the proxy. |
| Management API | Receives the calls that the proxy forwards with the real access token. |

The CLI is the only part that talks to the registry, the release hosts and your tenant. A plugin's Management API calls go only through the proxy, but the CLI does not block other network traffic from the plugin. See [Security and trust model](security.md) for that limit.

## Install and update

1. You run `auth0 plugin install <name>`. Install needs no login. It needs a tenant only to record the approval, and it uses `--tenant` or your default tenant.
2. The registry client updates the signed metadata from the registry URL, which is the base URL of the metadata directory. It follows any newer roots from the root built into the CLI, checks the signatures and expiry dates of the timestamp, snapshot and targets files, and downloads `index.json`. It then checks the index against the length and hash in the targets file, its own expiry date, and the newest index it accepted before.
3. The CLI picks the newest version that is not revoked, has an archive for your operating system, CPU and C library, and accepts this CLI version.
4. The consent screen shows the permissions of that version for your tenant, from the signed index. You can answer no, and nothing is downloaded.
5. The store downloads the archive from an allowed host into `tmp/`, stopping if it grows past the size cap. Then it checks the SHA-256 hash against the index.
6. The store unpacks the archive and compares the manifest inside with the index record. Only then are the files moved into `store/`.
7. The lockfile is rewritten through a temporary file and a rename. The approval is recorded last, and only after the install succeeded.

An update follows the same steps. If the permissions hash is the same as the one your tenant approved, the update asks nothing. If it changed, the screen shows only the difference, and the old version stays active until you approve. The previous version stays on disk, so a rollback can reuse it.

## Run

1. You run `auth0 <plugin> <command>`. The CLI takes out the flags it owns, such as `--tenant`, and passes the rest to the plugin untouched.
2. The CLI checks that plugins are not turned off, and that a `--tenant` you passed matches `AUTH0_DOMAIN` when the environment fixes the tenant.
3. The CLI checks the installed files against the lockfile. This happens before any login.
4. The CLI reads the index, checks that the version is not revoked, and checks that the command can run the way it was called.
5. The CLI checks that the tenant approved these permissions. A missing approval stops the run here, before any token is requested.
6. The CLI opens the session for the approved tenant and checks the files a second time.
7. The CLI starts the loopback proxy with the scopes of this command, and a new certificate and secret for the run.
8. The CLI starts the plugin with a short environment that holds the proxy address and the secret. The plugin's output goes straight to your terminal.
9. Each call from the plugin goes to the proxy, which checks the path, the method and the scope, forwards the call with the real token, and removes secret fields from the response.
10. When the plugin exits, the CLI stops the proxy and prints one line about any calls that were denied. That line is not printed in agent mode. The CLI then exits with the result of the run.

For the rules behind each check, see [Security and trust model](security.md).

## Files on disk

Plugin state lives in a `plugins` folder next to `config.json`, in your CLI config directory. The CLI stores no tokens or client secrets there. Those stay in the operating system keyring.

```plaintext
~/.config/auth0/
├── config.json
└── plugins/
├── plugins.lock.json installed plugins, hashes, active and pinned versions (0600)
├── approvals.json approvals per tenant, keyed by permissions hash (0600)
├── dev-links.json local builds from plugin dev link
├── hint.json when the update hint was last shown
├── registries/auth0/
│ ├── metadata/ latest root, timestamp, snapshot and targets files
│ ├── roots/ roots after the first one, as 2.root.json, 3.root.json
│ ├── index.json last verified index
│ ├── state.json newest index version accepted, last refresh
│ └── .lock held while the registry is refreshed
├── store/auth0/checkmate/
│ └── 1.0.0/
│ ├── checkmate the executable (0755), checkmate.exe on Windows
│ └── plugin.json the manifest from the archive (0600)
└── tmp/ downloads in progress
```

The path above is the one the CLI uses for `config.json`, which is `~/.config/auth0` on macOS and Linux. The lockfile and the approvals file are written to a temporary file and then renamed into place, so an interrupted write does not leave a half-written file behind. The folders are created with mode `0700`.

The first root is built into the CLI, so it is not stored. The `roots` folder holds the roots that the registry published after it, and it only has files once the registry has rotated a key. A refresh writes to these files only after everything verified, so a failed refresh leaves them as they were. When the CLI uses the saved index, it checks the roots, the targets file and the index again without the network.

Deleting the `plugins` folder removes every installed plugin and every approval. It does not remove your login, because the login stays in the keyring and in `config.json`.

## Where to go next

- [Security and trust model](security.md) explains the checks, the limits and how to report a problem.
- [Building a plugin](building.md) walks through `plugin dev create`, `dev link` and `dev validate`.
- [Reference](reference.md) lists the environment variables, reason codes and paths.
167 changes: 167 additions & 0 deletions docs/plugins/automation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
---
layout: default
title: Agent mode and automation
parent: Plugins
nav_order: 7
---

This page explains how plugins behave when no person is at the keyboard, in agent mode, with `--no-input`, and in CI. You will see what an agent or a script can do, what it is refused, and how to approve a plugin in CI without a prompt.

## The rule

Only a person approves a plugin. Everything else can be automated. An agent or a script can search, inspect, install, update and run plugins, but it cannot record an approval. Two ways to skip the prompt in CI: approve in a terminal first, or pin the hash in an environment variable. The pin is described below.

## Agent mode

Agent mode turns on JSON output, turns off prompts and turns off colors. It is on when you pass `--agent-mode`, when `AUTH0_AGENT_MODE=true` is set, or when the CLI detects an AI agent. Pass `--agent-mode=false` or set `AUTH0_AGENT_MODE=false` to turn it off.

Errors in agent mode, and with `--json`, are one JSON object on stderr. This is the error when `plugin approve` runs without a tenant to approve for:

```json
{"error":{"code":"validation","reason":"plugin_no_tenant","message":"there is no tenant to approve the plugin for. Pass --tenant <tenant>, or log in with: auth0 login"}}
```

`code` is the class of the failure, and `reason` is the specific cause. Scripts should branch on `reason`, because the message can change between releases. `details` is added when there is more to say. The reason codes for plugins are listed in [Troubleshooting](troubleshooting.md) and [Reference](reference.md).

### Install and update

In agent mode, with `--no-input`, and when there is no terminal, `auth0 plugin install` installs the plugin without an approval and exits with code 0. The output says what is still missing:

```bash
auth0 plugin install checkmate --tenant acme.us.auth0.com --agent-mode
```

```json
{
"id": "auth0/checkmate",
"version": "1.0.0",
"previousVersion": "",
"action": "installed",
"pinned": false,
"approvalRequired": true,
"approveCommand": "auth0 plugin approve auth0/checkmate --tenant acme.us.auth0.com"
}
```

An agent reads `approvalRequired` and passes `approveCommand` to a person. No flag approves a plugin during install. Running the plugin stays blocked until a person approves it or a pin covers it.

`auth0 plugin update` is stricter, because new permissions need an approval before they apply. If the new version asks for different permissions and nobody can answer, the update fails. The installed version stays active, and the message says why:

```text
checkmate 1.1.0 asks for permissions that are not approved, and an agent cannot approve plugins. checkmate was not updated, and 1.0.0 stays active. Run this in a terminal to review them: auth0 plugin update checkmate
```

`update --all` reports each plugin as an entry of a JSON array, and it fails if any plugin could not be updated. Pinned plugins and plugins that kept their permissions do not count as failures.

### Approve and uninstall

`auth0 plugin approve` needs a terminal. In agent mode, with `--no-input` and in a script, it exits with code 1 and returns the command that a person has to run as a JSON error:

```json
{"error":{"code":"unknown","reason":"unclassified","message":"only a person can approve a plugin. Run this in a terminal: auth0 plugin approve auth0/checkmate --tenant acme.us.auth0.com"}}
```

`auth0 plugin uninstall` in agent mode requires `--force`, like other destructive commands. Without it, the command fails and removes nothing.

### Running a plugin

A plugin command can run only in the ways that its manifest declares. The CLI refuses any other call before the plugin starts:

| You asked for | Refused when | Reason |
| --- | --- | --- |
| Agent mode | The command does not declare `output.agentMode` | `plugin_output_unsupported` |
| `--json` or `--json-compact` | The command does not declare `output.json` | `plugin_output_unsupported` |
| `--csv` | The command does not declare `output.csv` | `plugin_output_unsupported` |
| Agent mode, `--no-input`, or no terminal | The command is interactive, so it needs a terminal | `plugin_interactive` |

For example, a command that does not declare agent mode fails like this:

```json
{"error":{"code":"usage","reason":"plugin_output_unsupported","message":"auth0 checkmate env does not support agent mode (turn it off with --agent-mode=false), because its manifest does not declare output.agentMode"}}
```

When a plugin starts and then fails, the error has the code `plugin`, and `details` names the plugin and its version. If the plugin exited by itself, `details.exitCode` holds its exit code, and the reason is `plugin_exit`. A plugin that runs past its timeout is stopped along with its child processes, and it fails with `plugin_timeout`:

```json
{"error":{"code":"plugin","reason":"plugin_timeout","message":"checkmate 1.0.0 did not finish within 5s and was stopped","details":{"pluginId":"auth0/checkmate","version":"1.0.0"}}}
```

In agent mode the CLI does not print the line about denied API calls. A failed run carries that line in `details.deniedCalls` instead.

If you type the name of a plugin that is in the registry but not installed, the CLI returns the install command as data:

```json
{"error":{"code":"not_found","reason":"plugin_not_installed","message":"Plugin \"checkmate\" is available. Install it with: auth0 plugin install checkmate","details":{"pluginId":"auth0/checkmate","installCommand":"auth0 plugin install checkmate"}}}
```

### Finding plugin commands

`auth0 commands --json` lists plugin commands next to the core ones. A plugin command has `"source": "plugin"`, its `pluginId` and `pluginVersion`, the `requiredScopes` of the command, and an `output` object. The `output` object says which of `json`, `csv` and `agentMode` the command supports. An agent can pick a command and check which flags it may use without parsing the help text.

## Exit codes

The exit code is `0` on success and `1` on any failure, so scripts that only test for zero keep working. The one exception is an interrupt. When you stop the CLI with Ctrl-C, it exits with `130`. The kind of failure is in the error object, not in the exit code. When a plugin fails, its own exit code is in `details.exitCode`, and the CLI still exits with `1`.

## Approve a plugin in CI

Set `AUTH0_CLI_PLUGIN_APPROVALS` to a comma-separated list of `registry/name@sha256:<hash>` entries. A plugin whose permissions hash matches one of the pins counts as approved on every tenant, so pin only a version you have reviewed. Installing a plugin never approves it. Approval needs a person at a terminal or a pin, as in this example:

```bash
export AUTH0_CLI_PLUGIN_APPROVALS="auth0/checkmate@sha256:9b991b1b3d0c523b4d7aa0f05b5e970f3621bae92d157d563a0458ba28a5b9a7"
```

Take the hash from the `PERMISSIONS HASH` row of `auth0 plugin info checkmate`, or from the `permissionsHash` key of `auth0 plugin info checkmate --json`. Review the permissions once in a terminal before you pin the hash, because a pin says you reviewed exactly those permissions.

A few rules apply:

- A pin is checked by `install`, `update` and every run. With a matching pin, `install` and `update` do not ask, and the result has `"approvalRequired": false`.
- A pin matches the hash and nothing else. If a new version of the plugin asks for different permissions, the hash changes, and the pin no longer matches. The run then fails with `plugin_not_approved` until you review the new permissions and update the pin.
- You can list more than one pin, for example to cover a rollout: `auth0/checkmate@sha256:<hash>,auth0/checkmate@sha256:<other hash>`. Each entry needs the full ID with the registry name, as in `auth0/checkmate`.
- A pin is never written to `approvals.json`.
- An invalid value is an error, not an empty list, so a typo cannot look like having no pins:

```text
The value of AUTH0_CLI_PLUGIN_APPROVALS is not valid. (the entry "checkmate@sha256:abc" is not in the form registry/name@sha256:<64 lowercase hex digits>)
```

A pin works with `AUTH0_CLI_AUTH_MODE=env`, where the CLI reads the tenant and the credentials from the environment:

```bash
export AUTH0_CLI_AUTH_MODE=env
export AUTH0_DOMAIN=acme.us.auth0.com
export AUTH0_CLIENT_ID=<client-id>
export AUTH0_CLIENT_SECRET=<client-secret>
auth0 plugin install checkmate@1.0.0
auth0 checkmate --json
```

In this mode the CLI does not save the tenant or the credentials in `config.json` or in the keychain. Plugin files still go to the plugins folder described in [Managing plugins](managing.md).

In this mode the tenant is fixed by `AUTH0_DOMAIN`. Passing a different `--tenant` fails instead of running on another tenant:

```text
--tenant "other.us.auth0.com" conflicts with AUTH0_DOMAIN="acme.us.auth0.com"; in AUTH0_CLI_AUTH_MODE=env mode the tenant is fixed by AUTH0_DOMAIN. Drop --tenant, or set AUTH0_DOMAIN to the tenant you want to target.
```

An exact version install pins the plugin. So `auth0 plugin install checkmate@1.0.0` also stops `update --all` from moving it in a long-lived environment.

## Turn plugins off

`AUTH0_CLI_PLUGINS_DISABLED=true` blocks install, update, approve and running plugins. Search, list, info and uninstall keep working. Use it on managed machines and in images where plugins are not wanted:

```text
▸ Plugins are turned off on this machine by AUTH0_CLI_PLUGINS_DISABLED=true, so auth0 plugin install is not available. Searching, listing and uninstalling still work.
```

## Other switches

| Variable | Effect |
| --- | --- |
| `AUTH0_CLI_PLUGIN_UPDATE_HINT=false` | Turns off the hint about a newer plugin version, which shows at most once a day. The hint never shows in agent mode, with `--no-input`, with `--json`, `--json-compact` or `--csv`, or when the output is not a terminal. |
| `AUTH0_CLI_ANALYTICS=false` | Turns off usage events, including the plugin events. See [Security and trust model](security.md). |

## Next

- [Troubleshooting](troubleshooting.md): reason codes and fixes.
- [Reference](reference.md): environment variables and reason codes in one place.
- [Managing plugins](managing.md): the commands behind these flows.
Loading
Loading