diff --git a/biscuit/core/settings.py b/biscuit/core/settings.py
index 31532cc6236e36f18805fdea823756c56c0fc372..ee5438685a79dfaa12569e927df7a8775ab6823b 100644
--- a/biscuit/core/settings.py
+++ b/biscuit/core/settings.py
@@ -67,10 +67,6 @@ INSTALLED_APPS = [
     'django_select2',
     'hattori',
     'biscuit.core',
-    'django_otp',
-    'django_otp.plugins.otp_static',
-    'django_otp.plugins.otp_totp',
-    'two_factor',
     'impersonate',
 ]
 
@@ -103,7 +99,6 @@ MIDDLEWARE = [
     'impersonate.middleware.ImpersonateMiddleware',
     'django.contrib.messages.middleware.MessageMiddleware',
     'django.middleware.clickjacking.XFrameOptionsMiddleware',
-    'django_otp.middleware.OTPMiddleware',
     'easyaudit.middleware.easyaudit.EasyAuditMiddleware',
     'maintenance_mode.middleware.MaintenanceModeMiddleware',
     #    'django.middleware.cache.FetchFromCacheMiddleware'
@@ -323,15 +318,24 @@ CRON_CLASSES = [
 
 ANONYMIZE_ENABLED = _settings.get('maintenance.anonymisable', True)
 
-if _settings.get('2fa.yubikey.enabled', False):
-    INSTALLED_APPS.insert(INSTALLED_APPS.index('two_factor')+1, 'otp_yubikey')
+if _settings.get('2fa.enabled', False):
+    for app in ['two_factor', 'django_otp.plugins.otp_totp', 'django_otp.plugins.otp_static', 'django_otp']:
+        INSTALLED_APPS.insert(INSTALLED_APPS.index('biscuit.core')+1, app)
+    MIDDLEWARE.insert(MIDDLEWARE.index('django.contrib.auth.middleware.AuthenticationMiddleware')+1, 'django_otp.middleware.OTPMiddleware')
 
-if _settings.get('2fa.twilio.sid', None):
-    MIDDLEWARE.insert(MIDDLEWARE.index('django_otp.middleware.OTPMiddleware')+1, 'two_factor.middleware.threadlocals.ThreadLocals')
-    TWILIO_SID = _settings.get('2fa.twilio.sid')
-    TWILIO_TOKEN = _settings.get('2fa.twilio.token')
-    TWILIO_CALLER_ID = _settings.get('2fa.twilio.callerid')
-    TWO_FACTOR_CALL_GATEWAY = 'two_factor.gateways.twilio.gateway.Twilio'
-    TWO_FACTOR_SMS_GATEWAY = 'two_factor.gateways.twilio.gateway.Twilio'
+    if _settings.get('2fa.yubikey.enabled', False):
+        INSTALLED_APPS.insert(INSTALLED_APPS.index('two_factor')+1, 'otp_yubikey')
+
+    if _settings.get('2fa.call.enabled', False):
+        TWO_FACTOR_CALL_GATEWAY = 'two_factor.gateways.twilio.gateway.Twilio'
+
+    if _settings.get('2fa.sms.enabled', False):
+        TWO_FACTOR_SMS_GATEWAY = 'two_factor.gateways.twilio.gateway.Twilio'
+
+    if _settings.get('2fa.twilio.sid', None):
+        MIDDLEWARE.insert(MIDDLEWARE.index('django_otp.middleware.OTPMiddleware')+1, 'two_factor.middleware.threadlocals.ThreadLocals')
+        TWILIO_SID = _settings.get('2fa.twilio.sid')
+        TWILIO_TOKEN = _settings.get('2fa.twilio.token')
+        TWILIO_CALLER_ID = _settings.get('2fa.twilio.callerid')
 
 _settings.populate_obj(sys.modules[__name__])
diff --git a/biscuit/core/urls.py b/biscuit/core/urls.py
index 35431ae22e4bb317263d4c7d837d6989d6346a6c..62bd0792cc3d72f4ae911e96e87902d409097bca 100644
--- a/biscuit/core/urls.py
+++ b/biscuit/core/urls.py
@@ -3,8 +3,6 @@ from django.conf import settings
 from django.conf.urls.static import static
 from django.urls import include, path
 
-from two_factor.urls import urlpatterns as tf_urls
-
 import debug_toolbar
 
 from . import views
@@ -34,11 +32,13 @@ urlpatterns = [
     path('contact/', include('contact_form.urls')),
     path('impersonate/', include('impersonate.urls')),
     path('__i18n__/', include('django.conf.urls.i18n')),
-    path('select2/', include('django_select2.urls')),
-    path('', include(tf_urls))
+    path('select2/', include('django_select2.urls'))
 ]
 
 # Add URLs for optional features
+if 'two_factor' in settings.INSTALLED_APPS:
+    from two_factor.urls import urlpatterns as tf_urls  # noqa
+    urlpatterns += [path('', include(tf_urls))]
 if hasattr(settings, 'TWILIO_ACCOUNT_SID'):
     from two_factor.gateways.twilio.urls import urlpatterns as tf_twilio_urls  # noqa
     urlpatterns += [path('', include(tf_twilio_urls))]
diff --git a/pyproject.toml b/pyproject.toml
index e471cb10532ab3043c8a02ea9b5793c0ce941d98..e793a460f5369986d66053709baee70990977212 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -50,14 +50,15 @@ django-hattori = "^0.2"
 psycopg2 = "^2.8"
 django_select2 = "^7.1"
 requests = "^2.22"
-django-otp = "^0.7.4"
 django-formtools = "^2.1"
-django-two-factor-auth = "^1.9"
+django-otp = { version = "^0.7.4", optional = true }
+django-two-factor-auth = { version = "^1.9", optional = true }
 django-otp-yubikey = { version = '^0.5.2', optional = true }
 twilio = { version = "^6.33", optional = true }
 
 [tool.poetry.extras]
 ldap = ["django-auth-ldap"]
+2fa = ["django-otp", "django-two-factor-auth"]
 twilio = ["twilio"]
 yubikey = ["django-otp-yubikey"]