Skip to content

Multi Factor Authentication

zach115th edited this page Aug 31, 2026 · 2 revisions

Multi-Factor Authentication

Exempt accounts, the Set up MFA label and the moved account actions require IRIS-NG-v1.4.1. On IRIS-NG-v1.4.0 and earlier the button always reads Reset MFA, sits below the skills catalogue on the profile page, and every account is subject to enforcement — including the built-in administrator.

A second factor on top of the password, using time-based one-time passwords (TOTP).

Unlike Single Sign-On, MFA is configured in the interface: Advanced → Server Settings → Security → Enforce MFA for all users.


MFA is not LDAP

These are frequently conflated, partly because the upstream DFIR-IRIS documentation puts them on one page.

  • LDAP is an authentication type — it decides where the password is verified (a directory instead of the local database).
  • MFA is a second factor layered on top of whichever type you use.

They compose rather than compete. When LDAP is the authentication type, MFA enrolment confirms the user's identity by validating their password against the directory. Local accounts and LDAP accounts can both carry TOTP.

The one exception is OIDC — see SSO logins bypass MFA.

Which authenticator apps work

Any standard TOTP app. The implementation is TOTP (RFC 6238) with the universally supported parameter set — SHA-1, 6 digits, 30-second period:

  • Microsoft Authenticator
  • Google Authenticator
  • Authy, 1Password, Bitwarden, KeePassXC, Aegis, and others

Enrolment shows a QR code encoding a standard otpauth://totp/... URI, plus the secret in text form for apps where you would rather type it in.


Enabling it

Tick Enforce MFA for all users under Advanced → Server Settings → Security, then Save changes.

That checkbox is the entire administrative surface, and it applies immediately — no restart. There is nothing else to configure because TOTP exposes no server-side parameters here; enrolment is per user and happens at their next login.

It is all-or-nothing: there is no per-user or per-role MFA requirement.

What a user sees on first login after enforcement

  1. They sign in with their password as usual.
  2. They are redirected to MFA setup and shown a QR code.
  3. They scan it, then enter a current 6-digit code and their password to confirm.
  4. Every later login asks for a code after the password.

The QR code is one click further in

The profile page does not show the QR code inline. It shows a button — labelled Set up MFA before you have enrolled, Reset MFA afterwards — which links to /auth/mfa-setup, and that page renders the QR code.

The controls are gated on enforcement being on:

  • on each user's own profile page, next to Change password
  • per user under Advanced → Access Control → Users, for administrators

Because they are hidden while enforcement is off, the interface can look as though it has no MFA support at all before the box is ticked. Nothing is missing — the controls appear once you save.

Exempt accounts

Two kinds of account are never prompted for a second factor, even with enforcement on. Their MFA control is shown disabled with the reason in its tooltip rather than hidden, and /auth/mfa-setup refuses them, so an exempt account cannot enrol a secret that nothing would ever check.

Account Why
The built-in administrator (user #1) A deliberate break-glass account, by maintainer decision.
Service accounts They cannot log in interactively at all — _authenticate_password rejects them before the password is checked. They authenticate by API key, which has no MFA step. Exempting them in the UI only makes visible what was already true.

Security trade-off, stated plainly. Whoever holds the built-in administrator's password bypasses MFA entirely on the most privileged account in the system. That password becomes the whole security boundary for that account — give it a strong unique value and restrict who knows it. If you would rather not accept this, keep a second administrator account (which is subject to enforcement) for day-to-day work and reserve user #1 for recovery.

The exemption is keyed to user #1 specifically, not to the server_administrator permission — every administrator you create later must enrol like anyone else.

IRIS_MFA_ENABLED only seeds the initial value

The environment variable sets the default for that checkbox when the settings row is first created, and is never read again. On an existing instance, changing it in .env does nothing at all — the GUI owns the value from first boot onward.


SSO logins bypass MFA

An OIDC login is exempt from the MFA check by design.

If you use SSO, enforce MFA at your identity provider. Ticking the IRIS checkbox will not add a second factor for OIDC users. It still applies to local and LDAP logins, so a deployment using OIDC with local fallback enabled gets MFA on the fallback path only.


There are no recovery codes

Before enforcing MFA, understand the recovery story: there is no self-service recovery. A user who loses their authenticator needs an administrator to click Reset MFA. An administrator who loses theirs needs another administrator — or, if they are the only one, direct database access.

Full lockout — disable enforcement

The restart is required, not optional: server settings are cached in the process and are not re-read once loaded.

docker exec iriswebapp_db psql -U postgres -d iris_db \
  -c "UPDATE server_settings SET enforce_mfa = false;"
docker restart iriswebapp_app

Reset a single user instead

Clears their enrolment so they re-enrol at next login, leaving enforcement on for everyone else:

docker exec iriswebapp_db psql -U postgres -d iris_db \
  -c "UPDATE \"user\" SET mfa_secrets = NULL, mfa_setup_complete = false WHERE \"user\" = 'analyst';"

Prefer the Reset MFA button when you still have an administrator session — it does the same thing through the application.


Current limitations

Worth knowing before you roll this out widely:

Limitation Detail
No recovery codes The gap most likely to cost you an account. The built-in administrator is exempt, so it doubles as the break-glass route.
Issuer name is fixed Entries appear as IRIS in the authenticator app regardless of instance. Running more than one instance makes them hard to tell apart in the app's list.
No per-user or per-role policy Enforcement is global.
No parameter control Digits, period and algorithm are not configurable.
Not applied to OIDC By design — enforce at the IdP instead.

Troubleshooting

Symptom Cause
Nothing MFA-related visible in the UI The controls are hidden until Enforce MFA for all users is saved.
No QR code on the profile page Expected — the profile shows a Set up MFA / Reset MFA button; the QR is on the page it links to.
MFA button is greyed out The account is exempt — the built-in administrator or a service account. The tooltip says which.
MFA prompt never appears for SSO users Expected — OIDC logins skip MFA. Enforce it at the IdP.
Changing IRIS_MFA_ENABLED in .env does nothing Correct behaviour. It seeds the setting only on first creation; use the GUI checkbox.
Codes are rejected as invalid TOTP depends on clock agreement. Check time sync on both the server and the phone.
Enforcement toggled in the database but nothing changed Server settings are cached in the process — restart the app container.

Check what the application parsed at startup:

docker logs iriswebapp_app 2>&1 | grep -iE "MFA (enabled|disabled)"

Note this line reflects the environment variable read at boot, not the current database value. After the first boot, the checkbox is the source of truth.


Related pages

Clone this wiki locally