> ## Documentation Index
> Fetch the complete documentation index at: https://docs.neobank.proofof.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Configure a dedicated Auth0 tenant

> Create Management API credentials, review the tenant plan, reconcile Auth0, and deploy the generated runtime credentials.

# Configure a dedicated Auth0 tenant

Use the tracked reconciler to configure one dedicated Auth0 tenant for one neobank deployment. This guide covers tenant domains, Management API credentials, permissions, planning, apply, verification, and runtime credential deployment.

<Warning title="Dedicated tenants only">
  `auth0-apply` deletes unrelated non-system applications, custom APIs, Actions, and roles. It preserves users and connection objects — though it removes the managed applications from every connection other than `AUTH0_CONNECTION_NAME` — plus Auth0 system resources and the configured Management API client. Do not run it against a shared tenant.
</Warning>

## Check the prerequisites

Prepare these items before creating reconciliation credentials:

* An Auth0 tenant dedicated to this neobank deployment
* An Auth0 Dashboard account with permission to create applications and API grants
* An existing database connection, such as `Username-Password-Authentication`
* The final public application origin and API audience
* A verified custom domain, if the deployment uses one
* A clean repository checkout with dependencies installed

The reconciler creates customer and operator applications. It also creates the custom API, Post-Login Action, and operator role. It does not create users, role memberships, database connections, or an agent application.

## Identify the canonical and runtime domains

Auth0 uses two domain settings when a deployment has a custom domain:

* `AUTH0_CANONICAL_DOMAIN`: the Auth0 tenant hostname, such as `tenant.us.auth0.com`
* `NEOBANK_AUTH0_DOMAIN`: the issuer hostname used by the neobank, such as `login.bank.example`

Open **Applications → APIs → Auth0 Management API → Settings**. Copy the API identifier, then remove `https://` and `/api/v2/` to get `AUTH0_CANONICAL_DOMAIN`.

Open **Branding → Custom Domains** to confirm that the runtime custom domain has a ready status. Use that hostname for `NEOBANK_AUTH0_DOMAIN`. Use the canonical hostname for both settings when you do not use a custom domain.

The Management API audience always uses the canonical tenant domain. The reconciler builds this audience from `AUTH0_CANONICAL_DOMAIN`.

## Create the Management API application

Create a dedicated machine-to-machine (M2M) application for reconciliation:

1. Open **Applications → Applications** in the Auth0 Dashboard.
2. Select **Create Application**.
3. Enter a descriptive name, such as `Neobank Auth0 Reconciler`.
4. Select **Machine to Machine Applications**.
5. Select **Auth0 Management API**.
6. Grant the temporary permissions listed below.
7. Select **Authorize**.

The reconciler needs these permissions for its first apply:

| Resource         | Temporary permissions                                                                                    |
| ---------------- | -------------------------------------------------------------------------------------------------------- |
| Actions          | `read:actions`, `create:actions`, `update:actions`, `delete:actions`                                     |
| Applications     | `read:clients`, `create:clients`, `update:clients`, `delete:clients`, `read:client_credentials`          |
| Client grants    | `read:client_grants`, `update:client_grants`                                                             |
| Connections      | `read:connections`, `read:connections_options`, `update:connections`                                     |
| Guardian factors | `read:guardian_factors`, `update:guardian_factors`                                                       |
| MFA policies     | `read:mfa_policies`, `update:mfa_policies`                                                               |
| Tenant settings  | `read:tenant_settings`, `update:tenant_settings`                                                         |
| APIs             | `read:resource_servers`, `create:resource_servers`, `update:resource_servers`, `delete:resource_servers` |
| Roles            | `read:roles`, `create:roles`, `update:roles`, `delete:roles`                                             |

`update:client_grants` lets verification reduce this application to read-only access. Regrant the write permissions before a later apply.

## Copy the Management API client ID and secret

Open the M2M application after authorization. Copy its **Client ID** and **Client Secret** from the application’s **Settings** or **Credentials** tab.

Store these values as `AUTH0_MANAGEMENT_CLIENT_ID` and `AUTH0_MANAGEMENT_CLIENT_SECRET`. They identify the reconciler itself. They are not the customer or operator application credentials.

Do not copy an API Explorer access token into `.env.auth0`. The reconciler exchanges the client ID and secret for a short-lived Management API token on every command. It never stores or prints that token.

