Configuring Ignition User Self-Registration Accounts

Daniel Price6 min read
B&R AutomationHMI / SCADATroubleshooting
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Where does the login request actually stop?

Follow the credential. A user types a name and password into the login form. The client or Perspective session hands that pair to the Gateway. The Gateway resolves the project's assigned user source, and the user source's authenticator takes the submitted password, applies the stored salt and the configured hash algorithm, and compares the resulting digest against the stored value. Only if the digests match does role lookup happen and a session get created.

An account inserted with a raw SQL INSERT into the internal user table stops at that comparison. The password column holds whatever string was written; the authenticator hashes the submitted password and compares hash-to-plaintext. The two never match, so the login fails with a generic bad-credentials response — no error in the client that points at the schema, because from the Gateway's view nothing is broken. The row exists, the role assignment in the user-role table exists, and the account is simply unauthenticatable.

Second stop on the path: the internal user source is cached in Gateway memory. Rows written behind the Gateway's back are not guaranteed to be picked up until the user source refreshes. Even with a correctly formatted hash, out-of-band writes can leave the running Gateway serving a stale user list.

Which approaches will actually authenticate?

Approach Who computes the hash Cache-safe Migration/upgrade risk Verdict
Direct SQL insert into the internal auth tables You — must reproduce the Gateway's exact salting and hashing scheme No; requires a user source refresh or Gateway restart High — the internal schema and hash format are implementation detail and can change between versions Avoid
system.user.addUser() scripting function The Gateway, using the user source's own authenticator Yes; the user source is updated through its own API Low — the API is the supported contract Use this
Database user source with a custom authentication query You, inside a query you control and document Yes; the Gateway queries on demand Medium — you own password policy, hashing and lockout logic Only if credentials must live in an existing corporate schema
Manual creation in the Gateway web UI Gateway Yes None Works, but defeats self-registration

The deciding criterion here is not effort, it is who owns the hash. Reproducing the internal user source's salting scheme in application code couples your project to an undocumented storage format. The scripting API pushes the credential through the same code path the login uses, so the hash is correct by construction.

How do you create the account from a script?

Build the user object from the user source, populate the properties, attach roles, then submit it. Reference: system.user.addUser.

  1. Validate the submitted form data first — username uniqueness, password length and complexity, and any e-mail or contact fields your project requires. Do this before you touch the user source.
  2. Call system.user.getNewUser(userSource, username) to obtain an empty user object bound to the correct user source. Pass the exact user source name assigned to the project; an empty string resolves to the project's default.
  3. Set the password and any contact properties on that object, then add the role or roles the new account should receive.
  4. Call system.user.addUser(userSource, user). It returns a response object carrying warnings and errors — read it. Do not assume success because no exception was raised.
  5. If the response contains errors, surface them to the operator and do not report the account as created.
userSource = "default"
user = system.user.getNewUser(userSource, username)
user.set("password", password)
user.addRole("Operator")

response = system.user.addUser(userSource, user)
for w in response.getWarns():
    print w
for e in response.getErrors():
    print e

Role names are case-sensitive and must already exist in the user source. If the role string does not match an existing role, the account is created without the intended access and the user logs in to an empty or read-only project — a failure that looks like a permissions bug rather than a registration bug.

Where should the script run?

Account creation is a Gateway-scope operation. In Perspective the script already executes on the Gateway, so a button event handler or a message handler is sufficient. In Vision, the call is issued from the client but the work is performed on the Gateway over RPC — which means the client's logged-in identity is irrelevant to whether the call succeeds. Any user who can reach that window can create an account, including one with an elevated role, unless you constrain it.

Constrain the role list in code. Hard-code the single role that self-registered users receive; never bind it to a component the operator can edit. If registration must be reachable from an unauthenticated screen, put it on a dedicated project or view with no other functionality and rate-limit submissions.

How do you verify the account before handing it to the user?

  1. Read the returned response object for errors and warnings on every call.
  2. Query the account back with system.user.getUser(userSource, username) and confirm the username and the role list match what you intended.
  3. Open the Gateway web interface, go to the user source configuration and list its users. The new account must appear there. If the row exists in the database but not in this list, the write bypassed the user source API.
  4. Check the Gateway audit log for the authentication attempt when the user first logs in. A successful authentication followed by an authorization failure points at roles; a failed authentication points at the credential.
  5. Log in with the new credentials on a client that has no cached session, and confirm the project security zones and role-gated components behave as designed.

What breaks on this class of setup?

  • Half-migrated accounts. If earlier attempts left orphan rows in the internal tables, a later addUser call for the same username can collide. Remove the hand-written rows through the Gateway web interface, not with DELETE, so the user source stays consistent with its cache.
  • Wrong user source name. Scripts that hard-code a user source name that differs from the one assigned to the project create accounts nobody can use. Confirm the assignment in the project's security settings.
  • Password policy drift. The user source enforces its own password requirements; if your form validation is weaker, addUser rejects the account and your UI reports success unless you inspect the response.
  • No update path. Self-registration needs a matching password-change and account-disable path, otherwise every forgotten password becomes a Gateway administration task.
  • Custom hashing without documentation. If you go the Database user source route, the authentication query becomes a maintenance liability. Document the hash algorithm, salt storage and iteration count alongside the project.

Finish by logging in as the new account on a clean client and confirming the role-gated components render as intended.

FAQ

Can I create Ignition users by inserting rows directly into the internal auth tables?

You can insert the rows, but the accounts will not authenticate unless you reproduce the Gateway's exact salting and hashing scheme, and the running user source may not see them until it refreshes. Use system.user.addUser() instead.

Does system.user.addUser hash the password for me?

Yes. The call goes through the user source's own authenticator, so the credential is stored in the same format the login path expects — which is why gateway-created and script-created accounts behave identically.

Can I call system.user.addUser from a Vision client button?

Yes; the call is executed on the Gateway. Because of that, any operator who can reach the window can create an account, so hard-code the assigned role and restrict access to the registration screen.

Does the role have to exist before I assign it to a new user?

Yes. Role names are case-sensitive and must already exist in the user source; assigning a non-existent role produces an account that authenticates but has no project access.

Can I keep credentials in my own database schema instead?

Yes, with a Database user source and an authentication query you control, but then you own the hashing, password policy and lockout behavior. Document the algorithm and salt handling with the project.

Back to blog