Skip to content
Beta — Truss is in public beta. Documentation is actively updated but may not reflect the latest changes. Report issues on GitHub.

Authentication

Truss provides a self-service authentication system powered by Ory Kratos. It covers login, registration, account recovery, the settings flow, and multi-factor authentication (TOTP, WebAuthn security keys, recovery codes), plus passwordless options (passkeys and magic links). Everything is available via API and client SDKs.

Admin identity management (listing, creating, banning, or impersonating users) is not part of the open-source core. You can read identities through the service-role GET /v1/auth/identities endpoint or manage them directly via the Ory Kratos Admin API. Full admin identity management is a Truss Cloud feature.

Set these environment variables in apps/api/.env:

KRATOS_PUBLIC_URL=http://localhost:4433
KRATOS_ADMIN_URL=http://localhost:4434
KRATOS_ADMIN_TOKEN=your-admin-token
TRUSS_AUTH_REQUIRED=true

With TRUSS_AUTH_REQUIRED=true, the dashboard requires login. Set to false for local development without auth.

Optional configuration:

# Social/OIDC providers (comma-separated)
KRATOS_OIDC_PROVIDERS=google,github,apple,microsoft
# Identity schema ID (defaults to "default")
KRATOS_IDENTITY_SCHEMA_ID=default

The standard credential-based login flow. Truss uses Kratos API flows (not browser flows) to avoid CSRF issues when the frontend and backend are on different origins.

Dashboard: Authentication > Overview (login form)

Flow:

  1. Frontend calls GET /api/auth/login to initialize a Kratos login flow
  2. User submits credentials via POST /api/auth/login
  3. Server stores the session token in an HttpOnly cookie (truss_session)
  4. Subsequent requests are authenticated via the cookie
Terminal window
# 1. Initialize login flow
curl http://localhost:8787/api/auth/login
# 2. Submit credentials
curl -X POST http://localhost:8787/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "securepassword123"
}'

Time-based one-time password MFA using apps like Google Authenticator or Authy. Users can set up, verify, and remove TOTP from the settings page.

Dashboard: Authentication > Settings (MFA section)

API Endpoints:

MethodPathDescription
GET/api/auth/mfa/statusGet current MFA status (TOTP enabled, WebAuthn devices, recovery codes)
POST/api/auth/mfa/totp/setupStart TOTP setup — returns QR code URI and secret
POST/api/auth/mfa/totp/verifyVerify TOTP code to complete setup
DELETE/api/auth/mfa/totpRemove TOTP from the account
// Check MFA status
const status = await fetch(`${TRUSS_URL}/api/auth/mfa/status`, {
credentials: "include",
}).then(r => r.json());
// { totp: true, webauthn: false, recovery_codes: true, devices: [] }
// Start TOTP setup
const setup = await fetch(`${TRUSS_URL}/api/auth/mfa/totp/setup`, {
method: "POST",
credentials: "include",
}).then(r => r.json());
// { totpUrl: "otpauth://totp/Truss:user@example.com?secret=...", secret: "JBSWY3DPEHPK3PXP" }
// Verify TOTP code
await fetch(`${TRUSS_URL}/api/auth/mfa/totp/verify`, {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ totp_code: "123456" }),
});
// Remove TOTP
await fetch(`${TRUSS_URL}/api/auth/mfa/totp`, {
method: "DELETE",
credentials: "include",
});

Hardware security key support (YubiKey, etc.) via the FIDO2/WebAuthn protocol. Setup, verification, and removal are handled through the settings flow.

Dashboard: Authentication > Settings (MFA section)

API Endpoints:

MethodPathDescription
POST/api/auth/mfa/webauthn/setupStart WebAuthn registration — returns credential creation options
POST/api/auth/mfa/webauthn/verifyComplete WebAuthn registration with attestation response
DELETE/api/auth/mfa/webauthnRemove WebAuthn credential
// Start WebAuthn setup — returns options for navigator.credentials.create()
const options = await fetch(`${TRUSS_URL}/api/auth/mfa/webauthn/setup`, {
method: "POST",
credentials: "include",
}).then(r => r.json());
// Browser handles the key interaction
const credential = await navigator.credentials.create({
publicKey: options.publicKey,
});
// Complete registration
await fetch(`${TRUSS_URL}/api/auth/mfa/webauthn/verify`, {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
webauthn_register_displayname: "My YubiKey",
webauthn_register: JSON.stringify(credential),
}),
});
// Remove WebAuthn credential
await fetch(`${TRUSS_URL}/api/auth/mfa/webauthn`, {
method: "DELETE",
credentials: "include",
});

Backup codes for account recovery when MFA devices are unavailable. Generate a set of one-time codes, confirm them, or revoke them.

Dashboard: Authentication > Settings (MFA section)

API Endpoints:

