LiveView Authentication
Runtime authentication is the second identity realm: the operators who use LiveView screens. It is fully independent of Platform authentication — different user store, different token secret, different audience.
Why two realms
An operator on the plant floor and an engineer in the Platform need different things:
| Platform user | Runtime user | |
|---|---|---|
| Population | Tens | Hundreds, changing with shifts and contractors |
| Identity source | The QUBIQ configuration database | Your own directory or database |
| Authorises | Design and administration | Viewing screens, writing permitted tags |
| Token | JWT_SECRET | LIVE_JWT_SECRET, audience liveview |
Putting operators in the platform user table would mean every shift change touching the engineering system. Keeping them separate means a runtime token presented to a design-time endpoint fails on audience — the separation is structural, not a policy.
User Databases
A User Database is an external identity source holding runtime users. A project is bound to one.
| Project binding | LiveView behaviour |
|---|---|
| Unbound (default) | Public — anyone who can reach the URL sees the screens |
| Bound | /{project}/login required; runtime roles govern access and writes |
The binding is authoritative and written only through its dedicated endpoint — never through the project manifest — so an author cannot grant access by editing a resource.
Managing user databases requires Manage LiveView Auth, a super-admin permission by default.
When the bound database is gone Changed in 1.0.8
A project can name a User Database whose record no longer exists — it was deleted, or the project was restored from another machine. The project stays login-only, but nobody can sign in, so every screen shows no data. Before 1.0.8 nothing said so: the login answered as if the username were unknown, and the LiveView access field in Project Properties showed a blank Select…, which read as "nothing configured".
Now the field shows Missing User Database (deleted) with a warning, and a sign-in attempt answers that the project's user database no longer exists and where to fix it. Choose another database, or None — public.
Public projects Changed in 1.0.8
An unbound project is public: its screens open for anyone who can reach the URL, with no operator identity. 1.0.8 makes such a screen behave as designed instead of quietly failing:
| What the screen does | For an anonymous visitor |
|---|---|
| A named query that reads — a Form's dropdown options, a script reading a lookup table | Runs |
| A named query that writes | Refused: This named query changes data, so it needs a signed-in user with write access. |
system.files from a client script | Refused — file access needs a signed-in user |
| A Historian chart | Reads the project's designated historian, subject to Historian data access |
A named query counts as a read only when its declared kind is a read and its statement is one
SELECT or WITH that cannot write — no SELECT … INTO, no data-changing statement inside a WITH,
and for MongoDB no $out or $merge. Both are checked on the server, because a declared kind is
only a label. The same rule applies to an operator below the write baseline on a bound project.
A Studio user holding Manage Designer who opens a public project's LiveView while signed in to the Studio is recognised as such, so their named queries run with their own rights. Before 1.0.8 they were treated as anonymous, every named query was refused, and the refusal signed them out of the Studio.
Gateway variables a screen reads (vars.<name>, system.vars.get) are served to operators and
anonymous visitors, but only the variables that project's views and client scripts actually
reference. Listing the variable store from a LiveView returns that subset, not every project's
variables.
Runtime users
| Field | Meaning |
|---|---|
| Username, email | Identity |
| First / last name | Display |
| Approved | An administrator has admitted the account |
| Active | The account is not suspended |
| Phone, WhatsApp opt-in | Contact channels for notification |
| Roles | Runtime role names |
| Write level | Derived — the highest level across the user's roles |
| Last login | Diagnostics |
Approved and Active are orthogonal
Login requires both. The derived states are:
| Approved | Active | Status |
|---|---|---|
| false | — | pending — self-registered, awaiting admission |
| true | true | active |
| true | false | suspended |
An account is never "pending and active": approval sets both together. Self-registered users start unapproved; administrator-created users start approved.
The two gates mean "not yet admitted" and "admitted but currently suspended" are distinguishable — which matters when a contractor's access is paused rather than never granted.
Write-security levels
A small fixed hierarchy. A role maps to a level; a tag may require one; a write is allowed when the operator's level is at least the tag's.
| Level | Name | Meaning |
|---|---|---|
| 0 | Read | Cannot write at all |
| 1 | Operate | Baseline write capability |
| 2 | Supervise | Supervisor actions — recipes, mode changes |
| 3 | Admin | Calibration, safety-adjacent parameters |
The operator's level is the highest across their roles, and it is embedded in the runtime token so the write path checks it without a database round trip per write.
Tag Line1/Filler/SpeedSP writable, write level 2
Role "Operator" level 1 → write rejected
Role "Supervisor" level 2 → write allowed
Both gates are enforced server-side. Hiding a button is usability, not security.
Roles and rosters
| Concept | Purpose |
|---|---|
| Runtime role | Named group with a write level and view access |
| Role membership | Which users hold which roles |
| Roster | An on-call schedule of shifts and members |
| On-call now | Who is currently on shift — the target for notifications |
A shift counts as active when the current time falls within its window, or when it has no window (always-on).
Revocation
Runtime tokens have a TTL, but revocation must not wait for it — a suspended operator should lose access now, not in an hour.
QUBIQ keeps a short-TTL revocation cache of (user → active, roles), held for 45 seconds. The runtime middleware consults it rather than querying the external database on every request. On a cache miss it runs one lightweight lookup.
The result: cost is roughly one query per active user per cache window, and a suspended or role-changed operator is reflected within about that window.
To end every operator session at once, an administrator uses Sign out all in
Settings → Security Policy. It ends Studio sessions too. → Sessions
Self-registration
Where enabled, operators register themselves and land as pending. An administrator approves, which sets both approved and active, and assigns roles.
This suits sites with high turnover: the account exists and is attributable from the first day, but grants nothing until a human admits it.
Session behaviour
| Situation | Behaviour |
|---|---|
| Token expires | Re-authentication requested; screen state preserved where possible |
| Operator suspended | Access revoked within the revocation cache window |
| Roles changed | New level applies within the same window |
| Network drops | Session resumes on reconnect if the token is still valid |
| Platform restarts | Signed out, unless Keep sessions across restarts is on 1.0.8 |
| An administrator uses Sign out all | Signed out at once; unattended screens return to their sign-in page 1.0.8 |
The sign-in page Changed in 1.0.8
The LiveView sign-in — full page or overlay, chosen by Login presentation — shows the project's own logo when one is set, and the QUBIQ mark otherwise. → Branding
Kiosk panels
For a permanent panel, decide deliberately:
- A shared panel account is acceptable where the panel is physically secured and every action is attributable to the panel. Set its write level to the minimum that lets the shift work.
- Individual logins where actions must be attributable to a person — anything regulated, or anything where the audit journal has to answer "who".
Do not give a shared kiosk account level 3.
Checklist
- Every production project is bound to a User Database — no accidental public LiveView
-
LIVE_JWT_SECRETis set explicitly, not derived - Writable tags have write levels reflecting real consequence
- Roles map to levels matching actual responsibility
- Self-registration, if enabled, has a named approver
- Leavers are suspended, not deleted, so history stays attributable
- Kiosk accounts hold the minimum level that lets the shift work