Login security & two-factor
Configure account lockout, password rules, self-service password reset, magic links and two-factor authentication through the API, provision users without handling passwords, and keep integration accounts working with service accounts.
Yurbi includes a set of login-security features: account lockout after failed
attempts, self-service password reset and one-time sign-in links, two-factor
authentication (authenticator app, email code, text message or Duo), administrator
disable, forced password change, and an EXTERNAL authentication method for
accounts owned by an upstream identity provider.
Everything is off by default. An upgraded instance behaves exactly as before until an administrator turns a feature on, and every new field on the user and settings objects is optional — requests that omit them leave the account or setting unchanged. Existing integrations do not need to change.
This guide covers what an integrator needs: the settings keys, the user-state fields, the endpoints for provisioning and help-desk actions, and how to keep accounts your application signs in on behalf of users out of the two-factor and password-change screens.
1. Instance settings
All of these live in the settings object returned by
GetAppSettings and written by
SaveAppSettings. Boolean values are the strings
"True" and "False". In the interface they are under Settings → Server
Settings → Security Settings.
| Key | Default | Meaning |
|---|---|---|
enableloginlockout |
False |
5 failed password attempts lock the account for 5 minutes. Password sign-ins only; SSO, Windows, EXTERNAL and magic-link sign-ins are never counted. The counter is per server and clears on restart. |
allowpasswordreset |
False |
Shows Forgot password? on the sign-in page. Links expire in 15 minutes and work once. |
allowmagiclink |
False |
Shows Email me a sign-in link. Same 15-minute, single-use link; two-factor still applies after it. |
publicbaseurl |
empty | The address users reach Yurbi at, e.g. https://reports.example.com or https://example.com/yurbi under a sub-folder. Every emailed link is built from it. Required before either recovery switch, or AdminSendLink, will work. |
invite_subject, invite_message |
see below | Subject and intro text of the welcome email sent by AdminSendLink (kind: invite). Placeholders {product}, {username}, {firstname}. |
twofa_mode |
off |
off, optional (users may enroll; enrolled users are challenged) or required (every password user must enroll before reaching the application). |
twofa_methods |
duo,totp,email,sms |
Comma list of methods users may enroll. email needs SMTP; sms needs a text-message provider configured in the interface; duo needs the existing duo_* keys. |
enforcestrongpassword |
False |
Existing key: minimum length and breach screening for new passwords, applied to the reset page as well. |
SaveAppSettings replaces the whole object, so read first and send back what you
received. Two rules the interface enforces and the API does not check for you: a
twofa_mode other than off needs at least one method in twofa_methods, and
publicbaseurl must be set before allowpasswordreset or allowmagiclink is turned
on.
curl -X POST "https://your-yurbi-server.com/api/AppSettings/SaveAppSettings" \
-H "Content-Type: application/json" \
-d '{
"sessionToken": "YOUR_SESSION_TOKEN",
"appsettings": {
"...": "every key returned by GetAppSettings, unchanged, plus:",
"publicbaseurl": "https://reports.example.com",
"allowpasswordreset": "True",
"enableloginlockout": "True",
"twofa_mode": "optional",
"twofa_methods": "totp,email"
}
}'
A sensible rollout: set publicbaseurl and confirm email works → turn on
allowpasswordreset → set twofa_mode to optional and let administrators
enroll → switch to required.
2. User account fields
The user object used by SaveContact,
GetContactById and the other Users endpoints
carries the account's security state. All are optional on save.
| Field | Values | Notes |
|---|---|---|
AuthType |
PIN, SSO, Windows, EXTERNAL |
PIN is a password user. EXTERNAL marks an account authenticated entirely upstream; it holds no password and cannot use password reset, magic links or invite links. |
disabled |
true / false |
Blocks every sign-in method and ends the current session. Set it directly, or use DisableContact / EnableContact. |
bforcepasschange |
true / false |
The user must set a new password at their next password sign-in. Clears itself when they do. Ignored for SSO, Windows and EXTERNAL users. |
lockdate |
timestamp | Read-only. When lockout is on and the account is locked, the time the lock expires; otherwise 0001-01-01T00:00:00. Clear it early with UnlockContact. |
twofa |
none, setup_required, duo |
setup_required asks the user to enroll at their next sign-in (they may skip in Optional mode). duo assigns Duo. Authenticator, email and SMS are self-enrolled and are not removed by saving none. |
service_account |
true / false |
The account belongs to an integration, not a person: it is never asked for a second factor or a forced password change. Set it directly or with SetServiceAccount. See section 5. |
Password rules. Any Pin you send is trimmed of leading and trailing spaces, and a blank one is rejected. When enforcestrongpassword is on, a new password must be 12–128 characters, must not contain the login name, and must not be a common or publicly breached password; SaveContact returns the reason in ErrorMessage when one fails. Generate 32+ random characters for integration accounts and you will never hit a rule. For EXTERNAL users send withpin: false and no Pin.
3. Provisioning users without handling passwords
The usual flow sends a generated password to the user out of band. The alternative is an invite link: the user sets their own password from a welcome email, and your system never knows it.
- Create the user with
SaveContactas normal, using a long randomPinthe user will never see. - Call
AdminSendLinkwithkind: "invite".
curl -X POST "https://your-yurbi-server.com/api/Recovery/AdminSendLink" \
-H "Content-Type: application/json" \
-d '{ "sessionToken": "YOUR_SESSION_TOKEN", "loginName": "jsmith", "kind": "invite" }'
The user receives the welcome email with a Set your password link valid for 72
hours. If two-factor is required, they enroll immediately after. The same call
with kind: "reset" sends a standard 1-hour reset link and is the right action for
a help-desk "forgot my password" ticket — it works even when self-service reset is
off. Requirements: the user is AuthType PIN with an email address, SMTP is
configured, and publicbaseurl is set.
4. Help-desk and lifecycle actions
| Situation | Call |
|---|---|
| Person leaves or is suspended | DisableContact — ends their session now, keeps their content. Reverse with EnableContact. |
| Locked out after failed attempts | UnlockContact (only relevant when enableloginlockout is on). |
| Forgot password | AdminSendLink with kind: "reset". |
| Suspected shared or leaked password | SaveContact with bforcepasschange: true, or send a reset link. |
| Lost phone / new authenticator | TwoFactor/AdminStatus to see what is enrolled, then TwoFactor/AdminReset to clear it. The user re-enrolls at next sign-in if two-factor is required. |
| Password-age policy, or a breach | ForcePasswordResetAll with olderThanDays — every matching password user must change at next sign-in. Service accounts are skipped. |
| New integration, embed host or Server Sync user | SetServiceAccount with enabled: true before two-factor is turned on. |
All of these require an administrator session (ErrorCode 9000 /
PERMISSION_DENIED otherwise) and are written to the Audit Log.
5. Integrations that sign users in: service accounts
When your application calls DoLogin on an account's behalf —
for session-token SSO, embedding,
a scheduled export or Server Sync — nobody is there to type a two-factor code or
choose a new password. If the instance turns on two-factor or forces a password
change, DoLogin still returns ErrorCode 0 and a token, but the session is
pending:
| Response field | Meaning |
|---|---|
requiresTwoFactor: true, twoFactorMethod |
The account has a two-factor method enrolled and must enter a code in Yurbi. |
requiresTwoFactorEnrollment: true |
twofa_mode is required and the account has not enrolled. |
LoginUser.bforcepasschange: true |
A new password must be set before the token is useful. |
Every other call made with a pending token returns ErrorCode 9000 with
ErrorMessage TWO_FACTOR_PENDING.
The fix is to flag the account as a service account:
curl -X POST "https://your-yurbi-server.com/api/Contact/SetServiceAccount" \
-H "Content-Type: application/json" \
-d '{ "sessionToken": "YOUR_SESSION_TOKEN", "loginName": "svc-reporting", "enabled": true }'
A service account is never asked for a second factor or a forced password change,
whatever the instance's two-factor mode, so DoLogin always returns a usable
token. Do this for every account your integration signs in with, before the
instance's administrators turn on two-factor. It also covers the user Server Sync
signs in with on each destination server. The flag is visible in the Yurbi user
editor as Service account (API integration), and every sign-in where the second
factor was skipped is written to the Audit Log.
What a service account does not skip:
- Lockout. Five wrong passwords still lock it for 5 minutes (
ErrorCode 101fromDoLogin). A stale stored password is the usual cause; fix the credential before retrying, and rotate passwords on your own schedule withSaveContactrather than through a forced change. - Password rules when its password is set, and the disabled flag.
Rules: an administrator cannot flag their own account, and the built-in admin
account can only be flagged while signed in as admin — give integrations their
own accounts rather than using admin. Give each integration its own account:
Yurbi keeps one session per user, so two systems sharing an account sign each
other out.
Embedding end users who need two-factor. The embed flow signs in as the end
user's own account, so a person who is required to use two-factor cannot be
embedded as themselves. Embed with a service account that has the content access
those users need, or sign them in through SSO or the EXTERNAL method instead.
Check for the three response fields after every DoLogin and treat any of them
as "token not ready" rather than retrying.
6. The EXTERNAL authentication method
EXTERNAL is for accounts that are always authenticated by another system and
should never hold a Yurbi password — today an identity provider sending the
SSO header; SAML and OIDC are planned. Compared with SSO,
it is explicit about the contract:
- No password is stored or accepted. Password sign-in, password reset, magic links and invite links all refuse the account.
- Header-based SSO signs the account in exactly as for
SSO. - Lockout, forced password change and Yurbi's two-factor prompts do not apply; those controls belong to the identity provider.
- Saving a user as
EXTERNALclears any pending forced password change. ExistingSSOusers are unchanged.
7. Audit events
Every action above is recorded and visible through
GetAuditTypes and the Audit Log in the interface.
Type numbers (the type field; the table ID is one higher):
| Type | Event |
|---|---|
| 20 / 21 | Password reset requested / completed (includes admin-sent and invite links) |
| 22 / 23 | Magic link requested / used to sign in |
| 24 / 25 | User disabled / enabled |
| 26 / 27 | Account locked / unlocked |
| 28 / 29 | Two-factor enrolled / removed by the user |
| 30 / 31 / 32 | Two-factor challenge issued / code accepted / code rejected |
| 33 | Two-factor reset by an administrator |
| 34 / 35 | Backup codes generated / used |
| 36 / 37 | Trusted device remembered / revoked |
| 38 | Forced password reset for all users |
| 50 / 51 | Service account flag set / cleared |
| 52 | Two-factor skipped (a service account signed in) |
| 53 | Password changed (by the user, a forced change, or an administrator) |
Endpoints used
GetAppSettings/SaveAppSettings— security settingsSaveContact—AuthType,disabled,bforcepasschange,twofaAdminSendLink— invite and reset linksDisableContact/EnableContact/UnlockContactTwoFactor/AdminStatus/TwoFactor/AdminResetForcePasswordResetAllSetServiceAccount— integration accountsGetAuditTypes