Auth0 documents this flow in [Register Machine-to-Machine Applications](https://auth0.com/docs/get-started/auth0-overview/create-applications/machine-to-machine-apps) and [Get Management API Access Tokens for Production](https://auth0.com/docs/secure/tokens/access-tokens/management-api-access-tokens/get-management-api-access-tokens-for-production).

## Prepare `.env.auth0`

Create the ignored configuration file and restrict its mode before adding credentials:

```bash theme={null}
cp .env.auth0.example .env.auth0
chmod 600 .env.auth0
```

Fill every deployment identity value and the Management API fields:

```dotenv theme={null}
NEOBANK_APP_NAME=Example Neobank
NEOBANK_APP_NAME_AR=
NEOBANK_APP_MARK=N
NEOBANK_PUBLIC_ORIGIN=https://neobank.example
NEOBANK_API_ORIGIN=https://neobank.example
NEOBANK_AUTH0_DOMAIN=login.example
NEOBANK_AUTH0_CLIENT_ID=
NEOBANK_AUTH0_CLIENT_SECRET=
NEOBANK_AUTH0_AUDIENCE=https://neobank.example
NEOBANK_AUTH0_CLAIM_NAMESPACE=https://neobank.example/claims
NEOBANK_AUTH0_OPS_CLIENT_ID=
NEOBANK_AUTH0_OPS_CLIENT_SECRET=
NEOBANK_PROBLEM_TYPE_BASE=https://neobank.example/problems
AUTH0_CANONICAL_DOMAIN=tenant.us.auth0.com
AUTH0_MANAGEMENT_CLIENT_ID=management_client_id_here
AUTH0_MANAGEMENT_CLIENT_SECRET=management_client_secret_here
AUTH0_CONNECTION_NAME=Username-Password-Authentication
AUTH0_OPERATOR_ROLE_ID=
```

<Warning title="Replace every example value">
  Replace the application name, domains, audience, namespace, and credentials before running a plan. Keep the customer and operator credentials blank during the first reconciliation.
</Warning>

Leave these generated fields blank initially:

* `NEOBANK_AUTH0_CLIENT_ID`
* `NEOBANK_AUTH0_CLIENT_SECRET`
* `NEOBANK_AUTH0_OPS_CLIENT_ID`
* `NEOBANK_AUTH0_OPS_CLIENT_SECRET`
* `AUTH0_OPERATOR_ROLE_ID`

Apply writes each generated value to `.env.auth0` as soon as Auth0 creates it. The file must remain owned by the current user with mode `0600`.

## Review the tenant plan

Fetch the tenant and generate an exact redacted plan:

```bash theme={null}
make auth0-plan
```

The command prints every create, update, configure, and delete operation. It also prints preserved connections, users, and system resources. Secrets appear only as hashes.

Review every deletion. Copy the `plan_hash` only after confirming the entire plan.

## Apply the approved plan

Apply the exact reviewed state with its plan hash:

```bash theme={null}
make auth0-apply AUTH0_PLAN_HASH=approved_plan_hash_here
```

Apply fetches the tenant again before changing it. Any environment or tenant change invalidates the hash. Run `make auth0-plan` again and review the new hash when this happens.

The reconciler performs these operations:

* Creates `<app name> Customer` and `<app name> Operator` regular-web applications
* Configures `<API origin>/v1/session/callback` for both applications
* Creates `<app name> API` with the exact configured audience and API scopes
* Creates and deploys the `<app name> Identity` Post-Login Action
* Creates the unassigned `<app name> Operator` role
* Enables TOTP, platform WebAuthn, security-key WebAuthn, and recovery codes
* Enables custom MFA factor challenges from Post-Login Actions
* Enables the configured database connection for both applications
* Detaches both applications from every other connection — social and enterprise alike — so no login can bypass the passkey and Guardian step-up model. An existing user whose only identity lives on a detached connection loses access at apply time.
* Binds the deployed Action to the Post-Login trigger
* Deletes only the non-system resources listed in the approved plan

The Action deployment must match the desired code, runtime, dependencies, and secret names. Apply stops before binding or deletion when the deployed version does not converge.

## Verify the tenant and reduce permissions

Verify the resulting tenant:

```bash theme={null}
make auth0-verify
```

Verification requires a second reconciliation with no operations. It also checks the deployed Action, trigger binding, client-secret hashes, role identity, MFA factors, custom MFA Action setting, connection assignments, and API settings.

A successful first verification prints:

```text theme={null}
Auth0 verification passed
Reduced the Management API client grant to read-only scopes
```

Confirm idempotency with the reduced permissions:

```bash theme={null}
make auth0-plan
```

The final plan must contain `"operations": []`. A later apply requires the temporary write permissions again.

## Deploy the generated runtime credentials

After apply, `.env.auth0` contains the generated customer and operator application credentials:

| Runtime field                     | Auth0 resource                                               |
| --------------------------------- | ------------------------------------------------------------ |
| `NEOBANK_AUTH0_CLIENT_ID`         | `<app name> Customer` client ID                              |
| `NEOBANK_AUTH0_CLIENT_SECRET`     | `<app name> Customer` client secret                          |
| `NEOBANK_AUTH0_OPS_CLIENT_ID`     | `<app name> Operator` client ID                              |
| `NEOBANK_AUTH0_OPS_CLIENT_SECRET` | `<app name> Operator` client secret                          |
| `AUTH0_OPERATOR_ROLE_ID`          | Managed operator role ID used for stable retries and renames |

Export only the runtime settings on the administration machine:

```bash theme={null}
umask 077
grep -E '^(NEOBANK_APP_NAME|NEOBANK_APP_NAME_AR|NEOBANK_APP_MARK|NEOBANK_PUBLIC_ORIGIN|NEOBANK_API_ORIGIN|NEOBANK_AUTH0_DOMAIN|NEOBANK_AUTH0_CLIENT_ID|NEOBANK_AUTH0_CLIENT_SECRET|NEOBANK_AUTH0_AUDIENCE|NEOBANK_AUTH0_CLAIM_NAMESPACE|NEOBANK_AUTH0_OPS_CLIENT_ID|NEOBANK_AUTH0_OPS_CLIENT_SECRET|NEOBANK_PROBLEM_TYPE_BASE)=' \
  .env.auth0 > .env.runtime
```

Transfer `.env.runtime` through Secure Copy Protocol (SCP). Set `server` to the SSH account
and host. Set `server_dir` to the repository directory on that server:

```bash theme={null}
server=deploy_user@server.example
server_dir=/srv/neobank-wrapper
scp .env.runtime "$server:$server_dir/.env.runtime"
```

Do not copy `AUTH0_MANAGEMENT_CLIENT_ID`, `AUTH0_MANAGEMENT_CLIENT_SECRET`, or
`AUTH0_OPERATOR_ROLE_ID` to the server or application containers.

On the server, merge these values into the protected server environment and prepare the
deployment:

```bash theme={null}
cd /srv/neobank-wrapper
chmod 600 .env.server .env.runtime
./scripts/prepare_server.sh .env.server .env.runtime
make server-config
make caddy-config
make server-up
rm -f .env.runtime
```

Replace `/srv/neobank-wrapper` with `server_dir` when you use another path. After the server
starts successfully, delete the administration-machine copy with `rm -f .env.runtime`.
See [Single-server deployment](/deploy/dedicated-server) for new-server and existing-server
safety requirements.

Auth0 tokens stay in the FastAPI back end. The frontend receives only the public application name, optional Arabic name, and mark from `/v1/public-config`.

## Recover from reconciliation failures

Use the error message to select the recovery path:

| Failure                          | Cause and recovery                                                                                                                          |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `401 Invalid token`              | The M2M client has no Auth0 Management API grant. Authorize it and select the required permissions.                                         |
| `403 Forbidden`                  | The M2M grant is missing the permission required by the current endpoint. Compare it with the temporary permission table.                   |
| Stale plan hash                  | The tenant or `.env.auth0` changed after planning. Generate and review a new plan.                                                          |
| Action build mismatch or timeout | Auth0 did not return the exact deployed version before the timeout. Rerun the plan. Existing generated credentials remain in `.env.auth0`.  |
| Read timeout                     | Auth0 did not answer within the client timeout. Retry the read-only plan or verification command.                                           |
| Tenant does not match            | Verification printed remaining operations. Review and apply that exact plan before verifying again.                                         |
| Client secret drift              | Auth0 and `.env.auth0` contain different secrets. Approve a secret sync or rotation, then update the runtime secret store and `.env.auth0`. |

Retries do not reuse a stale hash. A partial apply may create resources before a later operation stops, so generate a new plan and review its remaining operations.

## Understand the Action claims and MFA behavior

The Action reads `CLAIM_NAMESPACE`, `CUSTOMER_CLIENT_ID`, `OPERATOR_CLIENT_ID`, and `OPERATOR_ROLE_NAME` from Action secrets. Its rendered code contains expected-value hashes and denies login when a secret drifts.

The Action maps the configured operator role to the stable `operator` claim value. It emits `<claim namespace>/roles` and can emit `<claim namespace>/actor_type` for configured machine clients. Operator login always requires multifactor authentication (MFA), while customer MFA follows explicit step-up requests. A primary passkey satisfies the step-up request. Otherwise, the Action challenges an enrolled factor before offering a new enrollment.

The tracked Action source contains no tenant domain or bank-specific value.
