Authentication Providers
By default, Dune manages admin users with local password accounts (PBKDF2-SHA256). For organizations that already have a central directory service, you can delegate authentication to an external provider.
The AuthProvider interface abstracts the identity verification step. Once a provider confirms who the user is, Dune's session management and permission system take over — all existing roles, revision history, and audit logging work unchanged.
Default: local accounts
No configuration needed. Users are managed in Admin → Users.
# site.yaml — this is the implicit default
admin:
# auth_provider: not set → local passwords
LDAP / Active Directory
Status: stub — the interface and configuration schema are stable. The
ldaptsintegration is documented below and can be activated by implementingsrc/admin/auth/ldap-provider.ts.
Configure in site.yaml:
admin:
auth_provider:
type: "ldap"
url: "ldaps://ldap.example.com:636"
baseDn: "ou=users,dc=example,dc=com"
# Service account for search (optional — omit for direct bind)
bindDn: "cn=dune-service,dc=example,dc=com"
bindPassword: "$LDAP_BIND_PASSWORD"
# Attribute names (defaults shown)
usernameAttr: "sAMAccountName" # Active Directory; use "uid" for OpenLDAP
emailAttr: "mail"
nameAttr: "cn"
# Group → role mapping (first match wins)
roleMap:
- group: "cn=cms-admins,ou=groups,dc=example,dc=com"
role: "admin"
- group: "cn=cms-editors,ou=groups,dc=example,dc=com"
role: "editor"
defaultRole: "author" # Role for users not in any mapped group
How it works
- User enters username + password in the admin login form
- Dune's
LdapAuthProviderbinds to the LDAP server as a service account and searches for the user DN - A second bind with the user's DN and submitted password verifies the credentials
- Group memberships are read and mapped to a Dune role via
roleMap - The first time a user logs in, a local
AdminUserrecord is auto-provisioned (no password set — only LDAP auth is used thereafter) - A session cookie is issued and the user lands on the dashboard
Auto-provisioning
Users authenticated via LDAP get a local AdminUser record created automatically on first login. The record stores role and display name (synced from LDAP on every login). You can still see and edit these users in Admin → Users, but their passwords are not used.
SAML 2.0
Status: stub — the interface and configuration schema are stable. The
samlifyintegration is documented below and can be activated by implementingsrc/admin/auth/saml-provider.ts.
Configure in site.yaml:
admin:
auth_provider:
type: "saml"
# Your SP (Service Provider) settings
entityId: "https://example.com/admin"
acsUrl: "https://example.com/admin/saml/acs" # POST /admin/saml/acs
# IdP metadata (URL or inline XML)
idpMetadata: "https://idp.example.com/metadata.xml"
# Attribute mapping (defaults shown)
usernameAttr: "username"
emailAttr: "email"
nameAttr: "displayName"
roleAttr: "groups"
# Group value → role mapping
roleMap:
- value: "cms-admins"
role: "admin"
- value: "cms-editors"
role: "editor"
defaultRole: "author"
Login flow
- User visits
/admin/loginand clicks "Sign in with SSO" - Dune's
SamlAuthProvider.initiateLogin()constructs an AuthnRequest and redirects to the IdP - After authentication, the IdP POSTs a signed SAML assertion to
POST /admin/saml/acs handleCallback()parses the assertion, extracts attributes, maps to a Dune role- The user is auto-provisioned (if new) and a session is created
Building a custom provider
Implement the AuthProvider interface from src/admin/auth/provider.ts:
import type { AuthProvider, AuthCredentials, AuthProviderUser } from "./provider.ts";
export class MyAuthProvider implements AuthProvider {
readonly type = "local" as const; // use "local" for non-stub custom providers
async authenticate(creds: AuthCredentials): Promise<AuthProviderUser | null> {
// Verify credentials against your system
// Return null on failure, AuthProviderUser on success
const user = await myIdentityService.verify(creds.username, creds.password);
if (!user) return null;
return {
externalId: user.id,
username: user.username,
email: user.email,
name: user.displayName,
role: "editor",
};
}
}
Then wire it up in your bootstrap.ts:
import { MyAuthProvider } from "./my-auth-provider.ts";
// Pass to createAdminHandler({ ..., authProvider: new MyAuthProvider() })
AuthProviderUser fields
| Field | Type | Description |
|---|---|---|
externalId |
string |
Stable external ID (LDAP DN, SAML NameID, etc.) |
username |
string |
Login name — used to find/create the local user |
email |
string? |
Used in the local user record |
name |
string? |
Display name |
role |
AdminRole? |
Override the local user's role on each login |
Configuration reference
admin:
auth_provider:
type: "local" | "ldap" | "saml"
# ... provider-specific fields as documented above
If auth_provider is not set, local password authentication is used and admin.auth_provider has no effect.