Complete reference for every endpoint. All endpoints return JSON. All request bodies are JSON unless noted otherwise.
Base URL: https://your-simpleauth-server:port (if AUTH_BASE_PATH is set, e.g., /auth, prefix all paths: https://your-simpleauth-server:port/auth/api/...)
Authentication types:
- Admin Key --
Authorization: Bearer YOUR_ADMIN_KEY(the master admin key from config) - Bearer Token --
Authorization: Bearer ACCESS_TOKEN(a JWT access token from login) - None -- No authentication required
Admin access: All admin endpoints require the master ADMIN_KEY. There is no admin role — admin access is controlled exclusively by the admin key.
Auth: None
Health check endpoint.
curl -k https://localhost:8080/health{"status": "ok"}Auth: Admin Key
Returns server configuration details.
curl -k -H "Authorization: Bearer ADMIN_KEY" \
https://localhost:8080/api/admin/server-info{
"hostname": "auth.corp.local",
"deployment_name": "sauth",
"jwt_issuer": "simpleauth",
"version": "dev",
"redirect_uri": "https://myapp.example.com/callback"
}Auth: None
Authenticate a user with username/password. Tries local password first, falls back to LDAP (if configured). Local users always take priority — SimpleAuth owns those credentials.
Request:
{
"username": "jsmith",
"password": "secret"
}Response (200):
{
"access_token": "eyJ...",
"refresh_token": "eyJ...",
"user": {
"guid": "550e8400-e29b-41d4-a716-446655440000",
"display_name": "John Smith",
"email": "jsmith@corp.local",
"department": "Engineering",
"company": "Acme Corp",
"job_title": "Senior Engineer",
"roles": ["admin", "user"],
"permissions": ["read:reports", "write:config"],
"groups": ["Engineering", "IT"]
}
}When the user has a forced password change pending, the response includes an additional field:
{
"access_token": "eyJ...",
"refresh_token": "eyJ...",
"force_password_change": true,
"user": { ... }
}Clients should check for force_password_change: true and redirect the user to a password change flow before allowing normal access.
Error responses:
400--{"error": "username and password required"}401--{"error": "invalid credentials"}403--{"error": "account disabled"}429--{"error": "too many login attempts"}(withRetry-Afterheader)
Auth: None
Exchange a refresh token for a new access token. Implements refresh token rotation with family-based reuse detection.
Request:
{
"refresh_token": "eyJ..."
}Response (200):
{
"access_token": "eyJ...(new)",
"refresh_token": "eyJ...(new, rotated)"
}Error responses:
401--{"error": "invalid refresh token"}(expired or malformed)401--{"error": "token reuse detected, all sessions revoked"}(replay attack detected)
Security note: If a refresh token is reused (replayed), SimpleAuth revokes the entire token family, invalidating all sessions for that login. This protects against token theft.
Auth: Bearer Token
Returns user information from a valid access token.
curl -k -H "Authorization: Bearer ACCESS_TOKEN" \
https://localhost:8080/api/auth/userinfoResponse (200):
{
"guid": "550e8400-...",
"preferred_username": "jsmith",
"display_name": "John Smith",
"email": "jsmith@corp.local",
"department": "Engineering",
"company": "Acme Corp",
"job_title": "Software Engineer",
"roles": ["admin"],
"permissions": ["read:reports"],
"groups": ["Engineering"],
"auth_source": "ldap"
}| Field | Description |
|---|---|
preferred_username |
Local username, falls back to email or display name |
auth_source |
"local" for password-based users, "ldap" for AD/LDAP users |
Auth: Admin Key (master admin key only)
Generate an access token for any user. Useful for testing and support scenarios.
Request:
{
"target_guid": "550e8400-..."
}Response (200):
{
"access_token": "eyJ...",
"expires_in": 3600,
"token_type": "Bearer",
"impersonated": true
}The token has a shorter TTL (impersonate_ttl, default 1 hour).
Auth: None (Kerberos SPNEGO)
Kerberos/SPNEGO authentication endpoint. The client sends an Authorization: Negotiate <base64-token> header. On success, returns JWT tokens.
Response (200):
{
"access_token": "eyJ...",
"refresh_token": "eyJ...",
"user": { ... }
}Error responses:
401--WWW-Authenticate: Negotiateheader (prompts browser to send Kerberos ticket)
Auth: Bearer Token
Change the authenticated user's password. Requires a valid access token. If the user already has a password set, the current password must be provided -- unless force_password_change is set on the user, in which case current_password is not required.
Enforces the configured password policy (minimum length, complexity requirements). Rejects passwords that appear in the user's password history (based on history_count setting). On success, clears the force_password_change flag.
Request:
{
"current_password": "oldpass",
"new_password": "newpass"
}Response (200):
{"status": "password updated"}Error responses:
401--{"error": "authorization required"}or{"error": "invalid token"}400--{"error": "new_password required"}or{"error": "new_password must be at least 6 characters"}400--{"error": "password does not meet policy requirements: ..."}(policy violation details)400--{"error": "password was recently used"}(password history check)403--{"error": "current password is incorrect"}
Auth: None
Redirects to /login.
Auth: None
Hosted login page. Renders a branded login form and handles form submission. If Kerberos is configured, shows an "Sign in with SSO" button.
On successful login without a redirect_uri, the user is redirected to /account with tokens in the URL fragment.
Auth: None
Logout endpoint. Clears SSO cookies and redirects to the login page with manual=1 to prevent auto-SSO from immediately logging the user back in.
Query parameters:
redirect_uri-- Passed through to the login page redirect
Behavior:
GET /logout?redirect_uri=https://myapp.example.com/callback
-> clears SSO cookies
-> 302 redirect to /login?manual=1&redirect_uri=https://myapp.example.com/callback
This is the recommended logout path for apps using auto-SSO (AUTH_AUTO_SSO=true). If your app redirects to /login after logout, auto-SSO may immediately re-authenticate the user without showing the login form. Redirecting to /logout instead ensures the SSO session is cleared first.
Not a breaking change.
/logincontinues to work as before./logoutis an additive endpoint.
Auth: None (Kerberos SPNEGO)
SSO login endpoint for the hosted login flow. Attempts Kerberos/SPNEGO authentication. On success, redirects to redirect_uri (or /account) with tokens in the URL fragment. On failure, redirects back to /login with an error message instead of hanging.
Query parameters:
redirect_uri-- Where to redirect after successful SSO login
Auth: None (page loads, then authenticates via JavaScript)
User self-service page. Shows the authenticated user's profile information and allows password changes.
- Profile view -- display name, email, username, department, company, job title, roles
- Password change -- current password + new password (uses
POST /api/auth/reset-passwordinternally) - LDAP users -- password change form is hidden; shows a note that their password is managed by the directory
The page reads the access token from the URL fragment (after login redirect) or from sessionStorage.
Auth: None
Diagnostic page for testing Kerberos/SPNEGO authentication. Shows the raw negotiation flow and results. Useful for troubleshooting SSO configuration.
SimpleAuth implements standard OIDC endpoints. All official SDKs (Go, JavaScript, Python, .NET) use the direct API endpoints by default (POST /api/auth/login, POST /api/auth/refresh, GET /.well-known/jwks.json, GET /api/auth/userinfo), but the OIDC endpoints are fully supported for use with any standard OIDC library.
All OIDC endpoints follow the URL pattern: /realms/{realm}/protocol/openid-connect/...
The realm defaults to your jwt_issuer config value (default: simpleauth). The client_id is hardcoded to simpleauth.
Auth: None
OIDC Discovery document. Also available at /realms/{realm}/.well-known/openid-configuration.
curl -k https://localhost:8080/.well-known/openid-configurationResponse (200):
{
"issuer": "https://localhost:8080/realms/simpleauth",
"authorization_endpoint": "https://localhost:8080/realms/simpleauth/protocol/openid-connect/auth",
"token_endpoint": "https://localhost:8080/realms/simpleauth/protocol/openid-connect/token",
"userinfo_endpoint": "https://localhost:8080/realms/simpleauth/protocol/openid-connect/userinfo",
"jwks_uri": "https://localhost:8080/realms/simpleauth/protocol/openid-connect/certs",
"introspection_endpoint": "https://localhost:8080/realms/simpleauth/protocol/openid-connect/token/introspect",
"end_session_endpoint": "https://localhost:8080/realms/simpleauth/protocol/openid-connect/logout",
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "client_credentials", "password", "refresh_token"],
"subject_types_supported": ["public"],
"id_token_signing_alg_values_supported": ["RS256"],
"scopes_supported": ["openid", "profile", "email", "roles"],
"token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post"],
"claims_supported": [
"sub", "iss", "aud", "exp", "iat", "name", "email",
"preferred_username", "realm_access",
"department", "company", "job_title", "groups"
]
}Auth: None
JSON Web Key Set. Contains the RSA public keys used to sign JWTs. Also available at /realms/{realm}/protocol/openid-connect/certs.
curl -k https://localhost:8080/.well-known/jwks.jsonResponse (200):
{
"keys": [
{
"kty": "RSA",
"use": "sig",
"kid": "key-id-here",
"alg": "RS256",
"n": "...",
"e": "AQAB"
}
]
}Auth: None
OAuth2 Authorization endpoint. Renders the hosted login page for the authorization code flow.
Query parameters:
client_id(required) -- Must besimpleauthredirect_uri-- Where to redirect after login (must match one of the allowed redirect URIs configured viaAUTH_REDIRECT_URIand/orAUTH_REDIRECT_URIS; supports wildcard*suffix matching)response_type-- Must becodestate-- CSRF protection value (passed through)nonce-- Replay protection for ID tokensscope-- Space-separated scopes (e.g.,openid profile email)
https://auth.example.com/realms/simpleauth/protocol/openid-connect/auth?client_id=simpleauth&redirect_uri=https://myapp.com/callback&response_type=code&state=xyz
On successful login, redirects to redirect_uri?code=AUTH_CODE&state=xyz.
Auth: Client credentials (Basic auth or form post)
OAuth2 Token endpoint. Supports four grant types.
Client authentication methods:
- HTTP Basic:
Authorization: Basic base64(simpleauth:any-value) - Form body:
client_id=simpleauth
The client_id is hardcoded to simpleauth. The client_secret field is accepted but not validated.
curl -k -X POST \
https://localhost:8080/realms/simpleauth/protocol/openid-connect/token \
-u "simpleauth:" \
-d "grant_type=authorization_code&code=AUTH_CODE&redirect_uri=https://myapp.com/callback"curl -k -X POST \
https://localhost:8080/realms/simpleauth/protocol/openid-connect/token \
-u "simpleauth:" \
-d "grant_type=password&username=jsmith&password=secret&scope=openid profile email"curl -k -X POST \
https://localhost:8080/realms/simpleauth/protocol/openid-connect/token \
-u "simpleauth:" \
-d "grant_type=client_credentials"curl -k -X POST \
https://localhost:8080/realms/simpleauth/protocol/openid-connect/token \
-u "simpleauth:" \
-d "grant_type=refresh_token&refresh_token=eyJ..."Response (200) -- all grant types:
{
"access_token": "eyJ...",
"refresh_token": "eyJ...",
"id_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 28800,
"scope": "openid profile email"
}Error responses (OAuth2 format):
{
"error": "invalid_grant",
"error_description": "invalid credentials"
}Error codes: invalid_request, invalid_client, invalid_grant, unsupported_grant_type, server_error
Auth: Bearer Token
OIDC UserInfo endpoint.
curl -k -H "Authorization: Bearer ACCESS_TOKEN" \
https://localhost:8080/realms/simpleauth/protocol/openid-connect/userinfoResponse (200):
{
"sub": "550e8400-...",
"name": "John Smith",
"email": "jsmith@corp.local",
"preferred_username": "jsmith@corp.local",
"department": "Engineering",
"company": "Acme Corp",
"job_title": "Senior Engineer",
"roles": ["admin"],
"groups": ["Engineering"],
"realm_access": {"roles": ["admin"]}
}Auth: Client credentials
RFC 7662 Token Introspection. Validates a token and returns its claims.
curl -k -X POST \
https://localhost:8080/realms/simpleauth/protocol/openid-connect/token/introspect \
-u "simpleauth:" \
-d "token=eyJ..."Response (200) -- active token:
{
"active": true,
"sub": "550e8400-...",
"iss": "https://localhost:8080/realms/simpleauth",
"exp": 1700000000,
"iat": 1699971200,
"token_type": "Bearer",
"client_id": "simpleauth",
"scope": "openid profile email",
"preferred_username": "jsmith@corp.local",
"name": "John Smith",
"email": "jsmith@corp.local"
}Response (200) -- inactive/invalid token:
{"active": false}Auth: None
OIDC End Session endpoint. Revokes all sessions for the user.
Parameters (query or form):
id_token_hint-- The user's ID token (used to identify the user and revoke sessions)post_logout_redirect_uri-- Where to redirect after logout
curl -k -X POST \
https://localhost:8080/realms/simpleauth/protocol/openid-connect/logout \
-d "id_token_hint=eyJ...&post_logout_redirect_uri=https://myapp.com"Auth: Admin Key
List all users. Password hashes are stripped from the response.
Query Parameters:
| Parameter | Value | Description |
|---|---|---|
include |
identities |
Include each user's identity mappings as an identities array. Without this parameter, the identities field is omitted (backward compatible). |
curl -k -H "Authorization: Bearer ADMIN_KEY" \
https://localhost:8080/api/admin/usersWith identities:
curl -k -H "Authorization: Bearer ADMIN_KEY" \
"https://localhost:8080/api/admin/users?include=identities"Each entry in the identities array contains:
| Field | Type | Description |
|---|---|---|
provider |
string | Identity provider (e.g. "local", "ldap") |
external_id |
string | User identifier within that provider (e.g. "kalahmad") |
Example response with ?include=identities:
[
{
"guid": "abc-123...",
"display_name": "Khalefa Ahmad",
"email": "kalahmad@corp.local",
"identities": [
{"provider": "local", "external_id": "kalahmad"},
{"provider": "ldap", "external_id": "kalahmad"}
]
}
]Auth: Admin Key
Create a local user.
Request:
{
"username": "jsmith",
"password": "a-strong-password",
"display_name": "John Smith",
"email": "jsmith@example.com",
"department": "Engineering",
"company": "Acme Corp",
"job_title": "Senior Engineer"
}The username field creates a local identity mapping (e.g., local:jsmith). The password field is optional — if omitted, the user can only authenticate via LDAP. If a password is provided, it must satisfy the configured password policy (minimum length, complexity requirements).
Response (201):
{
"guid": "550e8400-...",
"display_name": "John Smith",
"email": "jsmith@example.com"
}Auth: Admin Key
Get a single user by GUID.
Query Parameters:
| Parameter | Value | Description |
|---|---|---|
include |
identities |
Include the user's identity mappings as an identities array. Without this parameter, the identities field is omitted (backward compatible). |
curl -k -H "Authorization: Bearer ADMIN_KEY" \
"https://localhost:8080/api/admin/users/{guid}?include=identities"See GET /api/admin/users for the identities array format.
User object fields include:
| Field | Type | Description |
|---|---|---|
guid |
string | Unique user identifier |
display_name |
string | User's display name |
email |
string | User's email address |
department |
string | Department |
company |
string | Company |
job_title |
string | Job title |
disabled |
boolean | Whether the account is disabled |
force_password_change |
boolean | Whether the user must change password on next login |
failed_login_attempts |
integer | Number of consecutive failed login attempts |
locked_until |
datetime, nullable | Timestamp until which the account is locked (null if not locked) |
Auth: Admin Key
Update user fields. Only provided fields are updated.
Request:
{
"display_name": "Jonathan Smith",
"email": "jonathan.smith@example.com",
"department": "Platform",
"company": "Acme Corp",
"job_title": "Staff Engineer"
}Auth: Admin Key
Delete a user.
Auth: Admin Key
Set a user's password (admin override, no current password required).
Request:
{
"password": "new-password-here",
"force_change": true
}| Field | Description |
|---|---|
password |
The new password to set |
force_change |
Optional boolean. If true, the user will be required to change their password on next login |
Auth: Admin Key
Clears failed login attempts and lockout for a user. Use this to manually unlock an account that has been locked due to too many failed login attempts.
Response (200):
{"status": "ok"}Auth: Admin Key
Enable or disable a user account. Disabled users cannot log in.
Request:
{"disabled": true}Response (200):
{"guid": "550e8400-...", "disabled": true}Auth: Admin Key
Merge multiple user records into one. This is useful when the same person has separate accounts from different identity sources. Identity mappings, roles, and permissions are all merged.
Request:
{
"source_guids": ["guid-1", "guid-2"],
"display_name": "John Smith",
"email": "jsmith@corp.local"
}Response (200):
{
"merged_guid": "new-guid",
"sources": ["guid-1", "guid-2"]
}Auth: Admin Key
Reverse a merge operation. The user record has its merged_into pointer cleared.
Auth: Admin Key
List active sessions (non-expired, non-used refresh tokens) for a user.
Response (200):
[
{
"family_id": "fam-xxxx",
"created_at": "2024-01-15T10:30:00Z",
"expires_at": "2024-02-14T10:30:00Z"
}
]Auth: Admin Key
Revoke all sessions for a user. This revokes both refresh tokens and access tokens. Active access tokens are added to a blacklist and checked on every authenticated request, so revocation is immediate -- there is no waiting for token expiry. Forces the user to log in again everywhere.
SimpleAuth is the authority for roles and permissions -- they must be defined in the registries before they can be assigned to users. Use PUT /api/admin/role-permissions to define roles (and their associated permissions) and PUT /api/admin/permissions to define the master permissions list. On first startup, any roles or permissions already assigned to existing users are automatically registered into the respective registries.
Roles and permissions are global per SimpleAuth instance.
Auth: Admin Key
Get a user's roles.
Response (200):
["admin", "user"]Auth: Admin Key
Set a user's roles. Replaces the entire role list.
Note: All roles must be defined in the role registry first (via
PUT /api/admin/role-permissions), otherwise a400error is returned.
Request body: Array of strings.
["admin", "user", "manager"]Auth: Admin Key
Get a user's permissions.
Response (200):
["read:reports", "write:config"]Auth: Admin Key
Set a user's permissions.
Note: All permissions must be defined in the permissions registry first (via
PUT /api/admin/permissions), otherwise a400error is returned.
Request body: Array of strings.
["read:reports", "write:config", "delete:users"]Auth: Admin Key
Get default roles that are automatically assigned to new users when they first log in. Can also be set via the AUTH_DEFAULT_ROLES environment variable.
Auth: Admin Key
Set default roles for new users.
Note: Default roles must be defined in the role registry first (via
PUT /api/admin/role-permissions).
Request body: Array of strings.
["user", "viewer"]Auth: Admin Key
Get the role-to-permissions mapping. This defines which permissions are automatically granted by each role.
Auth: Admin Key
Set the role-to-permissions mapping.
Note: All permissions referenced in the mapping must be defined in the permissions registry first (via
PUT /api/admin/permissions).
Request body: Object mapping role names to permission arrays.
{
"admin": ["read:all", "write:all", "delete:all"],
"editor": ["read:all", "write:all"],
"viewer": ["read:all"]
}Auth: Admin Key
Returns all defined roles from the role registry.
curl -k -H "Authorization: Bearer ADMIN_KEY" \
https://localhost:8080/api/admin/roles["admin", "editor", "user", "viewer"]Auth: Admin Key
Returns all defined permissions from the permissions registry.
curl -k -H "Authorization: Bearer ADMIN_KEY" \
https://localhost:8080/api/admin/permissions["delete:all", "read:all", "write:all"]Auth: Admin Key
Sets the master permissions list. Replaces the entire permissions registry.
Request body: Array of strings.
["read:all", "write:all", "delete:all", "read:reports", "write:config"]Response (200):
["read:all", "write:all", "delete:all", "read:reports", "write:config"]Auth: Admin Key
Returns the current password policy configuration.
curl -k -H "Authorization: Bearer ADMIN_KEY" \
https://localhost:8080/api/admin/password-policyResponse (200):
{
"min_length": 8,
"require_uppercase": false,
"require_lowercase": false,
"require_digit": false,
"require_special": false,
"history_count": 0
}| Field | Description |
|---|---|
min_length |
Minimum password length |
require_uppercase |
Require at least one uppercase letter |
require_lowercase |
Require at least one lowercase letter |
require_digit |
Require at least one digit |
require_special |
Require at least one special character |
history_count |
Number of previous passwords to remember (0 = disabled) |
Auth: Admin Key
Idempotent endpoint for app startup. Defines permissions, roles, and ensures users exist with correct credentials. Safe to call on every boot -- designed for the "config is the source of truth" pattern.
All fields are optional. Only include what you need.
Request:
{
"permissions": ["posts:read", "posts:write", "users:manage", "admin:access"],
"role_permissions": {
"viewer": ["posts:read"],
"editor": ["posts:read", "posts:write"],
"admin": ["posts:read", "posts:write", "users:manage", "admin:access"]
},
"users": [
{
"username": "root",
"password": "from-env-var",
"display_name": "Root Admin",
"email": "root@example.com",
"roles": ["admin"],
"permissions": ["admin:access"],
"force_password": true
}
]
}| Field | Type | Description |
|---|---|---|
permissions |
string[] | Master permissions list. Replaces the permissions registry (same as PUT /api/admin/permissions) |
role_permissions |
object | Role-to-permissions mapping. Keys are role names, values are permission arrays (same as PUT /api/admin/role-permissions) |
users |
array | Users to ensure exist. See user fields below |
User fields:
| Field | Type | Description |
|---|---|---|
username |
string | Required. Local username. Used to look up the user via local identity mapping |
password |
string | Password to set. On new users, always set. On existing users, only set if force_password is true |
display_name |
string | Display name. Set on create; updated on existing users if provided |
email |
string | Email address. Set on create; updated on existing users if provided |
roles |
string[] | Roles to assign. Must be defined in role registry first (via permissions + role_permissions in the same request, or previously) |
permissions |
string[] | Direct permissions to assign. Must be defined in permissions registry first |
force_password |
boolean | If true, always reset the password (even if user already exists). If false or omitted, password is only set on newly created users |
User resolution: Each user is looked up by username via the local identity mapping. If no mapping exists, a new user is created with a local identity mapping.
Processing order: Permissions are defined first, then role-permissions, then users. This means you can define roles and permissions in the same request that assigns them to users.
curl -k -X POST \
https://localhost:8080/api/admin/bootstrap \
-H "Authorization: Bearer ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"permissions": ["posts:read", "posts:write", "admin:access"],
"role_permissions": {
"viewer": ["posts:read"],
"admin": ["posts:read", "posts:write", "admin:access"]
},
"users": [
{
"username": "root",
"password": "'"$ROOT_PASSWORD"'",
"display_name": "Root Admin",
"roles": ["admin"],
"force_password": true
}
]
}'Response (200):
{
"users": [
{
"username": "root",
"guid": "550e8400-e29b-41d4-a716-446655440000",
"created": false
}
],
"permissions_count": 3,
"role_permissions_count": 2
}| Field | Type | Description |
|---|---|---|
users |
array | One entry per user in the request. created is true if the user was newly created, false if it already existed |
permissions_count |
integer | Number of permissions defined (only present if permissions was provided) |
role_permissions_count |
integer | Number of roles defined (only present if role_permissions was provided) |
SimpleAuth supports a single LDAP/Active Directory configuration. All LDAP endpoints are under /api/admin/ldap (no provider IDs).
Auth: Admin Key
Get the current LDAP configuration. Returns null if not configured. The bind password is masked in the response.
Auth: Admin Key
Save or update the LDAP configuration.
Request:
{
"url": "ldaps://dc01.corp.local:636",
"base_dn": "DC=corp,DC=local",
"bind_dn": "CN=svc-sauth-prod,OU=Service Accounts,DC=corp,DC=local",
"bind_password": "ServiceAccountPassword",
"username_attr": "sAMAccountName",
"use_tls": true,
"skip_tls_verify": false,
"display_name_attr": "displayName",
"email_attr": "mail",
"department_attr": "department",
"company_attr": "company",
"job_title_attr": "title",
"groups_attr": "memberOf"
}| Field | Description |
|---|---|
username_attr |
LDAP attribute for username lookup. Common values: sAMAccountName (AD), userPrincipalName (AD), uid (OpenLDAP), mail |
custom_filter |
Optional. Advanced LDAP filter with {{username}} placeholder. Overrides username_attr when set. Example: (&(objectClass=person)(sAMAccountName={{username}})) |
domain |
Optional. AD domain name (e.g., corp.local). Used by auto-discover and setup scripts |
If the bind password is sent as ••••••••, the existing password is preserved (allows updating other fields without re-entering the password).
Auth: Admin Key
Remove the LDAP configuration.
Auth: Admin Key
Test connectivity and bind credentials for the saved LDAP configuration.
Response (200):
{"status": "ok"}Or on error:
{"status": "error", "error": "connection refused"}Auth: Admin Key
Search for a user in LDAP and preview the mapped attributes. Useful for verifying attribute mapping before importing users.
Request:
{"username": "alice"}Response (200):
{
"status": "ok",
"username": "alice",
"display_name": "Alice Johnson",
"email": "alice@corp.local",
"department": "Engineering",
"company": "Acme Corp",
"job_title": "Software Engineer",
"groups": ["Engineering", "IT"],
"dn": "CN=Alice Johnson,OU=Users,DC=corp,DC=local"
}Auth: Admin Key
Auto-discover LDAP configuration by connecting to a server, querying RootDSE for the base DN, and detecting whether it's Active Directory or OpenLDAP (to choose the correct username_attr). Saves the configuration on success.
Request:
{
"server": "dc01.corp.local",
"username": "svc-simpleauth",
"password": "ServicePassword"
}The server can be a hostname, hostname:port, or full URL (ldap:// or ldaps://). Default port is 389 for ldap, 636 for ldaps.
Response (200): Returns the discovered and saved LDAP configuration (with masked password).
Auth: Admin Key
Import LDAP configuration from the PowerShell setup script JSON output.
Request:
{
"server": "dc01.corp.local",
"username": "svc-simpleauth",
"password": "ServicePassword",
"domain": "corp.local",
"base_dn": "DC=corp,DC=local",
"service_hostname": "auth.corp.local",
"spn": "HTTP/auth.corp.local"
}If service_hostname is provided, Kerberos setup is automatically triggered after the LDAP config is saved.
Auth: Admin Key
Search the LDAP directory for users matching a query string. Searches across username, display name, and email attributes. Returns whether each user is already imported into SimpleAuth.
Request:
{
"query": "john",
"limit": 50
}Response (200):
[
{
"username": "jsmith",
"display_name": "John Smith",
"email": "jsmith@corp.local",
"department": "Engineering",
"company": "Acme Corp",
"job_title": "Senior Engineer",
"groups": ["Engineering"],
"dn": "CN=John Smith,OU=Users,DC=corp,DC=local",
"imported": false,
"user_guid": ""
},
{
"username": "jdoe",
"display_name": "John Doe",
"email": "jdoe@corp.local",
"department": "Marketing",
"company": "Acme Corp",
"job_title": "Manager",
"groups": ["Marketing"],
"dn": "CN=John Doe,OU=Users,DC=corp,DC=local",
"imported": true,
"user_guid": "660e8400-..."
}
]Auth: Admin Key
Import one or more LDAP users into SimpleAuth. For each username, looks up the user in LDAP, creates a SimpleAuth user record, creates an ldap identity mapping, and assigns default roles.
Request:
{
"usernames": ["alice", "bob", "charlie"]
}Response (200):
[
{"username": "alice", "status": "imported", "user_guid": "550e8400-..."},
{"username": "bob", "status": "exists", "user_guid": "660e8400-..."},
{"username": "charlie", "status": "error", "error": "user not found: sAMAccountName=charlie"}
]Status values: imported (newly created), exists (already imported), error (lookup or creation failed).
Auth: Admin Key
Sync a single user's profile from LDAP. Looks up the user by their configured username attribute in the LDAP directory, then updates their SimpleAuth profile (display name, email, department, company, job title).
Request:
{"username": "jsmith"}curl -k -X POST \
https://localhost:8080/api/admin/ldap/sync-user \
-H "Authorization: Bearer ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{"username": "jsmith"}'Response (200):
{
"status": "synced",
"user": { ... }
}Auth: Admin Key
Sync all users that have ldap identity mappings. Iterates all non-merged users with an LDAP mapping and updates their profiles from the directory.
curl -k -X POST \
https://localhost:8080/api/admin/ldap/sync-all \
-H "Authorization: Bearer ADMIN_KEY"Response (200):
{
"status": "completed",
"synced": 42,
"failed": 3,
"errors": ["charlie: user not found"]
}The errors array is only present when there are failures.
Auth: Admin Key
Set up Kerberos/SPNEGO authentication using the LDAP provider's AD credentials. Creates the SPN and generates a keytab.
Request:
{
"admin_username": "admin@CORP.LOCAL",
"admin_password": "AdminPassword"
}Auth: Admin Key
Remove Kerberos configuration (delete SPN, clean up keytab).
Auth: Admin Key
Check the status of Kerberos configuration (keytab exists, SPN configured, etc.).
Auth: Admin Key
Download an interactive PowerShell script for AD setup. The script has the SimpleAuth hostname pre-injected and offers:
- Create a new service account or use an existing one
- OU selection for new accounts
- SPN registration for Kerberos
- Config JSON export for one-click import into the admin UI
Returns a .ps1 file with UTF-8 BOM encoding.
Identity mappings link external identities (LDAP usernames, local usernames) to SimpleAuth user GUIDs. This is how SimpleAuth tracks that "jsmith" in AD is the same person across logins.
Provider names:
local-- Local username/password authenticationldap-- LDAP/Active Directory authentication
Auth: Admin Key
List all identity mappings.
Response (200):
[
{
"provider": "ldap",
"external_id": "jsmith",
"user_guid": "550e8400-..."
},
{
"provider": "local",
"external_id": "admin",
"user_guid": "660e8400-..."
}
]Auth: Admin Key
Get all identity mappings for a specific user.
Auth: Admin Key
Create or update an identity mapping.
Request:
{
"provider": "ldap",
"external_id": "jsmith"
}Auth: Admin Key
Delete an identity mapping.
Auth: Admin Key
Resolve an external identity to a SimpleAuth user GUID.
Query parameters:
provider-- The identity provider (e.g.,ldap,local)external_id-- The external identifier
curl -k -H "Authorization: Bearer ADMIN_KEY" \
"https://localhost:8080/api/admin/mappings/resolve?provider=local&external_id=jsmith"Response (200):
{"guid": "550e8400-..."}Auth: Admin Key
Download a complete database backup as a binary file.
curl -k -H "Authorization: Bearer ADMIN_KEY" \
https://localhost:8080/api/admin/backup -o backup.dbAuth: Admin Key
Restore from a backup file. Multipart form upload. This replaces the entire database.
curl -k -X POST -H "Authorization: Bearer ADMIN_KEY" \
-F "file=@backup.db" \
https://localhost:8080/api/admin/restoreResponse (200):
{"status": "restored"}Auth: Admin Key
Query the audit log. All authentication events, admin actions, and security events are logged.
Query parameters:
event-- Filter by event type (e.g.,login_success,login_failed)user-- Filter by user GUID (actor)from-- Start date (YYYY-MM-DD)to-- End date (YYYY-MM-DD)limit-- Max results (default 100)offset-- Pagination offset
curl -k -H "Authorization: Bearer ADMIN_KEY" \
"https://localhost:8080/api/admin/audit?event=login_failed&from=2024-01-01&limit=50"Response (200):
[
{
"id": "entry-uuid",
"timestamp": "2024-01-15T10:31:00Z",
"event": "login_failed",
"actor": "",
"ip": "192.168.1.100",
"data": {
"username": "jsmith",
"reason": "invalid credentials"
}
}
]Event types:
| Event | Description |
|---|---|
login_success |
Successful authentication |
login_failed |
Failed authentication attempt |
token_refreshed |
Refresh token used |
token_reuse |
Refresh token replay detected (security event) |
user_created |
New user created |
user_merged |
Users merged |
user_unmerged |
User unmerged |
role_changed |
User roles updated |
permission_changed |
User permissions updated |
default_roles_changed |
Default roles updated |
role_permissions_changed |
Role-to-permissions mapping updated |
sessions_revoked |
All sessions revoked for a user |
negotiate_success |
Kerberos/SPNEGO authentication succeeded |
negotiate_failed |
Kerberos/SPNEGO authentication failed |
oidc_authorize |
OIDC authorization code issued |
oidc_token |
OIDC token issued |
oidc_logout |
OIDC logout |
ldap_config_saved |
LDAP configuration saved |
ldap_config_removed |
LDAP configuration deleted |
ldap_config_imported |
LDAP config imported from setup script |
ldap_sync_user |
Single user synced from LDAP |
ldap_sync_all |
All LDAP users synced |
ldap_user_imported |
User imported from LDAP directory |
password_set |
User password was set (by admin or self-service) |
account_locked |
Account locked due to too many failed login attempts |
account_unlocked |
Account unlocked (by admin) |
impersonation |
Admin impersonated a user |
Auth: Admin Key
Returns the current runtime settings.
curl -k -H "Authorization: Bearer ADMIN_KEY" \
https://localhost:8080/api/admin/settingsResponse (200):
{
"redirect_uris": ["https://myapp.example.com/callback"],
"cors_origins": ["https://myapp.example.com"],
"password_policy": {
"min_length": 8,
"require_uppercase": true,
"require_lowercase": true,
"require_digit": true,
"require_special": false,
"history_count": 5
},
"lockout": {
"max_attempts": 5,
"duration_minutes": 15
},
"rate_limiting": {
"enabled": true,
"requests_per_second": 10
},
"default_roles": ["user"],
"audit_retention_days": 90,
"deployment_name": "sauth",
"auto_sso": false,
"auto_sso_delay": 3
}Auth: Admin Key
Update runtime settings. Only provided fields are updated.
Request:
{
"redirect_uris": ["https://myapp.example.com/callback", "https://other.example.com/callback"],
"cors_origins": ["https://myapp.example.com"],
"password_policy": {
"min_length": 12,
"require_uppercase": true,
"require_lowercase": true,
"require_digit": true,
"require_special": true,
"history_count": 10
},
"lockout": {
"max_attempts": 3,
"duration_minutes": 30
},
"rate_limiting": {
"enabled": true,
"requests_per_second": 5
},
"default_roles": ["user", "viewer"],
"audit_retention_days": 180,
"deployment_name": "prod-auth",
"auto_sso": true,
"auto_sso_delay": 5
}Response (200): Returns the full updated RuntimeSettings JSON (same shape as GET).
Auth: Admin Key
Triggers a graceful server restart. Active connections are allowed to complete before the server restarts.
curl -k -X POST -H "Authorization: Bearer ADMIN_KEY" \
https://localhost:8080/api/admin/restartResponse (200):
{"status": "restarting"}Auth: Admin Key
Returns information about the current database backend.
curl -k -H "Authorization: Bearer ADMIN_KEY" \
https://localhost:8080/api/admin/database/infoResponse (200):
{
"backend": "boltdb",
"health": "ok",
"size": 1048576,
"rows": 350,
"tables": {
"users": 120,
"sessions": 80,
"audit": 150
},
"connection_stats": {}
}Auth: Admin Key
Test connectivity to a Postgres database. If the specified database does not exist, it is automatically created.
Request:
{
"postgres_url": "postgres://user:pass@localhost:5432/simpleauth?sslmode=disable"
}Response (200):
{"status": "ok"}Auth: Admin Key
Start a database migration between backends.
Request (migrate to Postgres):
{
"postgres_url": "postgres://user:pass@localhost:5432/simpleauth?sslmode=disable",
"direction": "to_postgres"
}Request (migrate to BoltDB):
{
"direction": "to_boltdb"
}Response (200):
{"status": "migration_started"}Auth: Admin Key
Returns the current migration progress.
curl -k -H "Authorization: Bearer ADMIN_KEY" \
https://localhost:8080/api/admin/database/migrate/statusResponse (200):
{
"state": "migrating",
"progress": {
"users": {"total": 120, "migrated": 80},
"sessions": {"total": 80, "migrated": 80},
"audit": {"total": 150, "migrated": 45}
},
"items": {
"total": 350,
"migrated": 205
}
}| Field | Description |
|---|---|
state |
idle, migrating, completed, or failed |
progress |
Per-table migration counts |
items |
Aggregate item counts across all tables |
Auth: Admin Key
Switch the active database backend. Saves the backend choice to db.json and automatically triggers a server restart.
Request (switch to Postgres):
{
"backend": "postgres",
"postgres_url": "postgres://user:pass@localhost:5432/simpleauth?sslmode=disable"
}Request (switch to BoltDB):
{
"backend": "boltdb"
}Response (200):
{"status": "switched", "backend": "postgres"}The server will restart automatically after responding.
Auth: Admin Key
Download a bash script for setting up Linux Kerberos SSO. The script has the SimpleAuth hostname pre-injected and handles Kerberos client configuration, keytab setup, and SPN registration on Linux hosts.
Returns a .sh file.
curl -k -H "Authorization: Bearer ADMIN_KEY" \
https://localhost:8080/api/admin/linux-setup-script -o linux-sso-setup.sh- Timing-safe admin key comparison -- The admin key is compared using a constant-time comparison function to prevent timing side-channel attacks.
- CSRF protection on login forms -- Login form submissions include CSRF tokens to prevent cross-site request forgery.
- Rate limiting behind reverse proxies -- If SimpleAuth is deployed behind a reverse proxy (nginx, Caddy, etc.), configure
AUTH_TRUSTED_PROXIESso that rate limiting uses the real client IP fromX-Forwarded-Forheaders instead of the proxy's IP. - Redirect URI validation -- If the configured redirect URI list is empty, all redirect requests are rejected. This is a secure default -- you must explicitly allow at least one redirect URI for OAuth/OIDC flows to work.