| """ |
| MFAFactor model — stores TOTP/OTP configuration per user. |
| |
| Maps to AGENT.md DBML: `mfa_factors` table (Lines 228-235). |
| """ |
| from django.conf import settings |
| from django.db import models |
|
|
| from core.models import BaseModel |
|
|
|
|
| class MFAFactor(BaseModel): |
| """ |
| Multi-factor authentication factor for a user. |
| |
| Stores the encrypted TOTP secret. The `enabled` flag allows users |
| to set up MFA before activating it (verify first, then enable). |
| """ |
|
|
| class FactorType(models.TextChoices): |
| TOTP = "totp", "Time-based OTP" |
| SMS = "sms", "SMS OTP" |
| EMAIL = "email", "Email OTP" |
|
|
| user = models.ForeignKey( |
| settings.AUTH_USER_MODEL, |
| on_delete=models.CASCADE, |
| related_name="mfa_factors", |
| ) |
| type = models.CharField( |
| max_length=20, |
| choices=FactorType.choices, |
| default=FactorType.TOTP, |
| ) |
| secret = models.TextField( |
| help_text="Encrypted TOTP secret. Use core.services.encryption to encrypt/decrypt.", |
| ) |
| enabled = models.BooleanField(default=False) |
|
|
| class Meta: |
| db_table = "mfa_factors" |
| constraints = [ |
| models.UniqueConstraint( |
| fields=["user", "type"], |
| name="unique_mfa_factor_per_user_type", |
| ), |
| ] |
|
|
| def __str__(self) -> str: |
| status = "enabled" if self.enabled else "disabled" |
| return f"MFA ({self.type}) for {self.user_id} [{status}]" |
|
|