Configure authentication
Configure sign-in methods, credentials, origins, security policies, and session lifetimes.
Set your product’s sign-in methods, sign-up rules, password and MFA policies, passkeys and session lifetimes in Users → Configuration. Saved changes apply to the next authentication flow. Origins and callback allowlists use the configuration API; keys have separate management screens.
Before you start
- Lepsto Users installed on your product and permission to edit its configuration. Agents need product-scoped admin access for
users_config_upsert. - For email sign-in, a sender on a domain verified in the product’s mail toolkit.
- For production OAuth, your own registered Google or GitHub application credentials.
Make a change
Change only the settings you need. The screen has Signup, Password policy, Multi-factor authentication, Passkeys, Sessions, Email, OAuth providers and Waitlist widget sections.
- Open Users → Configuration and locate the relevant section.
- Edit its fields. Changes remain a draft until saved.
- Select Save changes to apply the changed fields, or Discard to abandon the draft.
Settings apply only to this product. Agents can read them with users_config_get and change them with users_config_upsert. Origin and redirect allowlists have no fields on this screen; their API names are given below. Users publishable keys and product public keys are managed separately.
Turn on a sign-in method
Enable the methods your customers need. Several methods can appear on the same sign-in screen, and users can add another method after creating an account.
Password: an email address and password, hashed with argon2id and checked against the policy below.
For either email method, first set email.from in Email to a sender on a verified mail domain. Neither method is offered until that sender exists.
Email one-time code: sends a six-digit code to enter in your form. Codes expire and allow limited attempts. Resending has a cooldown of about one minute and a daily cap.
Magic link: sends a link that opens a confirmation page. Clicking its button consumes the link; merely opening it does not, so mail scanners cannot use it. On another device, the user confirms the waiting device and transfers a short code to it. The session is created on the device that started sign-in.
OAuth: supports Google and GitHub. Shared Lepsto credentials are available for development. In production, register your own provider application and save its client ID and secret in OAuth providers; the secret is encrypted.
Passkeys: let the user authenticate with a WebAuthn credential on their device, often without typing an identifier. Enable them through the passkeys block below. Enrollment is self-service; an operator cannot enroll on someone else’s behalf. Passkeys can also satisfy a second-factor challenge. See Add passkeys.
Multi-factor authentication: offers authenticator apps (TOTP) and single-use backup codes. A factor counts after the user confirms it. Once enrolled, it gates password, email code, magic-link and OAuth sign-in; password reset does not bypass it. An operator reset requires identity verification, a delay, an audit record and an email allowing the user to stop it. See user management and MFA enrollment.
Choose who may sign up
Choose one mode in Signup:
Mode | Who creates the account |
|---|---|
Public | Anyone may create an account. The default. |
Invite-only | Accounts are created by you, by inviting a person from the directory screen or by running an import, and the user sets their first credential from the invitation email. |
Waitlist | A public sign-up form collects people without creating a way in; you invite them in batches later. See Run a waitlist. |
Use the API to allow or block email domains, reject disposable addresses or block + sub-addresses. These restrictions apply independently of sign-up mode. The Signup section edits only signupMode.
CAPTCHA is enabled by default for sign-up and is also triggered by repeated sign-in failures for an account or address.
Set the password policy
Edit Password policy to control sign-up and password changes:
Setting | Default |
|---|---|
Minimum length | 8 characters |
Maximum length | at least 64 characters, never silently truncated |
Breached-password check | on |
The policy uses length rather than required digits, symbols or mixed case. Breach checking sends neither the password nor its full hash to the public breach database. If that service is unavailable, registration can continue. The check runs again after sign-in and asks affected users to change their password.
Repeated failures trigger temporary lockouts by account and source. A lock expires automatically, and an email lets the user unlock sooner. Breach checks, lockout ceilings and rate limits are in the configuration’s protection fields.
Set the second-factor policy
Edit the mfa fields in Multi-factor authentication, or use users_config_upsert. Fields merge individually, leaving omitted values unchanged.
Field | Default | Range | What it decides |
|---|---|---|---|
required | false | — | Whether a second factor is mandatory for this product |
backupCodeCount | 10 | 1–20 | How many single-use backup codes a batch contains |
resetDelayHours | 72 | 0–720 | The delay on an operator-initiated factor reset — the window the user is emailed to abort it |
sensitiveAuthAgeMinutes | 10 | 1–1440 | How recently the caller must have authenticated to enrol or regenerate a factor |
maxSecondFactorAttempts | 5 | 1–50 | Wrong second-factor codes before the account is locked |
maxBackupCodeAttempts | 5 | 1–50 | Wrong backup codes before the account is locked, counted separately from the app’s codes |
stepUpGrantMinutes | 5 | 1–60 | How long a passed step-up challenge stays spendable |
A step-up grant authorizes one use. stepUpGrantMinutes sets how long the user has to use it, not how many operations it permits.
MFA enrollment is always available; there is no product-wide off switch that removes enrolled factors. With required: false, users opt in.
Setting required: true prevents users with a verified factor from removing their last factor, returning last_factor_required. It neither enrolls existing users nor locks them out. Separately, last_way_in prevents any user from removing their last sign-in method.
A shorter sensitiveAuthAgeMinutes asks for fresh authentication more often during enrollment and regeneration. A longer interval allows an already-open session to perform those operations for longer. The default is ten minutes.
Turn on passkeys
Edit Passkeys, between the MFA and Sessions sections, to configure your product as a WebAuthn relying party. The API’s passkeys fields merge individually too.
Field | On the screen | Default | What it decides |
|---|---|---|---|
enabled | Offer passkeys | false | Whether this product accepts passkeys at all. Off, the method is not offered and no credential can be enrolled |
rpId | Relying party ID | none | The domain credentials are bound to, when your allowed origins cannot decide it alone |
rpName | Relying party name | none | The product name the operating system’s passkey prompt shows. Defaults to the domain |
requireUserVerification | Require user verification | false | Refuse an assertion the authenticator did not verify. A verified one is aal2 either way |
Empty rpId and rpName boxes request derived values. Enter only a bare domain for rpId: no scheme, port or path. For example, use example.com, not https://example.com. Replace this example with your product’s domain.
The relying-party domain is resolved in this order:
- If
passkeys.enabledis false, passkeys are not offered. - If
rpIdis set, it must match or be a parent of every allowed-origin host. A parent matches whole domain labels:example.comcoversapp.example.com, but notnotexample.com. - Without
rpId, one host shared by all allowed origins can be used. - If no unambiguous domain can be resolved, passkeys stay off.
requireUserVerification defaults to false. Without it, an assertion lacking device user verification still counts as one factor and follows the usual second-factor rules. Enabling it rejects such assertions, including those from security keys that cannot verify the user.
There is no passkey required field or per-user enable switch. Your product decides whether to accept passkeys; each user enrolls their own. The authenticator’s result determines the assurance level for each sign-in.
Allow your origins and redirect URIs
Set exact allowed origins and callback addresses, including scheme, host and port. Wildcards and prefix matching are not supported.
allowedOrigins lists the origins serving your frontend. A browser request using upk_ is rejected unless its origin is listed.
redirectUriAllowlist lists permitted flow destinations: sign-in callbacks, OAuth return pages and post-verification pages. A flow cannot return to an unlisted address.
Updating either list replaces it. Include all entries you want to retain.
Maintain separate development and production entries, and remove entries for environments you no longer use.
Rotate a key
Users uses two different credentials. The browser publishable key starts flows; the backend product public key redeems handoff codes.
Publishable key | Product public key | |
|---|---|---|
Prefix | upk_ | lpk_ |
Created in | Users → Keys | Management API for route scope; Product → Settings → Public Access for toolkit scope |
Runs in | Browser | Backend only |
Secret | No | Yes; shown once |
Authorizes | Sign-up and sign-in flows | POST /code/exchange |
Checked by | Users and its origin allowlist | Platform public edge and key scope |
SDK clients derive the product URL from the configured product ID. The browser client refuses lpk_ credentials, and code exchange refuses a publishable key. Refresh uses the refresh token alone, local verification needs no credential, and user administration requires your platform identity rather than either key.
In Users → Keys, API keys lists publishable keys. Issue an API key creates a named key, and each row can be revoked; publishable values remain readable. Legacy usk_ server keys are listed as revoked and authenticate nothing. Code exchange now requires lpk_.
As an Owner or Admin, create the backend public key through the management API. Limit its scope to the exchange route:
{ "extension": "users", "method": "POST", "path": "/code/exchange" }The Public Access UI selects all or selected toolkits; it cannot select an individual route. A route-scoped key appears there as limited to specific routes. Saving it through the toolkit-level Edit scope editor replaces its route scope and widens access.
Use one key per backend. To replace it:
- Create a replacement through the management API with the same exchange-route scope and save its secret.
- Wait about 30 seconds for the edge snapshot to refresh, then deploy the replacement to your callers. Both keys remain valid during the changeover.
- Revoke the old key in Public Access after callers have switched. There is no separate rotation operation.
The separate JWT signing keys section in Users → Keys lists the keys that sign access tokens:
State | What it means |
|---|---|
Standby | Generated, not published, verifying nothing yet |
Current | Signs every token from now on |
Retired | No longer signs, but stays in JWKS until its overlap expires |
Revoked | Evicted from JWKS, so tokens it signed no longer verify |
Rotate signing key creates a new current signer. Retired keys stay available during the overlap, so rotation alone does not invalidate existing tokens. Revoke a key from its row to remove it from JWKS. Agents can perform these operations too.
Set session lifetimes
Edit these values in Sessions:
Setting | Default | Notes |
|---|---|---|
Access token lifetime | 10 minutes | accessTokenTtlMinutes; it cannot go below 5 minutes |
Session lifetime | 720 hours | sessionTtlHours — the session’s own lifetime |
Session inactivity window | 30 days | refreshSlidingDays; every refresh slides it forward |
Session absolute lifetime | 365 days | refreshAbsoluteDays, counted from sign-in and never extended |
Shorter access-token lifetimes reduce the delay before local verification stops accepting a revoked session, at the cost of more refreshes. Checked verification tests the live session with one additional call when you need immediate revocation. See sessions and tokens.
Password changes, email changes, MFA resets, bans and erasure end sessions regardless of configured lifetimes.
Verify it worked
Start a new flow after saving, because existing attempts do not demonstrate the new configuration:
- Open sign-in in a fresh browser session and check the offered methods.
- Complete an enabled method. If it fails, inspect the flow error and check the sender, provider credentials and origin allowlist.
- Test the affected boundary: for a password minimum change, confirm an undersized password is rejected at sign-up.
Next steps
- Run a sign-in flow: what each method looks like step by step.
- Add two-factor authentication: the prebuilt two-step screen your product renders.
- Add passkeys: turning passkeys on, and the screens your product renders.
- Sessions and tokens: verifying, refreshing and revoking.
- Manage your end-users: the management App and the audit trail.