HTTP Endpoints of nauthera-server
Docs version v1 (main)Nauthera is pre-1.0, in a design-partner programUpdated
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/, 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 | / | OpenID Connect Discovery 1.0 |
GET | / | 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
/ are mounted by hand. Both discovery documents are also served
at the RFC 8414 path-inserted locations on the server root,
/ and
/, 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: reference. |
POST | /token | Grants: authorization_code, refresh_token, client_credentials, urn:. Token exchange and the JWT bearer grant answer 503 temporarily_ 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.). 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_, backchannel_). 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, fam, email, email_verified, name,
preferred_username, member_org and member_org_slug. fam, on a user's access token,
names the grant family the token belongs to, which a replayed code or refresh token
revokes. member_org (in both tokens) and member_org_slug (in the ID token) name the
member organization an identity broker signed the account in
from. See
Tokens & Sender Constraint.
Dynamic client registration
| Method | Path | Notes |
|---|---|---|
POST | /register | RFC 7591. Needs an initial access token with nauthera.. |
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. Answers 404 not_found before the password is checked unless it names a client_id registered in the organization or continues the device flow. Asks for a captcha after repeated failures. |
POST | /v1/auth/passkey/options, /v1/auth/passkey/verify | Passwordless sign-in with a passkey, where the second-factor policy opens it. Needs a registered client like the password sign-in. |
GET | /v1/auth/captcha | The captcha this deployment asks for: a fresh ALTCHA challenge, or a hosted provider's site key. |
POST | /v1/auth/register | Self-registration. Off by default (auth.). Needs a captcha solution when one is configured. |
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. Needs a captcha solution. |
POST | / | Set a new password from the link. Ends the account's sessions, retires every refresh token it was given, and clears a lockout. |
POST | / | Choose a password from an invitation link, which also verifies the address. |
POST | / | Email a single-use link that verifies the address. Answers 202 whether or not the address has an account. Needs a captcha solution. |
POST | / | Mark the address verified from the link. |
GET | / | Send the browser to the organization's identity provider {slug}, with state, nonce and PKCE. The client must offer the provider. |
GET | / | The identity provider's redirect back: redeems its code, verifies its ID token, and signs in, links or signs up the account. Every refusal goes back to the sign-in page with federation_error. |
POST | /v1/auth/broker/route | A broker's email step: route an address to the member organization that verified its domain. The answer depends on the domain alone. |
GET | / | Send the browser to the member organization's own sign-in, with state, nonce and PKCE. The membership must be active and admit the client. |
GET | / | The member's redirect back: redeems its code, verifies its ID token, and signs in the broker's account linked to the member account. Every refusal goes back to the sign-in page with federation_error. |
POST | /v1/auth/sign-out | End the session. |
POST | /v1/auth/logout/confirm | Confirm an RP-initiated logout. |
POST | /v1/auth/consent, / | 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 | / | Confirm enrolment with a first code. |
POST | / | Acknowledge the recovery codes. |
POST | /v1/auth/mfa/totp/verify | Answer a TOTP challenge. |
POST | /, / | Email the code that enrols the emailed-code factor, and activate it with that code. |
POST | /v1/auth/mfa/email/send, / | Email a sign-in code, and answer the challenge with it. |
POST | /, / | Register a passkey as a second factor. |
POST | /, / | Answer the second-factor challenge with a passkey. |
POST | / | Decline the offer to add a passkey and finish the sign-in. |
POST | / | Answer the challenge with a recovery code. |
POST | / | Regenerate recovery codes. |
DELETE | /v1/auth/mfa/totp | Disable TOTP. |
What a change of second factor ends is on Multi-Factor Authentication; the screens are in the sign-in flows.
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/me | nauthera.orgs:read | orgs.read |
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 | / | nauthera.orgs:read | orgs.read |
GET | / | nauthera.orgs:read | orgs.read |
POST | / | nauthera.orgs:write | orgs.write |
GET | / | nauthera.insights:read | insights.read |
GET | / | nauthera.orgs:read | orgs.read |
GET | /, /, /v1/admin/problems, /v1/admin/problems/{id} | nauthera.orgs:read | orgs.read |
GET | / | nauthera.orgs:read | orgs.read |
POST | /, / | nauthera.brokers:write | brokers.manage, recent sign-in |
PUT | / | nauthera.brokers:write | brokers.manage, recent sign-in |
DELETE | / | nauthera.brokers:write | brokers.manage, recent sign-in |
GET | / | nauthera.orgs:read | orgs.read |
POST | /, / | nauthera.domains:write | domains.manage, recent sign-in |
DELETE | / | nauthera.domains:write | domains.manage, recent sign-in |
GET | /, / | nauthera.clients:read | clients.read |
GET | / | nauthera.insights:read | insights.read |
GET | /, / | nauthera.users:read | users.read |
POST | / | nauthera.users:write | users.create |
PATCH | / | nauthera.users:write | users.update |
DELETE | / | nauthera.users:write | users.delete, recent sign-in |
PUT | / | nauthera.users:write | users.credentials, recent sign-in |
POST | / | nauthera.users:write | users.credentials, recent sign-in |
POST | / | nauthera.users:write | users.credentials |
POST | /, /block, /reactivate, /unlock | nauthera.users:write | users.lifecycle, recent sign-in |
POST | / | nauthera.users:write | users.create |
GET | / | nauthera.users:read | users.read |
GET | / | nauthera.roles:read | roles.read |
PUT, DELETE | / | nauthera.roles:write | roles.assign, recent sign-in |
GET | / | nauthera.sessions:read | sessions.read |
DELETE | / | nauthera.sessions:write | sessions.revoke, recent sign-in |
GET | /, / | nauthera.sessions:read | sessions.read |
DELETE | / | nauthera.sessions:write | sessions.revoke |
DELETE | / | nauthera.sessions:write | sessions.revoke, recent sign-in |
GET | /, /, /, /, /v1/admin/audit-catalog | nauthera.audit:read | audit.read |
GET | / | nauthera.insights:read | insights.read |
GET | /, / | nauthera.threats:read | threats.read |
GET | / | nauthera.insights:read | insights.read |
The organization update changes the display name, FAPI mode, the verified-address
requirement, the default branding, and the
second-factor policy and network zones.
A policy or zones the deployment does not allow answer 400 naming the field and the
bound. Deleting an
organization, or changing it, takes effect on every replica within 30 seconds. The
snapshot counts an organization's users, sessions and clients, naming none. The
identity-provider list names the organization's identity providers
and never returns a provider's secret, only a fingerprint of its sealing. An invitation
emails a user who has no password a new link to choose one; it answers
409 mail_relay_missing when the organization has no email relay. A user's identities
are the identity provider accounts linked to it. The role list says which roles the user
holds and which the caller may grant or revoke; granting and revoking are bounded by
the caller's own roles, and revoking the last account that can grant roles answers
409 last_. Forgetting remembered browsers makes the next sign-in from
them answer the second factor again; it needs a reason, which the audit event records.
Suspending an account, until a set time or until it is reactivated, and blocking it end
every session it holds, retire its refresh grants and tell relying parties over
back-channel logout; reactivating lets it sign in again, and unlocking releases the lock
repeated wrong passwords set. Each needs a reason, and a suspension, block or reactivation
emails the account's holder. Requiring a password change sends the user's next password
sign-in to the page that sets a new one, and the reset operation emails the user the
forgot-password link, naming the administrator. Broker memberships let a
broker's clients sign in a member organization's users: the
broker invites, the member accepts, the broker chooses the clients a membership admits,
and either side ends it. An email-domain claim is verified against a DNS TXT record and
then routes sign-ins at that domain to the organization. The problem lists name the
resources an operator could not apply, with the reason and its runbook, for an
organization or, to platform staff, for resources in no organization. /v1/admin/me says
who the caller is and, per organization, which permissions their roles grant.
The audit ranking names the subjects behind matching records, at most 25, over at most
31 days; ranking or filtering by address also needs users.pii.read. The threat
overview and the shape of the refused sign-ins are aggregates over the audit log that
name no address, account or client.
The user list finds users by name or email (q, match) and filters by status,
locked, second_factor, email_verified 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, org-support, org-user-admin and
org-owner in any org, and the platform roles platform-viewer, platform-auditor and
platform-owner, which count only in the main org, where they reach every org. Only
org-owner and platform-owner hold roles.assign, brokers.manage and
domains.manage. platform-owner holds every permission, platform.audit included,
which platform-scoped audit records need. Each
organization's machine client holds org-user-admin and org-auditor, and the main
organization's also platform-owner.
No administrator can change the status, credentials, email address or username of, or
delete, their own account or one more privileged than theirs, nor grant or revoke a role
on their own account; such a call answers 403 protected_account. Ending an account's
sessions is not refused this way, since it only removes access. See
Admin API & Users.
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_ 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 client, session, insights, threats, role,
broker and email-domain scopes on top of the admin scopes, and a tenant organization's
console also nauthera.orgs:read. 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
https:, the code's runbook.
An OAuth error object carries the error's runbook as error_uri. 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":.
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.. See
Architecture.