Organizations
An organization (org) is a tenant. Its users, sessions, OAuth clients, signing keys and audit events belong to it, and it is addressed by a slug.
- One issuer per org. The main org is served at the root of the server. Every other
org is served under
/orgs/{slug}. Each has its own issuer URL, discovery document and JWKS. - Isolated identities. An email address is unique within an org, not across the server. Sessions and single sign-on never cross orgs.
- Per-org keys. Each org's signing keys are stored in the database, encrypted under
the deployment's key-encryption key (
keys.encryption_key). - Per-org policy. An org can be created in FAPI mode or FIPS mode (see below).
The main org
The main org has the fixed slug default (orgs.default_slug) and cannot be deleted.
It governs the rest: only the main org's administrators can create and delete orgs, and
a main-org administrator with the manage-organizations role can act on any org. The
main org is served at the root only, so /orgs/default/… answers 404 unknown_org.
URLs
For an issuer https://auth.example.com and an org acme:
| Main org | Org acme | |
|---|---|---|
| Issuer | https://auth.example.com | https://auth.example.com/orgs/acme |
| Discovery | /.well-known/openid-configuration | /orgs/acme/.well-known/openid-configuration |
| JWKS | /.well-known/jwks.json | /orgs/acme/.well-known/jwks.json |
| Authorize | /authorize | /orgs/acme/authorize |
| Token | /token | /orgs/acme/token |
Org discovery is also served at the RFC 8414 path-inserted form,
/.well-known/openid-configuration/orgs/acme and
/.well-known/oauth-authorization-server/orgs/acme.
Creating an org
Through the admin API, as a main-org administrator:
curl -s https://auth.example.com/v1/admin/orgs \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"slug": "acme", "display_name": "Acme Corp"}'The slug must match ^[a-z0-9][a-z0-9-]*$ and be at most 64 characters. The response
contains the org's issuer and, only this once, admin_m2m_secret, which is the
secret of the org's admin machine client.
From a local stack you can also use the CLI (create org, get org, delete org):
go run ./cmd/nauthera-cli create org acme --display-name "Acme Corp"Only the display name can be changed after creation (PATCH /v1/admin/orgs/{slug}).
Changing an org's authentication policy after creation is tracked in #231.
Admin clients
Every org gets two reserved OAuth clients so that people and automation can reach its admin API:
| Client ID | Type | Use |
|---|---|---|
{slug}-admin-console | Public, PKCE required, authorization_code | An administrator signs in interactively. The token carries the roles that user holds in the org. |
{slug}-admin-m2m | Confidential, client_credentials | Automation. Its org and admin roles are fixed when it is provisioned. |
For the main org, the M2M secret is written once to auth.admin.bootstrap_secret_path
(mode 0600, never overwritten) instead of to a log line. Read it, store it, then delete
the file. Users listed in auth.admin.subjects are promoted to administrators of the
main org at startup. Promotion is the only thing this does; it never demotes.
FAPI mode and FIPS mode
Two policies are fixed when an org is created through the gRPC CreateOrganization
call:
fapi_modeapplies part of the FAPI 2.0 Security Profile to the org. It requires pushed authorization requests and PKCE with S256, signs only with ES256, and caps the authorization code lifetime at 60 seconds. It does not yet refuse shared-secret clients (#33) or require sender-constrained tokens (#54).fips_modederives low-entropy secrets with PBKDF2-HMAC-SHA-256 instead of argon2id, and fails closed if the process is not running the FIPS 140-3 module.
Deleting an org
DELETE /v1/admin/orgs/{slug} removes an org other than the main one. Making deletion
take effect everywhere at once (cached runtimes, sessions and tokens) is tracked in #283
and #288.