MethodPathDescription
POST/api/auth/mfa/recovery-codes/generateGenerate a new set of recovery codes
POST/api/auth/mfa/recovery-codes/confirmConfirm codes have been saved (activates them)
DELETE/api/auth/mfa/recovery-codesRevoke all recovery codes
Terminal window
# Generate recovery codes
curl -X POST http://localhost:8787/api/auth/mfa/recovery-codes/generate \
-H "Cookie: truss_session=your-session-token"
# Returns: { "codes": ["abc123", "def456", ...] }
# Confirm codes saved
curl -X POST http://localhost:8787/api/auth/mfa/recovery-codes/confirm \
-H "Cookie: truss_session=your-session-token"
# Revoke all codes
curl -X DELETE http://localhost:8787/api/auth/mfa/recovery-codes \
-H "Cookie: truss_session=your-session-token"

Passwordless FIDO2/WebAuthn assertion flow. Users can sign in with biometrics or a security key without entering a password.

Dashboard: Authentication > Login (passkey option)

API Endpoints:

MethodPathDescription
GET/api/auth/login/passkeyInitialize a passkey login flow — returns assertion options
POST/api/auth/login/passkeyComplete passkey login with assertion response
// Initialize passkey login
const options = await fetch(`${TRUSS_URL}/api/auth/login/passkey`).then(r => r.json());
// Browser handles the key interaction
const assertion = await navigator.credentials.get({
publicKey: options.publicKey,
});
// Complete login
const session = await fetch(`${TRUSS_URL}/api/auth/login/passkey`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ webauthn_login: JSON.stringify(assertion) }),
}).then(r => r.json());

Passwordless login via a one-time code sent to the user’s email address. The user enters the code to complete authentication.

This is configured in Kratos as the code strategy. The flow uses the standard Kratos settings flow with method: "code".

Dashboard: Authentication > Login

Email-based passwordless login. The user receives a link that authenticates them when clicked.

Dashboard: Authentication > Login (magic link option)

API Endpoints:

MethodPathDescription
POST/api/auth/login/magic-linkSend a magic link to the user’s email
Terminal window
# Send magic link
curl -X POST http://localhost:8787/api/auth/login/magic-link \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com"}'

The Kratos link strategy handles token generation, email delivery, and session creation when the link is clicked.

Connect 18+ social identity providers for one-click sign-in.

Supported providers: Google, GitHub, Apple, Microsoft, Discord, GitLab, Facebook, Twitter/X, LinkedIn, Slack, Spotify, Twitch, Bitbucket, Dropbox, Yandex, VK, Dingtalk, and any custom OIDC provider.

Configuration:

# Enable providers (comma-separated)
KRATOS_OIDC_PROVIDERS=google,github,apple,microsoft
# Each provider needs its own credentials in the Kratos config:
# - client_id
# - client_secret
# - issuer_url (for generic OIDC)
# - scope
# - mapper_url (Jsonnet identity mapping)

Once configured in Kratos, OIDC providers surface as login options in the standard Kratos login flow returned by GET /api/auth/login.


Admin identity operations (listing, creating, updating, banning, or impersonating users) are not part of the open-source core. You have two options:

  • Read-only via Truss: the service-role GET /v1/auth/identities endpoint lists identities and GET /v1/auth/identities/:id returns a single identity (see Client API below).
  • Full management via Kratos: create, update, delete, and manage identities directly through the Ory Kratos Admin API using KRATOS_ADMIN_URL.

Full admin identity management (a user-management GUI, bulk import/export, impersonation, bans, session administration, and login-history analytics) is a Truss Cloud feature.


Truss integrates with the Have I Been Pwned (HIBP) API to check passwords against known data breaches. When enabled, users cannot set passwords that appear in breach databases.

This is configured in Kratos:

kratos.yml
selfservice:
methods:
password:
config:
haveibeenpwned_enabled: true

Configure minimum password length, similarity checks, and other rules. Password policy is set in the Kratos configuration file.

Kratos password policy options:

selfservice:
methods:
password:
config:
min_password_length: 8
identifier_similarity_check_enabled: true
haveibeenpwned_enabled: true
max_breaches: 0 # Reject any breached password

Require the highest available authentication level. When enabled, users with MFA configured must always provide their second factor.

Configured in the Kratos identity schema via aal (Authenticator Assurance Level):

  • aal1 — Password only
  • aal2 — Password + second factor required

Kratos natively protects against user enumeration attacks. Login and registration flows return identical responses whether an account exists or not, preventing attackers from discovering valid email addresses.

This is enabled by default in Kratos and requires no additional configuration.

Flow TTL limits throttle automated login attempts. Each Kratos flow has a configurable time-to-live, and expired flows must be re-initialized.

kratos.yml
selfservice:
flows:
login:
lifespan: 10m # Flow expires after 10 minutes
registration:
lifespan: 10m

Users recover their own accounts through the self-service recovery flow (powered by the Kratos link or code strategy). Recovery is a two-step flow: initialize it, then submit the email along with the returned flowId.

API Endpoints:

MethodPathDescription
GET/api/auth/recoveryInitialize a recovery flow
POST/api/auth/recoverySubmit the recovery request (email → code/link → new password)
Terminal window
# 1. Initialize a recovery flow (returns a flow with an "id")
curl http://localhost:8787/api/auth/recovery
# 2. Submit the recovery request
curl -X POST http://localhost:8787/api/auth/recovery \
-H "Content-Type: application/json" \
-d '{"flowId": "<flow-id>", "email": "user@example.com", "method": "link"}'

