Skip to main content

Users

A user in Liberty is the identity that signs in and carries roles. The Liberty Next → Administer → Access → Users tab is where you create local users, see OIDC users that have signed in at least once, assign roles, and toggle the active and superuser flags.

The same UI manages both identity backends (TOML / DB) and both identity sources (local / OIDC). The differences land on what fields are editable: OIDC users have no password (it's at the IdP), and their provider field is read-only.


The Users tab​

Settings · Access👤 Users🛡 Roles+ Add useradminAdmin User · admin@corp.local · localsuperuseradminaliceAlice Martin · alice.martin@corp.local · oidcanalystreporterbobBob Dupont · bob@corp.local · localinactivereaderClick a row to open the User editor.

Each card shows the username (monospace), full name, email, provider (local / oidc), the status chips and the assigned roles.

ChipMeaning
superuser (orange)is_superuser = true — bypasses every permission check.
inactive (red)is_active = false — the user can't sign in. Their existing sessions stop on next token refresh.
No roles (muted)No roles assigned and not a superuser — the user signs in but sees nothing.
Role names (purple)Each assigned role, one chip per role.

Add a local user​

Click + Add user. The User editor opens with every field empty.

Add userUsername *aliceFull name(optional)Alice MartinEmail(optional)alice@corp.localPassword *(8+ chars)••••••••••Rolesanalyst ✕reporter ✕Pick a role…Active✓the user can sign inSuperuserbypasses every permission checkCancelSave

Fields​

FieldRequiredNotes
UsernameyesLetters, digits, underscores, dots and hyphens. Used as the sign-in handle, the JWT subject, and the audit identity. Immutable after creation.
Full namenoThe display name shown in headers and audit columns.
EmailnoWhere a Forgot password? link is mailed (see below), and the notifications' address.
Passwordyes (for new users)At least 8 characters. Stored as an Argon2id hash, never readable back. Leave blank on edit to keep the current password.
Ask for a new password at the next sign-indefault on for a new userThe password given here is a temporary one: the user chooses their own at the first sign-in (see below). Local accounts only.
RolesnoChip picker — type or pick from the dropdown. The dropdown lists every role configured under the Roles tab.
Activeyes (default on)When off, the user can't sign in; existing sessions die on next token refresh.
SuperusernoWhen on, bypasses every permission check and every data scope. Use sparingly.
Data-scope attributesnoKey / value pairs read by the roles' data scope — see below.

Click Save — the user is created and the modal closes. The new user appears at the top of the list.

Endpoints behind the scenes​

ActionEndpoint
CreatePOST /admin/access/users
UpdatePATCH /admin/access/users/{username}
Password changePOST /admin/access/users/{username}/password

The TOML backend writes to config/auth.toml atomically; the DB backend commits to ly2_users / ly2_user_roles. Either way, the change takes effect immediately — the user can sign in with the new password right away.


Edit an existing user​

Click any user row to open the editor with their current values:

FieldEditableNotes
UsernamenoPinned at creation.
Full name / EmailyesUpdate freely.
Passwordyes (local users only)Leave blank to keep the current password. Typing a new one (8+ chars) sets it on Save.
RolesyesAdd / remove chips.
ActiveyesToggle to deactivate.
SuperuseryesToggle to grant / revoke superuser.
Data-scope attributesyesSee below.

Saves go through PATCH /admin/access/users/{username} for the property updates and POST /admin/access/users/{username}/password if a new password was typed.

When the user is signed in

Changes don't break their current session. The new roles / disabled flag / password take effect on the next refresh (within the access token TTL — default 1 hour) or sign-out + sign-in.


A new password at the first sign-in​

An account created — or reset by an administrator — with Ask for a new password at the next sign-in opens on Choose your password: the temporary password, the new one (8+ characters) and its confirmation. Until it's done, the server refuses every call but the change itself (HTTP 403 password change required), so the temporary password can't be used for anything else, through the page or the API. The new password takes effect at once — no new sign-in. The same from the command line: liberty-admin create-user … --must-change, liberty-admin set-password … --must-change. An OIDC account is never asked (its password is the identity provider's).

Forgot password​

When SMTP is configured (Liberty Next → Administer → App, notifications), the sign-in page shows Forgot password?. The user gives their username and the account's e-mail; when both match an active local account, a link is mailed to that address. The link works once and for an hour — it is bound to the password it was issued for, so any password change, the reset included, ends it. The page answers the same whether or not the two match, so it never tells who has an account; requests are limited per address. The link points at Public address (App, [app] public_url) — set it when users reach the server through a reverse proxy or another name; blank, the browser's address is used.

Data-scope attributes​

A role's data scope can take its values from the user instead of listing them: a rule Company (CO) = user.company lets one role serve every subsidiary, each user seeing their own companies.

In the user form, Data-scope attributes holds one row per attribute: a key (company) and a value (00001, 00050 — a comma makes a list). Add an attribute… suggests the keys the roles already use. A user who lacks the attribute a rule needs sees no row through that rule.

The users list shows the attributes on each card (company=00001,00050), and the user sees them, with their data scope in words, in their Profile window. From the command line: liberty-admin set-attributes alice company=00001,00050.


OIDC users — the differences​

OIDC users land in the Users list after their first sign-in — Liberty auto-creates them with provider = "oidc". The editor for an OIDC user looks the same, but:

FieldEditable for OIDC?Notes
UsernamenoSame as local.
Full name / Emailtechnically yes, but…On every OIDC sign-in, these fields are refreshed from the IdP claims. Manual edits get overwritten. Edit them at the IdP instead.
PasswordnoThe IdP holds the password. The field is disabled or rejected with HTTP 400.
RolesyesThis is the main reason operators visit OIDC users — to assign roles.
ActiveyesWhen off, sign-in is rejected even with valid OIDC tokens.
SuperuseryesToggle to grant the bypass.

The standard onboarding sequence:

  1. The new hire signs in via OIDC for the first time.
  2. They land with no roles → they see an empty app.
  3. They tell their admin "I'm in but see nothing".
  4. The admin opens Liberty Next → Administer → Access → Users, finds them, assigns the right roles, saves.
  5. The user refreshes the page (or signs out + back in) and now has access.

For installs that want to pre-create OIDC users with roles already attached, scripts can hit POST /admin/access/users with provider = "oidc" and the expected provider_subject (the sub claim). On first sign-in the framework finds the pre-created user and uses it. Standard installs skip this and assign roles after first sign-in.


Deactivating a user​

Users are not deleted: to take someone out, switch Active off and save. They can no longer sign in; their current session ends when its access token expires (one hour at most), since the renewal is refused. Roles, attributes and history are kept, and switching Active back on restores the account as it was.


What you actually do — typical workflows​

Onboard a local hire​

  1. Liberty Next → Administer → Access → Users → + Add user.
  2. Username = corporate handle, password = a one-time string they'll change, email = their address.
  3. Assign roles per their team (analyst, reporter, …).
  4. Save.
  5. Send them the temporary credentials through whatever channel your company uses.
  6. They sign in with it and land on Choose your password — with Ask for a new password at the next sign-in ticked (the default), nothing else opens until they've set their own.

Onboard an OIDC hire​

  1. They sign in via OIDC the first time — Liberty auto-creates the account.
  2. They report no access.
  3. Liberty Next → Administer → Access → Users, find them, assign roles, save.
  4. They refresh → they're in.

Change a user's password (you're the admin)​

  1. Liberty Next → Administer → Access → Users, click the user.
  2. Type the new password in the Password field (8+ chars); tick Ask for a new password at the next sign-in to make it a temporary one.
  3. Save.
  4. Tell the user. They sign in with the new password (and, if temporary, choose their own); their existing session keeps working until the access token expires.

With SMTP configured, users can also reset it themselves — Forgot password?.

Revoke an account immediately​

  1. Toggle Active off and save.
  2. If you can't tolerate the up-to-1-hour delay until their current access token expires, rotate the JWT signing secret (LIBERTY_JWT_SECRET) and restart. Every existing token is invalidated — a heavy hammer but the only immediate revocation Liberty offers.

Common pitfalls​

MistakeSymptomFix
OIDC user has no roles after sign-in.Expected — the operator hasn't assigned any yet.Open Liberty Next → Administer → Access → Users, assign roles.
Editing an OIDC user's email reverts on next sign-in.The IdP claim wins; the framework refreshes from the latest token.Edit the email at the IdP.
Trying to set a password for an OIDC user.Backend rejects with 400.OIDC users have no local password — they sign in through the provider.
Changing a user's role doesn't take effect immediately.The user's JWT still has the old roles.They refresh (or sign-out + sign-in) — within the access token TTL the new roles apply.
A user with an attribute-based scope sees nothing.The user has no value for the attribute the rule reads.Add it under Data-scope attributes.
Marking a user superuser to fix a permission gap.Works, but bypasses every check — including ones the user shouldn't be bypassing.Add the missing permission to one of their roles instead.

What's next​