This page covers the sign-in methods Govern runs itself. Sign-in through your own identity provider is on Single sign-on with SAML and Single sign-on with OIDC.
Sign-in methods
| Method | Status | Notes |
|---|---|---|
| Email and password | Working | Hosted sign-in at /oauth2/authorize; API sign-in at POST /api/v1/auth/login. Passwords are hashed with bcrypt. |
| TOTP second factor | Working | Authenticator apps, with one-time recovery codes. |
| Passkeys (WebAuthn) | Working | Registration, passwordless sign-in and use as a second factor. |
| Single sign-on | Working | SAML 2.0 and OpenID Connect connections in Identity Center. |
| Social sign-in | Working | Google, GitHub and Microsoft, per organisation. |
| Device authorisation grant (RFC 8628) | Working | For the Neural Inverse apps and CLI on machines without a browser: the app shows a code and the person approves it on the console's device page. |
| Magic link | Partial | The emailed link does not issue a session. Do not rely on it. |
Failed sign-ins answer the same way whether or not the account exists. Brute-force protection runs inside password sign-in, passkey sign-in, the hosted sign-in page, the email routes and root step-up.
Password policy
Each organisation has a password policy, read with GET /api/v1/organizations/{id}/password-policy (organizations:read) and changed with PUT on the same path (organizations:update).
| Field | Meaning |
|---|---|
min_length, max_length | Length bounds |
require_uppercase, require_lowercase, require_digit, require_special | Character classes |
history_count | How many previous passwords may not be reused |
max_age_days | Maximum password age |
lockout_threshold | Failed attempts before lockout |
lockout_duration_minutes | Lockout length |
POST /api/v1/auth/password/validate checks a candidate password against the policy without changing anything. People change their own password with POST /api/v1/auth/password/change (the current password is required).
TOTP and recovery codes
A person enrols an authenticator app from their own security page in the console, or through the API:
| Route | Purpose |
|---|---|
POST /api/v1/users/{id}/totp/enroll | Replace any pending factor and return a one-time secret and an otpauth:// URI |
POST /api/v1/users/{id}/totp/verify | Verify a six-digit code and turn the factor on |
GET /api/v1/users/{id}/totp | Enrolment status |
DELETE /api/v1/users/{id}/totp | Turn the factor off and remove it |
POST /api/v1/users/{id}/recovery-codes | Generate recovery codes |
GET /api/v1/users/{id}/recovery-codes/count | Remaining recovery codes |
TOTP secrets are encrypted with AES-256-GCM under the deployment's NI_AUTH_TOTP_ENCRYPTION_KEY. Codes accept one 30-second step of clock skew, and a code that has been used once cannot be replayed.
Passkeys
| Route | Purpose |
|---|---|
POST /api/v1/passkeys/register/begin, /complete | Register a passkey for the signed-in person; the attestation is verified |
GET /api/v1/users/{id}/passkeys | List a person's passkeys |
PATCH /api/v1/users/{id}/passkeys/{credentialId} | Rename one |
DELETE /api/v1/users/{id}/passkeys/{credentialId} | Delete one |
POST /api/v1/auth/passkeys/authenticate/begin, /complete | Passwordless sign-in, email first |
A person manages their own passkeys without any extra permission. An administrator needs users:read to list another person's passkeys, users:update to rename one and users:delete to delete one, and the person must belong to the administrator's organisation (and to no other organisation for a rename or delete). Anyone else gets 404. Administrator actions and refusals are audited (auth.passkey.admin_listed, auth.passkey.renamed, auth.passkey.deleted, auth.passkey.access_denied).
Sessions and tokens
The operator sets session limits with environment variables:
| Variable | Default | Meaning |
|---|---|---|
NI_AUTH_ACCESS_TOKEN_TTL | 15m | Access token lifetime |
NI_AUTH_REFRESH_TOKEN_TTL | 720h | Refresh token lifetime (30 days) |
NI_AUTH_SESSION_IDLE_TIMEOUT | 168h | A session nobody used for this long ends |
NI_AUTH_CONSOLE_SESSION_IDLE_TIMEOUT | 1h | Idle timeout of console sessions |
NI_AUTH_CONSOLE_SESSION_MAX_LIFETIME | 12h | Absolute lifetime of console sessions, at most 12h |
Console limits apply to the OAuth client named by NI_AUTH_CONSOLE_CLIENT_ID. Identity Center connections can set their own, shorter limits; see Single sign-on with SAML.
| Route | Purpose |
|---|---|
POST /api/v1/auth/logout | End the current session |
POST /api/v1/auth/token/refresh | Refresh an access token |
POST /api/v1/auth/token/revoke | Revoke a token (RFC 7009) |
POST /api/v1/auth/token/introspect | Introspect a token (RFC 7662); requires client authentication |
POST /api/v1/auth/sessions/revoke | End one session |
POST /api/v1/auth/sessions/revoke-all | End every session of the caller |
Signing out of a session issued through a connection whose identity provider has a logout endpoint answers with an end_session_url, and the client sends the browser there.
Where Govern asks for a fresh second factor
The general step-up authentication package in the source is not mounted, so there is no single step-up setting. These flows ask for a fresh factor themselves:
| Flow | What is required |
|---|---|
| Activating a just-in-time or break-glass role | A fresh TOTP code in the activation request. See Just-in-time access. |
| Transferring a verified domain, designating emergency administrators | A session whose MFA is at most 15 minutes old, or an MfaCode in the request (403 STEP_UP_REQUIRED otherwise). |
| Privileged actions in an SSO session | Re-authentication at the identity provider when the last authentication is older than the connection's re-authentication window (reauthentication_required). |
| Owner or Admin actions in an SSO session, when the connection requires identity provider MFA | An authentication that carried the configured amr or acr values (idp_mfa_required). |
| Root user tasks | POST /api/v1/auth/root/step-up re-authenticates the caller's own login. |
| Break-glass sign-in while SSO is required | A TOTP code or passkey after the password, always. |
Conditions on role bindings can read request.mfa_age_seconds, but HTTP requests do not report it yet, so a condition that reads it evaluates to an error: a deny that requires recent MFA applies, and an allow that requires it does not grant.
Risk-based sign-in
Risk policies are managed through POST/PUT/DELETE /api/v1/risk/policies* with security:manage and read with security:read; the console shows them read-only on the Security Hub risk policies page. A person may read their own risk history; reading someone else's needs security:investigate.