Email delivery (recovery, verification, welcome) and the email templates are configured in Kratos via its courier settings and Jsonnet/HTML templates. See the Ory Kratos email docs for template customization.


Auto-login after registration. When configured, users are automatically signed in after completing the registration flow (no separate login step).

This is a Kratos after-registration hook:

kratos.yml
selfservice:
flows:
registration:
after:
password:
hooks:
- hook: session

Copy-paste authentication components for common frameworks. Available in the dashboard under Authentication > SDK tab.

import { useState } from "react";
function LoginForm({ onSuccess }) {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState(null);
const handleSubmit = async (e) => {
e.preventDefault();
const res = await fetch("/api/auth/login", { method: "GET" });
const { flow_id } = await res.json();
const login = await fetch("/api/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ flow_id, email, password }),
});
if (login.ok) onSuccess(await login.json());
else setError("Invalid credentials");
};
return (
<form onSubmit={handleSubmit}>
<input type="email" value={email} onChange={e => setEmail(e.target.value)} placeholder="Email" />
<input type="password" value={password} onChange={e => setPassword(e.target.value)} placeholder="Password" />
{error && <p style={{ color: "red" }}>{error}</p>}
<button type="submit">Sign in</button>
</form>
);
}

Complete SDK examples for all 6 core auth flows (sign up, sign in, get session, update settings, recovery, logout) in 4 languages.

Dashboard: Authentication > SDK tab

import { Configuration, FrontendApi } from "@ory/client";
const kratos = new FrontendApi(new Configuration({
basePath: "http://localhost:4433",
baseOptions: { withCredentials: true },
}));
// Sign up
const { data: flow } = await kratos.createBrowserRegistrationFlow();
await kratos.updateRegistrationFlow({
flow: flow.id,
updateRegistrationFlowBody: {
method: "password",
password: "securePass123",
traits: { email: "user@example.com" },
},
});
// Sign in
const { data: loginFlow } = await kratos.createBrowserLoginFlow();
await kratos.updateLoginFlow({
flow: loginFlow.id,
updateLoginFlowBody: {
method: "password",
identifier: "user@example.com",
password: "securePass123",
},
});
// Get current session
const { data: session } = await kratos.toSession();
console.log(session.identity.traits.email);
// Logout
const { data: logoutFlow } = await kratos.createBrowserLogoutFlow();
await kratos.updateLogoutFlow({ token: logoutFlow.logout_token });

Authentication actions are logged to the audit trail. Query them by action type, search term, or date range via the client API.

API Endpoint:

MethodPathDescription
GET/v1/audit-logsQuery audit logs (requires service_role API key)
Terminal window
# Query audit logs via client API
curl "http://localhost:8787/v1/audit-logs?action=auth.login&limit=50" \
-H "apikey: truss_sk_your_key"

Logged actions include: auth.login, auth.register, auth.logout, auth.mfa.totp.setup, auth.mfa.webauthn.setup, and more.


The client API provides identity management for external tools and scripts, authenticated via API key rather than session cookie.

Base path: /v1/auth/

MethodPathDescription
GET/v1/auth/identitiesList identities (requires service_role key)
GET/v1/auth/identities/:idGet identity detail (requires service_role key)
const API_KEY = "truss_sk_your_service_role_key";
// List identities
const users = await fetch("http://localhost:8787/v1/auth/identities", {
headers: { apikey: API_KEY },
}).then(r => r.json());
// Get identity detail
const user = await fetch(`http://localhost:8787/v1/auth/identities/${userId}`, {
headers: { apikey: API_KEY },
}).then(r => r.json());

Users can update their own profile, password, and MFA settings through the settings flow.

Dashboard: User menu > Settings

API Endpoints:

MethodPathDescription
GET/api/auth/settingsInitialize a settings flow
POST/api/auth/settingsUpdate settings (profile, password, MFA)
Terminal window
# Initialize settings flow
curl http://localhost:8787/api/auth/settings \
-H "Cookie: truss_session=your-session-token"
# Update password
curl -X POST http://localhost:8787/api/auth/settings \
-H "Content-Type: application/json" \
-H "Cookie: truss_session=your-session-token" \
-d '{
"method": "password",
"password": "newSecurePassword123"
}'
# Update profile traits
curl -X POST http://localhost:8787/api/auth/settings \
-H "Content-Type: application/json" \
-H "Cookie: truss_session=your-session-token" \
-d '{
"method": "profile",
"traits": {"email": "newemail@example.com", "name": "Alice"}
}'

API Endpoints:

MethodPathDescription
GET/api/auth/registerInitialize a registration flow
POST/api/auth/registerComplete registration
Terminal window
# Initialize registration
curl http://localhost:8787/api/auth/register
# Register with email + password
curl -X POST http://localhost:8787/api/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "newuser@example.com",
"password": "securePass123"
}'