#!/usr/bin/env bash set -euo pipefail # Emergency 2FA removal for a single Authentik user. # Deletes every authenticator device (TOTP, WebAuthn, static backup codes, # SMS, Duo, mobile, endpoint) directly in Postgres — bypasses the API so it # works even if you can't complete the MFA challenge to log in. # # Usage: ./scripts/authentik-disable-2fa.sh [username] # Defaults to akadmin. USER_NAME="${1:-akadmin}" NS="authentik" CLUSTER="ak-pg" DEVICE_TABLES=( authentik_stages_authenticator_totp_totpdevice authentik_stages_authenticator_webauthn_webauthndevice authentik_stages_authenticator_static_staticdevice authentik_stages_authenticator_sms_smsdevice authentik_stages_authenticator_duo_duodevice authentik_stages_authenticator_mobile_mobiledevice authentik_stages_authenticator_endpoint_gdtc_endpointdevice ) # Find the current primary instance PRIMARY=$(kubectl get cluster -n "$NS" "$CLUSTER" -o jsonpath='{.status.currentPrimary}' 2>/dev/null || true) [[ -n "$PRIMARY" ]] || { echo "Could not find CNPG primary for $NS/$CLUSTER — is the cluster running?"; exit 1; } echo "Primary: $PRIMARY" # Sanity check user exists USER_ID=$(kubectl exec -n "$NS" "$PRIMARY" -c postgres -- \ psql -At -U postgres authentik \ -c "SELECT id FROM authentik_core_user WHERE username = '$USER_NAME';" | tr -d '[:space:]') [[ -n "$USER_ID" ]] || { echo "User '$USER_NAME' not found in authentik_core_user"; exit 1; } echo "User id: $USER_ID" # Build a DELETE for every device table, combined in a single transaction SQL="BEGIN;" for t in "${DEVICE_TABLES[@]}"; do # Skip tables that don't exist in this Authentik version (older/newer schemas) SQL+=" DO \$\$ BEGIN IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = '$t') THEN EXECUTE 'DELETE FROM $t WHERE user_id = $USER_ID'; END IF; END \$\$;" done SQL+=" COMMIT;" echo "Removing all MFA devices for $USER_NAME..." kubectl exec -n "$NS" "$PRIMARY" -c postgres -- \ psql -v ON_ERROR_STOP=1 -U postgres authentik -c "$SQL" echo echo "Remaining device counts for $USER_NAME:" for t in "${DEVICE_TABLES[@]}"; do count=$(kubectl exec -n "$NS" "$PRIMARY" -c postgres -- \ psql -At -U postgres authentik \ -c "SELECT count(*) FROM information_schema.tables WHERE table_name = '$t';" | tr -d '[:space:]') [[ "$count" == "1" ]] || continue n=$(kubectl exec -n "$NS" "$PRIMARY" -c postgres -- \ psql -At -U postgres authentik \ -c "SELECT count(*) FROM $t WHERE user_id = $USER_ID;" | tr -d '[:space:]') printf " %-60s %s\n" "$t" "$n" done echo echo "Done. User '$USER_NAME' can now log in with just the password." echo "Re-enroll MFA from the user's settings after logging in."