Authentication & Identity API Reference
Programmatic API reference for the SveltyCMS identity layer — login, profiles, 2FA, SAML SSO, OIDC login/callback/logout, RBAC permissions, API Keys, Magic Links, and WebAuthn/Passkeys.
On this page
The Authentication API handles the complete lifecycle of a user identity within SveltyCMS for programmatic access (SDK, mobile apps, external services). It manages everything from standard password-based authentication and Two-Factor Authentication (2FA) to enterprise Single Sign-On (SSO) via SAML 2.0.
Important: The browser-based login flow (
/login) now uses type-safe Remote Functions defined inauth.server.ts. These provide full TypeScript inference between components and server logic. The REST API endpoints below remain available for programmatic access (SDK, mobile apps, external services). See Login Security for the page-level authentication documentation.
⚡ Quick Reference
| Feature | HTTP Endpoint | Method | Permission Required |
|---|---|---|---|
| Login | /api/auth/login |
POST |
Public |
| Logout | /api/auth/logout |
POST |
Authenticated |
| OIDC Login | /api/auth/oidc-login |
GET |
Public |
| OIDC Callback | /api/auth/oidc-callback |
GET |
Public |
| OIDC Logout | /api/auth/oidc-logout |
GET, POST |
Public |
| Current User | /api/auth |
GET |
Authenticated |
| List Users | /api/user |
GET |
manage:user |
| Create User | /api/auth/create-user |
POST |
manage:user |
| API Keys | /api/api-keys |
GET |
Authenticated |
| Create Key | /api/api-keys |
POST |
Authenticated |
| Revoke Key | /api/api-keys/{id} |
DELETE |
Owner or Admin |
| Magic Link | auth.remote.ts (remote fn) |
POST |
Public |
| Passkey Auth | auth.remote.ts (remote fn) |
POST |
Public |
| Passkey Reg | auth.remote.ts (remote fn) |
POST |
Authenticated |
| 2FA Status | /api/auth/2fa/status | GET | Authenticated |
| 2FA Setup | /api/auth/2fa/setup | POST | Authenticated |
| 2FA Enable | /api/auth/2fa/enable | POST | Authenticated |
| 2FA Verify | /api/auth/2fa/verify | POST | Authenticated |
| 2FA Disable | /api/auth/2fa/disable | POST | Authenticated |
| Backup Codes | /api/auth/2fa/backup-codes | GET, POST | Authenticated |
Trusted Devices: The browser-based flow (
auth.remote.tsverify2FA) supportstrustDevice: trueto set a__Host-2fa-trusted-devicecookie (30-day TTL, HMAC-SHA256 signed). On subsequent logins from the same device, 2FA is silently skipped.
TOTP Secret Encryption: TOTP secrets are encrypted at rest using AES-256-GCM under the instance’s
ENCRYPTION_KEY. Legacy plaintext secrets are handled transparently — no migration needed.
| SAML Login | /api/auth/saml/login | GET | Public |
| SAML ACS | /api/auth/saml/acs | POST | Public |
| SAML Config | /api/auth/saml/config | POST | manage:system |
| Permissions | /api/permission/list | GET | manage:system |
1. Core Authentication
User Lifecycle
Standard authentication uses secure HttpOnly cookies to maintain session state across requests. Session cookies use the __Host- prefix on HTTPS connections per RFC 6265bis.
- Login:
POST /api/auth/login— Validates credentials (email + password), starts a session, sets__Host-auth_sessionscookie, and rotates the CSRF token. - Logout:
POST /api/auth/logout— Invalidates the current session, clears cookies, and invalidates the session cache. - Current User:
GET /api/auth— Returns the authenticated user object and their assigned permissions.
Profile Management
Users can update their own security attributes and metadata via dedicated endpoints:
- Update Attributes:
POST /api/auth/update-user-attributes(also acceptsPUTandPATCH) — Updates profile fields. Passuser_id: "self"for the current user. Non-admins may only update themselves; privilege fields (role,isAdmin,permissions, lockout/2FA secrets, etc.) are stripped for non-admin callers. - Save Avatar:
POST /api/auth/save-avatar— Supports both Multipart form-data (file upload) and JSON payloads (URL string).
2. Secure Identity Layer (2FA)
SveltyCMS supports mandatory or optional Two-Factor Authentication via Time-based One-Time Passwords (TOTP).
Security Architecture
- AES-256-GCM Encryption: TOTP secrets are encrypted at rest using the instance’s
ENCRYPTION_KEY. Secrets are never stored in plaintext in the database. Legacy plaintext secrets are handled transparently. - TOTP Replay Protection: A consumed-codes registry prevents replay attacks within the 90-second validity window (30s TOTP — 30s clock skew). Fail-closed design — registry errors reject the code.
- Timing-Safe Comparisons: All code comparisons use
crypto.timingSafeEqualto neutralize side-channel attacks. - Configurable Window: Set
TOTP_WINDOW=2(env var, default 1) for — 60s tolerance on clock-drifted devices. Max: 5.
2FA Setup Flow (with Pending State)
- Initiate Setup:
POST /api/auth/2fa/setup— Generates a TOTP secret and QR code. The encrypted secret is persisted immediately withtwoFactorPending: true. If setup is interrupted (page close, browser crash), re-initiating resumes the existing pending setup. - Verify & Enable:
POST /api/auth/2fa/enable— Verifies the TOTP code with the pending secret and enables 2FA. Payload:{ "code": "123456", "secret": "...", "backupCodes": [...] }. Thesecretfield is optional — if omitted, the stored pending secret is used. On success:twoFactorPendingcleared,is2FAEnabled: true.
2FA Verification Flow
When 2FA is enabled, a standard login will return a 2FA_REQUIRED status, requiring a second step. Plugins can also force 2FA via the afterAuthenticate hook — see Plugin Architecture.
Endpoint: POST /api/auth/2fa/verify
Payload: { "userId": "...", "code": "123456" }
Trusted Devices (“Remember This Device”)
After successful 2FA verification, the browser flow can pass trustDevice: true:
- Sets a
__Host-2fa-trusted-devicecookie (httpOnly, secure, strict, 30-day TTL) - Cookie is HMAC-SHA256 signed under the instance’s
ENCRYPTION_KEY— forgery-proof - Binds to device fingerprint (IP prefix + user-agent hash)
- On subsequent logins from the same device, 2FA is silently skipped
- Supports up to 5 trusted devices per user (FIFO eviction)
- Disabling 2FA clears all trusted devices
Backup Codes
- Retrieve:
GET /api/auth/2fa/backup-codes— Returns backup codes if 2FA is enabled. - Regenerate:
POST /api/auth/2fa/regenerate-backup-codes— Generates new backup codes.
Disable 2FA
Endpoint: POST /api/auth/2fa/disable
Payload: { "password": "current_password" } — Requires current password verification before disabling.
3. Enterprise SSO (SAML 2.0)
For enterprise environments, SveltyCMS acts as a Service Provider (SP) and integrates with Identity Providers (IdP) like Okta or Azure AD via the lightweight @node-saml/node-saml library — zero database dependencies.
SAML Configuration
To connect an external IdP, the admin must provide the XML metadata.
Endpoint: POST /api/auth/saml/config
Payload: { "tenant": "...", "rawMetadata": "<XML_CONTENT>" }
SAML Assertion Consumer Service
The IdP redirects the user to the ACS endpoint after authentication.
Endpoint: POST /api/auth/saml/acs — Processes the SAML response, validates the assertion, and starts a session.
Just-In-Time (JIT) Provisioning
The system automatically creates user records upon the first successful SAML login if they don’t already exist, mapping IdP attributes to SveltyCMS roles.
4. OIDC SSO (OpenID Connect)
SveltyCMS acts as an OpenID Connect relying party for enterprise SSO: authorization-code login with OIDC discovery + JWKS verification, local session creation, and RP-Initiated Logout.
Implementation: src/databases/auth/sso-session.ts · handlers in handlers/auth.ts (oidc-login, oidc-callback, oidc-logout).
Authorization-code login
Start — GET /api/auth/oidc-login
| Query | Required | Description |
|---|---|---|
provider |
Yes | Provider id from SSO_PROVIDERS (e.g. azure-ad, auth0) |
redirect_uri |
No | Defaults to {origin}/api/auth/oidc-callback |
Flow:
- Load providers from settings; 404 if
providerunknown - Generate
state+nonce(CSPRNG); store in short-lived HttpOnly cookieoidc_login_state(10 min,SameSite=Lax) - OIDC discovery (
/.well-known/openid-configuration) populates authorize/token/jwks/end_session when not set manually - 302 redirect to OP
authorization_endpoint(openid profile emailby default)
Callback — GET /api/auth/oidc-callback
| Query | Required | Description |
|---|---|---|
code |
Yes | Authorization code from OP |
state |
Yes | Must match cookie state |
error |
No | OP error → 400 |
Flow:
- Validate
stateagainst cookie; clear cookie - Exchange
codeat token endpoint (client_id/client_secretfrom provider config) - Verify
id_tokensignature via JWKS (RS256/ES256; 1h JWKS cache). Withoutjwks_uri, only structural claim checks - Resolve email from
emailorpreferred_usernameclaims - Look up existing local user by email (no silent JIT create on this path — create user or invite first)
auth.createSession({ user_id, tenantId, expires })+ session cookie (getSessionCookieName)- Attach SSO metadata (
id_token_hint, provider) for RP logout
RP-Initiated Logout
Endpoint: GET|POST /api/auth/oidc-logout
Parameters:
| Param | Required | Description |
| :---- | :------- | :---------- |
| id_token_hint | Recommended | The ID Token from the original authentication, used by the OP to identify the session |
| post_logout_redirect_uri | Optional | Where to redirect the browser after logout. Must match the provider’s allowlist |
| state | Optional | Opaque value for CSRF protection, echoed back by the OP |
Flow:
- Browser sends logout request with OIDC params
- SveltyCMS terminates the local session and clears cookies
- If the OP has an
end_session_endpoint(config or discovery), the browser is redirected there for federated logout - On success, the browser lands at
post_logout_redirect_uri(if provided and valid)
Provider configuration
SSO providers are registered via the SSO_PROVIDERS system setting (JSON array of SsoProviderConfig):
| Field | Purpose |
|---|---|
id, issuer |
Provider key + OIDC issuer URL |
clientId, clientSecret |
Authorization-code client credentials |
allowedRedirectUris |
Allowlist for logout (and callback) redirect URIs; supports * suffix |
authorizationEndpoint, tokenEndpoint, jwksUri, endSessionEndpoint |
Optional overrides; filled from discovery when omitted |
scopes |
Default openid profile email |
Security:
- Login CSRF via HttpOnly
statecookie;noncebound into authorize request post_logout_redirect_urivalidated against provider allowlist- Secrets never returned to the client; JWKS fetch timeouts (5s)
- Non-SSO sessions fall through to standard logout
- Multiple OIDC providers supported simultaneously
5. RBAC & Permissions
The authorization system is built on granular permissions assigned to user roles.
- Check Permissions:
GET /api/permission/listreturns the full registry of available permissions in the system. - Role Assignment: Managed via
POST /api/auth/update-roles(requiresmanage:system) orPATCH /api/user/{id}(requiresmanage:user).
6. API Keys (Machine-to-Machine)
API Keys enable programmatic access for CI/CD pipelines, external services, and headless clients. Keys use the sck_* prefix and are stored as SHA-256 hashes — plaintext is never persisted.
Key Management
| Operation | Endpoint | Method | Description |
|---|---|---|---|
| List Keys | /api/api-keys |
GET |
Returns all keys for authenticated user (no hash) |
| Create Key | /api/api-keys |
POST |
Returns plaintext sck_* key once |
| Revoke Key | /api/api-keys/{id} |
DELETE |
Owner or admin only; purges credential cache |
Security: Plaintext keys are shown exactly once at creation. After that, only the SHA-256 hash is stored. Revoked keys stop authenticating within 60 seconds (credential cache TTL).
Create Key Request
POST /api/api-keys
{
"name": "Staging CI",
"scopes": ["content:read", "media:read"],
"expiresAt": "2026-12-31T23:59:59Z"
}
Create Key Response
{
"success": true,
"data": {
"_id": "key_abc123",
"name": "Staging CI",
"prefix": "sck_a1b2c3d4",
"key": "sck_a1b2c3d4e5f6...",
"scopes": ["content:read", "media:read"]
}
}
6. Magic Links (Passwordless)
Magic Links provide passwordless authentication via email. Implemented as SvelteKit Remote Functions in auth.remote.ts for type-safe client-server communication.
Flow
- Request: Client calls
requestMagicLink({ email })remote function - Token Creation: Server creates a single-use
magic_linktoken (15-min TTL) and emails it - Verification:
+page.server.tsload function detectsmagic_tokenquery param, callsverifyMagicLink() - Session: On success, a session is created and the user is redirected
Security Properties
- Tokens are consumed atomically (TOCTOU-safe
consumeToken) - Uniform response prevents email enumeration
- Account lockout is checked before sending
- Every request and verification is audit-logged
7. WebAuthn / Passkeys
WebAuthn provides phishing-resistant biometric authentication via platform authenticators. Implemented as SvelteKit Remote Functions.
Remote Functions
| Function | Auth Required | Description |
|---|---|---|
getPasskeyAuthOptions |
No | Returns challenge for login |
verifyPasskeyAuth |
No | Validates assertion, creates session |
getPasskeyRegisterOptions |
Yes | Returns challenge for registration |
verifyPasskeyRegister |
Yes | Validates attestation, stores credential |
Database
Authenticators are stored as a JSON array on the User record:
interface Authenticator {
credentialId: string;
publicKey: JsonWebKey; // COSE to JWK converted
counter: number; // Clone detection
transports?: string[]; // ["internal", "usb", "nfc", "ble"]
}
8. Guest / Anonymous Auth
Public routes automatically receive an ephemeral ANONYMOUS_USER identity with the guest role. No session is created — access is stateless and read-only.
| Permission | Granted |
|---|---|
content:read |
✅ |
content:write |
❌ |
media:read |
✅ |
media:write |
❌ |
admin:access |
❌ |
Guest identity is assigned by handle-authentication.ts middleware when no valid session or bearer token is present. On subsequent authentication, the guest context is transparently upgraded to the real user session.
Related Documents
- Token Management (tokens.ts)
- SCIM 2.0 Provisioning (scim.ts)
- Login Security
- Authentication System Architecture