[project] name = "authentik" version = "2026.2.0-rc1" description = "" authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }] requires-python = "==3.14.*" dependencies = [ "ak-guardian==3.2.0", "argon2-cffi==25.1.0", "cachetools==6.2.4", "channels==4.3.2", "cryptography==46.0.3", "dacite==1.9.2", "deepmerge==2.0", "defusedxml==0.7.1", "django-channels-postgres", "django-countries==7.6.1", "django-cte==2.0.0", "django-dramatiq-postgres", "django-filter==25.2", "django-model-utils==5.0.0", "django-pglock==1.8.0", "django-pgtrigger==4.17.0", "django-postgres-cache", "django-postgres-extra==2.0.9", "django-prometheus==2.4.1", "django-storages[s3]==1.14.6", "django-tenants==3.9.0", "django==5.2.10", "djangoql==0.18.1", "djangorestframework==3.16.1", "docker==7.1.0", "drf-orjson-renderer==1.8.0", "drf-spectacular==0.28.0", "dumb-init==1.2.5.post1", "duo-client==5.5.0", "fido2==2.1.0", "geoip2==5.2.0", "geopy==2.4.1", "google-api-python-client==2.188.0", "gssapi==1.10.1", "gunicorn==23.0.0", "jsonpatch==1.33", "jwcrypto==1.5.6", "kubernetes==33.1.0", "ldap3==2.9.1", "lxml==6.0.2", "msgraph-sdk==1.52.0", "opencontainers==0.0.15", "packaging==25.0", "paramiko==4.0.0", "psycopg[c,pool]==3.3.2", "pydantic-scim==0.0.8", "pydantic==2.12.5", "pyjwt==2.10.1", "pyrad==2.4", "python-kadmin-rs==0.6.3", "pyyaml==6.0.3", "requests-oauthlib==2.0.0", "scim2-filter-parser==0.7.0", "sentry-sdk==2.49.0", "service-identity==24.2.0", "setproctitle==1.3.7", "structlog==25.5.0", "swagger-spec-validator==3.0.4", "twilio==9.9.1", "ua-parser==1.0.1", "unidecode==1.4.0", "urllib3<3", "uvicorn[standard]==0.40.0", "watchdog==6.0.0", "webauthn==2.7.0", "wsproto==1.3.2", "xmlsec==1.3.17", "zxcvbn==4.5.0", ] [dependency-groups] dev = [ "aws-cdk-lib==2.234.1", "bandit==1.9.2", "black==25.12.0", "bpython==0.26", "codespell==2.4.1", "colorama==0.4.6", "constructs==10.4.4", "coverage[toml]==7.13.1", "daphne==4.2.1", "debugpy==1.8.19", "django-stubs[compatible-mypy]==5.2.8", "djangorestframework-stubs[compatible-mypy]==3.16.7", "drf-jsonschema-serializer==3.0.0", "freezegun==1.5.5", "importlib-metadata==8.7.1", "k5test==0.10.4", "lxml-stubs==0.5.1", "mypy==1.19.1", "pdoc==16.0.0", "pytest-django==4.11.1", "pytest-github-actions-annotate-failures==0.3.0", "pytest-randomly==4.0.1", "pytest-timeout==2.4.0", "pytest==9.0.2", "requests-mock==1.12.1", "ruff==0.14.11", "selenium==4.39.0", "types-channels==4.3.0.20250822", "types-ldap3==2.9.13.20251121", ] [tool.uv] no-binary-package = [ # This differs from the no-binary packages in the Dockerfile. This is due to the fact # that these packages are built from source for different reasons than cryptography and kadmin. # These packages are built from source to link against the libxml2 on the system which is # required for functionality and to stay up-to-date on both libraries. # The other packages specified in the dockerfile are compiled from source to link against the # correct FIPS OpenSSL libraries "lxml", "xmlsec", ] [tool.uv.sources] ak-guardian = { workspace = true } django-channels-postgres = { workspace = true } django-dramatiq-postgres = { workspace = true } django-postgres-cache = { workspace = true } opencontainers = { git = "https://github.com/vsoch/oci-python", rev = "ceb4fcc090851717a3069d78e85ceb1e86c2740c" } [tool.uv.workspace] members = [ "packages/ak-guardian", "packages/django-channels-postgres", "packages/django-dramatiq-postgres", "packages/django-postgres-cache", ] [project.scripts] ak = "lifecycle.ak:main" [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.bandit] exclude_dirs = ["**/node_modules/**"] [tool.codespell] skip = [ "**/.env", # Environment files "**/.venv", # Python virtual environment "**/node_modules", # Node modules "**/package-lock.json", # NPM package lock "schema.yml", # OpenAPI schema "./blueprints/schema.json", # Generated blueprint schema "go.sum", # Go module file "locale", # Django locale files "**/web/src/locales", # Generated TypeScript locale "**/web/xliff", # XLIFF translation files "**/custom-elements.json", # TypeScript custom element definitions "**/storybook-static", # Storybook build output "**/playwright-report", # Playwright test output "unittest.xml", # Pytest output "./htmlcov", # Coverage HTML output "**/out", # TypeScript type-checking output "**/dist", # Distributed build output "./website/build", # TODO: Remove this after moving website to docs "./website/**/build", # TODO: Remove this after moving website to docs "./docs/build", # Docusaurus Topic docs build output "./docs/**/build", # Docusaurus workspaces output "*.api.mdx", # Generated API docs "./gen-ts-api", # Generated TypeScript API "./gen-py-api", # Generated Python API "./gen-go-api", # Generated Go API "./data", # Media files "./media", # Legacy media files ] dictionary = ".github/codespell-dictionary.txt,-" ignore-words = ".github/codespell-words.txt" [tool.black] line-length = 100 target-version = ['py314'] exclude = 'node_modules' [tool.ruff] line-length = 100 target-version = "py314" exclude = ["**/migrations/**", "**/node_modules/**"] [tool.ruff.lint] select = [ "E", # pycodestyle "F", # Pyflakes "I", # isort "UP", # pyupgrade "B", # flake8-bugbear "DJ", # django "PL", # pylint "BLE", # flake8-blind-except ] ignore = [ "DJ001", # Avoid using `null=True` on string-based fields, "PLC0415", # `import` should be at the top-level of a file ] [tool.ruff.lint.pylint] max-args = 7 max-branches = 18 max-returns = 10 [tool.mypy] plugins = ["mypy_django_plugin.main", "mypy_drf_plugin.main", "pydantic.mypy"] exclude = ['^gen-py-api/'] [[tool.mypy.overrides]] module = ["django_tenants.*", "dramatiq.*", "pglock.*"] follow_untyped_imports = true [[tool.mypy.overrides]] module = ["cron_converter.*", "msgpack.*"] ignore_missing_imports = true [[tool.mypy.overrides]] module = [ "authentik.admin.*", "authentik.api.*", "authentik.blueprints.*", "authentik.brands.*", "authentik.core.*", "authentik.crypto.*", "authentik.endpoints.*", "authentik.enterprise.*", "authentik.events.*", "authentik.flows.*", "authentik.lib.*", "authentik.outposts.*", "authentik.policies.*", "authentik.policies.dummy.*", "authentik.policies.event_matcher.*", "authentik.policies.expiry.*", "authentik.policies.expression.*", "authentik.policies.geoip.*", "authentik.policies.password.*", "authentik.policies.reputation.*", "authentik.providers.ldap.*", "authentik.providers.oauth2.*", "authentik.providers.proxy.*", "authentik.providers.rac.*", "authentik.providers.radius.*", "authentik.providers.saml.*", "authentik.providers.scim.*", "authentik.rbac.*", "authentik.recovery.*", "authentik.root.*", "authentik.sources.kerberos.*", "authentik.sources.ldap.*", "authentik.sources.oauth.*", "authentik.sources.plex.*", "authentik.sources.saml.*", "authentik.sources.scim.*", "authentik.sources.telegram.*", "authentik.stages.authenticator_duo.*", "authentik.stages.authenticator_email.*", "authentik.stages.authenticator_sms.*", "authentik.stages.authenticator_static.*", "authentik.stages.authenticator_totp.*", "authentik.stages.authenticator_validate.*", "authentik.stages.authenticator_webauthn.*", "authentik.stages.authenticator.*", "authentik.stages.captcha.*", "authentik.stages.consent.*", "authentik.stages.deny.*", "authentik.stages.dummy.*", "authentik.stages.email.*", "authentik.stages.identification.*", "authentik.stages.invitation.*", "authentik.stages.password.*", "authentik.stages.prompt.*", "authentik.stages.redirect.*", "authentik.stages.user_delete.*", "authentik.stages.user_login.*", "authentik.stages.user_logout.*", "authentik.stages.user_write.*", "authentik.tasks.*", "authentik.tasks.schedules.*", "authentik.tenants.*", "guardian.*", "lifecycle.*", "tests.e2e.*", "tests.integration.*", "tests.openid_conformance.*", ] ignore_errors = true [tool.django-stubs] django_settings_module = "authentik.root.settings" [tool.coverage.run] source = ["authentik"] relative_files = true omit = [ "*/asgi.py", "manage.py", "*/migrations/*", "*/management/commands/*", "*/apps.py", # TODO: Remove this after moving website to docs "website/", "docs/", ] [tool.coverage.report] sort = "Cover" skip_covered = true precision = 2 exclude_lines = [ "pragma: no cover", # Don't complain about missing debug-only code: "def __unicode__", "def __str__", "def __repr__", "if self.debug", "if TYPE_CHECKING", # Don't complain if tests don't hit defensive assertion code: "raise AssertionError", "raise NotImplementedError", # Don't complain if non-runnable code isn't run: "if 0:", "if __name__ == .__main__.:", ] show_missing = true [tool.pytest.ini_options] DJANGO_SETTINGS_MODULE = "authentik.root.settings" python_files = ["tests.py", "test_*.py", "*_tests.py"] junit_family = "xunit2" addopts = "-p authentik.root.test_plugin --junitxml=unittest.xml -vv --full-trace --doctest-modules --import-mode=importlib --ignore=authentik/tasks/setup.py" filterwarnings = [ "ignore:defusedxml.lxml is no longer supported and will be removed in a future release.:DeprecationWarning", "ignore:SelectableGroups dict interface is deprecated. Use select.:DeprecationWarning", ]