Skip to content

The v1 docs follow nauthera-server's main branch and its 0.x releases on the way to 1.0, so fields and APIs can still change.

v1

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/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

MethodPathSpecification
GET/.well-known/openid-configurationOpenID Connect Discovery 1.0
GET/.well-known/oauth-authorization-serverRFC 8414
GET/.well-known/jwks.jsonRFC 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

MethodPathNotes
GET, POST/authorizeAuthorization 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/parPushed authorization requests (RFC 9126). Returns a single-use urn:ietf:params:oauth:request_uri: reference.
POST/tokenGrants: 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_authorizationDevice authorization grant (RFC 8628). verification_uri is the issuer followed by /device.
POST/introspectRFC 7662, including cnf for DPoP-bound tokens. Caller-bound: only the confidential client the token was issued to may introspect it.
POST/revokeRFC 7009. Revokes refresh tokens; access tokens run to expiry (#91).
GET, POST/userinfoClaims gated by scope. A DPoP-bound token must be sent with the DPoP scheme and a matching proof.
GET, POST/end_sessionRP-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_iframeOIDC 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, 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

MethodPathNotes
POST/registerRFC 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.

MethodPathPurpose
POST/v1/auth/sign-inPassword 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/verifyPasswordless sign-in with a passkey, where the second-factor policy opens it. Needs a registered client like the password sign-in.
GET/v1/auth/captchaThe captcha this deployment asks for: a fresh ALTCHA challenge, or a hosted provider's site key.
POST/v1/auth/registerSelf-registration. Off by default (auth.registration.enabled). Needs a captcha solution when one is configured.
POST/v1/auth/password-resetEmail a single-use link to set a new password. Answers 202 whether or not the address has an account. Needs a captcha solution.
POST/v1/auth/password-reset/confirmSet a new password from the link. Ends the account's sessions, retires every refresh token it was given, and clears a lockout.
POST/v1/auth/invitation/acceptChoose a password from an invitation link, which also verifies the address.
POST/v1/auth/verify-email/resendEmail a single-use link that verifies the address. Answers 202 whether or not the address has an account. Needs a captcha solution.
POST/v1/auth/verify-email/confirmMark the address verified from the link.
GET/v1/auth/federation/{slug}/startSend the browser to the organization's identity provider {slug}, with state, nonce and PKCE. The client must offer the provider.
GET/v1/auth/federation/{slug}/callbackThe 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/routeA broker's email step: route an address to the member organization that verified its domain. The answer depends on the domain alone.
GET/v1/auth/broker/{member}/startSend 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/v1/auth/broker/{member}/callbackThe 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-outEnd the session.
POST/v1/auth/logout/confirmConfirm an RP-initiated logout.
POST/v1/auth/consent, /v1/auth/consent/decisionLoad and answer the consent prompt.
POST/v1/auth/device/verify, /v1/auth/device/decisionLook up and approve or deny a device user code.
GET/v1/auth/mfaMFA status.
GET/v1/auth/mfa/totp/enrollStart TOTP enrolment.
POST/v1/auth/mfa/totp/activateConfirm enrolment with a first code.
POST/v1/auth/mfa/totp/recovery-ackAcknowledge the recovery codes.
POST/v1/auth/mfa/totp/verifyAnswer a TOTP challenge.
POST/v1/auth/mfa/email/enroll, /v1/auth/mfa/email/activateEmail the code that enrols the emailed-code factor, and activate it with that code.
POST/v1/auth/mfa/email/send, /v1/auth/mfa/email/verifyEmail a sign-in code, and answer the challenge with it.
POST/v1/auth/mfa/passkey/registration/options, /v1/auth/mfa/passkey/registration/verifyRegister a passkey as a second factor.
POST/v1/auth/mfa/passkey/authentication/options, /v1/auth/mfa/passkey/authentication/verifyAnswer the second-factor challenge with a passkey.
POST/v1/auth/mfa/passkey/offer/declineDecline the offer to add a passkey and finish the sign-in.
POST/v1/auth/mfa/recovery-code/verifyAnswer the challenge with a recovery code.
POST/v1/auth/mfa/recovery-codesRegenerate recovery codes.
DELETE/v1/auth/mfa/totpDisable 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.

MethodPathScopePermission
GET/v1/admin/menauthera.orgs:readorgs.read
GET/v1/admin/orgs, /v1/admin/orgs/{slug}nauthera.orgs:readorgs.read
POST/v1/admin/orgsnauthera.orgs:writeorgs.write
PATCH, DELETE/v1/admin/orgs/{slug}nauthera.orgs:writeorgs.write
GET/v1/admin/orgs/{slug}/themenauthera.orgs:readorgs.read
GET/v1/admin/orgs/{slug}/mailnauthera.orgs:readorgs.read
POST/v1/admin/orgs/{slug}/mail/testnauthera.orgs:writeorgs.write
GET/v1/admin/orgs/{slug}/snapshotnauthera.insights:readinsights.read
GET/v1/admin/orgs/{slug}/identity-providersnauthera.orgs:readorgs.read
GET/v1/admin/orgs/{slug}/problems, /v1/admin/orgs/{slug}/problems/{id}, /v1/admin/problems, /v1/admin/problems/{id}nauthera.orgs:readorgs.read
GET/v1/admin/orgs/{slug}/broker-membershipsnauthera.orgs:readorgs.read
POST/v1/admin/orgs/{slug}/broker-memberships, /v1/admin/orgs/{slug}/broker-memberships/{id}/acceptnauthera.brokers:writebrokers.manage, recent sign-in
PUT/v1/admin/orgs/{slug}/broker-memberships/{id}/clientsnauthera.brokers:writebrokers.manage, recent sign-in
DELETE/v1/admin/orgs/{slug}/broker-memberships/{id}nauthera.brokers:writebrokers.manage, recent sign-in
GET/v1/admin/orgs/{slug}/email-domainsnauthera.orgs:readorgs.read
POST/v1/admin/orgs/{slug}/email-domains, /v1/admin/orgs/{slug}/email-domains/{id}/verifynauthera.domains:writedomains.manage, recent sign-in
DELETE/v1/admin/orgs/{slug}/email-domains/{id}nauthera.domains:writedomains.manage, recent sign-in
GET/v1/admin/orgs/{slug}/clients, /v1/admin/orgs/{slug}/clients/{id}nauthera.clients:readclients.read
GET/v1/admin/orgs/{slug}/clients/{id}/usagenauthera.insights:readinsights.read
GET/v1/admin/orgs/{slug}/users, /v1/admin/orgs/{slug}/users/{id}nauthera.users:readusers.read
POST/v1/admin/orgs/{slug}/usersnauthera.users:writeusers.create
PATCH/v1/admin/orgs/{slug}/users/{id}nauthera.users:writeusers.update
DELETE/v1/admin/orgs/{slug}/users/{id}nauthera.users:writeusers.delete, recent sign-in
PUT/v1/admin/orgs/{slug}/users/{id}/passwordnauthera.users:writeusers.credentials, recent sign-in
POST/v1/admin/orgs/{slug}/users/{id}/password-changenauthera.users:writeusers.credentials, recent sign-in
POST/v1/admin/orgs/{slug}/users/{id}/password-resetnauthera.users:writeusers.credentials
POST/v1/admin/orgs/{slug}/users/{id}/suspend, /block, /reactivate, /unlocknauthera.users:writeusers.lifecycle, recent sign-in
POST/v1/admin/orgs/{slug}/users/{id}/invitationnauthera.users:writeusers.create
GET/v1/admin/orgs/{slug}/users/{id}/identitiesnauthera.users:readusers.read
GET/v1/admin/orgs/{slug}/users/{id}/rolesnauthera.roles:readroles.read
PUT, DELETE/v1/admin/orgs/{slug}/users/{id}/roles/{role}nauthera.roles:writeroles.assign, recent sign-in
GET/v1/admin/orgs/{slug}/users/{id}/devicesnauthera.sessions:readsessions.read
DELETE/v1/admin/orgs/{slug}/users/{id}/devices/rememberednauthera.sessions:writesessions.revoke, recent sign-in
GET/v1/admin/orgs/{slug}/sessions, /v1/admin/orgs/{slug}/users/{id}/session-historynauthera.sessions:readsessions.read
DELETE/v1/admin/orgs/{slug}/sessions/{sid}nauthera.sessions:writesessions.revoke
DELETE/v1/admin/orgs/{slug}/users/{id}/sessionsnauthera.sessions:writesessions.revoke, recent sign-in
GET/v1/admin/orgs/{slug}/audit-events, /v1/admin/orgs/{slug}/audit-events/tail, /v1/admin/orgs/{slug}/audit-events/top, /v1/admin/orgs/{slug}/audit-events/{id}, /v1/admin/audit-catalognauthera.audit:readaudit.read
GET/v1/admin/orgs/{slug}/audit-events/countsnauthera.insights:readinsights.read
GET/v1/admin/orgs/{slug}/threats, /v1/admin/orgs/{slug}/threats/sign-insnauthera.threats:readthreats.read
GET/v1/admin/deployment/client-ipnauthera.insights:readinsights.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_role_administrator. 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_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 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://nauthera.io/problems/<code>, 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":"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)

PathPurpose
/metricsPrometheus exposition
/healthzLiveness
/readyzReadiness
/startupzStartup
/debug/pprof/*Only when server.admin.pprof: true

gRPC listener (:9091)

grpc.health.v1.Health and nauthera.admin.v1.AdminService. See Architecture.