diff --git a/.gitignore b/.gitignore
index 7cd853fbb..6232ea83e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,7 @@ example/settings_private.py
/.tox/
/htmlcov/
/docs/_build/
+/dist/
.eggs/
.idea/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 70fcf8f01..3fd2d8ea3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,12 @@
### Changed
- Allow django-phonenumber-field 8.x.x.
+### Added
+- Support confirmation for Django 5.1.
+
+### Removed
+- Dropped support for Django <4.2.
+
## 1.16.0
### Fixed
- Avoid potentially empty `
` on the profile page.
diff --git a/README.rst b/README.rst
index 98f6cc63d..b8cfc7b3f 100644
--- a/README.rst
+++ b/README.rst
@@ -37,8 +37,8 @@ providing Django sessions with a foreign key to the user. Although the package
is optional, it improves account security control over
``django.contrib.sessions``.
-Compatible with supported Django and Python versions. At the moment of writing that
-includes 3.2, 4.0, 4.1, and 4.2 on Python 3.8, 3.9, 3.10 and 3.11.
+Compatible with supported Django and Python versions. At the moment of writing
+that includes 4.2, 5.0, and 5.1 on Python 3.8 to 3.12.
Documentation is available at `readthedocs.io`_.
diff --git a/docs/requirements.rst b/docs/requirements.rst
index 505688df7..296d7f777 100644
--- a/docs/requirements.rst
+++ b/docs/requirements.rst
@@ -3,8 +3,8 @@ Requirements
Django
------
-Supported Django versions are supported. Currently this list includes Django 3.2, 4.0,
-4.1, 4.2 and 5.0.
+Supported Django versions are supported. Currently this list includes Django 4.2,
+5.0 and 5.1.
Python
------
diff --git a/pyproject.toml b/pyproject.toml
index d7f441cc7..d9e56c15a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -17,7 +17,7 @@ maintainers = [
license = {text = "MIT"}
requires-python = ">= 3.8"
dependencies = [
- "Django>=3.2",
+ "Django>=4.2",
"django_otp>=0.8.0",
"qrcode>=4.0.0,<7.99",
"django-phonenumber-field<9",
@@ -28,11 +28,9 @@ classifiers = [
"Development Status :: 5 - Production/Stable",
"Environment :: Web Environment",
"Framework :: Django",
- "Framework :: Django :: 3.2",
- "Framework :: Django :: 4.0",
- "Framework :: Django :: 4.1",
"Framework :: Django :: 4.2",
"Framework :: Django :: 5.0",
+ "Framework :: Django :: 5.1",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
diff --git a/tox.ini b/tox.ini
index 980b519e8..a5289e9a0 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,8 +1,7 @@
[tox]
envlist =
- py{38,39,310}-dj32-{normal,yubikey,custom_user,webauthn}
- py{38,39,310,311}-dj{40,41,42}-{normal,yubikey,custom_user,webauthn}
- py{310,311,312}-dj{50,main}-{normal,yubikey,custom_user,webauthn}
+ py{38,39,310,311,312}-dj42-{normal,yubikey,custom_user,webauthn}
+ py{310,311,312}-dj{50,51,main}-{normal,yubikey,custom_user,webauthn}
[gh-actions]
python =
@@ -14,11 +13,9 @@ python =
[gh-actions:env]
DJANGO =
- 3.2: dj32
- 4.0: dj40
- 4.1: dj41
4.2: dj42
5.0: dj50
+ 5.1: dj51
main: djmain
VARIANT =
normal: normal
@@ -41,11 +38,9 @@ basepython =
py311: python3.11
py312: python3.12
deps =
- dj32: Django<4.0
- dj40: Django<4.1
- dj41: Django<4.2
dj42: Django<5.0
dj50: Django<5.1
+ dj51: Django<5.2
djmain: https://github.com/django/django/archive/main.tar.gz
webauthn: -rrequirements_e2e.txt
extras =
diff --git a/two_factor/__init__.py b/two_factor/__init__.py
index 9144e571b..8b1378917 100644
--- a/two_factor/__init__.py
+++ b/two_factor/__init__.py
@@ -1,4 +1 @@
-import django
-if django.VERSION <= (3, 2):
- default_app_config = 'two_factor.apps.TwoFactorConfig'
diff --git a/two_factor/plugins/yubikey/__init__.py b/two_factor/plugins/yubikey/__init__.py
index cdfd190ea..8b1378917 100644
--- a/two_factor/plugins/yubikey/__init__.py
+++ b/two_factor/plugins/yubikey/__init__.py
@@ -1,4 +1 @@
-import django
-if django.VERSION <= (3, 2):
- default_app_config = 'two_factor.plugins.yubikey.apps.TwoFactorYubikeyConfig'
diff --git a/two_factor/views/core.py b/two_factor/views/core.py
index 0afb1a68b..d37c95329 100644
--- a/two_factor/views/core.py
+++ b/two_factor/views/core.py
@@ -13,6 +13,7 @@
from django.contrib.auth import REDIRECT_FIELD_NAME, login
from django.contrib.auth.decorators import login_required
from django.contrib.auth.forms import AuthenticationForm
+from django.contrib.auth.views import RedirectURLMixin
from django.contrib.sites.shortcuts import get_current_site
from django.core.signing import BadSignature
from django.forms import Form, ValidationError
@@ -50,12 +51,6 @@
get_remember_device_cookie, validate_remember_device_cookie,
)
-try:
- from django.contrib.auth.views import RedirectURLMixin
-except ImportError: # django<4.1
- from django.contrib.auth.views import (
- SuccessURLAllowedHostsMixin as RedirectURLMixin,
- )
logger = logging.getLogger(__name__)
REMEMBER_COOKIE_PREFIX = getattr(settings, 'TWO_FACTOR_REMEMBER_COOKIE_PREFIX', 'remember-cookie_')
diff --git a/two_factor/views/utils.py b/two_factor/views/utils.py
index f6fc8072e..456e6cbd9 100644
--- a/two_factor/views/utils.py
+++ b/two_factor/views/utils.py
@@ -5,7 +5,9 @@
from django.conf import settings
from django.contrib.auth import load_backend
from django.core.exceptions import SuspiciousOperation
-from django.core.signing import BadSignature, SignatureExpired
+from django.core.signing import (
+ BadSignature, SignatureExpired, b62_decode, b62_encode,
+)
from django.utils.crypto import salted_hmac
from django.utils.decorators import method_decorator
from django.utils.encoding import force_bytes
@@ -14,14 +16,6 @@
from formtools.wizard.storage.session import SessionStorage
from formtools.wizard.views import SessionWizardView
-try:
- from django.core.signing import b62_decode, b62_encode
-except ImportError: # Django < 4.0
- # Deprecated in Django 4.0, removed in Django 5.0
- from django.utils import baseconv
- b62_decode = baseconv.base62.decode
- b62_encode = baseconv.base62.encode
-
logger = logging.getLogger(__name__)