MFA Setup (TOTP)¶
Authentik supports TOTP-based multi-factor authentication on all SSO services. MFA enforcement is currently disabled — Google OAuth 2FA covers all users via Google's own 2FA enforcement.
Current Status¶
MFA enforcement: DISABLED (not_configured_action: skip)
All SSO logins go through Google OAuth, which enforces Google's own 2FA. Adding Authentik-level TOTP on top was deemed redundant for the current setup. The playbook, stages, and flows remain in place and can be re-enabled at any time.
To re-enable enforcement:
ssh ansible@ansible-shared-cwiq-io
ansible-helper
cd authentik
ansible-playbook -i inventory.ini policies/configure-mfa.yml
Services Covered¶
When enabled, MFA applies to all services using Authentik SSO:
| Service | Protocol |
|---|---|
| AWS Identity Center | SAML |
| Shared GitLab | OIDC |
| Dev GitLab | OIDC |
| Vault | OIDC |
| Taiga | OIDC |
| Grafana | OIDC |
| Semaphore | OIDC |
| IcingaWeb2 | Proxy |
| SonarQube | Proxy |
Service accounts and API tokens bypass authentication flows entirely — MFA does not affect them.
Authentication Flows¶
MFA is enforced on both login paths:
Username/Password Login (default-authentication-flow)¶
[Identification] (order 10) — username/email + "Sign in with Google"
↓
[Password] (order 20) — password entry
↓
[MFA Validation] (order 30) — TOTP code prompt
↓ No device? → Auto-enrollment (scan QR, verify code, save recovery codes)
[User Login] (order 100) — session created
Google OAuth Login (default-source-authentication)¶
[Google OAuth callback] — Google handles password + Google 2FA
↓
[MFA Validation] (order -1) — Authentik TOTP prompt
↓ No device? → Auto-enrollment
[User Login] (order 0) — session created
Both flows use the same MFA validation stage — settings apply uniformly.
Configuration¶
Set these variables in group_vars/all.yml before running the playbook:
| Variable | Default | Description |
|---|---|---|
mfa_totp_digits |
6 |
Digits in TOTP code (6 or 8) |
mfa_recovery_code_count |
10 |
Recovery codes generated per user |
mfa_recovery_code_length |
16 |
Character length per recovery code |
mfa_not_configured_action |
configure |
Behavior when user has no TOTP device |
mfa_not_configured_action Values¶
| Value | Behavior | When to Use |
|---|---|---|
configure |
Prompt user to enroll TOTP on next login | Initial rollout — recommended |
skip |
MFA optional, users can bypass | Temporarily disable enforcement |
deny |
Block login until MFA is configured | Strict enforcement after rollout |
Managing MFA Enforcement¶
Enable MFA Enforcement¶
This runs with the default mfa_not_configured_action: configure — users without a TOTP device are prompted to enroll on their next login.
Disable MFA Enforcement¶
Disabling MFA requires explicit confirmation
This prevents accidental changes.
ansible-playbook -i inventory.ini policies/configure-mfa.yml \
-e "mfa_not_configured_action=skip" \
-e "confirm_mfa_disable=yes_i_understand_the_risk"
Strict Mode — Block Users Without MFA¶
User Experience¶
First Login (No TOTP Device)¶
- User logs in (username/password or Google OAuth)
- Authentik detects no TOTP device → shows enrollment screen
- User scans QR code with authenticator app (Google Authenticator, Authy, 1Password)
- User enters the 6-digit code to verify
- Authentik displays recovery codes — user saves them securely
- Login completes
Subsequent Logins¶
- User logs in normally
- Enters 6-digit TOTP code from their authenticator app
- Login completes
Recovery Codes¶
Each recovery code is single-use. If a user loses their authenticator device, they can use a recovery code to log in, then enroll a new device at /if/user/#/settings.
Recovery Procedures¶
User Lost Their Authenticator Device¶
If the user has recovery codes:
- Log in with a recovery code instead of a TOTP code
- Enroll a new TOTP device at
https://sso.shared.cwiq.io/if/user/#/settings
If the user has no recovery codes:
- Admin: Admin UI > Directory > Users > [user] > MFA Devices
- Delete the user's TOTP device
- User re-enrolls on next login (if
not_configured_actionisconfigure)
Admin Locked Out¶
The akadmin account behaves like any other user — it will be prompted to enroll if not_configured_action is configure.
If no admin can access the UI at all:
Emergency only — disables MFA for ALL users
A second team member must be notified before proceeding. Document the timestamp and reason. Re-enable MFA immediately after resolving the issue.
from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage
stage = AuthenticatorValidateStage.objects.filter(
flow__slug="default-authentication-flow"
).first()
if stage:
stage.not_configured_action = "skip"
stage.save()
print("MFA enforcement disabled — RE-ENABLE IMMEDIATELY")
After resolving, re-run policies/configure-mfa.yml and review Authentik event logs for any logins during the bypass window.
Rollout Strategy¶
- Pre-provision admin MFA — Manually configure TOTP for all admin/privileged accounts via the Admin UI before running the playbook. This eliminates the enrollment window for high-privilege accounts.
- Pre-test — Have one admin verify their TOTP works across different SSO services (AWS, GitLab) to confirm HA session handling across both instances.
- Enable — Run the playbook with default
configureaction. - Monitor — Watch Authentik event logs for enrollment events over 1–2 weeks.
- Enforce (optional) — Change to
denymode to block unenrolled users.
Idempotency¶
The playbook is fully idempotent: - TOTP and recovery code stages are only created if they don't already exist - The validation stage is updated via PATCH if it already exists - Running the playbook multiple times is safe
Related Documentation¶
- Architecture — Authentik HA, health endpoints
- App Onboarding — Which services use Authentik SSO
- User Lifecycle — User provisioning and group management