Endpoints
All paths below are relative to an organization's base URL. That is the server root for
the main org and /orgs/{slug} for every other org (see
Organizations). Every public-listener endpoint on this page
is declared in the OpenAPI document api/openapi/nauthera.yaml, except those marked as
mounted by hand, and the discovery document advertises only endpoints that actually
serve requests. A test drives the live server and fails the build if an advertised
endpoint answers temporarily_unavailable.
Discovery
| Method | Path | Specification |
|---|---|---|
GET | /.well-known/openid-configuration | OpenID Connect Discovery 1.0 |
GET | /.well-known/oauth-authorization-server | RFC 8414 |
GET | /.well-known/jwks.json | RFC 7517, with ES256 and RS256 keys |
For every org other than the main one, the discovery documents and the JWKS under
/orgs/{slug}/.well-known/ are mounted by hand. Both discovery documents are also served
at the RFC 8414 path-inserted locations on the server root,
/.well-known/openid-configuration/orgs/{slug} and
/.well-known/oauth-authorization-server/orgs/{slug}, which are mounted by hand too.
OAuth 2.0 and OpenID Connect
| Method | Path | Notes |
|---|---|---|
GET, POST | /authorize | Authorization code flow. response_type=code, response_mode=query, PKCE S256. Supports prompt (none, login, consent, select_account), max_age, id_token_hint, login_hint, resource, dpop_jkt, a signed request object, or a PAR request_uri. Returns iss (RFC 9207). |
POST | /par | Pushed authorization requests (RFC 9126). Returns a single-use urn:ietf:params:oauth:request_uri: reference. |
POST | /token | Grants: authorization_code, refresh_token, client_credentials, urn:ietf:params:oauth:grant-type:device_code. Token exchange and the JWT bearer grant answer 503 temporarily_unavailable and are not advertised. |
POST | /device_authorization | Device authorization grant (RFC 8628). verification_uri is the issuer followed by /device. |
POST | /introspect | RFC 7662, including cnf for DPoP-bound tokens. Caller-bound: only the confidential client the token was issued to may introspect it. |
POST | /revoke | RFC 7009. Revokes refresh tokens; access tokens run to expiry (#91). |
GET, POST | /userinfo | Claims gated by scope. A DPoP-bound token must be sent with the DPoP scheme and a matching proof. |
GET, POST | /end_session | RP-initiated logout. Ends the session at once when a verified id_token_hint names the signed-in user, redirects without asking when nobody is signed in, and asks the user to confirm in every other case. |
GET | /check_session_iframe | OIDC Session Management. Off by default (auth.session_management.enabled). The flag controls the discovery entry, session_state and the browser-state cookie; the frame itself is served either way, and any page may embed it. The advertised URL needs a client_id query parameter to answer (#313). |
Front-channel and back-channel logout are both supported and advertised
(frontchannel_logout_supported, backchannel_logout_supported). Front-channel runs
only when /end_session ends the session at once.
Tokens. Access tokens are JWTs with typ: at+jwt (RFC 9068), signed with ES256 or
RS256. Depending on scope and flow, ID tokens and access tokens carry sub, iss,
aud, exp, iat, jti, nbf, auth_time, nonce, at_hash, amr, acr, sid,
scope, client_id, org, roles, email, email_verified, name and
preferred_username. See Tokens & Sender Constraint.
Dynamic client registration
| Method | Path | Notes |
|---|---|---|
POST | /register | RFC 7591. Needs an initial access token with nauthera.clients:register. |
GET, PUT, DELETE | /register/{client_id} | RFC 7592. Needs the registration access token. Read and update responses do not repeat it (#78). |
Hosted sign-in API
The hosted sign-in UI calls these JSON endpoints. They are part of the public contract, but you will rarely call them yourself.
| Method | Path | Purpose |
|---|---|---|
POST | /v1/auth/sign-in | Password sign-in. May answer with an MFA step. |
POST | /v1/auth/register | Self-registration. Off by default (auth.registration.enabled). |
POST | /v1/auth/password-reset | Email a single-use link to set a new password. Answers 202 whether or not the address has an account. |
POST | /v1/auth/password-reset/confirm | Set a new password from the link. Ends the account's sessions and clears a lockout. |
POST | /v1/auth/verify-email/resend | Email a single-use link that verifies the address. Answers 202 whether or not the address has an account. |
POST | /v1/auth/verify-email/confirm | Mark the address verified from the link. |
POST | /v1/auth/sign-out | End the session. |
POST | /v1/auth/logout/confirm | Confirm an RP-initiated logout. |
POST | /v1/auth/consent, /v1/auth/consent/decision | Load and answer the consent prompt. |
POST | /v1/auth/device/verify, /v1/auth/device/decision | Look up and approve or deny a device user code. |
GET | /v1/auth/mfa | MFA status. |
GET | /v1/auth/mfa/totp/enroll | Start TOTP enrolment. |
POST | /v1/auth/mfa/totp/activate | Confirm enrolment with a first code. |
POST | /v1/auth/mfa/totp/recovery-ack | Acknowledge the recovery codes. |
POST | /v1/auth/mfa/totp/verify | Answer a TOTP challenge. |
POST | /v1/auth/mfa/recovery-code/verify | Answer the challenge with a recovery code. |
POST | /v1/auth/mfa/recovery-codes | Regenerate recovery codes. |
DELETE | /v1/auth/mfa/totp | Disable TOTP. |
Admin API
The admin API lives at the server root, and the organization it acts on is named in the
path. A token acts in the org whose issuer minted it, and only a token minted for one of
that org's two reserved admin clients is accepted: the dashboard's {slug}-admin-console
or the {slug}-admin-m2m machine client. A token from any other client is refused, whatever
scopes and roles stand behind it. Every call needs an access token that carries the
operation's scope, and its caller must hold the operation's permission, which the
contract declares per operation (x-permission). Permissions come from the roles the
caller holds in the token's org: for a user, read from the database on every call; for
an org's {slug}-admin-m2m client, the roles it was provisioned with. A token can act in
another org only if its caller holds a platform role in the main org. See
Admin API & Users.
| Method | Path | Scope | Permission |
|---|---|---|---|
GET | /v1/admin/orgs, /v1/admin/orgs/{slug} | nauthera.orgs:read | orgs.read |
POST | /v1/admin/orgs | nauthera.orgs:write | orgs.write |
PATCH, DELETE | /v1/admin/orgs/{slug} | nauthera.orgs:write | orgs.write |
GET | /v1/admin/orgs/{slug}/theme | nauthera.orgs:read | orgs.read |
GET | /v1/admin/orgs/{slug}/mail | nauthera.orgs:read | orgs.read |
POST | /v1/admin/orgs/{slug}/mail/test | nauthera.orgs:write | orgs.write |
GET | /v1/admin/orgs/{slug}/clients, /v1/admin/orgs/{slug}/clients/{id} | nauthera.clients:read | clients.read |
GET | /v1/admin/orgs/{slug}/clients/{id}/usage | nauthera.insights:read | insights.read |
GET | /v1/admin/orgs/{slug}/users, /v1/admin/orgs/{slug}/users/{id} | nauthera.users:read | users.read |
POST | /v1/admin/orgs/{slug}/users | nauthera.users:write | users.create |
PATCH | /v1/admin/orgs/{slug}/users/{id} | nauthera.users:write | users.update |
DELETE | /v1/admin/orgs/{slug}/users/{id} | nauthera.users:write | users.delete, recent sign-in |
PUT | /v1/admin/orgs/{slug}/users/{id}/password | nauthera.users:write | users.credentials, recent sign-in |
GET | /v1/admin/orgs/{slug}/users/{id}/devices | nauthera.sessions:read | sessions.read |
GET | /v1/admin/orgs/{slug}/sessions, /v1/admin/orgs/{slug}/users/{id}/session-history | nauthera.sessions:read | sessions.read |
DELETE | /v1/admin/orgs/{slug}/sessions/{sid} | nauthera.sessions:write | sessions.revoke |
DELETE | /v1/admin/orgs/{slug}/users/{id}/sessions | nauthera.sessions:write | sessions.revoke, recent sign-in |
GET | /v1/admin/orgs/{slug}/audit-events, /v1/admin/orgs/{slug}/audit-events/tail, /v1/admin/orgs/{slug}/audit-events/{id}, /v1/admin/audit-catalog | nauthera.audit:read | audit.read |
GET | /v1/admin/orgs/{slug}/audit-events/counts | nauthera.insights:read | insights.read |
GET | /v1/admin/deployment/client-ip | nauthera.insights:read | insights.read |
The user list finds users by name or email (q, match) and filters by status,
locked, second_factor and creation and last sign-in dates; it sorts by created_at,
email, id, last_login_at or username. The client list searches and filters by
type, authentication method, grant type and source. The device list names the browsers
and networks a user has signed in from. The session list covers active sessions and
filters by user or client; a user's session history lists the ones that ended. The audit
counts bucket events over time, grouped and filtered like the audit list. The tail returns
audit records in the order they reached the store, from where the last read left off, for
a SIEM or detector following the log. The mail test sends a message through the org's
relay to the calling administrator's own address and reports what the relay answered. The
client-IP endpoint reports where the deployment reads the client address from
(security.client_ip), which trusted proxies are wider than they need to be, and whether
recent traffic looks like it arrives through a proxy the configuration does not name. Date-windowed
lists and counts take from and to, at most 90 days apart. Every list is
cursor-paginated.
The built-in roles are org-viewer, org-auditor, user-support, manage-users,
org-owner and manage-org in any org, and the platform roles platform-viewer,
platform-auditor and manage-organizations, which count only in the main org, where
they reach every org. manage-organizations holds every permission, platform.audit
included, which platform-scoped audit records need. An operation marked recent sign-in
also needs a token whose sign-in is no older than auth.admin.max_auth_age (default
15 minutes), with a second factor when mfa.required is set; an older or weaker one
gets a 401 insufficient_user_authentication with an RFC 9470 step-up challenge. A
token with no sign-in behind it, such as a machine client's, is refused there.
The dashboard's console client registers the session, client, insights and role scopes
on top of the admin scopes. The machine clients register only the admin scopes:
nauthera.users:read, nauthera.users:write and nauthera.audit:read, plus
nauthera.orgs:read and nauthera.orgs:write for the main org's.
Errors
The OAuth endpoints (/authorize, /par, /token, /device_authorization,
/introspect, /revoke and /end_session) answer with the RFC 6749 error object, and
/register with the RFC 7591 one. Every other endpoint, /userinfo included, answers
with an RFC 9457 application/problem+json document that carries no internal detail. Its
code is one of the codes the OpenAPI document lists, and its type is
urn:problem-type:<code>. A problem without a code, such as a generic 500, has
type: about:blank. A 401 from /userinfo also carries a WWW-Authenticate
challenge. Every response carries an X-Request-ID header.
A request refused by the rate limit, or for a repeated header, gets a problem document
on every endpoint, OAuth ones included. So does a request to an OAuth endpoint that
cannot be read, repeats a form parameter or fails schema validation, instead of the
RFC 6749 error object (#324), and a request to /register whose body cannot be read, is
missing or fails schema validation, instead of the RFC 7591 one. A browser navigation can
get an HTML error page instead of the JSON body, and always does when the rate limit
refuses it. See Errors & Troubleshooting.
OAuth responses are sent with Cache-Control: no-store.
Public banner
GET /healthz on the public listener answers {"service":"nauthera-server","version":"…"}.
It reports the build version and checks no dependency. It is mounted by hand, so it is
not in the OpenAPI document. The Kubernetes probes use the admin listener.
The admin dashboard is served as static files at /admin/ for the
main org and /orgs/{slug}/admin/ for every other org, also mounted by hand. Every other
path that no endpoint claims is the hosted sign-in UI, except an unknown /v1/ path,
which answers a 404 problem document.
Admin listener (:9090)
| Path | Purpose |
|---|---|
/metrics | Prometheus exposition |
/healthz | Liveness |
/readyz | Readiness |
/startupz | Startup |
/debug/pprof/* | Only when server.admin.pprof: true |
gRPC listener (:9091)
grpc.health.v1.Health and nauthera.admin.v1.AdminService. See
Architecture.