# Auth0

URL: https://docs.hanzo.ai/docs/guides/migrate/auth0

> Auth0 is the identity provider your app redirects to. Here that is /v1/iam — 159 operations, OIDC end to end, and the org is a first-class noun rather than an add-on.

Auth0 authenticates a person and hands your app a token. `/v1/iam` (159
operations) is the same job: an OIDC provider with `authorize`, `token`,
`userinfo`, `introspect` and `revoke` at the addresses the spec names them, plus
the users, orgs, roles and applications behind them.

The difference worth knowing before you port anything: \*_the organization is not
an upgrade here._\* Auth0 sells Organizations as a tier on top of tenants. In
Hanzo IAM the org is the tenant — `owner` is a column on every user, and every
other Hanzo capability scopes on that same value.

## Start here

Mint a key, confirm it resolves to somebody, then write your first Auth0 user across.

```bash
# 1. mint a key — sk- belongs on a server, pk- is safe in a browser
curl -sS -X POST https://api.hanzo.ai/v1/account/keys \
  -H "Authorization: Bearer $HANZO_SESSION" \
  -H 'Content-Type: application/json' \
  -d '{"type":"secret"}'

# 2. who does that key speak for?
curl -sS https://api.hanzo.ai/v1/iam/whoami \
  -H "Authorization: Bearer $HANZO_API_KEY"

# 3. move one Auth0 user across — idempotent, so the export loop can re-run
curl -sS -X POST https://api.hanzo.ai/v1/iam/admin/users/upsert \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"owner":"acme","name":"ada","email":"ada@acme.com","displayName":"Ada Lovelace"}'
```

Step 2 is the cheapest proof the key works: it names the caller, or says plainly
that nobody is signed in rather than failing. Step 3 lands a user in the org
named by `owner` — there is no org header because the address carries it — and
omitting `password` keeps whatever that user already had, so running the export
loop twice rotates nobody's credentials.

### Core capabilities

| Capability  | What it does                                                                           | Operations |
| ----------- | -------------------------------------------------------------------------------------- | ---------- |
| `/v1/iam`   | Users, orgs, roles, applications, and the OIDC endpoints in front of them              | 159        |
| `/v1/risk`  | Scores one sign-in against your org's own model, and holds the regime it decides under | 11         |
| `/v1/audit` | Your org's own trail, narrowed by actor, action or resource                            | 1          |

## Nouns

| Auth0                                     | Hanzo                                                          |
| ----------------------------------------- | -------------------------------------------------------------- |
| Tenant                                    | Nothing. The deployment is the tenant boundary                 |
| Organization                              | Org — `/v1/iam/organizations`, addressed `{owner}/{name}`      |
| Application (SPA, M2M, Regular Web)       | Application — `/v1/iam/applications`                           |
| User                                      | User — `/v1/iam/users`, addressed `{owner}/{name}`             |
| Connection (database, social, enterprise) | A provider on the application                                  |
| Role, Permission                          | `/v1/iam/roles`, `/v1/iam/permissions` — both `{owner}/{name}` |
| Rule / Action / Hook                      | Nothing. See below                                             |
| Management API token                      | The same bearer as everything else                             |
| `/oauth/token`                            | `POST /v1/iam/oauth/token`                                     |
| `/userinfo`                               | `GET /v1/iam/oauth/userinfo`                                   |
| `/.well-known/openid-configuration`       | The same path, under `/v1/iam`                                 |
| SCIM (Enterprise)                         | `/v1/iam/scim/v2/Users` — six operations, not a tier           |

`{owner}/{name}` is the shape of every identity address: the org, then the
thing. It is why a user and an org can share a name without colliding, and why
no request needs an org field — the address carries it.

## The call

Auth0, exchanging client credentials:

```bash
curl -sS --request POST \
  --url https://YOUR_TENANT.auth0.com/oauth/token \
  --header 'content-type: application/json' \
  --data '{
    "client_id": "abc123",
    "client_secret": "s3cr3t",
    "audience": "https://api.example.com",
    "grant_type": "client_credentials"
  }'
```

Hanzo:

```bash
curl -sS -X POST https://hanzo.id/v1/iam/oauth/token \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=client_credentials' \
  -d 'client_id=abc123' \
  -d 'client_secret=s3cr3t'
```

Form-encoded, because that is what RFC 6749 §4.4.2 specifies and what every OIDC
client library already sends. The `audience` is the client id: a token is minted
for the application that asked for it, so there is no second identifier to
register and keep in step.

Listing users in an org:

```bash
curl -sS "https://hanzo.id/v1/iam/users?owner=acme" \
  -H "Authorization: Bearer $TOKEN"
```

## What does not carry

**No Rules, Actions or Hooks.** Auth0 runs your JavaScript inside the login
transaction — to add claims, deny a sign-in, or call out mid-flow. Nothing here
executes customer code during authentication, and that is deliberate: a login
that can run arbitrary code is a login that can be made to hang. Post-login work
belongs in your own handler, or in `/v1/auto` where it can fail without taking
the sign-in with it.

**No Universal Login page builder.** The hosted login page is
[hanzo.id](https://hanzo.id) and it is branded by domain, not by a template you
edit in a dashboard. A brand surface is configuration in the deployment.

**Lock and the Auth0 SDKs do not port.** Use any standards-compliant OIDC
client against the discovery document; `@hanzo/iam` is the one we publish.

**No log streams or anomaly detection.** Auth0 ships sign-in telemetry to a
SIEM and scores attempts. Sign-in events land on the ordinary audit trail
(`/v1/audit`), and the risk decision is `/v1/risk` — a separate capability with
its own vocabulary, not a toggle inside identity.

**Migration is a write, not an import.** There is no password-hash import
endpoint. `POST /v1/iam/admin/users/upsert` is idempotent and takes a user at a
time; a bulk export from Auth0 becomes a loop against it.
