12
113
Fork
You've already forked django-allauth
149

SMS-based MFA? #4546

Open
opened 2025年09月11日 21:50:39 +02:00 by trowbrds · 3 comments

We're investigating moving to allauth, but unfortunately, many of our users choose SMS for their 2FA. It seems like perhaps there might have been some discussion about this (#3434), but the link (https://github.com/pennersr/django-allauth/discussions/3438) no longer works.

Would SMS-based MFA method be a welcome contribution?

We're investigating moving to allauth, but unfortunately, many of our users choose SMS for their 2FA. It seems like perhaps there might have been some discussion about this (#3434), but the link (https://github.com/pennersr/django-allauth/discussions/3438) no longer works. Would SMS-based MFA method be a welcome contribution?

That's great idea, we may mimic part of Django Two-Factor Authentication lib
https://django-two-factor-auth.readthedocs.io/en/stable/configuration.html#twilio-gateway

That's great idea, we may mimic part of Django Two-Factor Authentication lib https://django-two-factor-auth.readthedocs.io/en/stable/configuration.html#twilio-gateway

It would indeed be a nice feature to offer. Random thoughts:

  • Phone support is already there, this can build on top of that.
  • Configuration-wise, we need MFA_SUPPORTED_TYPES = {..., "phone"}
  • This should only be allowed if the phone number is a true second factor. E.g. if you sign up by phone only, and not provide an email address, then obviously, this wouldn't be MFA.
  • To be determined how the UI/interaction would look like. E.g. will we simply rely on the existing add/change phone form which is separate from the MFA section, and, inside the MFA setup, only offer an "Enable/disable phone 2FA" button?
It would indeed be a nice feature to offer. Random thoughts: - Phone support is already there, this can build on top of that. - Configuration-wise, we need `MFA_SUPPORTED_TYPES = {..., "phone"}` - This should only be allowed if the phone number is a true second factor. E.g. if you sign up by phone only, and not provide an email address, then obviously, this wouldn't be MFA. - To be determined how the UI/interaction would look like. E.g. will we simply rely on the existing add/change phone form which is separate from the MFA section, and, inside the MFA setup, only offer an "Enable/disable phone 2FA" button?
Contributor
Copy link

I have implemented it in a different way, instead of using the "phone support" in the adapter/etc, I instead:

  • extended the Authenticator model to add a new SMS type
  • updated allauth.mfa.stages.AuthenticateStage.handle to send the SMS if SMS 2FA is active
  • updated allauth.mfa.stages.AuthenticateStage._should_handle to check for the new SMS 2FA type
  • added an error message too_many_sms_2fa_codes to allauth.mfa.adapter.DefaultMFAAdapter.error_messages
  • created a new "flow" to activate/deactivate SMS, creating the SMS type Authenticator model, sending authenticator added/removed signals and emails
  • added new templates and forms for activating/deactivating 2FA SMS
  • overriding the existing authenticate form, to add a new "resend SMS" link
  • added a model to keep the last N requested codes, along with a manager with methods to cleanup stale ones and "consume" one atomically (by locking and then deleting the row if successful)
  • a new rate-limit category of request_sms_2fa_code is added (defaults to 20/m/ip,5/m/key)
  • adding a number validates it against app-settings' NUMBER_VALIDATOR so that premium-rate/etc numbers can be rejected (defaults to a noop so everything is allowed)
  • the actual generating & sending of the SMS is deferred to an app-settings-configurable SMS_CODE_GENERATOR and SMS_CODE_SENDER so code formats can be varied and any SMS provider can be plugged in

If you are interested, this is something I'm sure I can convince my client to upstream.

I have implemented it in a different way, instead of using the "phone support" in the adapter/etc, I instead: * extended the `Authenticator` model to add a new SMS type * updated `allauth.mfa.stages.AuthenticateStage.handle` to _send_ the SMS if SMS 2FA is active * updated `allauth.mfa.stages.AuthenticateStage._should_handle` to check for the new SMS 2FA type * added an error message `too_many_sms_2fa_codes` to `allauth.mfa.adapter.DefaultMFAAdapter.error_messages` * created a new "flow" to activate/deactivate SMS, creating the SMS type Authenticator model, sending authenticator added/removed signals and emails * added new templates and forms for activating/deactivating 2FA SMS * overriding the existing authenticate form, to add a new "resend SMS" link * added a model to keep the last N requested codes, along with a manager with methods to cleanup stale ones and "consume" one atomically (by locking and then deleting the row if successful) * a new rate-limit category of `request_sms_2fa_code` is added (defaults to `20/m/ip,5/m/key`) * adding a number validates it against app-settings' `NUMBER_VALIDATOR` so that premium-rate/etc numbers can be rejected (defaults to a noop so everything is allowed) * the actual generating & sending of the SMS is deferred to an app-settings-configurable `SMS_CODE_GENERATOR` and `SMS_CODE_SENDER` so code formats can be varied and any SMS provider can be plugged in If you are interested, this is something I'm sure I can convince my client to upstream.
Sign in to join this conversation.
No Branch/Tag specified
main
fix/account-middleware-reauth-exc-vs-api
65.12.x
65.11.x
feat/openid-connect-id-token
fix/phone-vs-social-signup
65.8.x
feat/idp
feat/resend-code-or-change
fix/phone-signup-conflict
feat/phone
fix/headless-email-verification-by-code
fix/a11y
feat/facebook-limited-login
fix/headless-only-connect-reauth
fix/email-added-vs-code-verification
feat/login-required-middleware
0.63.x
refactor-unique-true-field
0.62.x
0.57.x
0.60.x
0.55.x
feat-metamask
65.18.0
65.17.0
65.16.1
65.16.0
65.15.1
65.15.0
65.14.3
65.14.2
65.14.1
65.14.0
65.13.1
65.13.0
65.12.1
65.12.0
65.11.2
65.11.1
65.11.0
65.10.0
65.9.0
65.8.1
65.8.0
65.7.0
65.6.0
65.5.0
65.4.1
65.4.0
65.3.1
65.3.0
65.2.0
65.1.0
65.0.2
65.0.1
65.0.0
64.2.1
64.2.0
64.1.0
64.0.0
0.63.6
0.63.5
0.63.4
0.63.3
0.63.2
0.63.1
0.63.0
0.62.1
0.62.0
0.57.2
0.61.1
0.57.1
0.61.0
0.60.1
0.60.0
0.59.0
0.58.2
0.58.1
0.58.0
0.57.0
0.56.1
0.56.0
0.55.2
0.55.1
0.55.0
0.54.0
0.53.1
0.53.0
0.52.0
0.51.0
0.50.0
0.49.0
0.48.0
0.47.0
0.46.0
0.45.0
0.44.0
0.43.0
0.42.0
0.41.0
0.40.0
0.39.1
0.39.0
0.38.0
0.37.1
0.37.0
0.36.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.0
0.30.0
0.29.0
0.28.0
0.27.0
0.26.1
0.26.0
0.25.2
0.25.1
0.25.0
0.24.1
0.24.0
0.23.0
0.22.0
0.21.0
0.20.0
0.19.1
0.19.0
0.18.0
0.17.0
0.16.1
0.16.0
0.15.0
0.14.2
0.14.1
0.14.0
0.13.0
0.12.0
0.11.1
0.11.0
0.10.1
0.10.0
0.9.0
0.8.3
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
4 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
allauth/django-allauth#4546
Reference in a new issue
allauth/django-allauth
No description provided.
Delete branch "%!s()"

Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?