diff --git a/.copier-answers.yml b/.copier-answers.yml
index c4abe3067350bfc59a029c12300e716e45138b37..f2f7fe30dcabf2168c2b1f9464dabc1d58a9d61f 100644
--- a/.copier-answers.yml
+++ b/.copier-answers.yml
@@ -1,5 +1,5 @@
 # Changes here will be overwritten by Copier
-_commit: v0.2.2
+_commit: v0.3.3
 _src_path: https://gitlab.softwareheritage.org/swh/devel/swh-py-template.git
 description: Software Heritage authentication utilities
 distribution_name: swh-auth
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index cf6989529ba743dd5661fa7fb18b4e7104122b7d..e777d356acfad2c3032000524d32354e19e798f9 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,18 +1,18 @@
 repos:
   - repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v4.6.0
+    rev: v5.0.0
     hooks:
       - id: trailing-whitespace
       - id: check-json
       - id: check-yaml
 
   - repo: https://github.com/python/black
-    rev: 24.8.0
+    rev: 25.1.0
     hooks:
       - id: black
 
   - repo: https://github.com/PyCQA/isort
-    rev: 5.13.2
+    rev: 6.0.0
     hooks:
       - id: isort
 
@@ -20,14 +20,14 @@ repos:
     rev: 7.1.1
     hooks:
       - id: flake8
-        additional_dependencies: [flake8-bugbear==24.4.26]
+        additional_dependencies: [flake8-bugbear==24.12.12, flake8-pyproject]
 
   - repo: https://github.com/codespell-project/codespell
-    rev: v2.3.0
+    rev: v2.4.1
     hooks:
       - id: codespell
         name: Check source code spelling
-        stages: [commit]
+        stages: [pre-commit]
       - id: codespell
         name: Check commit message spelling
         stages: [commit-msg]
diff --git a/mypy.ini b/mypy.ini
deleted file mode 100644
index 41b05dad83fb245012ad4a42580e4af0d2c4c6f9..0000000000000000000000000000000000000000
--- a/mypy.ini
+++ /dev/null
@@ -1,31 +0,0 @@
-[mypy]
-namespace_packages = True
-warn_unused_ignores = True
-explicit_package_bases = True
-# ^ Needed for mypy to detect py.typed from swh packages installed
-# in editable mode
-
-# support for django magic: https://github.com/typeddjango/django-stubs
-plugins = mypy_django_plugin.main, mypy_drf_plugin.main
-
-[mypy.plugins.django-stubs]
-django_settings_module = swh.auth.tests.django.app.apptest.settings
-
-# 3rd party libraries without stubs (yet)
-[mypy-jwcrypto.*]
-ignore_missing_imports = True
-
-[mypy-keycloak.*]
-ignore_missing_imports = True
-
-[mypy-pkg_resources.*]
-ignore_missing_imports = True
-
-[mypy-starlette.*]
-ignore_missing_imports = True
-
-[mypy-aiocache.*]
-ignore_missing_imports = True
-
-# [mypy-add_your_lib_here.*]
-# ignore_missing_imports = True
diff --git a/pyproject.toml b/pyproject.toml
index 9f5126dedc93e18cb16472bd3486bc55e9ecac96..83d5e7a56e9e20abd4e89fbac212c9bf9cee9c4d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -60,3 +60,41 @@ ensure_newline_before_comments = true
 line_length = 88
 force_sort_within_sections = true
 known_first_party = ['swh']
+
+[tool.django-stubs]
+django_settings_module = "swh.auth.tests.django.app.apptest.settings"
+
+[tool.mypy]
+namespace_packages = true
+warn_unused_ignores = true
+explicit_package_bases = true
+# ^ Needed for mypy to detect py.typed from swh packages installed
+# in editable mode
+
+plugins = ["mypy_django_plugin.main",
+           "mypy_drf_plugin.main"]
+
+# 3rd party libraries without stubs (yet)
+[[tool.mypy.overrides]]
+module = [
+    "aiocache.*",
+]
+ignore_missing_imports = true
+
+[tool.flake8]
+select = ["C", "E", "F", "W", "B950"]
+ignore = [
+    "E203", # whitespaces before ':' <https://github.com/psf/black/issues/315>
+    "E231", # missing whitespace after ','
+    "E501", # line too long, use B950 warning from flake8-bugbear instead
+    "W503" # line break before binary operator <https://github.com/psf/black/issues/52>
+]
+max-line-length = 88
+
+[tool.pytest.ini_options]
+addopts = "-p no:flask"
+norecursedirs = "build docs .*"
+asyncio_mode = "strict"
+consider_namespace_packages = true
+DJANGO_SETTINGS_MODULE = "swh.auth.tests.django.app.apptest.settings"
+
diff --git a/pytest.ini b/pytest.ini
deleted file mode 100644
index 3f6ba8970407ca766eaf255e596a987ac1f5176e..0000000000000000000000000000000000000000
--- a/pytest.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[pytest]
-addopts = -p no:flask
-norecursedirs = build docs .*
-asyncio_mode = strict
-consider_namespace_packages = true
-DJANGO_SETTINGS_MODULE = swh.auth.tests.django.app.apptest.settings
diff --git a/requirements-test.txt b/requirements-test.txt
index c0f5c5449d9e7925be48f97d3107e5d97686ed19..5f5996aba515750501c41f0c2a5d3992c9af63e7 100644
--- a/requirements-test.txt
+++ b/requirements-test.txt
@@ -6,5 +6,6 @@ pytest-mock
 requests_mock
 swh.core[testing]
 types-click
+types-jwcrypto
 types-pytz
 types-pyyaml
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 2c446b475ceea8a1105c2d161d12b40caa2a20b9..0000000000000000000000000000000000000000
--- a/setup.cfg
+++ /dev/null
@@ -1,8 +0,0 @@
-[flake8]
-# E203: whitespaces before ':' <https://github.com/psf/black/issues/315>
-# E231: missing whitespace after ','
-# E501: line too long, use B950 warning from flake8-bugbear instead
-# W503: line break before binary operator <https://github.com/psf/black/issues/52>
-select = C,E,F,W,B950
-ignore = E203,E231,E501,W503
-max-line-length = 88
diff --git a/tox.ini b/tox.ini
index ac00147675889fdcaaa2a76f4e2c13e7179a823a..24245d69a5b1b5b22f711a9096026b9712194f3b 100644
--- a/tox.ini
+++ b/tox.ini
@@ -24,7 +24,7 @@ commands =
 [testenv:black]
 skip_install = true
 deps =
-  black==24.8.0
+  black==25.1.0
 commands =
   {envpython} -m black --check swh
 
@@ -32,8 +32,10 @@ commands =
 skip_install = true
 deps =
   flake8==7.1.1
-  flake8-bugbear==24.4.26
+  flake8-bugbear==24.12.12
+  flake8-pyproject==1.2.3
   pycodestyle==2.12.1
+
 commands =
   {envpython} -m flake8
 
@@ -41,7 +43,7 @@ commands =
 extras =
   testing
 deps =
-  mypy==1.11.1
+  mypy==1.15.0
 commands =
   mypy swh