Multi-factor (MFA)
Multi-factor authentication (MFA) adds a second proof of identity on top of the user’s password. In Taranac, MFA is not bolted on at one entry point — it reaches the web admin UI, TACACS+ device administration, RADIUS AAA, and 802.1X / captive-portal network access. One enrollment serves all of them: the user picks a provider once, and every context uses that same enrollment. What differs between them is who decides the factor is required — each path has its own switch — and what a transport is physically able to carry, which is why some of them accept only a push.
Taranac supports both push factors (the user approves a login on a separate device — “bouncer” style) and OTP factors (the user types a time-based code). Push is what makes MFA usable on a network terminal: at a TACACS+ or RADIUS prompt the user enters only their password, the session waits, and an approval notification arrives on their phone — no awkward concatenation of password and code.
Key concepts
Section titled “Key concepts”- Provider — the mechanism that delivers and verifies the second factor (TOTP, Telegram, Multifactor, Email push, or Taranac MFA). Each user has exactly one active provider, which the user chooses from the list you allow.
- Mode — how a provider verifies:
totp(user types a code),push(user approves/denies on a device), orcombined(push with a TOTP fallback behind a single QR). - Enrollment — the one-time linking of a user to a provider. Every enrollment requires an activation step (the user confirms one push or types one code) before it becomes active.
- Challenge — a single pending push request during login. It ends as
approved,denied, orexpired, and its TTL depends on who created it: 120 s for Telegram, for Taranac MFA and for the TACACS+/RADIUS/802.1X daemons, 300 s for Email push, and Multifactor’s own RADIUS timeout plus ten seconds. An expiry is written to the audit log (MFA_PUSH_EXPIRED), not merely left as a row status — resolved challenges are deleted 24 hours later, so a status alone would not survive the night. - Enforcement — no one is forced through MFA merely because they enrolled. Each login path has its own switch, and they are independent: see Making MFA mandatory.
mfa_required(group flag) — the switch for the web console and API. Any group a user belongs to that hasmfa_requiredset forces that user to have MFA there. Providers themselves are not pinned to groups — the user always picks their own provider.
Supported providers
Section titled “Supported providers”These are the five providers shipped in Taranac. Do not expect others.
| Provider | Category | Modes | What the user needs |
|---|---|---|---|
| TOTP | Local | totp | Any authenticator app (Google Authenticator, Aegis, etc.) |
| Telegram | External | push | A Telegram account, linked to a bot you run |
| Multifactor | External | push, totp | A Multifactor (RADIUS) account |
| Email push | Hybrid | push | A working email address (uses your existing SMTP) |
| Taranac MFA | External | combined, totp | The Taranac MFA mobile app (push via FCM, TOTP fallback) |
Categories:
- Local (TOTP) runs entirely on the server with no external dependency. Codes are generated and verified locally. This is the most resilient option — if the backend’s outbound network or an external service is down, TOTP users still authenticate.
- External providers reach an outside service: Telegram via the Bot API, Multifactor over the RADIUS protocol, Taranac MFA over its own REST API.
- Hybrid (Email push) sends an approve/deny link through your SMTP server; the link points back to Taranac.
Default verification mode per provider
Section titled “Default verification mode per provider”When a user enrolls, the provider starts in its default mode:
| Provider | Default mode |
|---|---|
| TOTP | totp |
| Telegram | push |
| Multifactor | push |
| Email push | push |
| Taranac MFA | combined |
How MFA layers onto each login path
Section titled “How MFA layers onto each login path”| Context | TOTP (code) | Push (Telegram / Multifactor / Email / Taranac MFA) |
|---|---|---|
| Web admin UI | Code entered on a second screen, after the password | Browser shows a “waiting for approval” screen and polls until approved |
| TACACS+ (device CLI) | Code appended to the end of the password | Session waits; push lands on the user’s device; on approval, access is granted |
| RADIUS (device admin, PAP) | Appended to the password, or asked for in a second round — see below | Handler waits for the push result |
| RADIUS (device admin, MS-CHAPv2) | Fails closed | The only form MFA can take on that transport |
| 802.1X / EAP | Fails closed | The only form MFA can take there |
| Captive portal (BYOD registration) | Code in the portal form | Portal polls until approved |
Where a code is appended, Taranac takes the last 6 or 8 characters of what was typed, and only if they are all digits; everything before them is the password.
Enrolling a user
Section titled “Enrolling a user”Enrollment is self-service: a user opens their MFA settings, picks an allowed provider, and confirms it once.
- The user opens My Profile → Two-factor authentication and selects a provider from the list of those you’ve enabled.
- Taranac starts the enrollment and returns whatever the provider needs — a QR code and secret (TOTP, Taranac MFA), a Telegram deep link / QR, or a test push / email.
- The user completes the activation step:
- TOTP / Taranac MFA (totp): scan the QR in an authenticator app, then type the first code.
- Telegram: open the deep link (valid 10 minutes) and press Start in the bot to link the account — then approve the enrollment test push in the bot. A web click alone never activates it, and a denial is reported as such instead of an endless “pending”. You can re-send the test push without restarting the enrollment.
- Other push providers: approve the test push (or type the fallback code).
- On success the enrollment becomes active and (for TOTP) backup codes are shown once.
Helping users get started: setup links
Section titled “Helping users get started: setup links”You do not have to walk each user through enrollment. Send a setup link to a single user from their user page, or to every member of a group at once from the group card on Integrations → MFA. The link lets the recipient configure MFA without a full login, and is single-use and time-limited (default 60 minutes); sending a new one invalidates any earlier unused link, and activating MFA burns the outstanding one. This is the practical way to roll MFA out to a team after you flip mfa_required on their group.
Redeeming the link does not hand out a session. It is exchanged for a 15-minute token whose declared purpose opens the enrollment endpoints and nothing else — it cannot reach the profile, the password, or anything the rest of the API guards. Group sends report per user why each was skipped (already configured, no email address, inactive) rather than a bare count, and you can choose between “new users only” and “reset and resend to all”.
Setup links can also go out by themselves: turn on Send MFA setup link on user creation (mfa.send_setup_link_on_create) and every new account — local or synced from a directory — is emailed one, provided SMTP is configured.
Making MFA mandatory
Section titled “Making MFA mandatory”There is no single master switch that turns MFA on for everybody. Each login path decides for itself, and you can leave one demanding a second factor while another does not:
| Path | What makes MFA mandatory | Where it lives |
|---|---|---|
| Web console & API | mfa_required on any group the user belongs to | The group’s edit form, or the group card on Integrations → MFA |
| TACACS+ / RADIUS device administration | The matched AAA policy rule’s mfa field (off / required), gated by tacacs.mfa_mode and radius.mfa_mode | AAA policy |
| 802.1X | The matched NAC rule’s mfa_required | 802.1X authentication |
| Captive portal (BYOD device registration) | Require MFA on the BYOD flow | Captive portals |
A user in an mfa_required group who has not enrolled cannot complete a web login at all: they are told MFA setup is required and handed nothing else — you unblock them with a setup link. On a device login the same account is refused with mfa_not_configured.
The corollary is worth stating plainly, because it surprises people: a voluntary enrollment is not challenged on the web console. If none of the user’s groups sets mfa_required, the web login issues session tokens on the password alone, however carefully that user enrolled. The exception is the captive portal — a BYOD flow with Require MFA on challenges any user who has a provider, voluntary enrollments included. If you want a second factor honoured on the console, the group flag is the way to ask for it.
Administering existing users
Section titled “Administering existing users”| Action | Where | Permission | Effect |
|---|---|---|---|
| View a user’s MFA status | Infrastructure → Identity → Users | mfa.view | Shows the active provider and mode |
| Reset a user’s MFA | Infrastructure → Identity → Users | mfa.edit | Removes every enrollment — use when a device is lost |
| Remove one enrollment | Their own profile, or a user’s page | self, else mfa.edit | Deletes a single method |
| Regenerate backup codes | Infrastructure → Identity → Users (TOTP) | mfa.edit | Issues a fresh set of one-time recovery codes |
| Send setup link | A user’s page, or the group card on Integrations → MFA | mfa.edit | Emails a one-time enrollment link |
| Enable/disable a provider | Integrations → MFA | mfa.manage | Controls which providers users may choose |
| Read provider health | Integrations → MFA | mfa.view | Runs the live checks below |
A user cannot remove their own last enrollment while a group they belong to sets mfa_required — the refusal is what stops someone opting out of a policy that applies to them. Switching to a different provider is still open to them, because that never leaves the account without a factor. An admin reset is not bound by this.
An admin reset also does more than delete rows: where the provider holds state of its own it is cleaned up too — every Taranac MFA token for that user is wiped on the MFA server, and a linked Telegram chat is told it has been unlinked.
When to use which provider
Section titled “When to use which provider”| If you need… | Choose | Why |
|---|---|---|
| Maximum resilience, no external dependency | TOTP | Works offline; survives a backend outage; nothing to operate |
| Smooth approvals for network logins, self-hosted | Taranac MFA | Push via your own MFA server + FCM, with TOTP fallback in one enrollment |
| Push without running a mobile backend | Telegram | Uses a Telegram bot you create; familiar to most users |
| To reuse an existing Multifactor deployment | Multifactor | Speaks RADIUS to your Multifactor service; push or TOTP |
| A zero-install fallback for occasional users | Email push | Approve/deny by email link; uses your existing SMTP |
General guidance:
- Always keep TOTP enabled. It is the only provider with no external dependency and is the safe last line of defence if push delivery fails. Note what it cannot do: an enrollment whose mode is
totpis refused on 802.1X and on MS-CHAPv2 device administration, because neither can carry a typed code. - For device administrators logging in over TACACS+/RADIUS, prefer a push provider (Taranac MFA or Telegram) so they never have to append codes — and if any of your devices speak only MS-CHAPv2, a push-capable provider is not a preference but a requirement.
- Email push is convenient but only as strong as the user’s mailbox — treat it as a fallback rather than the primary factor for privileged accounts.
Provider health
Section titled “Provider health”Taranac actively checks that each enabled provider can do its job, so you can spot a broken second factor before users are locked out:
| Provider | Health check |
|---|---|
| TOTP | Always healthy (local, no dependencies) |
| Email push | SMTP server reachable (connect + EHLO, no authentication) |
| Telegram | Bot token valid and Bot API reachable (getMe); on the node running the update loop, also whether another system is polling the same bot — a getUpdates conflict means approvals may be delivered elsewhere |
| Taranac MFA | /healthz on the MFA server: its database, its crypto and its Firebase connection |
| Multifactor | Configuration present (host and secret) and the host’s DNS resolves, plus a runtime check of whether recent push requests got a RADIUS reply at all. There is no active probe — Multifactor does not answer Status-Server |
A watchdog runs these checks every 90 seconds and reconciles an mfa:<provider> alert from the result. The alert is per node, because reachability is: one node may reach a provider while its peer cannot, and a healthy peer must not clear the node that is broken. A disabled provider never alerts. Each check has a 10-second ceiling and runs on its own database session, so one slow provider cannot stall the others.
During login a provider that cannot be reached produces a 502-style error rather than a silent failure, so nobody is stranded without being told. One case is handled specially: if the enrolled Taranac MFA token has been removed on the MFA server, Taranac drops the now-dangling enrollment, audits why, and treats the user as “MFA required but not set up” — they can re-enroll instead of being locked out by pushes to a token that no longer exists.
How the second factor itself is protected
Section titled “How the second factor itself is protected”A second factor is only worth the difficulty it adds if the step around it cannot be walked past. Four properties are worth knowing, because each closes something that was once open:
- The token between the password and the factor is not a session. Every JWT Taranac signs now declares what it is for, and each endpoint accepts only the purposes it names — deny by default. The token you hold after the password but before the factor opens
/mfa/verifyand nothing else. Previously it was signed with the same key and the same shape as a full access token and nothing checked the difference, so a stolen password alone was enough to reach enrollment and swap an attacker’s own second factor over the victim’s. The same separation keeps a captive-portal token from being replayed against the web verify, and vice versa: they carry different purposes. - Guessing the code has a budget, and minting a fresh token does not reset it. Five failed verifications against one token burn it; the holder must authenticate again from the password. The count belongs to the token rather than the account or the IP — which is exactly what removes “re-login and keep guessing” as a way around the limit, and means only the attacker’s own token dies, never the victim’s ability to log in. It is recorded across workers and cluster nodes, so fanning the same token out does not multiply the allowance. A burned token is refused before the provider runs, so it costs no TOTP maths and sends no push. On top of that,
/mfa/verifyis rate-limited to 10 requests a minute, which keeps the audit log readable. - Removing someone else’s factor is an administrative act. The enrollment-removal endpoint is self-service, so for a while the only thing between any authenticated user and somebody else’s second factor was not knowing the enrollment’s UUID. It now requires
mfa.editto remove an enrollment that is not your own. - The Telegram webhook authenticates the caller. The secret check used to begin with “if a secret was sent”, so a request carrying no header skipped verification entirely — and a forged update could make the bot disclose which Taranac accounts are linked to a given Telegram account, into a chat of the caller’s choosing. The check is now driven by the configuration, not by what arrived: the delivery mode must be
webhook, a secret must be configured, the header must be present, and the comparison is constant-time.
TOTP replay. A one-time code cannot be reused inside its validity window by aiming the retry at a different worker process or cluster node: before accepting a code, the TACACS+ daemon atomically claims that code’s 30-second time step in a shared ledger, and a second use loses the race. It falls back to per-process protection only when the database write path is unavailable, such as during a failover. The claim is made on the TACACS+ device-administration path; the RADIUS daemon and the web-console verify do not consult the ledger, so there a code remains usable for the rest of its own window.
Reference
Section titled “Reference”Key system settings (mfa.*)
Section titled “Key system settings (mfa.*)”| Setting | Default | Purpose |
|---|---|---|
mfa.mode | enabled | System MFA mode shown on Integrations → MFA; disabled greys the page out — see the caution below |
mfa.challenge.ttl | 120 s | How long a Telegram push challenge stays valid (the UI accepts 30–600) |
mfa.email_push.ttl | 300 s | Lifetime of an Email-push approve/deny link |
mfa.internal.challenge_ttl | 120 s | How long the TACACS+, RADIUS and 802.1X daemons wait on a push |
mfa.backup_codes.enabled | true | Generate TOTP backup codes |
mfa.backup_codes.count | 10 | Number of backup codes per TOTP enrollment |
mfa.send_setup_link_on_create | false | Email a setup link to every newly created account, local or directory-synced |
mfa.<provider>.enabled | see note | Whether users may choose a given provider |
tacacs.mfa_mode | enabled | Whether TACACS+ enforces a policy rule’s MFA requirement at all |
radius.mfa_mode | append | disabled, challenge or append for RADIUS device administration |
What MFA records in the audit log
Section titled “What MFA records in the audit log”Every MFA action is audited: enrollment and activation, removal (including the automatic removal when a method is replaced, which carries reason: replaced), each verification success or failure, push approved / denied / expired, admin reset, backup-code use, and Telegram link/unlink. Two events are worth knowing by name:
MFA_PUSH_EXPIRED— a push approval request lapsed unanswered. It is written exactly once per challenge, whoever notices first (the poller, the provider callback or the cleanup sweep), and it carries whether the challenge timed out or the provider errored. This is what makes “how many pushes went unanswered” answerable at all: the challenge table is an operational queue that deletes resolved rows after 24 hours, so history lives in the audit log or nowhere.MFA_TOKEN_BURNED— one token spent its five-failure budget. Distinct fromMFA_VERIFY_FAILED, which is a single wrong code; a burn is the shape a guessing attempt leaves behind.
Use these events (under the authentication log) to investigate suspicious approvals or repeated failures.
Related
Section titled “Related”- Users & groups — reset MFA, regenerate backup codes, send setup links, and set
mfa_requiredon a group - Taranac MFA — the push/OTP server and mobile app behind the Taranac MFA provider
- RBAC — the
mfa.view/mfa.edit/mfa.managepermissions that gate MFA administration - LDAP directories — directory-backed users can also enroll a second factor
- AAA policy — the rule field that requires MFA for a device-administration login
- 802.1X authentication — why a network login can only ever be satisfied by a push
- AAA logging — where MFA audit events surface
- Deployment bundle & full specs: github.com/TaranacLabs/taranac