Authentication & sessions
Login
src/routes/login/+page.svelte renders <LoginForm> from
src/lib/components/auth/LoginForm.svelte. The form uses
sveltekit-superforms with a Zod schema (username, password both
required) and POSTs to /api/v1/auth/login.
On success it stores the user in userProfileStore and invokes the
onsuccess callback, which the login page uses to redirect to the saved
redirect URL (or /). On failure it maps RFC7807 error responses to
i18n-localized messages, with special handling for rate-limited attempts
(showing minutes remaining).
Code
Session expiration
When an API call returns 401, the request is enqueued in the
session-expired store and the SessionExpiredDialog opens so the user can
re-authenticate without losing context.
src/lib/auth/session-expired-store.svelte.ts exposes:
| Member | Purpose |
|---|---|
enqueue(input, init) | Enqueues a failed request and opens the dialog; returns a Promise<Response> that resolves after re-login |
drainPending() | Returns and clears the queued requests (called after successful re-login) |
setFailed() | Marks the last login attempt as failed (shows the error alert + "Go to login" button) |
close() | Closes the dialog and resets state |
State is exposed read-only via state (isOpen, pendingRequests,
hasFailedAttempt). After a successful re-login in the dialog,
handleLoginSuccess() calls close() and drainPending(), and the
enqueued requests are retried with a _sessionRetry flag to avoid
re-triggering the dialog.
Password policy
The backend exposes the active password policy at
GET /api/v1/system/password-policy. The usePasswordPolicy() composable
fetches it and exposes both the rules and a derived validation regex.
Policies
| Policy | Rules enforced |
|---|---|
alpha_numeric | Length 8–64, alphanumeric only |
letter_and_number | Length, ≥1 letter, ≥1 number |
letter_number_special | Length, letter, number, special char |
mixed_case_special | Length, lowercase, uppercase, number, special char |
Allowed special characters: *-_.#@!|?^:
The regex patterns are kept in sync with the backend exactly:
Code
Composable API
Code
Password checklist
PasswordChecklist.svelte renders a live checklist of the active rules
(length, letter, lowercase, uppercase, number, special) with
check/circle icons, updating as the user types. It takes password,
rules: PasswordChecklistRule[], and optional specialChars.
Change password dialog
ChangePasswordDialog.svelte (in the entity-list-table dialogs) loads the
policy on open, validates the new password against policy.regex, requires
confirmation match, and POSTs to
/api/v1/entities/user_profiles/{uuid}/change-password. It embeds
<PasswordChecklist> for inline feedback.
Password UI components
The src/lib/components/ui/password/ family wraps password input with
strength checking via @zxcvbn-ts/core:
Password.Root— context provider (usePassword({ hidden, minScore }))Password.Input— the input itself (usePasswordInput({ value, ref }))Password.ToggleVisibility— show/hide togglePassword.Copy— copy-to-clipboardPassword.Strength— zxcvbn strength meterPasswordInputField— convenience wrapper combining Root + Input + Toggle
zxcvbn is lazy-loaded and strength checking is debounced with request
cancellation. When the score is below minScore, the input's custom
validity is set.
Active roles
useActiveRoles() fetches roles from /api/v1/system/roles/active and
exposes state.roles (ActiveRole[] with idp_role, label_key,
permissions, is_admin) plus a derived roleNames getter for use in
forms.
MCP OAuth consent
src/lib/mcp-oauth.ts provides OAuth client helpers for the MCP consent
screen at /mcp/consent:
| Function | Purpose |
|---|---|
parseConsentParams(searchParams) | Parses OAuth params from the URL query string |
parseScopes(scopeString) | Splits the scope string into { name, description } entries |
buildApproveUrl(params, beBaseUrl) | Builds the backend authorize URL with consent_approved=true |
buildDenyUrl(params) | Builds the deny redirect URL per RFC 6749 §4.1.2.1 |
The consent page checks /api/v1/auth/me; if the user is not logged in it
shows <LoginForm> and reloads on success. Otherwise it shows the client
name and requested scopes, then redirects to the approve or deny URL.
Form guards
Two composables consolidate logic shared across the settings form pages:
useFormGuard
Derives hasChanges and canSave from a SuperForm instance. Because
Superform's auto-subscription does not work inside .svelte.ts files, it
takes getter functions for $tainted and $errors plus the form's
isTainted function:
Code
useUnsavedChangesGuard
Registers navigation guards for unsaved changes:
Code
handleBeforeUnload— for<svelte:window onbeforeunload>, prevents browser close/refreshhandleCancel— for the footer Cancel button; confirms then closes the window or navigates back- Internally registers a SvelteKit™
beforeNavigateguard that prompts on in-app navigation