Admin integration¶
django-multifactor ships an opinionated UserAdmin mixin that surfaces
factor usage in the Django admin. It is purely additive — no behaviour
changes unless you opt in.
Adding the mixin¶
# myapp/admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth import get_user_model
from multifactor.admin import MultifactorUserAdmin
User = get_user_model()
@admin.register(User)
class StaffUserAdmin(UserAdmin, MultifactorUserAdmin):
pass
After this:
The user changelist gains a Multifactor column (boolean green/red dot) showing whether the user has at least one enabled factor.
A Using Multifactor authentication? filter appears in the sidebar (Yes / No).
The user detail page gains a read-only inline listing each of the user’s
UserKeyrows with an enable/disable toggle.
Source: multifactor/admin.py.
Disabling individual pieces¶
Three class-level flags let you turn parts off:
@admin.register(User)
class StaffUserAdmin(UserAdmin, MultifactorUserAdmin):
multifactor_list_display = False # hide the changelist column
multifactor_filter = False # hide the sidebar filter
multifactor_inline = False # hide the inline on the detail page
Emergency disable: turning off a user’s factors¶
The inline lets an admin toggle enabled on each key. Disabling a key
makes it stop working immediately:
active_factors(request)doesn’t drop the existing session marker, buthas_multifactor()re-checksenabled=Trueon each protected view, so the user is challenged on next page load.The key is not deleted, so an audit trail is preserved.
To completely revoke MFA for a user (e.g. they lost their hardware key and
have no backup, no fallback), an admin can delete the user’s UserKey rows
via the inline. The user will then have no factors and:
Will not be challenged on
factors=0views.Will be sent to the Add factor page on
RequireMultiAuthMixinviews.Will be sent to
multifactor:authenticatewith no available method onfactors>=1views — they need to log in, register a new factor, and try again.
A custom action — disable MFA on selected users¶
from django.contrib import admin
from multifactor.models import UserKey
@admin.action(description="Disable MFA for selected users")
def disable_mfa(modeladmin, request, queryset):
UserKey.objects.filter(user__in=queryset).update(enabled=False)
@admin.register(User)
class StaffUserAdmin(UserAdmin, MultifactorUserAdmin):
actions = [disable_mfa]
Warning
This is a powerful action. Limit it to a small group of trusted admins (consider a custom permission) and audit every use.
Showing the count of factors per user¶
from django.db.models import Count
@admin.register(User)
class StaffUserAdmin(UserAdmin, MultifactorUserAdmin):
def get_queryset(self, request):
return (
super()
.get_queryset(request)
.annotate(
factor_count=Count(
"multifactor_keys", filter=Q(multifactor_keys__enabled=True)
),
)
)
def factor_count(self, obj):
return obj.factor_count
factor_count.short_description = "Active factors"
Note the multifactor_keys related name on the UserKey.user ForeignKey
(models.py:21).
Auditing factor registrations¶
For an audit trail of “who registered which factor when”, listen for the
post-save signal on UserKey:
# myapp/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from multifactor.models import UserKey
@receiver(post_save, sender=UserKey)
def audit_factor_change(sender, instance, created, **kwargs):
import logging
log = logging.getLogger("audit.mfa")
log.info(
"userkey %s for user=%s type=%s enabled=%s",
"created" if created else "updated",
instance.user_id,
instance.key_type,
instance.enabled,
)
Wire that in apps.ready().
See also¶
Models reference — fields available on
UserKey.Branding — for theming the user-facing pages.