Skip to content

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.

A push challenge: the user enters only their password at a TACACS+ login, the session waits, and an approval on their phone grants access.
  • 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), or combined (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, or expired, 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 has mfa_required set forces that user to have MFA there. Providers themselves are not pinned to groups — the user always picks their own provider.

These are the five providers shipped in Taranac. Do not expect others.

ProviderCategoryModesWhat the user needs
TOTPLocaltotpAny authenticator app (Google Authenticator, Aegis, etc.)
TelegramExternalpushA Telegram account, linked to a bot you run
MultifactorExternalpush, totpA Multifactor (RADIUS) account
Email pushHybridpushA working email address (uses your existing SMTP)
Taranac MFAExternalcombined, totpThe 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.

When a user enrolls, the provider starts in its default mode:

ProviderDefault mode
TOTPtotp
Telegrampush
Multifactorpush
Email pushpush
Taranac MFAcombined
ContextTOTP (code)Push (Telegram / Multifactor / Email / Taranac MFA)
Web admin UICode entered on a second screen, after the passwordBrowser shows a “waiting for approval” screen and polls until approved
TACACS+ (device CLI)Code appended to the end of the passwordSession 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 belowHandler waits for the push result
RADIUS (device admin, MS-CHAPv2)Fails closedThe only form MFA can take on that transport
802.1X / EAPFails closedThe only form MFA can take there
Captive portal (BYOD registration)Code in the portal formPortal 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.

Enrollment is self-service: a user opens their MFA settings, picks an allowed provider, and confirms it once.

  1. The user opens My Profile → Two-factor authentication and selects a provider from the list of those you’ve enabled.
  2. 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.
  3. 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).
  4. On success the enrollment becomes active and (for TOTP) backup codes are shown once.

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.

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:

PathWhat makes MFA mandatoryWhere it lives
Web console & APImfa_required on any group the user belongs toThe group’s edit form, or the group card on Integrations → MFA
TACACS+ / RADIUS device administrationThe matched AAA policy rule’s mfa field (off / required), gated by tacacs.mfa_mode and radius.mfa_modeAAA policy
802.1XThe matched NAC rule’s mfa_required802.1X authentication
Captive portal (BYOD device registration)Require MFA on the BYOD flowCaptive 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.

ActionWherePermissionEffect
View a user’s MFA statusInfrastructure → Identity → Usersmfa.viewShows the active provider and mode
Reset a user’s MFAInfrastructure → Identity → Usersmfa.editRemoves every enrollment — use when a device is lost
Remove one enrollmentTheir own profile, or a user’s pageself, else mfa.editDeletes a single method
Regenerate backup codesInfrastructure → Identity → Users (TOTP)mfa.editIssues a fresh set of one-time recovery codes
Send setup linkA user’s page, or the group card on Integrations → MFAmfa.editEmails a one-time enrollment link
Enable/disable a providerIntegrations → MFAmfa.manageControls which providers users may choose
Read provider healthIntegrations → MFAmfa.viewRuns 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.

If you need…ChooseWhy
Maximum resilience, no external dependencyTOTPWorks offline; survives a backend outage; nothing to operate
Smooth approvals for network logins, self-hostedTaranac MFAPush via your own MFA server + FCM, with TOTP fallback in one enrollment
Push without running a mobile backendTelegramUses a Telegram bot you create; familiar to most users
To reuse an existing Multifactor deploymentMultifactorSpeaks RADIUS to your Multifactor service; push or TOTP
A zero-install fallback for occasional usersEmail pushApprove/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 totp is 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.

Taranac actively checks that each enabled provider can do its job, so you can spot a broken second factor before users are locked out:

ProviderHealth check
TOTPAlways healthy (local, no dependencies)
Email pushSMTP server reachable (connect + EHLO, no authentication)
TelegramBot 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
MultifactorConfiguration 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.

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/verify and 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/verify is 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.edit to 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.

SettingDefaultPurpose
mfa.modeenabledSystem MFA mode shown on Integrations → MFA; disabled greys the page out — see the caution below
mfa.challenge.ttl120 sHow long a Telegram push challenge stays valid (the UI accepts 30–600)
mfa.email_push.ttl300 sLifetime of an Email-push approve/deny link
mfa.internal.challenge_ttl120 sHow long the TACACS+, RADIUS and 802.1X daemons wait on a push
mfa.backup_codes.enabledtrueGenerate TOTP backup codes
mfa.backup_codes.count10Number of backup codes per TOTP enrollment
mfa.send_setup_link_on_createfalseEmail a setup link to every newly created account, local or directory-synced
mfa.<provider>.enabledsee noteWhether users may choose a given provider
tacacs.mfa_modeenabledWhether TACACS+ enforces a policy rule’s MFA requirement at all
radius.mfa_modeappenddisabled, challenge or append for RADIUS device administration

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 from MFA_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.

  • Users & groups — reset MFA, regenerate backup codes, send setup links, and set mfa_required on a group
  • Taranac MFA — the push/OTP server and mobile app behind the Taranac MFA provider
  • RBAC — the mfa.view / mfa.edit / mfa.manage permissions 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