Metric · Security

What is MFA adoption rate, and how do you measure it in Metabase?

MFA adoption rate is the share of active users with a strong second factor enrolled — the single highest-leverage number in identity security, since stolen passwords remain the most common initial access vector. Measure it in Metabase from user and factor data synced from Okta or Auth0, broken down by factor strength and department rather than reported as one org-wide average.

TL;DRCOUNT(users with MFA enrolled) ÷ COUNT(active human users), with the numerator split by strongest factor type. The unprotected-admins list matters more than the percentage.

What MFA adoption rate measures

It measures how much of your workforce a phished password can still compromise. Tracked properly it's three numbers, not one: the enrollment rate (who has any second factor), the factor mix (how many are phishing-resistant versus SMS), and the gap list (which active accounts — especially privileged ones — have nothing). It's also a standing compliance requirement, which is why it belongs next to control pass rate on the audit-readiness view.

What data does it need?

  • A users table with status, user_type (person vs. service account), department, admin flag, and last_login_at.
  • An mfa_enrollments table: one row per user per enrolled factor, with factor_type.
  • A factor-strength ranking (WebAuthn > TOTP/push > SMS) encoded in the query, so "strongest factor per user" is well-defined.
  • Authentication logs if you also want usage — enrollment alone doesn't prove the factor is ever exercised.

SQL patterns

Adoption by department, split by strongest factor typePostgreSQL
WITH strongest AS (
  -- one row per user: their strongest enrolled factor
  SELECT
    user_id,
    MIN(
      CASE factor_type
        WHEN 'webauthn' THEN 1  -- phishing-resistant
        WHEN 'totp' THEN 2
        WHEN 'push' THEN 3
        WHEN 'sms' THEN 4       -- weakest
      END
    ) AS factor_rank
  FROM mfa_enrollments
  GROUP BY user_id
)
SELECT
  u.department,
  COUNT(*) AS active_users,
  ROUND(100.0 * COUNT(s.user_id) / COUNT(*), 1) AS enrolled_pct,
  COUNT(*) FILTER (WHERE s.factor_rank = 1) AS phishing_resistant,
  COUNT(*) FILTER (WHERE s.factor_rank IN (2, 3)) AS app_based,
  COUNT(*) FILTER (WHERE s.factor_rank = 4) AS sms_only
FROM users u
LEFT JOIN strongest s ON s.user_id = u.id
WHERE u.status = 'active'
  AND u.user_type = 'person'  -- exclude service accounts
GROUP BY u.department
ORDER BY enrolled_pct;
Active users without MFA, riskiest firstPostgreSQL
SELECT
  u.email,
  u.department,
  u.is_admin,
  u.last_login_at::date AS last_login
FROM users u
LEFT JOIN mfa_enrollments e ON e.user_id = u.id
WHERE u.status = 'active'
  AND u.user_type = 'person'
  AND e.user_id IS NULL
-- admins first, then most recently active:
-- the riskiest unprotected accounts at the top
ORDER BY u.is_admin DESC, u.last_login_at DESC;

Pitfalls

Including service and deactivated accounts.→ Service accounts can't enroll in MFA and deactivated users can't log in — both distort the rate. Scope the denominator to active human accounts, and audit service-account credentials as their own exercise.
Counting SMS the same as a hardware key.→ SMS is phishable and SIM-swappable; WebAuthn isn't. An adoption rate that treats them as equivalent overstates your posture — always report the factor mix alongside the headline rate.
Reporting only the org-wide average.→ 97% adoption with three unenrolled admins is a worse posture than the number implies. Segment by privilege and department, and rank the gap list admins-first.
Treating enrollment as usage.→ An enrolled factor that's never exercised — because a legacy app skips MFA, or a policy exempts "trusted" networks — protects nothing. Check authentication logs for factor usage, not just the enrollment table.

Where this metric applies

Metrics

Dashboards

FAQ

Do all second factors count the same?
They shouldn't. SMS codes are phishable and SIM-swappable; TOTP and push resist SIM swaps but still fall to real-time phishing proxies; WebAuthn/FIDO2 keys resist phishing outright. Report the factor mix — phishing-resistant, app-based, SMS-only — not just an enrolled/not-enrolled split. A 99% adoption rate built on SMS is a much weaker posture than the headline suggests, which is why the query ranks each user by their strongest factor.
Who belongs in the denominator?
Active human accounts — and getting that scope right is most of the work. Service accounts authenticate with keys or tokens and can't enroll in MFA; deactivated users can't log in at all. Both, left in, distort the rate in whichever direction their count happens to lean. Filter to status = 'active' and user_type = 'person' using the account-type fields Okta and Auth0 expose, and audit the service-account list separately.
Why isn't the org-wide average enough?
Because attackers don't target the average — they target admins. An org at 97% with three unenrolled administrators has a worse practical posture than one at 90% with every privileged account on hardware keys. Segment by department and privilege, and keep the unprotected-accounts list — admins first — as the standing work queue on an identity and access analytics dashboard.
How do you calculate MFA adoption rate?
Divide users with at least one enrolled second factor by all active human users: COUNT(mfa_enrolled) / COUNT(active users). Scope the denominator to status = 'active' and user_type = 'person', then break the numerator down by strongest factor type so SMS-only enrollment doesn't masquerade as full protection.
How do you track MFA adoption rate in Metabase?
Sync users and factor enrollments from Okta or Auth0 into users and mfa_enrollments tables. Chart adoption by department with the factor-type mix, keep the ranked unprotected-accounts list as the follow-up queue, and pair the whole view with failed login rate on the same identity dashboard — enrollment tells you who's protected, login telemetry tells you who's being attacked.