Compare commits

...

42 Commits

Author SHA1 Message Date
Teffen Ellis
9b8e057402 website/docs: Backport version picker updates.
Fix import path.

Show unlisted entries if release.

Fix sidebar rendering.

Fix positioning of pre-release note. Tidy phrasing.

Clarify pre-release vs draft.

website/docs: Fix version parsing.
2025-12-19 15:42:00 +01:00
authentik-automation[bot]
e263af2dd9 web/elements: progress-bar and table loading header (cherry-pick #18934 to version-2025.12) (#18939)
web/elements: progress-bar and table loading header (#18934)

* add ak-progress-bar



* make intermediate smaller



* add table



* hide table overflow



---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-12-19 00:06:02 +01:00
authentik-automation[bot]
3a59911a2b website/docs: release notes: add endpoint device links to 2025.12 notes (cherry-pick #18940 to version-2025.12) (#18947)
website/docs: release notes: add endpoint device links to 2025.12 notes (#18940)

Add links to release notes

Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
2025-12-18 21:59:53 +00:00
authentik-automation[bot]
bbf31e99c3 website/docs: endpoint devices (cherry-pick #18634 to version-2025.12) (#18946)
website/docs: endpoint devices (#18634)

* Initial

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* Apply suggestions from code review




* Apply suggestions from code review




* Apply suggestions

* Apply suggestions

* Apply suggestions from code review




* Apply suggestions from code review




* WIP

* Apply suggestions from code review




* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* fixes



* WIP

* Optimised images with calibre/image-actions

* Optimised images with calibre/image-actions

* Optimised images with calibre/image-actions

* Optimised images with calibre/image-actions

* Optimised images with calibre/image-actions

* Optimised images with calibre/image-actions

* Optimised images with calibre/image-actions

* Fix anchor

* Update website/docs/endpoint-devices/index.mdx




* WIP

---------

Signed-off-by: Dewi Roberts <dewi@goauthentik.io>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Dominic R <dominic@sdko.org>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2025-12-18 19:40:56 +00:00
authentik-automation[bot]
9d5bd42f3e stages/identification: replace sleep with make_password (cherry-pick #18883 to version-2025.12) (#18943)
Co-authored-by: Jens L. <jens@goauthentik.io>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-12-18 19:10:01 +01:00
authentik-automation[bot]
e721dae6da web/flow: Fix spurious double submit on ak-stage-autosubmit (cherry-pick #18727 to version-2025.12) (#18933)
web/flow: Fix spurious double submit  on ak-stage-autosubmit (#18727)

* Fix double submission on ak-stage-autosubmit

* use updated correctly



---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Victor Nawothnig <dminuoso@icloud.com>
Co-authored-by: Victor Nawothnig <Victor.Nawothnig+git@icloud.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-12-18 16:25:19 +01:00
authentik-automation[bot]
af3106b144 packages/ak-guardian: cast safely (cherry-pick #18929 to version-2025.12) (#18931)
Co-authored-by: Simonyi Gergő <28359278+gergosimonyi@users.noreply.github.com>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-12-18 16:24:59 +01:00
authentik-automation[bot]
5b55103575 tests/e2e: handle StaleElementReferenceException in parse_json_content (cherry-pick #18842 to version-2025.12) (#18919)
Co-authored-by: Marcelo Elizeche Landó <marcelo@goauthentik.io>
2025-12-18 14:08:49 +01:00
authentik-automation[bot]
ee4ecf929f release: 2025.12.0-rc2 2025-12-17 22:03:04 +00:00
authentik-automation[bot]
8336556a6f root: fix docker-compose data mount (cherry-pick #18903 to version-2025.12) (#18918)
root: fix docker-compose data mount (#18903)

Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-12-17 19:53:44 +00:00
authentik-automation[bot]
709aad1d3b core/groups: optimize prefetch queries to fetch only required fields (cherry-pick #18448 to version-2025.12) (#18914)
core/groups: optimize prefetch queries to fetch only required fields (#18448)

Co-authored-by: João C. Fernandes <joaocfernandes@gmail.com>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-12-17 17:50:44 +00:00
authentik-automation[bot]
fb7ab4937c web/admin: reword some things on the device view page (cherry-pick #18785 to version-2025.12) (#18913)
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-12-17 17:55:14 +01:00
authentik-automation[bot]
5df1726d80 website/docs: 2025.12: remove superfluous changes (cherry-pick #18910 to version-2025.12) (#18912)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-12-17 17:25:13 +01:00
authentik-automation[bot]
9fdb568843 ci/release-tag: checkout correct branch for make test-docker (cherry-pick #18880 to version-2025.12) (#18911)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-12-17 16:24:56 +01:00
authentik-automation[bot]
8e76f56f89 api: fix latest version for public schema (cherry-pick #18902 to version-2025.12) (#18909)
Co-authored-by: Jens L. <jens@goauthentik.io>
fix latest version for public schema (#18902)
2025-12-17 16:14:16 +01:00
authentik-automation[bot]
05d3791577 website/docs: added list of Int Guide contributors (also edited frontmatter) (cherry-pick #18888 to version-2025.12) (#18907)
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-12-17 16:10:50 +01:00
authentik-automation[bot]
d00dd7eb90 api: fix page_size with invalid query param (cherry-pick #18879 to version-2025.12) (#18908)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
fix page_size with invalid query param (#18879)
2025-12-17 16:10:07 +01:00
authentik-automation[bot]
8d2e404017 stages/authenticator_*: fix code input field not string (cherry-pick #18875 to version-2025.12) (#18906)
Co-authored-by: Jens L. <jens@goauthentik.io>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
fix code input field not string (#18875)
2025-12-17 16:04:32 +01:00
authentik-automation[bot]
95eb2af25e tasks/middleware: close connections on worker status update database error (cherry-pick #18881 to version-2025.12) (#18905)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
close connections on worker status update database error (#18881)
2025-12-17 15:46:53 +01:00
authentik-automation[bot]
cbc00a501b web: fix file upload form (cherry-pick #18808 to version-2025.12) (#18884)
Co-authored-by: Dominic R <dominic@sdko.org>
fix file upload form (#18808)
2025-12-17 14:02:31 +01:00
authentik-automation[bot]
480645d897 website/docs: add icon info to style guide (cherry-pick #18832 to version-2025.12) (#18837)
Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-12-17 14:02:12 +01:00
authentik-automation[bot]
997c767c95 web/admin: endpoint: change wording and add helper text (cherry-pick #18871 to version-2025.12) (#18890)
Co-authored-by: Dewi Roberts <dewi@goauthentik.io>
Co-authored-by: Dominic R <dominic@sdko.org>
Co-authored-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
Co-authored-by: Teffen Ellis <teffen@sister.software>
2025-12-17 14:00:02 +01:00
authentik-automation[bot]
5a54e1dc9a web: fix notification counter (cherry-pick #18781 to version-2025.12) (#18882)
Co-authored-by: Alexander Tereshkin <96586+atereshkin@users.noreply.github.com>
fix notification counter (#18781)
2025-12-16 18:44:22 +01:00
authentik-automation[bot]
49b1952566 website/docs: Add docs for passkey autofill (WebauthN Conditional UI) (cherry-pick #18805 to version-2025.12) (#18870)
Co-authored-by: Marcelo Elizeche Landó <marcelo@goauthentik.io>
Co-authored-by: Dominic R <dominic@sdko.org>
2025-12-16 18:11:02 +01:00
authentik-automation[bot]
e73edc2fce web/admin: fix read-only provider selection for application form (cherry-pick #18768 to version-2025.12) (#18803)
Co-authored-by: Dominic R <dominic@sdko.org>
fix read-only provider selection for application form (#18768)
2025-12-16 18:10:49 +01:00
authentik-automation[bot]
409652e874 web: add custom message with links for empty data export list (cherry-pick #18830 to version-2025.12) (#18876)
Co-authored-by: Alexander Tereshkin <96586+atereshkin@users.noreply.github.com>
2025-12-16 18:09:52 +01:00
authentik-automation[bot]
1d3fb6431f website/docs: 2025.10.3 release notes (cherry-pick #18868 to version-2025.12) (#18873)
website/docs: 2025.10.3 release notes (#18868)

* website/docs: 2025.10.3 release notes



* format



---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
2025-12-16 17:06:16 +01:00
authentik-automation[bot]
76cfada60f website/docs: adjust RBAC-related details in 2025.12 release notes (cherry-pick #18863 to version-2025.12) (#18869)
website/docs: adjust RBAC-related details in 2025.12 release notes (#18863)

* website/docs: adjust RBAC-related details in 2025.12 release notes

* adjust wording




---------

Signed-off-by: Simonyi Gergő <28359278+gergosimonyi@users.noreply.github.com>
Co-authored-by: Simonyi Gergő <28359278+gergosimonyi@users.noreply.github.com>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
2025-12-16 10:39:50 -05:00
authentik-automation[bot]
ac45f80551 outposts: fix permission errors for related certificates (cherry-pick #18861 to version-2025.12) (#18866)
Co-authored-by: Jens L. <jens@goauthentik.io>
fix permission errors for related certificates (#18861)
2025-12-16 15:23:49 +01:00
authentik-automation[bot]
5ea85f086a web/admin/rbac: misc object permission fixes (cherry-pick #18859 to version-2025.12) (#18865)
Co-authored-by: Simonyi Gergő <28359278+gergosimonyi@users.noreply.github.com>
fixes (#18859)
2025-12-16 14:54:31 +01:00
authentik-automation[bot]
e3f657746c rbac: alter migrated direct permission roles (cherry-pick #18860 to version-2025.12) (#18864)
Co-authored-by: Simonyi Gergő <28359278+gergosimonyi@users.noreply.github.com>
2025-12-16 13:56:35 +01:00
authentik-automation[bot]
001b56e2cc release: 2025.12.0-rc1 2025-12-16 04:59:24 +00:00
Marcelo Elizeche Landó
ecbfd2f0de add skip s3_test_server_available to TestResolveFileUrlS3Backend 2025-12-16 01:29:40 -03:00
authentik-automation[bot]
45753397e1 admin/files: fix get_objects_for_user queryset argument in FileUsedByView (cherry-pick #18845 to version-2025.12) (#18847)
admin/files: fix get_objects_for_user queryset argument in FileUsedByView (#18845)

Co-authored-by: Dominic R <dominic@sdko.org>
Co-authored-by: Marcelo Elizeche Landó <marcelo@goauthentik.io>
2025-12-16 01:15:30 +00:00
Marcelo Elizeche Landó
dc6fe1dafe core: skip s3 tests if endpoint isn't available (#18841)
skip s3 tests if endpoint isn't available
2025-12-15 20:38:01 -03:00
authentik-automation[bot]
d5e8f2f416 admin/files: revert add check for /media existence (#18636) (cherry-pick #18829 to version-2025.12) (#18838)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-12-15 16:45:43 +01:00
authentik-automation[bot]
d73af5a2b4 packages/django-dramatiq-postgres: broker: close django connections on consumer close (cherry-pick #18833 to version-2025.12) (#18836)
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Co-authored-by: Norman Ziebal <norman.ziebal@mail.schwarz>
close django connections on consumer close (#18833)
2025-12-15 15:01:08 +01:00
authentik-automation[bot]
7042f2bba8 core: list applications fix (cherry-pick #18798 to version-2025.12) (#18828)
Co-authored-by: Ryan Pesek <44002516+ryanpesek@users.noreply.github.com>
fix (#18798)
2025-12-15 12:47:20 +00:00
authentik-automation[bot]
efeb260fa8 tests/e2e: retry detached shadow roots (cherry-pick #18796 to version-2025.12) (#18799)
tests/e2e: retry detached shadow roots (#18796)

tests(e2e): retry detached shadow roots

Co-authored-by: Marcelo Elizeche Landó <marcelo@goauthentik.io>
2025-12-14 00:50:23 +01:00
authentik-automation[bot]
29e90092ea website/release notes: Update v2025.12 release notes (cherry-pick #18797 to version-2025.12) (#18800)
website/release notes: Update v2025.12 release notes (#18797)

* website/release notes: Update v2025.12 release notes



* fix linting

---------

Signed-off-by: Marcelo Elizeche Landó <marcelo@goauthentik.io>
Co-authored-by: Marcelo Elizeche Landó <marcelo@goauthentik.io>
2025-12-13 00:03:26 +00:00
Marcelo Elizeche Landó
0abe865023 Revert "Update docker compose command to start postgresql with s3"
This reverts commit 220c65a41a.
2025-12-12 20:59:44 -03:00
Marcelo Elizeche Landó
220c65a41a Update docker compose command to start postgresql with s3
Signed-off-by: Marcelo Elizeche Landó <marcelo@goauthentik.io>
2025-12-12 19:47:24 -03:00
104 changed files with 16116 additions and 389 deletions

View File

@@ -49,8 +49,12 @@ jobs:
test:
name: Pre-release test
runs-on: ubuntu-latest
needs:
- check-inputs
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5
with:
ref: "version-${{ needs.check-inputs.outputs.major_version }}"
- run: make test-docker
bump-authentik:
name: Bump authentik version

View File

@@ -3,7 +3,7 @@
from functools import lru_cache
from os import environ
VERSION = "2025.12.0-rc1"
VERSION = "2025.12.0-rc2"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@@ -37,7 +37,7 @@ class VersionSerializer(PassiveSerializer):
def get_version_latest(self, _) -> str:
"""Get latest version from cache"""
if get_current_tenant().schema_name == get_public_schema_name():
if get_current_tenant().schema_name != get_public_schema_name():
return authentik_version()
version_in_cache = cache.get(VERSION_CACHE_KEY)
if not version_in_cache: # pragma: no cover

View File

@@ -240,7 +240,9 @@ class FileUsedByView(APIView):
for field in fields:
q |= Q(**{field: params.get("name")})
objs = get_objects_for_user(request.user, f"{app}.view_{model_name}", model)
objs = get_objects_for_user(
request.user, f"{app}.view_{model_name}", model.objects.all()
)
objs = objs.filter(q)
for obj in objs:
serializer = UsedBySerializer(

View File

@@ -1,9 +1,4 @@
from pathlib import Path
from django.conf import settings
from authentik.blueprints.apps import ManagedAppConfig
from authentik.lib.config import CONFIG
class AuthentikFilesConfig(ManagedAppConfig):
@@ -11,20 +6,3 @@ class AuthentikFilesConfig(ManagedAppConfig):
label = "authentik_admin_files"
verbose_name = "authentik Files"
default = True
@ManagedAppConfig.reconcile_global
def check_for_media_mount(self):
if settings.TEST:
return
from authentik.events.models import Event, EventAction
if (
CONFIG.get("storage.media.backend", CONFIG.get("storage.backend", "file")) == "file"
and Path("/media").exists()
):
Event.new(
EventAction.CONFIGURATION_ERROR,
message="/media has been moved to /data/media. "
"Check the release notes for migration steps.",
).save()

View File

@@ -1,10 +1,13 @@
from unittest import skipUnless
from django.test import TestCase
from authentik.admin.files.tests.utils import FileTestS3BackendMixin
from authentik.admin.files.tests.utils import FileTestS3BackendMixin, s3_test_server_available
from authentik.admin.files.usage import FileUsage
from authentik.lib.config import CONFIG
@skipUnless(s3_test_server_available(), "S3 test server not available")
class TestS3Backend(FileTestS3BackendMixin, TestCase):
"""Test S3 backend functionality"""

View File

@@ -1,10 +1,16 @@
"""Test file service layer"""
from unittest import skipUnless
from django.http import HttpRequest
from django.test import TestCase
from authentik.admin.files.manager import FileManager
from authentik.admin.files.tests.utils import FileTestFileBackendMixin, FileTestS3BackendMixin
from authentik.admin.files.tests.utils import (
FileTestFileBackendMixin,
FileTestS3BackendMixin,
s3_test_server_available,
)
from authentik.admin.files.usage import FileUsage
from authentik.lib.config import CONFIG
@@ -81,6 +87,7 @@ class TestResolveFileUrlFileBackend(FileTestFileBackendMixin, TestCase):
self.assertEqual(result, "http://example.com/files/media/public/test.png")
@skipUnless(s3_test_server_available(), "S3 test server not available")
class TestResolveFileUrlS3Backend(FileTestS3BackendMixin, TestCase):
@CONFIG.patch("storage.media.s3.custom_domain", "s3.test:8080/test")
@CONFIG.patch("storage.media.s3.secure_urls", False)

View File

@@ -1,11 +1,26 @@
import shutil
import socket
from tempfile import mkdtemp
from urllib.parse import urlparse
from authentik.admin.files.backends.s3 import S3Backend
from authentik.admin.files.usage import FileUsage
from authentik.lib.config import CONFIG, UNSET
from authentik.lib.generators import generate_id
S3_TEST_ENDPOINT = "http://localhost:8020"
def s3_test_server_available() -> bool:
"""Check if the S3 test server is reachable."""
parsed = urlparse(S3_TEST_ENDPOINT)
try:
with socket.create_connection((parsed.hostname, parsed.port), timeout=2):
return True
except OSError:
return False
class FileTestFileBackendMixin:
def setUp(self):
@@ -57,7 +72,7 @@ class FileTestS3BackendMixin:
for key in s3_config_keys:
self.original_media_s3_settings[key] = CONFIG.get(f"storage.media.s3.{key}", UNSET)
self.media_s3_bucket_name = f"authentik-test-{generate_id(10)}".lower()
CONFIG.set("storage.media.s3.endpoint", "http://localhost:8020")
CONFIG.set("storage.media.s3.endpoint", S3_TEST_ENDPOINT)
CONFIG.set("storage.media.s3.access_key", "accessKey1")
CONFIG.set("storage.media.s3.secret_key", "secretKey1")
CONFIG.set("storage.media.s3.bucket_name", self.media_s3_bucket_name)
@@ -70,7 +85,7 @@ class FileTestS3BackendMixin:
for key in s3_config_keys:
self.original_reports_s3_settings[key] = CONFIG.get(f"storage.reports.s3.{key}", UNSET)
self.reports_s3_bucket_name = f"authentik-test-{generate_id(10)}".lower()
CONFIG.set("storage.reports.s3.endpoint", "http://localhost:8020")
CONFIG.set("storage.reports.s3.endpoint", S3_TEST_ENDPOINT)
CONFIG.set("storage.reports.s3.access_key", "accessKey1")
CONFIG.set("storage.reports.s3.secret_key", "secretKey1")
CONFIG.set("storage.reports.s3.bucket_name", self.reports_s3_bucket_name)

View File

@@ -15,7 +15,9 @@ class Pagination(pagination.PageNumberPagination):
def get_page_size(self, request):
if self.page_size_query_param in request.query_params:
return min(super().get_page_size(request), request.tenant.pagination_max_page_size)
page_size = super().get_page_size(request)
if page_size is not None:
return min(super().get_page_size(request), request.tenant.pagination_max_page_size)
return request.tenant.pagination_default_page_size
def get_paginated_response(self, data):

View File

@@ -180,10 +180,10 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet):
)
def _filter_applications_with_launch_url(
self, applications: QuerySet[Application]
self, paginated_apps: QuerySet[Application]
) -> list[Application]:
applications = []
for app in applications:
for app in paginated_apps:
if app.get_launch_url():
applications.append(app)
return applications

View File

@@ -33,6 +33,16 @@ from authentik.endpoints.connectors.agent.auth import AgentAuth
from authentik.rbac.api.roles import RoleSerializer
from authentik.rbac.decorators import permission_required
PARTIAL_USER_SERIALIZER_MODEL_FIELDS = [
"pk",
"username",
"name",
"is_active",
"last_login",
"email",
"attributes",
]
class PartialUserSerializer(ModelSerializer):
"""Partial User Serializer, does not include child relations."""
@@ -42,16 +52,7 @@ class PartialUserSerializer(ModelSerializer):
class Meta:
model = User
fields = [
"pk",
"username",
"name",
"is_active",
"last_login",
"email",
"attributes",
"uid",
]
fields = PARTIAL_USER_SERIALIZER_MODEL_FIELDS + ["uid"]
class RelatedGroupSerializer(ModelSerializer):
@@ -262,7 +263,14 @@ class GroupViewSet(UsedByMixin, ModelViewSet):
base_qs = Group.objects.all().prefetch_related("roles")
if self.serializer_class(context={"request": self.request})._should_include_users:
base_qs = base_qs.prefetch_related("users")
# Only fetch fields needed by PartialUserSerializer to reduce DB load and instantiation
# time
base_qs = base_qs.prefetch_related(
Prefetch(
"users",
queryset=User.objects.all().only(*PARTIAL_USER_SERIALIZER_MODEL_FIELDS),
)
)
else:
base_qs = base_qs.prefetch_related(
Prefetch("users", queryset=User.objects.all().only("id"))

View File

@@ -18,10 +18,9 @@ def migrate_object_permissions(apps: Apps, schema_editor: BaseDatabaseSchemaEdit
RoleModelPermission = apps.get_model("guardian", "RoleModelPermission")
def get_role_for_user_id(user_id: int) -> Role:
name = f"ak-managed-role--user-{user_id}"
name = f"ak-migrated-role--user-{user_id}"
role, created = Role.objects.using(db_alias).get_or_create(
name=name,
managed=name,
)
if created:
role.users.add(user_id)
@@ -32,11 +31,10 @@ def migrate_object_permissions(apps: Apps, schema_editor: BaseDatabaseSchemaEdit
if not role:
# Every django group should already have a role, so this should never happen.
# But let's be nice.
name = f"ak-managed-role--group-{group_id}"
name = f"ak-migrated-role--group-{group_id}"
role, created = Role.objects.using(db_alias).get_or_create(
group_id=group_id,
name=name,
managed=name,
)
if created:
role.group_id = group_id

View File

@@ -86,7 +86,7 @@ class OutpostConfig:
class OutpostModel(Model):
"""Base model for providers that need more objects than just themselves"""
def get_required_objects(self) -> Iterable[models.Model | str]:
def get_required_objects(self) -> Iterable[models.Model | str | tuple[str, models.Model]]:
"""Return a list of all required objects"""
return [self]
@@ -332,41 +332,35 @@ class Outpost(ScheduledModel, SerializerModel, ManagedModel):
"""Create per-object and global permissions for outpost service-account"""
# To ensure the user only has the correct permissions, we delete all of them and re-add
# the ones the user needs
with transaction.atomic():
user.remove_all_perms_from_managed_role()
for model_or_perm in self.get_required_objects():
if isinstance(model_or_perm, models.Model):
model_or_perm: models.Model
code_name = (
f"{model_or_perm._meta.app_label}.view_{model_or_perm._meta.model_name}"
)
try:
user.assign_perms_to_managed_role(code_name, model_or_perm)
except (Permission.DoesNotExist, AttributeError) as exc:
LOGGER.warning(
"permission doesn't exist",
code_name=code_name,
user=user,
model=model_or_perm,
try:
with transaction.atomic():
user.remove_all_perms_from_managed_role()
for model_or_perm in self.get_required_objects():
if isinstance(model_or_perm, models.Model):
code_name = (
f"{model_or_perm._meta.app_label}.view_{model_or_perm._meta.model_name}"
)
Event.new(
action=EventAction.SYSTEM_EXCEPTION,
message=(
"While setting the permissions for the service-account, a "
"permission was not found: Check "
"https://docs.goauthentik.io/troubleshooting/missing_permission"
),
).with_exception(exc).set_user(user).save()
else:
app_label, perm = model_or_perm.split(".")
permission = Permission.objects.filter(
codename=perm,
content_type__app_label=app_label,
)
if not permission.exists():
LOGGER.warning("permission doesn't exist", perm=model_or_perm)
continue
user.assign_perms_to_managed_role(permission.first())
user.assign_perms_to_managed_role(code_name, model_or_perm)
elif isinstance(model_or_perm, tuple):
perm, obj = model_or_perm
user.assign_perms_to_managed_role(perm, obj)
else:
user.assign_perms_to_managed_role(model_or_perm)
except (Permission.DoesNotExist, AttributeError) as exc:
LOGGER.warning(
"permission doesn't exist",
code_name=code_name,
user=user,
model=model_or_perm,
)
Event.new(
action=EventAction.SYSTEM_EXCEPTION,
message=(
"While setting the permissions for the service-account, a "
"permission was not found: Check "
"https://docs.goauthentik.io/troubleshooting/missing_permission"
),
).with_exception(exc).set_user(user).save()
LOGGER.debug(
"Updated service account's permissions",
obj_perms=user.get_all_obj_perms_on_managed_role(),
@@ -431,7 +425,7 @@ class Outpost(ScheduledModel, SerializerModel, ManagedModel):
Token.objects.filter(identifier=self.token_identifier).delete()
return self.token
def get_required_objects(self) -> Iterable[models.Model | str]:
def get_required_objects(self) -> Iterable[models.Model | str | tuple[str, models.Model]]:
"""Get an iterator of all objects the user needs read access to"""
objects: list[models.Model | str] = [
self,
@@ -445,7 +439,9 @@ class Outpost(ScheduledModel, SerializerModel, ManagedModel):
if self.managed:
for brand in Brand.objects.filter(web_certificate__isnull=False):
objects.append(brand)
objects.append(brand.web_certificate)
objects.append(("view_certificatekeypair", brand.web_certificate))
objects.append(("view_certificatekeypair_certificate", brand.web_certificate))
objects.append(("view_certificatekeypair_key", brand.web_certificate))
return objects
def __str__(self) -> str:

View File

@@ -51,10 +51,12 @@ class OutpostTests(TestCase):
permissions = outpost.user.get_all_obj_perms_on_managed_role().order_by(
"content_type__model"
)
self.assertEqual(len(permissions), 3)
self.assertEqual(len(permissions), 5)
self.assertEqual(permissions[0].object_pk, str(keypair.pk))
self.assertEqual(permissions[1].object_pk, str(outpost.pk))
self.assertEqual(permissions[2].object_pk, str(provider.pk))
self.assertEqual(permissions[1].object_pk, str(keypair.pk))
self.assertEqual(permissions[2].object_pk, str(keypair.pk))
self.assertEqual(permissions[3].object_pk, str(outpost.pk))
self.assertEqual(permissions[4].object_pk, str(provider.pk))
# Remove provider from outpost, user should only have access to outpost
outpost.providers.remove(provider)

View File

@@ -93,11 +93,13 @@ class LDAPProvider(OutpostModel, BackchannelProvider):
def __str__(self):
return f"LDAP Provider {self.name}"
def get_required_objects(self) -> Iterable[models.Model | str]:
required_models = [self, "authentik_core.view_user", "authentik_core.view_group"]
def get_required_objects(self) -> Iterable[models.Model | str | tuple[str, models.Model]]:
required = [self, "authentik_core.view_user", "authentik_core.view_group"]
if self.certificate is not None:
required_models.append(self.certificate)
return required_models
required.append(("view_certificatekeypair", self.certificate))
required.append(("view_certificatekeypair_certificate", self.certificate))
required.append(("view_certificatekeypair_key", self.certificate))
return required
class Meta:
verbose_name = _("LDAP Provider")

View File

@@ -179,11 +179,13 @@ class ProxyProvider(OutpostModel, OAuth2Provider):
def __str__(self):
return f"Proxy Provider {self.name}"
def get_required_objects(self) -> Iterable[models.Model | str]:
required_models = [self]
def get_required_objects(self) -> Iterable[models.Model | str | tuple[str, models.Model]]:
required = [self]
if self.certificate is not None:
required_models.append(self.certificate)
return required_models
required.append(("view_certificatekeypair", self.certificate))
required.append(("view_certificatekeypair_certificate", self.certificate))
required.append(("view_certificatekeypair_key", self.certificate))
return required
class Meta:
verbose_name = _("Proxy Provider")

View File

@@ -1,10 +1,14 @@
"""proxy provider tests"""
from json import loads
from django.urls import reverse
from rest_framework.test import APITestCase
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
from authentik.core.models import Application
from authentik.core.tests.utils import create_test_admin_user, create_test_cert, create_test_flow
from authentik.lib.generators import generate_id
from authentik.outposts.models import Outpost, OutpostType
from authentik.providers.oauth2.models import ClientTypes
from authentik.providers.proxy.models import ProxyMode, ProxyProvider
@@ -127,3 +131,55 @@ class ProxyProviderTests(APITestCase):
self.assertEqual(response.status_code, 200)
provider: ProxyProvider = ProxyProvider.objects.get(name=name)
self.assertEqual(provider.client_type, ClientTypes.CONFIDENTIAL)
def test_sa_fetch(self):
"""Test fetching the outpost config as the service account"""
outpost = Outpost.objects.create(name=generate_id(), type=OutpostType.PROXY)
provider = ProxyProvider.objects.create(name=generate_id())
Application.objects.create(name=generate_id(), slug=generate_id(), provider=provider)
outpost.providers.add(provider)
res = self.client.get(
reverse("authentik_api:proxyprovideroutpost-list"),
HTTP_AUTHORIZATION=f"Bearer {outpost.token.key}",
)
body = loads(res.content)
self.assertEqual(body["pagination"]["count"], 1)
def test_sa_perms_cert(self):
"""Test permissions to access a configured certificate"""
cert = create_test_cert()
outpost = Outpost.objects.create(name=generate_id(), type=OutpostType.PROXY)
provider = ProxyProvider.objects.create(name=generate_id(), certificate=cert)
Application.objects.create(name=generate_id(), slug=generate_id(), provider=provider)
outpost.providers.add(provider)
res = self.client.get(
reverse("authentik_api:proxyprovideroutpost-list"),
HTTP_AUTHORIZATION=f"Bearer {outpost.token.key}",
)
body = loads(res.content)
self.assertEqual(body["pagination"]["count"], 1)
cert_id = body["results"][0]["certificate"]
self.assertEqual(cert_id, str(cert.pk))
res = self.client.get(
reverse(
"authentik_api:certificatekeypair-view-certificate",
kwargs={
"pk": cert_id,
},
),
HTTP_AUTHORIZATION=f"Bearer {outpost.token.key}",
)
self.assertEqual(res.status_code, 200)
# res = self.client.get(
# reverse(
# "authentik_api:certificatekeypair-view-private-key",
# kwargs={
# "pk": cert_id,
# },
# ),
# HTTP_AUTHORIZATION=f"Bearer {outpost.token.key}",
# )
# self.assertEqual(res.status_code, 200)

View File

@@ -64,10 +64,12 @@ class RadiusProvider(OutpostModel, Provider):
return RadiusProviderSerializer
def get_required_objects(self) -> Iterable[models.Model | str]:
def get_required_objects(self) -> Iterable[models.Model | str | tuple[str, models.Model]]:
required = [self, "authentik_stages_mtls.pass_outpost_certificate"]
if self.certificate is not None:
required.append(self.certificate)
required.append(("view_certificatekeypair", self.certificate))
required.append(("view_certificatekeypair_certificate", self.certificate))
required.append(("view_certificatekeypair_key", self.certificate))
return required
def __str__(self):

View File

@@ -6,7 +6,7 @@ from django.http.request import QueryDict
from django.template.exceptions import TemplateSyntaxError
from django.utils.translation import gettext_lazy as _
from rest_framework.exceptions import ValidationError
from rest_framework.fields import BooleanField, CharField, IntegerField
from rest_framework.fields import BooleanField, CharField
from authentik.events.models import Event, EventAction
from authentik.flows.challenge import (
@@ -47,7 +47,7 @@ class AuthenticatorEmailChallengeResponse(ChallengeResponse):
device: EmailDevice
code = IntegerField(required=False)
code = CharField(required=False)
email = CharField(required=False)
component = CharField(default="ak-stage-authenticator-email")

View File

@@ -5,7 +5,7 @@ from django.http import HttpRequest, HttpResponse
from django.http.request import QueryDict
from django.utils.translation import gettext_lazy as _
from rest_framework.exceptions import ValidationError
from rest_framework.fields import BooleanField, CharField, IntegerField
from rest_framework.fields import BooleanField, CharField
from authentik.flows.challenge import (
Challenge,
@@ -38,7 +38,7 @@ class AuthenticatorSMSChallengeResponse(ChallengeResponse):
device: SMSDevice
code = IntegerField(required=False)
code = CharField(required=False)
phone_number = CharField(required=False)
component = CharField(default="ak-stage-authenticator-sms")

View File

@@ -5,7 +5,7 @@ from urllib.parse import quote
from django.http import HttpRequest, HttpResponse
from django.http.request import QueryDict
from django.utils.translation import gettext_lazy as _
from rest_framework.fields import CharField, IntegerField
from rest_framework.fields import CharField
from rest_framework.serializers import ValidationError
from authentik.flows.challenge import (
@@ -32,10 +32,10 @@ class AuthenticatorTOTPChallengeResponse(ChallengeResponse):
device: TOTPDevice
code = IntegerField()
code = CharField()
component = CharField(default="ak-stage-authenticator-totp")
def validate_code(self, code: int) -> int:
def validate_code(self, code: str) -> str:
"""Validate totp code"""
if not self.device:
raise ValidationError(_("Code does not match"))

View File

@@ -1,10 +1,9 @@
"""Identification stage logic"""
from dataclasses import asdict
from random import SystemRandom
from time import sleep
from typing import Any
from django.contrib.auth.hashers import make_password
from django.core.exceptions import PermissionDenied
from django.db.models import Q
from django.http import HttpResponse
@@ -161,8 +160,8 @@ class IdentificationChallengeResponse(ChallengeResponse):
op="authentik.stages.identification.validate_invalid_wait",
name="Sleep random time on invalid user identifier",
):
# Sleep a random time (between 90 and 210ms) to "prevent" user enumeration attacks
sleep(0.030 * SystemRandom().randint(3, 7))
# hash a random password on invalid identifier, same as with a valid identifier
make_password(make_password(None))
# Log in a similar format to Event.new(), but we don't want to create an event here
# as this stage is mostly used by unauthenticated users with very high rate limits
self.stage.logger.info(

View File

@@ -245,7 +245,10 @@ class WorkerStatusMiddleware(Middleware):
WorkerStatusMiddleware.keep(status)
except DB_ERRORS: # pragma: no cover
sleep(10)
pass
try:
connections.close_all()
except DB_ERRORS:
pass
@staticmethod
def keep(status: WorkerStatus):

View File

@@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://goauthentik.io/blueprints/schema.json",
"type": "object",
"title": "authentik 2025.12.0-rc1 Blueprint schema",
"title": "authentik 2025.12.0-rc2 Blueprint schema",
"required": [
"version",
"entries"
@@ -14510,7 +14510,8 @@
"description": "Show the user the 'Remember me on this device' toggle, allowing repeat users to skip straight to entering their password."
},
"webauthn_stage": {
"type": "integer",
"type": "string",
"format": "uuid",
"title": "Webauthn stage",
"description": "When set, and conditional WebAuthn is available, allow the user to use their passkey as a first factor."
}

View File

@@ -31,13 +31,13 @@ services:
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.12.0-rc1}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.12.0-rc2}
ports:
- ${COMPOSE_PORT_HTTP:-9000}:9000
- ${COMPOSE_PORT_HTTPS:-9443}:9443
restart: unless-stopped
volumes:
- ./media:/data/media
- ./data:/data
- ./custom-templates:/templates
worker:
command: worker
@@ -52,12 +52,12 @@ services:
AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:?secret key required}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.12.0-rc1}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.12.0-rc2}
restart: unless-stopped
user: root
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./media:/data/media
- ./data:/data
- ./certs:/certs
- ./custom-templates:/templates
volumes:

View File

@@ -1 +1 @@
2025.12.0-rc1
2025.12.0-rc2

View File

@@ -18,7 +18,7 @@ Parameters:
Description: authentik Docker image
AuthentikVersion:
Type: String
Default: 2025.12.0-rc1
Default: 2025.12.0-rc2
Description: authentik Docker image tag
AuthentikServerCPU:
Type: Number

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@goauthentik/authentik",
"version": "2025.12.0-rc1",
"version": "2025.12.0-rc2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@goauthentik/authentik",
"version": "2025.12.0-rc1",
"version": "2025.12.0-rc2",
"dependencies": {
"@eslint/js": "^9.39.1",
"@goauthentik/eslint-config": "./packages/eslint-config",

View File

@@ -1,6 +1,6 @@
{
"name": "@goauthentik/authentik",
"version": "2025.12.0-rc1",
"version": "2025.12.0-rc2",
"private": true,
"type": "module",
"dependencies": {

View File

@@ -1,27 +1,18 @@
"""Convenient shortcuts to manage or check object permissions."""
from functools import lru_cache, partial
from functools import lru_cache
from typing import Any, TypeVar
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.db import connection
from django.db.models import (
AutoField,
BigIntegerField,
CharField,
Count,
ForeignKey,
IntegerField,
Model,
PositiveIntegerField,
PositiveSmallIntegerField,
QuerySet,
SmallIntegerField,
UUIDField,
)
from django.db.models.expressions import Value
from django.db.models.functions import Cast, Replace
from django.db.models.expressions import RawSQL
from guardian.core import ObjectPermissionChecker
from guardian.ctypes import get_content_type
@@ -295,42 +286,33 @@ def get_objects_for_user( # noqa: PLR0912 PLR0915
.filter(object_pk_count__gte=len(codenames))
)
# object_pk is a varchar, while the queryset's pk is probably an integer or a uuid, so we cast
handle_pk_field = _handle_pk_field(queryset)
if handle_pk_field is not None:
perms_queryset = perms_queryset.annotate(obj_pk=handle_pk_field(expression=pk_field))
pk_field = "obj_pk"
return queryset.filter(pk__in=perms_queryset.values_list(pk_field, flat=True))
def _handle_pk_field(queryset):
# pk is either UUID or an integer type, while object_pk is a varchar
pk = queryset.model._meta.pk
if isinstance(pk, ForeignKey):
return _handle_pk_field(pk.target_field)
def _cast_type(pk):
if isinstance(pk, ForeignKey):
return _cast_type(pk.target_field)
if isinstance(pk, UUIDField):
return "uuid"
return "bigint"
if isinstance( # noqa: UP038
pk,
(
IntegerField,
AutoField,
BigIntegerField,
PositiveIntegerField,
PositiveSmallIntegerField,
SmallIntegerField,
),
):
return partial(Cast, output_field=BigIntegerField())
cast_type = _cast_type(pk)
if isinstance(pk, UUIDField):
if connection.features.has_native_uuid_field:
return partial(Cast, output_field=UUIDField())
return partial(
Replace,
text=Value("-"),
replacement=Value(""),
output_field=CharField(),
)
return None
perms_queryset = perms_queryset.values_list(pk_field, flat=True)
# The raw subquery is done to ensure that casting only takes place after the WHERE clause of
# `perms_queryset` is ran. Otherwise, the query planner may decide to cast every `object_pk`,
# which breaks (for example) if it tries to cast an integer to a UUID. In such a case, the WHERE
# of `perms_queryset` will remove any integer.
# However, the subquery might get optimized out by the query planner, which would cause the same
# cast issue as before. To prevent the subquery from being collapsed in the query below, we add
# OFFSET 0.
perms_subquery_sql, perms_subquery_params = perms_queryset.query.sql_with_params()
subquery = RawSQL(
f"""
SELECT ("permission_subquery"."{pk_field}")::{cast_type} as "object_pk"
FROM ({perms_subquery_sql}) "permission_subquery"
OFFSET 0
""", # nosec
perms_subquery_params,
)
return queryset.filter(pk__in=subquery)

View File

@@ -529,3 +529,7 @@ class _PostgresConsumer(Consumer):
conn.close()
except DATABASE_ERRORS:
pass
try:
connections.close_all()
except DATABASE_ERRORS:
pass

View File

@@ -1,6 +1,6 @@
[project]
name = "authentik"
version = "2025.12.0-rc1"
version = "2025.12.0-rc2"
description = ""
authors = [{ name = "authentik Team", email = "hello@goauthentik.io" }]
requires-python = "==3.13.*"

View File

@@ -1,7 +1,7 @@
openapi: 3.0.3
info:
title: authentik
version: 2025.12.0-rc1
version: 2025.12.0-rc2
description: Making authentication simple.
contact:
email: hello@goauthentik.io
@@ -33586,7 +33586,8 @@ components:
minLength: 1
default: ak-stage-authenticator-email
code:
type: integer
type: string
minLength: 1
email:
type: string
minLength: 1
@@ -33833,7 +33834,8 @@ components:
minLength: 1
default: ak-stage-authenticator-sms
code:
type: integer
type: string
minLength: 1
phone_number:
type: string
minLength: 1
@@ -34107,7 +34109,8 @@ components:
minLength: 1
default: ak-stage-authenticator-totp
code:
type: integer
type: string
minLength: 1
required:
- code
AuthenticatorTOTPStage:

6
scripts/generate_docker_compose.py Normal file → Executable file
View File

@@ -1,3 +1,5 @@
#!/usr/bin/env python3
from yaml import safe_dump
from authentik import authentik_version
@@ -42,7 +44,7 @@ base = {
"image": authentik_image,
"ports": ["${COMPOSE_PORT_HTTP:-9000}:9000", "${COMPOSE_PORT_HTTPS:-9443}:9443"],
"restart": "unless-stopped",
"volumes": ["./media:/data/media", "./custom-templates:/templates"],
"volumes": ["./data:/data", "./custom-templates:/templates"],
},
"worker": {
"command": "worker",
@@ -62,7 +64,7 @@ base = {
"user": "root",
"volumes": [
"/var/run/docker.sock:/var/run/docker.sock",
"./media:/data/media",
"./data:/data",
"./certs:/certs",
"./custom-templates:/templates",
],

View File

@@ -23,8 +23,10 @@ from docker.models.containers import Container
from docker.models.networks import Network
from selenium import webdriver
from selenium.common.exceptions import (
DetachedShadowRootException,
NoSuchElementException,
NoSuchShadowRootException,
StaleElementReferenceException,
TimeoutException,
WebDriverException,
)
@@ -247,36 +249,60 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
Raises a clear test failure if the element isn't found, the text doesn't appear
within `timeout` seconds, or the text is not valid JSON.
"""
use_body = context is None
wait_timeout = timeout or self.wait_timeout
def get_context() -> WebElement:
"""Get or refresh the context element."""
if use_body:
return self.driver.find_element(By.TAG_NAME, "body")
return context
def get_text_safely() -> str:
"""Get element text, re-finding element if stale."""
for _ in range(5):
try:
return get_context().text.strip()
except StaleElementReferenceException:
sleep(0.5)
return get_context().text.strip()
def get_inner_html_safely() -> str:
"""Get innerHTML, re-finding element if stale."""
for _ in range(5):
try:
return get_context().get_attribute("innerHTML") or ""
except StaleElementReferenceException:
sleep(0.5)
return get_context().get_attribute("innerHTML") or ""
try:
if context is None:
context = self.driver.find_element(By.TAG_NAME, "body")
get_context()
except NoSuchElementException:
self.fail(
f"No element found (defaulted to <body>). Current URL: {self.driver.current_url}"
)
wait_timeout = timeout or self.wait_timeout
wait = WebDriverWait(context, wait_timeout)
wait = WebDriverWait(self.driver, wait_timeout)
try:
wait.until(lambda d: len(d.text.strip()) != 0)
wait.until(lambda d: len(get_text_safely()) != 0)
except TimeoutException:
snippet = context.text.strip()[:500].replace("\n", " ")
snippet = get_text_safely()[:500].replace("\n", " ")
self.fail(
f"Timed out waiting for element text to appear at {self.driver.current_url}. "
f"Current content: {snippet or '<empty>'}"
)
body_text = context.text.strip()
inner_html = context.get_attribute("innerHTML") or ""
body_text = get_text_safely()
inner_html = get_inner_html_safely()
if "redirecting" in inner_html.lower():
try:
wait.until(lambda d: "redirecting" not in d.get_attribute("innerHTML").lower())
wait.until(lambda d: "redirecting" not in get_inner_html_safely().lower())
except TimeoutException:
snippet = context.text.strip()[:500].replace("\n", " ")
inner_html = context.get_attribute("innerHTML") or ""
snippet = get_text_safely()[:500].replace("\n", " ")
inner_html = get_inner_html_safely()
self.fail(
f"Timed out waiting for redirect to finish at {self.driver.current_url}. "
@@ -284,8 +310,8 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
f"{inner_html or '<empty>'}"
)
inner_html = context.get_attribute("innerHTML") or ""
body_text = context.text.strip()
inner_html = get_inner_html_safely()
body_text = get_text_safely()
snippet = body_text[:500].replace("\n", " ")
@@ -326,18 +352,23 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
while attempts < SHADOW_ROOT_RETRIES:
try:
host = container.find_element(By.CSS_SELECTOR, selector)
return host.shadow_root
except NoSuchShadowRootException:
except (
NoSuchElementException,
NoSuchShadowRootException,
DetachedShadowRootException,
StaleElementReferenceException,
):
attempts += 1
sleep(0.2)
# re-find host in case it was re-attached
try:
host = container.find_element(By.CSS_SELECTOR, selector)
except NoSuchElementException:
# loop and retry finding host
pass
inner_html = host.get_attribute("innerHTML") or "<no host>"
inner_html = "<no host>"
if host is not None:
try:
inner_html = host.get_attribute("innerHTML") or "<no host>"
except (DetachedShadowRootException, StaleElementReferenceException):
inner_html = "<stale host>"
raise RuntimeError(
f"Failed to obtain shadow root for {selector} after {attempts} attempts. "

2
uv.lock generated
View File

@@ -185,7 +185,7 @@ wheels = [
[[package]]
name = "authentik"
version = "2025.12.0rc1"
version = "2025.12.0rc2"
source = { editable = "." }
dependencies = [
{ name = "ak-guardian" },

4
web/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@goauthentik/web",
"version": "2025.12.0-rc1",
"version": "2025.12.0-rc2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@goauthentik/web",
"version": "2025.12.0-rc1",
"version": "2025.12.0-rc2",
"license": "MIT",
"workspaces": [
"./packages/*"

View File

@@ -1,6 +1,6 @@
{
"name": "@goauthentik/web",
"version": "2025.12.0-rc1",
"version": "2025.12.0-rc2",
"license": "MIT",
"private": true,
"scripts": {

View File

@@ -99,6 +99,9 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
const alertMsg = msg(
"Using this form will only create an Application. In order to authenticate with the application, you will have to manually pair it with a Provider.",
);
const providerFromInstance = this.instance?.provider;
const providerValue = providerFromInstance ?? this.provider;
const providerPrefilled = !this.instance && this.provider !== undefined;
return html`
${this.instance ? nothing : html`<ak-alert level="pf-m-info">${alertMsg}</ak-alert>`}
@@ -134,9 +137,10 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
<ak-provider-search-input
name="provider"
label=${msg("Provider")}
value=${ifPresent(this.instance?.provider)}
.value=${providerValue}
.readOnly=${providerPrefilled}
?blankable=${!providerPrefilled}
help=${msg("Select a provider that this application should use.")}
blankable
></ak-provider-search-input>
<ak-backchannel-providers-input
name="backchannelProviders"

View File

@@ -13,6 +13,7 @@ import { Provider, ProvidersAllListRequest, ProvidersApi } from "@goauthentik/ap
import { html, nothing } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
const renderElement = (item: Provider) => item.name;
const renderValue = (item: Provider | undefined) => item?.pk;
@@ -53,6 +54,9 @@ export class AkProviderInput extends AKElement {
@property({ type: Number })
value?: number;
@property({ type: Boolean, attribute: "readonly" })
readOnly = false;
@property({ type: Boolean })
required = false;
@@ -76,6 +80,8 @@ export class AkProviderInput extends AKElement {
};
render() {
const readOnlyValue = this.readOnly && typeof this.value === "number";
return html` <ak-form-element-horizontal name=${this.name}>
${AKLabel(
{
@@ -86,7 +92,9 @@ export class AkProviderInput extends AKElement {
},
this.label,
)}
${readOnlyValue
? html`<input type="hidden" name=${this.name} value=${this.value ?? ""} />`
: nothing}
<ak-search-select
.fieldID=${this.fieldID}
.selected=${this.#selected}
@@ -94,7 +102,9 @@ export class AkProviderInput extends AKElement {
.renderElement=${renderElement}
.value=${renderValue}
.groupBy=${doGroupBy}
?blankable=${!!this.blankable}
?blankable=${readOnlyValue ? false : !!this.blankable}
?readonly=${this.readOnly}
name=${ifDefined(readOnlyValue ? undefined : this.name)}
>
</ak-search-select>
${this.help ? html`<p class="pf-c-form__helper-text">${this.help}</p>` : nothing}

View File

@@ -77,7 +77,11 @@ export class AgentConnectorSetup extends AKElement {
<p>${msg("Afterwards, select the enrollment token you want to use:")}</p>
</div>
<div class="pf-l-grid__item pf-m-12-col">
<p>${msg("Then download the configuration to deploy the authentik Agent")}</p>
<p>
${msg(
"Next, download the configuration to deploy the authentik Agent via MDM",
)}
</p>
</div>
</div>
<div class="pf-l-grid__item pf-m-6-col pf-l-grid">

View File

@@ -77,10 +77,13 @@ export class EnrollmentTokenForm extends WithBrandConfig(ModelForm<EnrollmentTok
value=${ifDefined(this.instance?.name)}
required
></ak-text-input>
<ak-form-element-horizontal label=${msg("Device Group")} name="deviceGroup">
<ak-form-element-horizontal label=${msg("Device Access Group")} name="deviceGroup">
<ak-endpoints-device-group-search
.group=${this.instance?.deviceGroup}
></ak-endpoints-device-group-search>
<p class="pf-c-form__helper-text">
${msg("Select a device access group to be added to upon enrollment.")}
</p>
</ak-form-element-horizontal>
<ak-form-element-horizontal name="expiring">
<label class="pf-c-switch">

View File

@@ -68,7 +68,7 @@ export class DeviceViewPage extends AKElement {
? msg(str`Device ${this.device?.name}`)
: msg("Loading device..."),
description: this.device?.facts.data.os
? this.device?.facts.data.os?.name + " " + this.device?.facts.data.os?.version
? `${this.device?.facts.data.os?.name} ${this.device?.facts.data.os?.version}`
: undefined,
icon: "fa fa-laptop",
});
@@ -110,7 +110,7 @@ export class DeviceViewPage extends AKElement {
?good=${this.device.facts.data.network?.firewallEnabled}
></ak-status-label>`,
],
[msg("Group"), this.device.accessGroupObj?.name ?? "-"],
[msg("Device access group"), this.device.accessGroupObj?.name ?? "-"],
[
msg("Actions"),
html`<ak-forms-modal>
@@ -162,13 +162,13 @@ export class DeviceViewPage extends AKElement {
></ak-status-label>`,
],
[
msg("Disk size"),
msg("Primary disk size"),
rootDisk?.capacityTotalBytes
? getSize(rootDisk.capacityTotalBytes)
: "-",
],
[
msg("Disk usage"),
msg("Primary disk usage"),
rootDisk?.capacityTotalBytes && rootDisk.capacityUsedBytes
? html`<progress
value="${rootDisk.capacityUsedBytes}"

View File

@@ -1,3 +1,5 @@
import "#elements/ak-progress-bar";
import { AKElement } from "#elements/Base";
import { PFColor } from "#elements/Label";
@@ -9,7 +11,6 @@ import { customElement, state } from "lit/decorators.js";
import PFCard from "@patternfly/patternfly/components/Card/card.css";
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
import PFProgress from "@patternfly/patternfly/components/Progress/progress.css";
import PFSplit from "@patternfly/patternfly/layouts/Split/split.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
@@ -21,7 +22,7 @@ export class EnterpriseStatusCard extends AKElement {
@state()
summary?: LicenseSummary;
static styles: CSSResult[] = [PFBase, PFDescriptionList, PFCard, PFSplit, PFProgress];
static styles: CSSResult[] = [PFBase, PFDescriptionList, PFCard, PFSplit];
renderSummaryBadge() {
switch (this.summary?.status) {
@@ -81,66 +82,32 @@ export class EnterpriseStatusCard extends AKElement {
</div>
</dl>
<div class="pf-l-split__item pf-m-fill">
<div
class="pf-c-progress ${internalUserPercentage > 100
<ak-progress-bar
class="${internalUserPercentage > 100
? "pf-m-danger"
: ""} ${internalUserPercentage >= 80 ? "pf-m-warning" : ""}"
id="internalUsers"
value=${internalUserPercentage}
>
<div class="pf-c-progress__description">
${msg("Internal user usage")}
</div>
<div class="pf-c-progress__status" aria-hidden="true">
<span class="pf-c-progress__measure"
>${msg(
str`${internalUserPercentage < Infinity ? internalUserPercentage : "∞"}%`,
)}</span
>
</div>
<div
class="pf-c-progress__bar"
role="progressbar"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="${internalUserPercentage}"
>
<div
class="pf-c-progress__indicator"
style="width:${Math.min(internalUserPercentage, 100)}%;"
></div>
</div>
</div>
<div
class="pf-c-progress ${externalUserPercentage > 100
<span slot="description">${msg("Internal user usage")}</span>
<span slot="status">
${msg(
str`${internalUserPercentage < Infinity ? internalUserPercentage : "∞"}%`,
)}
</span>
</ak-progress-bar>
<ak-progress-bar
class="${externalUserPercentage > 100
? "pf-m-danger"
: ""} ${externalUserPercentage >= 80 ? "pf-m-warning" : ""}"
id="externalUsers"
value=${externalUserPercentage}
>
<div class="pf-c-progress__description">
${msg("External user usage")}
</div>
<div class="pf-c-progress__status" aria-hidden="true">
<span class="pf-c-progress__measure"
>${msg(
str`${externalUserPercentage < Infinity ? externalUserPercentage : "∞"}%`,
)}</span
>
</div>
<div
class="pf-c-progress__bar"
role="progressbar"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="${externalUserPercentage < Infinity
? externalUserPercentage
: "∞"}"
>
<div
class="pf-c-progress__indicator"
style="width:${Math.min(externalUserPercentage, 100)}%;"
></div>
</div>
</div>
<span slot="description">${msg("External user usage")}</span>
<span slot="status">
${msg(
str`${externalUserPercentage < Infinity ? externalUserPercentage : "∞"}%`,
)}
</span>
</ak-progress-bar>
</div>
</div>
</div>

View File

@@ -91,6 +91,20 @@ export class DataExportListPage extends TablePage<DataExport> {
</div>
</dl>`;
}
protected renderEmpty(_inner?: TemplateResult): TemplateResult {
return super.renderEmpty(
html`<ak-empty-state icon=${this.pageIcon}
><span
>${msg(
html`To create a data export, navigate to
<a href="#/identity/users">Directory > Users</a> or to
<a href="#/events/log">Events > Logs</a>.`,
)}</span
>
</ak-empty-state>`,
);
}
}
declare global {

View File

@@ -16,18 +16,33 @@ import { createRef, ref } from "lit/directives/ref.js";
// Same regex is used in the backend as well
const VALID_FILE_NAME_PATTERN = /^[a-zA-Z0-9._/-]+$/;
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/source
// This is perfect for the "pattern" attribute
const VALID_FILE_NAME_PATTERN_STRING = VALID_FILE_NAME_PATTERN.source;
// Note: browsers compile `pattern` using the new `v` RegExp flag (Unicode sets). Under `/v`,
// both `/` and `-` must be escaped inside character classes.
const VALID_FILE_NAME_PATTERN_STRING = "^[a-zA-Z0-9._\\/\\-]+$";
function assertValidFileName(fileName: string): void {
if (!VALID_FILE_NAME_PATTERN.test(fileName)) {
throw new Error(
msg("Filename can only contain letters, numbers, dots, hyphens, and underscores"),
msg(
"Filename can only contain letters, numbers, dots, hyphens, underscores, and slashes",
),
);
}
}
function getFileExtension(fileName: string): string {
const lastDot = fileName.lastIndexOf(".");
if (lastDot <= 0) return "";
return fileName.slice(lastDot);
}
function hasBasenameExtension(fileName: string): boolean {
const baseName = fileName.split("/").pop() ?? fileName;
const lastDot = baseName.lastIndexOf(".");
return lastDot > 0;
}
@customElement("ak-file-upload-form")
export class FileUploadForm extends Form<Record<string, unknown>> {
@property({ type: String, useDefault: true })
@@ -57,36 +72,36 @@ export class FileUploadForm extends Form<Record<string, unknown>> {
throw new PreventFormSubmit("Selected file not provided", this);
}
assertValidFileName(this.selectedFile.name);
const api = new AdminApi(DEFAULT_CONFIG);
const customName = typeof data.fileName === "string" ? data.fileName.trim() : "";
const customName = typeof data.name === "string" ? data.name.trim() : "";
// If custom name provided, validate and append original extension
// Only validate the original filename if no custom name is provided
let finalName = this.selectedFile.name;
if (customName) {
assertValidFileName(customName);
const ext = this.selectedFile.name.substring(this.selectedFile.name.lastIndexOf("."));
finalName = customName + ext;
const ext = getFileExtension(this.selectedFile.name);
finalName =
ext && !hasBasenameExtension(customName) ? `${customName}${ext}` : customName;
} else {
assertValidFileName(this.selectedFile.name);
}
return api
.adminFileCreate({
file: this.selectedFile,
name: finalName,
usage: this.usage,
})
.then(() => {
showMessage({
level: MessageLevel.success,
message: msg("File uploaded successfully"),
});
assertValidFileName(finalName);
this.reset();
})
.finally(() => {
this.clearFileInput();
});
await api.adminFileCreate({
file: this.selectedFile,
name: finalName,
usage: this.usage,
});
showMessage({
level: MessageLevel.success,
message: msg("File uploaded successfully"),
});
this.reset();
this.clearFileInput();
}
renderForm() {
@@ -101,7 +116,7 @@ export class FileUploadForm extends Form<Record<string, unknown>> {
@change=${this.#fileChangeListener}
/>
</ak-form-element-horizontal>
<ak-form-element-horizontal label=${msg("File Name")} name="fileName">
<ak-form-element-horizontal label=${msg("File Name")} name="name">
<input
type="text"
class="pf-c-form-control"

View File

@@ -95,7 +95,7 @@ export class RoleAssignedObjectPermissionTable extends Table<RoleAssignedObjectP
>
</ak-rbac-role-object-permission-form>
<button slot="trigger" class="pf-c-button pf-m-primary">
${msg("Assign role permissions")}
${msg("Assign Object Permission")}
</button>
</ak-forms-modal>`;
}
@@ -135,9 +135,9 @@ export class RoleAssignedObjectPermissionTable extends Table<RoleAssignedObjectP
const assignedToModel = item.modelPermissions.some(
(uperm) => uperm.codename === perm.codename,
);
const assignedToObject = item.objectPermissions.some(
(uperm) => uperm.codename === perm.codename,
);
const assignedToObject = item.objectPermissions
.filter((uperm) => uperm.objectPk === this.objectPk)
.some((uperm) => uperm.codename === perm.codename);
let tooltip: string | null = null;
if (assignedToModel && assignedToObject) {

View File

@@ -51,6 +51,11 @@ export class NavigationButtons extends WithSession(AKElement) {
Styles,
];
connectedCallback(): void {
super.connectedCallback();
this.refreshNotifications();
}
protected async refreshNotifications(): Promise<void> {
const { currentUser } = this;

View File

@@ -0,0 +1,99 @@
import { PFSize } from "#common/enums";
import { AKElement } from "#elements/Base";
import { spread } from "@open-wc/lit-helpers";
import { css, html, nothing } from "lit";
import { customElement, property } from "lit/decorators.js";
import PFProgress from "@patternfly/patternfly/components/Progress/progress.css";
/**
* @slot description - Description text above the progress bar, on the left
* @slot status - Human-readable status above the progress bar, on the right
*/
@customElement("ak-progress-bar")
export class ProgressBar extends AKElement {
static styles = [
PFProgress,
css`
.pf-c-progress {
overflow: hidden;
}
.pf-c-progress.pf-m-indeterminate {
--pf-c-progress__bar--Height: 2px;
--pf-c-progress--GridGap: 0;
margin-bottom: calc(var(--pf-c-progress__bar--Height) * -1);
z-index: 1;
position: relative;
}
.pf-c-progress.pf-m-indeterminate .pf-c-progress__bar .pf-c-progress__indicator {
width: 100%;
height: 100%;
animation: indeterminateAnimation 1s infinite linear;
transform-origin: 0% 50%;
}
@keyframes indeterminateAnimation {
0% {
transform: translateX(0) scaleX(0);
}
40% {
transform: translateX(0) scaleX(0.4);
}
100% {
transform: translateX(100%) scaleX(0.5);
}
}
`,
];
@property({ type: Number })
min = 0;
@property({ type: Number })
max = 100;
@property({ type: Number })
value = 0;
@property({ type: Boolean })
indeterminate = false;
@property()
size: PFSize = PFSize.Medium;
render() {
const barAttrs: { [key: string]: unknown } = {};
const indicatorAttrs: { [key: string]: unknown } = {};
if (!this.indeterminate) {
barAttrs["aria-valuemin"] = this.min;
barAttrs["aria-valuemax"] = this.max;
barAttrs["aria-valuenow"] = this.value;
indicatorAttrs.style = `"width:${Math.min(this.value, 100)}%;";`;
}
return html`<div
class="pf-c-progress ${this.classList} ${this.indeterminate
? "pf-m-indeterminate"
: ""} ${this.size.toString()}"
>
${this.hasSlotted("description")
? html`
<div class="pf-c-progress__description">
<slot name="description"></slot>
</div>
`
: nothing}
${this.hasSlotted("status")
? html`
<div class="pf-c-progress__status" aria-hidden="true">
<span class="pf-c-progress__measure">
<slot name="status"></slot>
</span>
</div>
`
: nothing}
<div class="pf-c-progress__bar" role="progressbar" ${spread(barAttrs)}>
<div class="pf-c-progress__indicator" ${spread(indicatorAttrs)}></div>
</div>
</div> `;
}
}

View File

@@ -23,6 +23,7 @@ type Group<T> = [string, T[]];
export interface ISearchSelectBase<T> {
blankable?: boolean;
readOnly?: boolean;
query?: string;
objects?: T[];
selectedObject: T | null;
@@ -93,6 +94,14 @@ export abstract class SearchSelectBase<T>
@property({ type: Boolean })
public creatable?: boolean;
/**
* Prevent user interaction while still rendering the current value.
* @property
* @attr
*/
@property({ type: Boolean, attribute: "readonly" })
public readOnly = false;
/**
* An initial string to filter the search contents,
* and the value of the input which further serves to restrict the search.
@@ -254,6 +263,8 @@ export abstract class SearchSelectBase<T>
}
#searchListener = (event: InputEvent) => {
if (this.readOnly) return;
const value = (event.target as SearchSelectView).rawValue;
if (!value) {
@@ -277,6 +288,8 @@ export abstract class SearchSelectBase<T>
};
private onSelect(event: InputEvent) {
if (this.readOnly) return;
const value = (event.target as SearchSelectView).value;
if (!value) {
@@ -381,6 +394,7 @@ export abstract class SearchSelectBase<T>
.options=${options}
value=${ifPresent(value)}
?blankable=${this.blankable}
?readonly=${this.readOnly}
label=${ifPresent(this.label)}
name=${ifPresent(this.name)}
placeholder=${ifPresent(this.placeholder)}

View File

@@ -24,6 +24,7 @@ export interface ISearchSelectView {
value?: string;
open: boolean;
blankable: boolean;
readOnly: boolean;
caseSensitive: boolean;
name?: string;
placeholder: string;
@@ -126,6 +127,14 @@ export class SearchSelectView extends AKElement implements ISearchSelectView {
@property({ type: Boolean })
public blankable = false;
/**
* Prevents user interaction while showing the current value.
*
* @attr
*/
@property({ type: Boolean, attribute: "readonly" })
public readOnly = false;
/**
* If not managed, make the matcher case-sensitive during interaction. If managed,
* the manager must handle this.
@@ -248,6 +257,8 @@ export class SearchSelectView extends AKElement implements ISearchSelectView {
//#region Event Listeners
#clickListener = (_ev: Event) => {
if (this.readOnly) return;
this.open = !this.open;
this.#inputRef.value?.focus();
};
@@ -263,6 +274,8 @@ export class SearchSelectView extends AKElement implements ISearchSelectView {
}
#searchKeyupListener = (event: KeyboardEvent) => {
if (this.readOnly) return;
if (event.key === "Escape") {
event.stopPropagation();
event.preventDefault();
@@ -277,6 +290,8 @@ export class SearchSelectView extends AKElement implements ISearchSelectView {
};
#searchKeydownListener = (event: KeyboardEvent) => {
if (this.readOnly) return;
if (!this.open) return;
switch (event.key) {
@@ -339,6 +354,8 @@ export class SearchSelectView extends AKElement implements ISearchSelectView {
}
#inputListener = (_ev: InputEvent) => {
if (this.readOnly) return;
if (!this.managed) {
this.findValueForInput();
this.requestUpdate();
@@ -356,6 +373,8 @@ export class SearchSelectView extends AKElement implements ISearchSelectView {
};
#listKeydownListener = (event: KeyboardEvent) => {
if (this.readOnly) return;
if (event.key === "Tab" && event.shiftKey) {
event.preventDefault();
@@ -364,6 +383,8 @@ export class SearchSelectView extends AKElement implements ISearchSelectView {
};
#changeListener = (event: InputEvent) => {
if (this.readOnly) return;
if (!event.target) {
return;
}
@@ -441,6 +462,7 @@ export class SearchSelectView extends AKElement implements ISearchSelectView {
@keyup=${this.#searchKeyupListener}
@keydown=${this.#searchKeydownListener}
value=${this.displayValue}
?readonly=${this.readOnly}
/>
</div>
</div>

View File

@@ -1,3 +1,4 @@
import "#elements/ak-progress-bar";
import "#elements/EmptyState";
import "#elements/buttons/SpinnerButton/index";
import "#elements/chips/Chip";
@@ -866,6 +867,11 @@ export abstract class Table<T extends object>
`;
}
protected renderLoadingBar() {
if (!this.loading) return nothing;
return html`<ak-progress-bar indeterminate></ak-progress-bar>`;
}
protected renderTable(): TemplateResult {
const totalItemCount = this.data?.pagination.count ?? -1;
@@ -877,7 +883,9 @@ export abstract class Table<T extends object>
${this.renderTablePagination()}
</div>`;
return html`${this.needChipGroup ? this.renderChipGroup() : nothing}
return html`${this.renderLoadingBar()}${this.needChipGroup
? this.renderChipGroup()
: nothing}
${this.renderToolbarContainer()}
<div part="table-container">
<table

View File

@@ -41,9 +41,12 @@ export class RedirectStage extends BaseStage<RedirectChallenge, FlowChallengeRes
return new URL(this.challenge.to, document.baseURI).toString();
}
firstUpdated(changed: PropertyValues<this>): void {
super.firstUpdated(changed);
updated(changed: PropertyValues<this>): void {
super.updated(changed);
if (!changed.has("challenge")) {
return;
}
if (this.promptUser) {
document.addEventListener("keydown", (ev) => {
if (ev.key === "Enter") {

View File

@@ -28,7 +28,8 @@ export class AutosubmitStage extends BaseStage<
updated(changed: PropertyValues<this>): void {
super.updated(changed);
if (this.challenge.url !== undefined) {
if (changed.has("challenge") && this.challenge.url !== undefined) {
console.debug("authentik/flow/stages/autosubmit: submitting");
this.form?.submit();
}
}

View File

@@ -36,6 +36,8 @@ Firefox has some known issues regarding TouchID (see https://bugzilla.mozilla.or
Passwordless authentication currently only supports WebAuthn devices, which provides for the use of passkeys, security keys and biometrics. For an alternate passwordless setup, see [Password stage](../password/index.md#passwordless-login), which supports other types.
If you want users to authenticate with a passkey via the browser's built-in passkey/autofill UI on the **Identification** screen ("conditional UI" / passkey autofill), configure it in the [Identification stage](../identification/index.mdx#passkey-autofill-webauthn-conditional-ui). This requires a **discoverable credential (aka resident key)**.
To configure passwordless authentication, create a new Flow with the designation set to _Authentication_.
As first stage, add an _Authenticator validation_ stage, with the WebAuthn device class allowed.

View File

@@ -0,0 +1,18 @@
---
title: Endpoint stage
---
This stage integrates with the [Endpoint Device](../../../../endpoint-devices/index.mdx) functionality and allows authentik to verify whether the device executing a flow is registered.
The Endpoint stage fetches [device facts](../../../../endpoint-devices/device-compliance/device-reporting.md#device-facts) via a configured [connector](../../../../endpoint-devices/device-compliance/connectors.md) and injects them into the flow context. These device facts can be used by other stages and policies to make device compliance decisions.
## Connector
Select the [connector](../../../../endpoint-devices/device-compliance/connectors.md) that the Endpoint stage will use to obtain device facts.
## Mode
Select whether the presence of a registered endpoint device is required for the stage to succeed.
- If the mode is set to required, and device verification fails, the the user is not able to proceed with the flow.
- If the mode is set to optional, authentik will attempt to verify the device, and if it doesn't receive a response within the specified `challenge_idle_timeout`, authentik will continue without attaching a device to the flow.

View File

@@ -26,6 +26,46 @@ The CAPTCHA stage you use must be configured to use the "Invisible" mode, otherw
To run a CAPTCHA process in the background while the user is entering their identification, a CAPTCHA stage can be selected here. If a CAPTCHA stage is selected in the Identification stage, the CAPTCHA stage should not be bound to the flow.
## Passkey autofill (WebAuthn conditional UI):ak-version[2025.12]
When configured, the Identification stage can offer passkey login directly from the browser's passkey/autofill UI (also known as "conditional UI"). This allows a user to select a passkey without first typing their username.
authentik will automatically fall back to the normal identification flow when passkey autofill is not available.
### Requirements
- **HTTPS** is required for WebAuthn (except on `localhost`).
- **Browser support** for WebAuthn conditional mediation is required.
- Users must have a compatible **discoverable credential (aka resident key)** (most passkeys created by platform authenticators and password managers are discoverable).
- **Correct domain**: users must access authentik using the same hostname the passkey was created for.
### Configuration
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Flows and Stages** > **Stages** and either create or edit an [Authenticator validation stage](../authenticator_validate/index.mdx) that allows the **WebAuthn** device class.
3. Navigate to **Flows and Stages** > **Stages** and edit your Identification stage. Under **Passkey settings** set **WebAuthn Authenticator Validation Stage** to the Authenticator validation stage from step 2.
4. Click **Update** to save the changes.
5. Ensure users have enrolled a passkey/WebAuthn device (for example using the [WebAuthn / FIDO2 / Passkeys Authenticator setup stage](../authenticator_webauthn/index.mdx)).
### Notes
- The passkey prompt is triggered by the browser when the user focuses the username field.
- If a user has multiple passkeys, the browser will show a picker.
- If passkey login is used, the flow context will have `auth_method` set to `auth_webauthn_pwl`.
- In the default authentication flow blueprint, authentik skips the MFA validation stage after passkey login using an expression policy. If you want passkey login to still require an additional factor, disable or adjust that policy binding on the MFA stage.
### Troubleshooting
- **No passkey prompt appears**
- Ensure the Identification stage has **WebAuthn Authenticator Validation Stage** set.
- Ensure you're using **HTTPS** (except on `localhost`).
- Check browser support for conditional UI.
- Ensure the login page is not embedded in an iframe as some browsers block conditional UI outside top-level browsing contexts.
- **Passkey prompt appears, but login falls back to username/password**
- Ensure the referenced Authenticator validation stage allows the **WebAuthn** device class.
- Ensure the user has a valid, confirmed WebAuthn device enrolled.
## Enrollment/Recovery Flow
These fields specify if and which flows are linked on the form. The enrollment flow is linked as `Need an account? Sign up.`, and the recovery flow is linked as `Forgot username or password?`.

View File

@@ -8,6 +8,8 @@ This is a generic password prompt which authenticates the current `pending_user`
There are two different ways to configure passwordless authentication; you can follow the instructions [here](../authenticator_validate/index.mdx#passwordless-authentication) to allow users to directly authenticate with their authenticator (only supported for WebAuthn devices), or dynamically skip the password stage depending on the users device, which is documented here.
If you want users to be able to pick a passkey from the browser's passkey/autofill UI without entering a username first, configure **Passkey autofill (WebAuthn conditional UI)** in the [Identification stage](../identification/index.mdx#passkey-autofill-webauthn-conditional-ui). This is separate from configuring a dedicated passwordless flow, and can be used alongside normal identification flows.
Depending on what kind of device you want to require the user to have:
#### WebAuthn

View File

@@ -394,6 +394,8 @@ When documenting errors, follow this structure:
- **Diagrams**:
- Use [Mermaid](https://mermaid.js.org/) for creating diagrams directly in markdown. Mermaid is our preferred tool for documentation diagrams as it allows for version control and easy updates.
- For more complex diagrams, you can use tools like [Draw.io](https://draw.io). Ensure high contrast and text descriptions.
- **authentik icons**:
- For authentik icons in integration guides, reference assets from the user's own self-hosted instance to avoid external calls, for example: `https://authentik.company/static/dist/assets/icons/icon.svg`
---

View File

@@ -2,7 +2,7 @@
* @file Docusaurus Documentation config.
*
* @import { UserThemeConfig, UserThemeConfigExtra } from "@goauthentik/docusaurus-config";
* @import { AKReleasesPluginOptions } from "@goauthentik/docusaurus-theme/releases/plugin"
* @import { AKReleasesPluginOptions } from "@goauthentik/docusaurus-theme/releases/common"
* @import { AKRedirectsPluginOptions } from "@goauthentik/docusaurus-theme/redirects/plugin"
* @import { Options as RedirectsPluginOptions } from "@docusaurus/plugin-client-redirects";
*/

View File

@@ -0,0 +1,14 @@
---
title: Deployment
sidebar_label: Deployment
---
import DocCardList from "@theme/DocCardList";
You can deploy the authentik Agent on [Linux](./linux.md), [macOS](./macos.md), and [Windows](./windows.md) devices.
Documentation for large-scale deployments using [Mobile Device Management (MDM)](./mdm.mdx) tools is also available.
Select a topic below to continue:
<DocCardList />

View File

@@ -0,0 +1,88 @@
---
title: Deploy authentik Agent on Linux
sidebar_label: Linux
tags: [authentik Agent, linux, deploy, packages]
---
## What it can do
- Retrieves information about the host and reports it to authentik, see [Device Compliance](../../device-compliance/index.mdx).
- Authorize Sudo elevation, see [Sudo authorization](../../device-authentication/sudo-authorization.md).
- SSH to Linux hosts using authentik credentials, see [SSH authentication](../../device-authentication/ssh-authentication.mdx).
- Authenticate CLI applications using authentik credentials, see [CLI application authentication](../../device-authentication/cli-app-authentication/index.mdx).
## Prerequisites
You must [configure your authentik deployment](../configuration.md) to support the authentik Agent.
## Create an enrollment token
If you have already created have an enrollment token, skip to the [next section](#install-the-authentik-agent-on-linux).
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Endpoint Devices** > **Connectors**.
3. Click on the authentik Agent connector that you created when [configuring your authentik deployment](../configuration.md) to support the authentik agent.
4. Under **Enrollment Tokens**, click **Create**, and configure the following settings:
- **Token name**: provide a descriptive name for the token
- **Device group _(optional)_**: select a device access group for the device to be added to after completing enrollment
- **Expiring _(optional)_**: set whether or not the enrollment token will expire
5. Click **Create**.
6. _(Optional)_ Click the **Copy** icon in the **Actions** column to copy the enrollment token. This value will be required if [enabling a device for device compliance](#enable-device-compliance-and-ssh-access).
## Install the authentik Agent on Linux
Follow these steps to install the authentik Agent on your Linux device:
1. Open a Terminal session and install the required GPG key:
```sh
curl -fsSL https://pkg.goauthentik.io/keys/gpg-key.asc | sudo gpg --dearmor -o /usr/share/keyrings/authentik-keyring.gpg
```
2. Add the repository:
```sh
echo "deb [signed-by=/usr/share/keyrings/authentik-keyring.gpg] https://pkg.goauthentik.io stable main" | sudo tee /etc/apt/sources.list.d/authentik.list
```
3. Update your repositories and install the authentik Agent packages:
```sh
sudo apt update
sudo apt install authentik-cli authentik-agent authentik-sysd
```
4. Confirm that the authentik Agent is installed by opening a terminal window and entering the following command: `ak`
You should see a response that starts with: `authentik CLI v<version_number>`
## Enable device authentication
To enable [device authentication features](../../device-authentication/index.mdx), the device must be connected to an authentik deployment. To do so, follow these steps:
1. Open a Terminal session and run the following command:
```sh
ak config setup --authentik-url https://authentik.company
```
2. Your default browser will open and direct you to the authentik login page. Once authenticated, the authentik Agent will be configured.
## Enable device compliance and SSH access
To enable [device compliance features](../../device-compliance/index.mdx) and the device [accepting SSH connections](../../device-authentication/ssh-authentication.mdx), you must join the device to an authentik domain.
1. Open a Terminal session and run the following command:
```sh
ak-sysd domains join <deployment_name> --authentik-url https://authentik.company
```
- `deployment_name` is the name that will be used to identify the authentik deployment on the device.
- `https://authentik.company` is the fully qualified domain name of the authentik deployment.
2. You will be prompted to enter your [enrollment token](#create-an-enrollment-token).
3. Once provided, the device will be enrolled with your authentik deployment and should appear on the [Devices page](../../manage-devices.mdx) after a [check-in](../../device-compliance/device-reporting.md) is completed.
## Logging
authentik Agent logs are available via the system journal (`systemd`) or `syslog`, depending on the distribution.

View File

@@ -0,0 +1,73 @@
---
title: Deploy authentik Agent on macOS
sidebar_label: macOS
tags: [authentik Agent, mac, macos, deploy]
---
## What it can do
- Retrieves information about the host for use in authentik, see [Device Compliance](../../device-compliance/index.mdx).
- SSH to Linux hosts using authentik credentials, see [SSH authentication](../../device-authentication/ssh-authentication.mdx).
- Authenticate CLI applications using authentik credentials, see [CLI application authentication](../../device-authentication/cli-app-authentication/index.mdx).
## Prerequisites
You must [configure your authentik deployment](../configuration.md) to support the authentik Agent.
## Create an enrollment token
If you have already created have an enrollment token, skip to the [next section](#install-the-authentik-agent-on-macos).
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Endpoint Devices** > **Connectors**.
3. Click on the authentik Agent connector that you created when [configuring your authentik deployment](../configuration.md) to support the authentik agent.
4. Under **Enrollment Tokens**, click **Create**, and configure the following settings:
- **Token name**: provide a descriptive name for the token
- **Device group _(optional)_**: select a device access group for the device to be added to after completing enrollment
- **Expiring _(optional)_**: set whether or not the enrollment token will expire
5. Click **Create**.
6. _(Optional)_ Click the **Copy** icon in the **Actions** column to copy the enrollment token. This value will be required if [enabling a device for device compliance](#enable-device-compliance).
## Install the authentik Agent on macOS
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Endpoint Devices** > **Connectors**.
3. Click on the authentik Agent connector that you created when [configuring your authentik deployment](../configuration.md) to support the authentik agent.
4. Under **Setup**, click **macOS** to download the authentik Agent installer.
5. Once the download is complete, attempt to install the package. Default Apple security settings should block the install.
- This can be avoided by Option + Right Clicking the package and clicking **Open**.
- Alternatively use the following command to remove the package from quarantine: `xattr -r -d com.apple.quarantine "$HOME/Downloads/authentik agent installer.pkg"`
6. Confirm that the authentik Agent is installed by opening a Terminal window and entering the following command: `ak`
You should see a response that starts with: `authentik CLI v<version_number>`
## Enable device authentication
To enable [device authentication features](../../device-authentication/index.mdx), you must connect the device to an authentik deployment. To do so, follow these steps:
1. Open a Terminal session and run the following command:
```sh
ak config setup --authentik-url https://authentik.company
```
2. Your default browser will open and direct you to the authentik login page. Once authenticated, the authentik Agent will be configured.
## Enable device compliance
To enable [device compliance features](../../device-compliance/index.mdx), you must join the device to an authentik domain.
1. Open a Terminal session and run the following command:
```sh
ak-sysd domains join <deployment_name> --authentik-url https://authentik.company
```
- `deployment_name` is the name that will be used to identify the authentik deployment on the device.
- `https://authentik.company` is the fully qualified domain name of the authentik deployment.
2. You will be prompted to enter your [enrollment token](#create-an-enrollment-token).
3. Once provided, the device will be enrolled with your authentik deployment and should appear on the [Devices page](../../manage-devices.mdx) after a [check-in](../../device-compliance/device-reporting.md) is completed.
## Logging
The authentik Agent uses macOS's native logging abilities. To retrieve the logs, open the Console application and then filter for authentik-related processes such as `authentik-agent` or `authentik-sysd`.

View File

@@ -0,0 +1,57 @@
---
title: Deploy authentik Agent via MDM
sidebar_label: MDM
tags: [authentik Agent, mdm, fleet, deploy]
---
authentik Agent can be deployed at scale to multiple devices via Mobile Device Management (MDM) tools.
## Prerequisites
You must [configure your authentik deployment](../configuration.md) to support the authentik Agent.
## Create an enrollment token
If you have already created have an enrollment token, skip to the next section.
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Endpoint Devices** > **Connectors**.
3. Click on the authentik Agent connector that you created when [configuring your authentik deployment](../configuration.md) to support the authentik agent.
4. Under **Enrollment Tokens**, click **Create**, and configure the following settings:
- **Token name**: provide a descriptive name for the token
- **Device group _(optional)_**: select a device access group for the device to be added to after completing enrollment
- **Expiring _(optional)_**: set whether or not the enrollment token will expire
5. Click **Create**.
## Windows
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Endpoint Devices** > **Connectors**.
3. Click on the authentik Agent connector that you created when [configuring your authentik deployment](../configuration.md) to support the authentik agent.
4. Under **Setup**, select the enrollment token that you wish to use for enrolling devices.
5. Click **Windows** and then click either **Download** or **Copy** to obtain your SyncML MDM configuration snippet.
This SyncML snippet can be used by Microsoft Intune, Microsoft Endpoint Manager and other MDM tools to deploy the changes required to support the authentik Agent.
The following two registry keys (`REG_SZ`) are added by the configuration snippet:
- `HKLM/SOFTWARE/authentik Security Inc./Platform/ManagedConfig/RegistrationToken`
- `HKLM/SOFTWARE/authentik Security Inc./Platform/ManagedConfig/URL`
## macOS
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Endpoint Devices** > **Connectors**.
3. Click on the authentik Agent connector that you created when [configuring your authentik deployment](../configuration.md) to support the authentik agent.
4. Under **Setup**, select the enrollment token that you wish to use for enrolling devices.
5. Click **macOS** and then click either **Download** or **Copy** to obtain your MDM policy.
This policy can be used by Apple Business Manager, Fleet, and other MDM tools to deploy the changes required to support the authentik Agent.
:::warning MDM only
Apple requires that this policy be applied to a device via an MDM tool. It will not function if manually applied to a device.
:::
### User registration
Upon deploying the authentik Agent to a device, the user will receive a notification asking them to register with authentik. When a user follows the registration they are asked to authenticate with authentik, once authenticated the device is enrolled in authentik and associated with the user.

View File

@@ -0,0 +1,92 @@
---
title: Deploy authentik Agent on Windows
sidebar_label: Windows
tags: [authentik Agent, windows]
---
## What it can do
- Retrieves information about the host for use in authentik, see [Device Compliance](../../device-compliance/index.mdx).
- SSH to Linux hosts using authentik credentials, see [SSH authentication](../../device-authentication/ssh-authentication.mdx).
- Authenticate CLI applications using authentik credentials, see [CLI application authentication](../../device-authentication/cli-app-authentication/index.mdx).
:::warn Supported Windows Versions
The authentik Agent is currently only tested on Windows 11 and Windows Server 2022. Other versions may work but are untested.
:::
## Windows Credential Provider
Windows Credential Provider (WCP) is a component of the authentik Agent that allows logging in to Windows workstations using authentik credentials.
It currently only supports local login; RDP login is not supported.
:::warning
- When WCP is enabled, the password of the Windows user account that's used to login is set to a random string.
- WCP can cause issues with user encrypted directories.
- Support with Active directory has not been confirmed yet.
- Offline login is currently not supported.
:::
## Prerequisites
You must [configure your authentik deployment](../configuration.md) to support the authentik Agent.
## Create an enrollment token
If you have already created have an enrollment token, skip to the [next section](#install-the-authentik-agent-on-windows).
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Endpoint Devices** > **Connectors**.
3. Click on the authentik Agent connector that you created when [configuring your authentik deployment](../configuration.md) to support the authentik agent.
4. Under **Enrollment Tokens**, click **Create**, and configure the following settings:
- **Token name**: provide a descriptive name for the token
- **Device group _(optional)_**: select a device access group for the device to be added to after completing enrollment
- **Expiring _(optional)_**: set whether or not the enrollment token will expire
5. Click **Create**.
6. _(Optional)_ Click the **Copy** icon in the **Actions** column to copy the enrollment token. This value will be required if [enabling a device for device compliance](#enable-device-compliance).
## Install the authentik Agent on Windows
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Endpoint Devices** > **Connectors**.
3. Click on the authentik Agent connector that you created when [configuring your authentik deployment](../configuration.md) to support the authentik agent.
4. Under **Setup**, click **Windows** to download the authentik Agent installer.
5. Once the download is complete, install the MSI file.
6. _(Optional)_ During installation, select [Windows Credential Provider](#windows-credential-provider) if you want to log in to the Windows device using authentik credentials.
7. Confirm that the authentik Agent is installed by opening a PowerShell or Terminal window and entering the following command: `ak`
You should see a response that starts with: `authentik CLI v<version_number>`
## Enable device authentication
To enable [device authentication features](../../device-authentication/index.mdx), you must connect the device to an authentik deployment. To do so, follow these steps:
1. Open a Terminal and run the following command:
```sh
ak config setup --authentik-url https://authentik.company
```
2. Your default browser will open and direct you to the authentik login page. Once authenticated, the authentik Agent will be configured.
## Enable device compliance
To enable [device compliance features](../../device-compliance/index.mdx), you must join the device to an authentik domain.
1. Open a Terminal session and run the following command:
```sh
ak-sysd domains join <deployment_name> --authentik-url https://authentik.company
```
- `deployment_name` is the name that will be used to identify the authentik deployment on the device.
- `https://authentik.company` is the fully qualified domain name of the authentik deployment.
2. You will be prompted to enter your [enrollment token](#create-an-enrollment-token).
3. Once provided, the device will be enrolled with your authentik deployment and should appear on the [Devices page](../../manage-devices.mdx) after a [check-in](../../device-compliance/device-reporting.md) is completed.
## Logging
The authentik Agent primarily outputs logs to Windows Event Viewer.
WCP logs to the `wcp.log` located in `C:\Program Files\Authentik Security Inc\wcp`.

View File

@@ -0,0 +1,140 @@
---
title: Agent CLI commands
sidebar_label: Agent CLI commands
tags: [authentik Agent, authentik cli, ak cli, ak, cli, ak-sysd, commands]
---
The following commands are available when interacting with the authentik Agent via the command line.
:::info Flags
Most of the CLI commands have a `-v`/`--verbose` flag for verbose output.
Use the `-h`/`--help` flag to access help information.
:::
## authentik-cli commands
### auth
Commands for authenticating with different CLI applications.
```bash
ak auth <command>
```
- `aws` - Authenticate to AWS with the authentik profile.
- `kubectl` - Authenticate to a Kubernetes Cluster with the authentik profile.
- `raw` - Authenticate to arbitrary API calls.
- `vault` - Generate a JWT for authenticating to HashiCorp Vault.
### completion
Generate the autocompletion script for the specified shell.
```bash
ak completion <command>
```
- `bash` - Generate the autocompletion script for bash.
- `fish` - Generate the autocompletion script for fish.
- `powershell` - Generate the autocompletion script for PowerShell.
- `zsh` - Generate the autocompletion script for zsh.
### config
Configure authentik CLI
```bash
ak config <command>
```
- `list-profiles` - List profiles that are enabled on the device. Each profile is associated with a separate authentik deployment.
- `setup` - Configure authentik CLI.
### help
Output help information about any command.
```bash
ak help <command>
```
Where `<command>` is any authentik CLI command you want help with, for example: `ak help ssh`
### ssh
Establish an SSH connection with the target endpoint device.
```bash
ak ssh <hostname>
```
### system
Commands for interacting with authentik sessions.
```bash
ak system <command>
```
- `status` - Status about the current session.
### whoami
Check user account details for a given profile.
```bash
ak whoami
```
## authentik-sysd commands
### agent
Used to run the authentik system agent
```bash
ak-sysd agent
```
`-d` for debug
`--disable-component` to disable a component, can be used multiple times.
TODO @BeryJu document the ids of components
### completion
Generate the autocompletion script for the specified shell.
```bash
ak-sysd completion <command>
```
- `bash` - Generate the autocompletion script for bash.
- `fish` - Generate the autocompletion script for fish.
- `powershell` - Generate the autocompletion script for powershell.
- `zsh` - Generate the autocompletion script for zsh.
### domains
```bash
ak-sysd domains <command>
```
- `join` - Join an authentik domain, for example `ak-sysd domains join <name_for_authentik_domain> -a <authentik_URL>`
### help
```bash
ak-sysd help <command>
```
Where `<command>` is any authentik CLI command you want help with, for example: `ak-sysd help domains`
### troubleshoot
```bash
ak-sysd troubleshoot <command>
```
- `check` - Check status of authentik agent components. Useful on Linux as there are various components being used.
- `inspect` - Outputs the state database that the agent has.
- `facts` - Outputs device facts. These are the facts that are sent to authentik for device reporting.

View File

@@ -0,0 +1,60 @@
---
title: Configuration
sidebar_label: Configuration
tags: [authentik Agent, connector, configure, configuration]
---
Before deploying the authentik Agent, configure your authentik deployment. This involves:
- Importing the [Device code flow](../../add-secure-apps/providers/oauth2/device_code.md)
- Creating an OAuth application and provider
- Creating a [Connector](../device-compliance/connectors.md)
## Import OAuth device code flow
The OAuth device code flow enables secure authentication for input-limited clients like CLI tools and is required for the authentik Agent to function.
If you have already deployed the authentik OAuth device code flow, skip to the [next section](#create-an-application-and-provider-in-authentik-for-cli).
1. Download the [device code flow blueprint file](https://raw.githubusercontent.com/goauthentik/platform/refs/heads/main/hack/authentik/blueprints/oauth2-device-code.yaml).
2. Log in to authentik as an administrator and open the authentik Admin interface.
3. Navigate to **Flows and Stages** > **Flows**.
4. Click **Import**
5. Select the downloaded blueprint and click **Import**.
6. Navigate to **System** > **Brands** and click the **Edit** icon on the default brand.
7. Set **Default code flow** to the newly created device code flow and click **Update**.
Alternatively, manually create the flow by following the instructions in the [Device code flow documentation](../../add-secure-apps/providers/oauth2/device_code.md#create-and-apply-a-device-code-flow).
## Create an application and provider in authentik for CLI
The authentik agent requires an OAuth application/provider pair to handle authentication.
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Applications** > **Applications** and click **Create with Provider** to create an application and provider pair. (Alternatively you can first create a provider separately, then create the application and connect it with the provider.)
- **Application**: provide a descriptive name (e.g. `authentik-cli`), an optional group for the type of application, the policy engine mode, and optional UI settings.
- **Choose a Provider type**: select **OAuth2/OpenID Connect** as the provider type.
- **Configure the Provider**: provide a name (or accept the auto-provided name), the authorization flow to use for this provider, and the following required configurations.
- Set the **Client type** to `Public`.
- Set the **Client ID** to `authentik-cli`.
- Select any available signing key.
- Under **Advanced protocol settings**:
- In addition to the three default **Selected Scopes**, add the `authentik default OAuth Mapping: OpenID 'offline_access'` scope.
- **Configure Bindings** _(optional)_: you can create a [binding](../../../add-secure-apps/flows-stages/bindings/) (policy, group, or user) to manage access to the application.
3. Click **Submit** to save the new application and provider.
## Create the authentik Agent connector
The authentik Agent [Connector](../device-compliance/connectors.md) allows device information to be reported to authentik.
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Endpoint Devices** > **Connectors** and click **Create**.
3. Select **Agent Connector** as the agent type and click **Next**.
4. Configure the following required settings:
- **Connector name**: provide a descriptive name (e.g. `authentik Agent`)
- **Refresh interval**: select how often the agent will attempt to update its configuration.
- **Enabled**: toggle to enable the connector.
- Under **Authentication settings**:
- **Federated OIDC Providers**: add the `authentik-cli` provider that you created in the previous section.
5. Click **Finish**.

View File

@@ -0,0 +1,7 @@
---
title: Development
sidebar_label: Development
tags: [authentik Agent, development, repository]
---
The authentik Agent and associated components are developed in the [authentik Platform GitHub repository](https://github.com/goauthentik/platform). For source code and information on contributing to the project, refer to the documentation included in the GitHub repository.

View File

@@ -0,0 +1,46 @@
---
title: authentik Agent
sidebar_label: authentik Agent
---
import DocCardList from "@theme/DocCardList";
## What is the authentik Agent?
The authentik Agent is a service that can be installed on Linux, macOS, and Windows devices. It provides the following capabilities:
- [Device Compliance](../device-compliance/index.mdx) by reporting information about Endpoint Devices to authentik
- [Local device login](../device-authentication/local-device-login/index.mdx) with authentik credentials
- [Connecting via SSH to Endpoint Devices](../device-authentication/ssh-authentication.mdx) with authentik credentials
- [Sudo authorization](../device-authentication/sudo-authorization.md) with authentik credentials
- [Authenticating to CLI applications](../device-authentication/cli-app-authentication/index.mdx) such as kubectl and AWS with authentik credentials
## authentik Agent components
The authentik Agent consists of several components:
| Platform | Component | Description | Dependencies |
| ------------------------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| **Linux, macOS, Windows** | `authentik-cli` | Provides CLI commands for interacting with `authentik-agent`. | `authentik-agent` |
| **Linux, macOS, Windows** | `authentik-agent` | User service. | `authentik-sysd` |
| **Linux, macOS, Windows** | `authentik-sysd` | System service. | None |
| **Linux only** | `libpam-authentik` | PAM Module for token-based and interactive authentication via authentik. Used for [SSH authentication](../device-authentication/ssh-authentication.mdx) and [local device login](../device-authentication/local-device-login/index.mdx). | `authentik-sysd` |
| **Linux only** | `libnss-authentik` | NSS Module that makes Linux aware of authentik users. All authentik users will be visible to Linux - but won't be able to login unless configured via device access groups. Provides a consistent `uid` and `gid` for users on all Endpoint Devices. | `authentik-sysd`, `libpam-authentik` |
| **Windows only** | `Windows Credential Provider` (WCP) | Enables logging in to Windows devices using authentik credentials. | `authentik-sysd` |
## Technical information
All authentik Agent components communicate via gRPC and Unix domain sockets.
- `sys.sock` for general communication
- `sys-ctrl.sock` for domain join
## Important considerations
Sentry reporting is currently enabled by default and cannot be disabled. This will be configurable in a future release.
## More information
For more information refer to each of the topics below:
<DocCardList />

View File

@@ -0,0 +1,10 @@
---
title: authentik Agent Releases
sidebar_label: Release Notes
---
import DocCardList from "@theme/DocCardList";
Release notes for recent authentik Agent versions
<DocCardList />

View File

@@ -0,0 +1,4 @@
---
title: temp
sidebar_label: temp
---

View File

@@ -0,0 +1,45 @@
---
title: AWS CLI authentication
sidebar_label: AWS
tags: [authentik Agent, authentik cli, aws, cli]
---
You can use the authentik Agent to authenticate to the AWS CLI with authentik credentials.
## Prerequisites
- The [authentik Agent deployed on it](../../authentik-agent/agent-deployment/index.mdx) must be deployed on your device.
## authentik configuration
To support the integration of authentik Agent with AWS CLI, you need to create an application/provider pair in authentik.
### Create an application and provider in authentik for AWS CLI
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Applications** > **Applications** and click **Create with Provider** to create an application and provider pair. (Alternatively you can first create a provider separately, then create the application and connect it with the provider.)
- **Application**: provide a descriptive name (e.g. `authentik-aws-cli`), an optional group for the type of application, the policy engine mode, and optional UI settings.
- **Choose a Provider type**: select **OAuth2/OpenID Connect** as the provider type.
- **Configure the Provider**: provide a name (or accept the auto-provided name), the authorization flow to use for this provider, and the following required configurations.
- Set the **Client type** to `Public`.
- Set the **Client ID** to `authentik-aws-cli`.
- Select any available signing key.
- Under **Machine-to-Machine authentication settings** add the `authentik-cli` provider as a **Federated OIDC Provider**.
- **Configure Bindings** _(optional)_: you can create a [binding](../../../../add-secure-apps/flows-stages/bindings/) (policy, group, or user) to manage access to the application.
3. Click **Submit** to save the new application and provider.
## Authenticate to AWS CLI with the authentik Agent
To authenticate to the AWS CLI with the authentik agent, use the following command:
```bash
ak auth aws
```
**Available flags:**
- `-c, --client-id <string>` - Client ID
- `-e, --region <string>` - AWS region (default: `eu-central-1`)
- `-r, --role-arn <string>` - IAM Role ARN
- `-h, --help` - Display help information

View File

@@ -0,0 +1,21 @@
---
title: CLI application authentication
sidebar_label: CLI application authentication
tags: [authentik Agent, authentik cli, kubernetes, k8s, aws, cli]
---
import DocCardList from "@theme/DocCardList";
The authentik Agent can authenticate to CLI applications such as [`aws`](./aws.mdx) and [`kubectl`](./k8s.mdx).
## How CLI authentication works
First, `authentik-agent` and `authentik-cli` request an authentik token from the [authentik-cli OAuth Provider](../../authentik-agent/configuration.md#create-an-application-and-provider-in-authentik-for-cli) and exchange it for a token from the specified Kubernetes or AWS provider.
This token is cached until expiration. This improves performance by eliminating repeated token requests.
## More information
For more information refer to each of the topics below:
<DocCardList />

View File

@@ -0,0 +1,80 @@
---
title: Kubernetes CLI authentication
sidebar_label: Kubernetes
tags: [authentik Agent, authentik cli, kubernetes, k8s, kubectl, cli]
---
You can use the authentik Agent to authenticate to `kubectl` with authentik credentials.
## Prerequisites
- The device that you're using must have the [authentik Agent deployed on it](../../authentik-agent/agent-deployment/index.mdx).
## authentik configuration
To support the integration of authentik Agent with `kubectl`, you need to create an application/provider pair in authentik.
### Create an application and provider in authentik for Kubernetes
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Applications** > **Applications** and click **Create with Provider** to create an application and provider pair. (Alternatively you can first create a provider separately, then create the application and connect it with the provider.)
- **Application**: provide a descriptive name (e.g. `authentik-kubernetes`), an optional group for the type of application, the policy engine mode, and optional UI settings.
- **Choose a Provider type**: select **OAuth2/OpenID Connect** as the provider type.
- **Configure the Provider**: provide a name (or accept the auto-provided name), the authorization flow to use for this provider, and the following required configurations.
- Set the **Client type** to `Public`.
- Set the **Client ID** to `authentik-kubernetes`.
- Select any available signing key.
- Under **Machine-to-Machine authentication settings** add the `authentik-cli` provider as a **Federated OIDC Provider**.
- **Configure Bindings** _(optional)_: you can create a [binding](../../../../add-secure-apps/flows-stages/bindings/) (policy, group, or user) to manage access to the application.
3. Click **Submit** to save the new application and provider.
## Kubernetes configuration
To integrate the authentik Agent with your kubernetes deployment, you'll need to configure kubeadm.
### Configure kubeadm settings
Add the following `extraArgs` to your `kubeadm_config.yml` file:
```yaml
- name: oidc-client-id
value: authentik-kubernetes
- name: oidc-groups-claim
value: groups
- name: oidc-groups-prefix
value: "oidc:"
- name: oidc-issuer-url
value: https://authentik.company/application/o/<application-slug>/
- name: oidc-username-claim
value: email
```
Run the following command to apply the changes to an existing Kubernetes cluster:
:::warning
This command will restart the API server. Plan accordingly for production environments.
:::
```sh
kubeadm upgrade apply <version> --config=kubeadm_config.yml
```
Where `<version>` matches the target Kubernetes version specified in your config file.
:::info Example config file
An example `kubeadm_config.yml` is available on the [authentik Platform GitHub repository](https://github.com/BeryJu/infrastructure/blob/main/roles/beryjuio_kube/templates/kubeadm_config.yml#L11-L20).
:::
## Authenticate to kubectl with the authentik Agent
To authenticate to kubectl with the authentik agent, use the following command:
```bash
ak auth kubectl
```
**Available flags:**
- `-c, --client-id <string>` - Client ID
- `-h, --help` - Display help information

View File

@@ -0,0 +1,18 @@
---
title: Device access groups
sidebar_label: Device access groups
tags: [authentik Agent, device authentication, device login, device groups]
---
Device access groups control access to endpoint devices. You can organize devices into groups and bind users, user groups, and policies to determine access.
## Creating a device access group
To create a device access group, follow these steps:
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Endpoint Devices** > **Device Access Groups** and click **Create**.
3. Provide a **Group name** and click **Create**.
4. Expand the newly created device access group.
5. Click either **Create and bind Policy** or **Bind existing Policy / Group / User**.
6. Once you've configured the desired access for the device access group, click **Finish**.

View File

@@ -0,0 +1,19 @@
---
title: Device authentication
sidebar_label: Device authentication
---
import DocCardList from "@theme/DocCardList";
The [authentik Agent](../authentik-agent/index.mdx) supports multiple types of authentication and authorization using authentik credentials:
- [Local device login](./local-device-login/index.mdx) - Log in to Windows endpoint devices.
- [SSH authentication](./ssh-authentication.mdx) - Connect from one endpoint device to another via SSH.
- [Sudo authorization](./sudo-authorization.md) - Authorize sudo elevation on an endpoint device.
- [Authenticate CLI applications](./cli-app-authentication/index.mdx) - Authenticate CLI based applications like `aws` and `kubectl`.
[Device access groups](./device-access-groups.mdx) allow you to control which users have access to a device.
For more information, pick a topic below:
<DocCardList />

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@@ -0,0 +1,17 @@
---
title: Local device login
sidebar_label: Local device login
tags: [authentik Agent, device login, device authentication, windows credential provider, wcp]
---
import DocCardList from "@theme/DocCardList";
Local device login allows you to log in to devices using authentik credentials instead of separate local accounts.
Users authenticate with authentik, just like they do for any application. The operating system login screen on a laptop, desktop, or server is connected to authentik.
Local device login is currently only supported on Windows. Linux support is possible but not yet implemented. Configuration and testing with various Linux login managers (SDDM, GDM, etc.) and PAM implementations is pending.
For more information, pick a topic below:
<DocCardList />

View File

@@ -0,0 +1,9 @@
---
title: Linux local device login
sidebar_label: Linux
tags: [authentik Agent, device login, device authentication, linux]
---
Local device login is currently only supported on Windows.
Linux support is possible but not yet implemented. Configuration and testing with various Linux login managers (SDDM, GDM, etc.) and PAM implementations is pending.

View File

@@ -0,0 +1,40 @@
---
title: Windows local device login
sidebar_label: Windows
tags: [authentik Agent, device login, device authentication, windows credential provider, wcp]
---
## Windows Credential Provider
Windows Credential Provider (WCP) is a component of the authentik Agent that allows logging in to Windows devices using authentik credentials.
It currently only supports local login; RDP login is not supported.
:::warning
- WCP is currently only tested on Windows 11 and Windows Server 2022.
- When WCP is enabled, the password of the Windows user account that's used to login is set to a random string.
- WCP can cause issues with user encrypted directories.
- Support with Active directory has not been confirmed yet.
- Offline login is currently not supported.
:::
## Prerequisites
You need to have deployed the authentik Agent including the WCP component on the Windows device, see [Deploy the authentik Agent on Windows](../../authentik-agent/agent-deployment/windows.md) for more details.
## How it works
- The system agent requests an authentication and authorization URL from authentik, using its token.
- This URL is opened in a browser which also injects the device token information, allowing authenitk to know that the login request is executed on the same machine.
- The end user logs in normally using the standard authentik interface and flows
- Once finished, the browser is redirect to a well-defined location and uses the token it receives to finish authentication and authorization through the system agent
## How to log in to a Windows device
1. On the Windows login screen, click the authentik icon:
![Windows login screen](authentik-login.png)
2. A browser window will open and prompt you for your authentik credentials.
3. Once authenticated, you will be logged in to the Windows device.

View File

@@ -0,0 +1,56 @@
---
title: SSH authentication
sidebar_label: SSH authentication
tags: [ssh, authentik Agent]
---
You can use the [authentik Agent](../authentik-agent/index.mdx) to authenticate SSH connections ubetween endpoint devices using authentik credentials.
Currently, only [Linux](../authentik-agent/agent-deployment/linux.md) devices can serve as SSH endpoints. See [Configure SSH authentication on an endpoint device](#configure-ssh-authentication-on-an-endpoint-device) section for more details.
## Prerequisites
- The [authentik Agent must be deployed](../authentik-agent/agent-deployment/index.mdx) on both the source and SSH target devices.
- The target device needs to be configured, see the [Configure SSH authentication on an endpoint device](#configure-ssh-authentication-on-an-endpoint-device) section below.
## How to SSH to an endpoint device
To SSH to a configured [Linux host](../authentik-agent/agent-deployment/linux.md) using the authentik Agent:
1. Open a Terminal session and run the following command:
```
ak ssh <hostname>
```
2. If not already authenticated, you will be prompted for authentik credentials.
3. Once authenticated, the SSH session will connect.
## Configure SSH authentication on an endpoint device
If you want a Linux Endpoint Device to support accepting SSH connections using authentik credentials, you will need to install the `libpam-authentik` package in addition to the authentik Agent. This is a PAM Module, which provides token-based and interactive authentication via authentik.
Authentication is only possible if the Linux device is aware of the authentik user which is attempting to authenticate. This can be achieved in one of two ways:
1. **Provision user accounts** - Create users on the Linux device with usernames that match authentik users that need to authenticate to the device. This can be done manually or via automation tools like Ansible.
2. **`libnss-authentik`** - This is a package that can be installed on the Linux device. It is an NSS module that makes the Linux device aware of authentik users. Similar to adding a Linux device to an Active Directory or LDAP domain.
### Install the `libpam-authentik` package _(required)_
:::info Prerequisites
You must have already deployed and configured the authentik Agent on the device.
:::
Run the following command to install the `libpam-authentik` package:
```sh
sudo apt install libpam-authentik
```
### Install the `libnss-authentik` package _(optional)_
Run the following command to install the `libnss-authentik` package:
```sh
sudo apt install libnss-authentik
```

View File

@@ -0,0 +1,43 @@
---
title: Sudo authorization
sidebar_label: Sudo authorization
tags: [sudo, authentik Agent]
---
You can use the [authentik Agent](../authentik-agent/index.mdx) to authorize sudo elevation when connected to a [Linux endpoint device via SSH](./ssh-authentication.mdx).
When you run a sudo command in this situation, the sudo authorization will be handled by the authentik Agent.
## Prerequisites
- [authentik Agent needs to be deployed](../authentik-agent/agent-deployment/index.mdx) on the device.
- Sudo authorization needs to be configured on the device, see the [Configure sudo authorization on an endpoint device](#configure-sudo-authorization-on-an-endpoint-device) section below.
## Configure sudo authorization on an endpoint device
If you want a Linux Endpoint Device to support authorizing using authentik credentials, you will need to install the `libpam-authentik` package in addition to the authentik Agent. This is a PAM Module, which provides token-based and interactive authentication via authentik.
Authorization is only possible if the Linux device is aware of the authentik user which is attempting to authorize. This can be achieved in one of two ways:
1. **Provision user accounts** - Create users on the Linux device with usernames that match authentik users that need to authorize sudo to the device. This can be done manually or via automation tools like Ansible.
2. **`libnss-authentik`** - This is a package that can be installed on the Linux device. It is an NSS module that makes the Linux device aware of authentik users. Similar to adding a Linux device to an Active Directory or LDAP domain.
### Install the `libpam-authentik` package _(required)_
:::info Prerequisites
You must have already deployed and configured the authentik Agent on the device.
:::
Run the following command to install the `libpam-authentik` package:
```sh
sudo apt install libpam-authentik
```
### Install the `libnss-authentik` package _(optional)_
Run the following command to install the `libnss-authentik` package:
```sh
sudo apt install libnss-authentik
```

View File

@@ -0,0 +1,46 @@
---
title: authentik browser extension
sidebar_label: Browser extension
tags: [device compliance, compliance, browser extension, extension]
---
import TabItem from "@theme/TabItem";
import Tabs from "@theme/Tabs";
The authentik browser extension is required for device compliance functionality, and is currently available for Chrome-based and Firefox-based browsers.
The browser extension connects to the [authentik Agent](../authentik-agent/index.mdx). It supplies [device facts](./device-reporting.md#device-facts) that [stages](../../add-secure-apps/flows-stages/stages/index.md) and [policies](../../customize/policies/index.md) can use during execution of authentik [flows](../../add-secure-apps/flows-stages/flow/index.md). This enables device compliance functionality such as limiting access to applications based on operating system, see [device compliance policy](./device-compliance-policy.md) for more details.
## Deploy the authentik browser extension
<Tabs
defaultValue="chrome"
values={[
{label: 'Chrome-based', value: 'chrome'},
{label: 'Firefox-based', value: 'firefox'},
]}>
<TabItem value="chrome">
(TODO - Jens finalizing adding extension to store)
1. Open Google Chrome.
2. Go to the the authentik browser extension page in the [Chrome Web Store](https://chromewebstore.google.com).
3. Click **Add to Chrome**.
4. Review the permissions and click **Add extension**.
</TabItem>
<TabItem value="firefox">
(TODO - Jens finalizing adding extension to store)
1. Open Firefox.
2. Go to the the authentik browser extension page in the [Firefox Add-ons site](https://addons.mozilla.org).
3. Click **Add to Firefox**.
4. Review the permissions and click **Add** (or **Install**).
</TabItem>
</Tabs>
## Configuration verification
Verify that the extension shows as installed in your extensions list.

View File

@@ -0,0 +1,21 @@
---
title: Configuration
sidebar_label: Configuration
tags: [device compliance, compliance, configuration]
---
## Prerequisites
Device compliance functionality requires the following:
- [Configure authentik to support the authentik Agent](../authentik-agent/configuration.md).
- [Deploy the authentik Agent on the device](../authentik-agent/agent-deployment/index.mdx).
- [Deploy the authentik browser extension on the endpoint device](./browser-extension.mdx).
## Configuration verification
Verify that the endpoint device is reporting its facts on the [Devices](../manage-devices.mdx) page.
## Using device compliance
Now that the endpoint device is configured, you can create [device compliance policies](./device-compliance-policy.md) to control access to applications and more.

View File

@@ -0,0 +1,45 @@
---
title: Connectors
sidebar_label: Connectors
tags: [device compliance, compliance, connectors, authentik Agent, fleet]
---
Connectors allow device information to be reported to authentik.
They can be used standalone or alongside the [authentik Agent](../authentik-agent/index.mdx).
Currently, the only supported connectors is the [authentik Agent](#authentik-agent)
## Connectors
The following connectors are currently supported:
### authentik Agent
- Unlike other connectors, the agent connector is used by the agent directly compared to other connectors talking to separate systems and APIs to integrate with other agents. Hence the functionality of the agent connector behaves differently than other connectors.
- the agent connector mainly holds configuration for the agent itself, as well as implementing certain platform specific protocols like Apple's Platform SSO.
## Adding a connector
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Endpoint Devices** > **Connectors** and click **Create**.
3. Select the connector type and click **Next**, and configure the following required settings:
- **Connector name**: provide a descriptive name for the connector.
- **Refresh interval**: select how often the agent will attempt to update it's configuration.
- **Enabled**: enable or disable the connector.
4. Click **Finish**.
## Editing a connector
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Endpoint Devices** > **Connectors**.
3. Click on the connector that you wish to edit.
4. Update any settings that you want to change.
5. Click **Update**.
## Deleting a connector
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Endpoint Devices** > **Connectors**.
3. Select the connector that you wish to delete.
4. Click **Delete**.

View File

@@ -0,0 +1,95 @@
---
title: Device compliance policy
sidebar_label: Device compliance policy
tags: [device compliance, compliance, device access, policy]
toc_max_heading_level: 4
---
Device compliance policies are used to limit access to authentik and applications based on [Device Compliance](./index.mdx) information.
Device compliance policies are currently in development and inaccessible.
However, similar functionality can be achieved with existing stages and policies.
## Prerequisites
You must have [configured compliance](./configuration.md) in authentik and on the endpoint device.
## Accessing device facts within a flow
To access device facts within a flow, the flow must include an [Endpoint stage](../../add-secure-apps/flows-stages/stages/endpoint/index.md). The Endpoint stage fetches device facts via a configured [Connector](./connectors.md) and adds them to the [Flow context](../../add-secure-apps/flows-stages/flow/context/index.mdx).
The following example shows how to use these facts within an expression policy.
```python
flow_plan = request.context.get("flow_plan") #set a flow_plan object
device = flow_plan.context.get("device") #set a device object
name = device.name #the name of the device
```
## Examples
The following are examples of how device compliance can currently be implemented:
### Only allow authentication via endpoint devices
If your goal is to only allow authentication via endpoint devices, this is achievable by adding an [Endpoint stage](../../add-secure-apps/flows-stages/stages/endpoint/index.md) to your authentication flow.
#### Create an Endpoint stage
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Flows and Stages > Flows**.
3. Select the authentication flow that you want to modify.
4. Open the **Stage Bindings** tab and click **Create and bind stage**.
5. Select Endpoint stage as the stage type, click **Next**, and configure the following settings:
- **Name**: provide a name for the stage
- **Connector**: select a connector for the stage to fetch device facts from (e.g. `authentik agent`)
- **Mode**: set to `Device required`
6. Click **Next**.
7. Select the order for the stage. Ensure that this places the Endpoint stage in the flow wherever you want device access to be checked.
8. Click **Finish**.
### Only allow authentication via a specific type of endpoint device
If your goal is to only allow authentication via a specific type of endpoint device, this is achievable by adding an [Endpoint stage](../../add-secure-apps/flows-stages/stages/endpoint/index.md) and a [Deny stage](../../add-secure-apps/flows-stages/stages/deny.md) to your authentication flow.
The following example will only allow authentication via Apple devices.
#### Create an Endpoint stage
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Flows and Stages > Flows**.
3. Select the authentication flow that you want to modify.
4. Open the **Stage Bindings** tab and click **Create and bind stage**.
5. Select Endpoint stage as the stage type, click **Next**, and configure the following settings:
- **Name**: provide a name for the stage
- **Connector**: select a connector for the stage to fetch device facts from (e.g. `authentik agent`)
- **Mode**: set to `Device required`
6. Click **Next**.
7. Select the **Order** for the stage. Ensure that this places the Endpoint stage in the flow wherever you want device access to be checked.
8. Click **Finish**.
#### Create a Deny stage
9. On the **Stage Bindings** tab, click **Create and bind stage**.
10. Select **Deny Stage** as the stage type and configure the following settings:
- **Name**: provide a name for the stage
- **Deny message**: provide a message explaining why access was denied
11. Click **Next**.
12. Select the **Order** for the stage. Ensure that this number is higher than the Endpoint stage created in the previous section.
13. Click **Finish**.
14. Expand the Deny stage that you just created and click **Create and bind Policy**.
15. Select **Expression policy** as the policy type, click **Next**, and configure the following settings:
- **Name**: provide a descriptive name for the policy
- **Expression**:
```python
flow_plan = request.context.get("flow_plan")
device = flow_plan.context.get("device")
if device.manufacturer.lower() != "apple":
return True
```
:::info Deny stage
Because this is a deny stage, the policy must evaluate true when a requirement is not met.
:::
16. Click **Next** and then click **Finish**.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -0,0 +1,46 @@
---
title: Device reporting
sidebar_label: Device reporting
tags:
[
device compliance,
compliance,
device facts,
device reporting,
device check-in,
check-in,
facts,
]
---
Endpoint Devices registered with authentik via a connector, such as the [authentik Agent](./connectors.md#authentik-agent) connector, regularly [check-in](#device-check-in) with authentik and report their [device facts](#device-facts).
These facts are shown on the [Devices](../manage-devices.mdx) page and are also accessible to policies and can be used to make policy decisions. See [Device Compliance Policy](./device-compliance-policy.md) for more details.
## Device check-in
When a device registered with authentik reports its [device facts](#device-facts), this is called a device check-in. These check-ins occur on a regular configurable schedule and can also be set to occur whenever a device is associated with an [Endpoint stage](../../add-secure-apps/flows-stages/stages/endpoint/index.md).
## Device facts
Device facts are informational snippets about a device, such as its operating system, serial number, installed applications, running processes, and more. These facts can are supplied to authentik flows via the [authentik browser extension](browser-extension.mdx) to be used in making policy decisions. For example, you can create a policy that only allows endpoint devices that are running a recent OS version to access an application.
### Advanced device facts :ak-enterprise
This feature is still in development and will be announced soon.
## Endpoint devices in event logs
Authentication events involving endpoint devices are included in the [event logs](../../sys-mgmt/events/logging-events.md). For example:
![Example of device authentication event](device-event-example.png)
### Search for an endpoint device in the event logs :ak-enterprise
To search for event logs matching a specific endpoint device:
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Events** > **Logs**.
3. In the search bar, enter: `context.device.name = "<device_name>"`
For more information on searching the events logs, see [Logging events](../../sys-mgmt/events/logging-events.md).

View File

@@ -0,0 +1,14 @@
---
title: Device Compliance
sidebar_label: Device Compliance
---
import DocCardList from "@theme/DocCardList";
Device compliance lets authentik verify that a user's device meets security and configuration criteria, such as operating system version, disk encryption, antivirus status etc, before allowing access to resources.
Device compliance is currently in very early preview. Device compliance policies are currently inaccessible and therefore have to be emulated with existing policies.
For more information, pick a topic below:
<DocCardList />

View File

@@ -0,0 +1,61 @@
---
title: Endpoint Devices
sidebar_label: Endpoint Devices
---
import DocCardList from "@theme/DocCardList";
:::danger Early preview
The endpoint devices feature set is currently in early preview and in development. **It is not recommended for production use.**
The features, methods, and even the name are yet to be confirmed.
Breaking changes and bugs should be expected.
:::
:::info Enterprise License Required
An enterprise license is required to access some of these features, refer to the [current features overview table](#features-overview) below for more details.
During this early preview stage, short trial licenses are available for testers. Please contact us via hello@goauthentik.io for more details.
:::
## What are endpoint devices?
Endpoint devices are end-user devices or servers that are registered with authentik.
There are two purposes for registration: [Device authentication](./device-authentication/index.mdx) and [Device compliance](./device-compliance/index.mdx).
Devices can be registered by installing the [authentik Agent](./authentik-agent/index.mdx) which supports:
- [Device compliance](./device-compliance/index.mdx) by reporting information about endpoint devices to authentik.
- [Local device login](./device-authentication/local-device-login/index.mdx) with authentik credentials.
- [Connecting via SSH to endpoint devices](./device-authentication/ssh-authentication.mdx) with authentik credentials.
- [Sudo authorization](./device-authentication/sudo-authorization.md) with authentik credentials.
- [Authenticating to CLI applications](./device-authentication/cli-app-authentication/index.mdx) such as kubectl and AWS with authentik credentials.
Alternatively, [Connectors](./device-compliance/connectors.md) allow authentik to be integrated with third party services such as Fleet. This allows for device information to be reported to authentik for [Device compliance](./device-compliance/index.mdx) purposes.
## Features overview
| Feature | Linux | Windows | macOS | Status |
| --------------------------------------------------------- | -------------- | -------------- | ----------------- | ------------------------------------------------------------------------------------------- |
| **Local device login** | Open source | :ak-enterprise | :ak-enterprise \* | Available for early preview on Windows. |
| **SSH authentication** | Open source | N/A | :ak-enterprise | Available for early preview. Only supports Linux SSH targets. macOS targets in development. |
| **Sudo authorization** | Open source | N/A | N/A | Available for early preview. |
| **Device compliance** | Open source | Open source | Open source | Available for early preview. |
| **Advanced device compliance** | :ak-enterprise | :ak-enterprise | :ak-enterprise | In development. |
| **authentik Agent ** | Open source | Open source | Open source | Available for early preview. |
| **Fleet Connectors** | :ak-enterprise | :ak-enterprise | :ak-enterprise | Available for early preview. |
| **Other Connectors** (Entra, Intune, Cloudflare WARP etc) | :ak-enterprise | :ak-enterprise | :ak-enterprise | In development. |
\*TODO: explain how PSSO works
## How to provide feedback and report bugs
Report issues via our [GitHub](https://github.com/goauthentik/platform/issues). Please include as much information as possible to assist us in troubleshooting.
## More information
For more information refer to each of the topics below:
<DocCardList />

View File

@@ -0,0 +1,38 @@
---
title: Manage devices
sidebar_label: Manage devices
tags: [devices, device info, device facts, managing devices]
---
The Devices page provides a list of all endpoint devices registered with your authentik deployment. Refer to [Device reporting](./device-compliance/device-reporting.md) for more details on how [device facts](./device-compliance/device-reporting.md#device-facts) are reported to authentik.
Each Endpoint Device can be expanded to view more detailed information.
## Accessing the Devices page
To access the Devices page, follow these steps:
1. Log in to authentik as an administrator and open the authentik Admin interface.
2. Navigate to **Endpoint Devices** > **Devices**.
3. Specific endpoint device information can be accessed by clicking on a device.
## Specific device
Once you have selected a specific endpoint device you will have access to the following tabs:
### Overview
Provides an overview of the endpoint device:
- **Device details**: basic facts about the device: name, hostname, serial number, operating system, firewall status and device access group.
- **Hardware**: basic hardware facts about the device: manufacturer, model, cpu, memory, disk encryption status, primary disk size, primary disk usage.
- **Connections**: shows the current [connectors](./device-compliance/connectors.md) that are enabled for the device and when the last [check-in](./device-compliance/device-reporting.md#device-check-in) occurred.
- **Users/Groups**: shows the users and groups that have access to the device. Controlled via [device access groups](./device-authentication/device-access-groups.mdx).
### Processes
Lists the processes that were running on the device when its last check-in occurred.
### Users
### Groups

View File

@@ -389,6 +389,34 @@ If you had persistence for Redis configured, you can delete the PVC and PV after
- web/flows: improvements for hCaptcha (cherry-pick #16882 to version-2025.10) (#18128)
- web/sfe: downgrade bootstrap that was accidentally upgraded (cherry-pick #18157 to version-2025.10) (#18171)
## Fixed in 2025.10.3
- core: list applications fix (cherry-pick #18798 to version-2025.10) (#18827)
- core: optimize list applications (cherry-pick #18330 to version-2025.10) (#18791)
- enterprise/stages/mtls: fix traefik certificate parsing (cherry-pick #18607 to version-2025.10) (#18645)
- flows: refresh unauthenticated tabs (cherry-pick #18621 to version-2025.10) (#18633)
- lib/sync/outgoing: check if there is a provider before creating tasks (cherry-pick #18394 to version-2025.10) (#18397)
- outpost/proxyv2: more tests, fix pg password with spaces, and existing session on restart (cherry-pick #18211 to version-2025.10) (#18742)
- outposts: set container healthcheck inline (cherry-pick #18298 to version-2025.10) (#18370)
- packages/django-channels-postgres: fix notify size check (cherry-pick #18347 to version-2025.10) (#18409)
- packages/django-dramatiq-postgres: broker: close django connections on consumer close (cherry-pick #18833 to version-2025.10) (#18835)
- providers/scim: compare users/groups before sending update request (cherry-pick #18456 to version-2025.10) (#18465)
- root: fix missing authentik_device cookie causing error (cherry-pick #18642 to version-2025.10) (#18644)
- root: skip current tab when refreshing others (cherry-pick #18674 to version-2025.10) (#18675)
- sources/ldap: make server info optional (cherry-pick #18648 to version-2025.10) (#18654)
- stages/prompt: set allow_blank for \_read_only fields (cherry-pick #18297 to version-2025.10) (#18406)
- web: Fix row expansion on modal trigger buttons. (cherry-pick #18412 to version-2025.10) (#18647)
- web: Fix stale table rows (cherry-pick #17940 to version-2025.10) (#18373)
- web: Fix stale table rows (cherry-pick #17940 to version-2025.10) (#18408)
- web: Hide device picker when challenges are not present. (cherry-pick #18611 to version-2025.10) (#18681)
- web: Improved table selection behavior (cherry-pick #18622 to version-2025.10) (#18685)
- web: revert Fix stale table rows (cherry-pick #17940 to version-2025.10) (#18407)
- web/admin: add entitlement search (cherry-pick #18291 to version-2025.10) (#18390)
- web/admin: fix brands default switch label (cherry-pick #18518 to version-2025.10) (#18522)
- web/admin: fix event volume chart not updating with query (cherry-pick #18649 to version-2025.10) (#18653)
- web/admin: fix wording in password stage (cherry-pick #18393 to version-2025.10) (#18395)
- web/admin: fixes capitalization in application wizard title (cherry-pick #17959 to version-2025.10) (#17962)
## API Changes
#### What's Changed

File diff suppressed because it is too large Load Diff

View File

@@ -318,6 +318,7 @@ const items = [
"add-secure-apps/flows-stages/stages/captcha/index",
"add-secure-apps/flows-stages/stages/deny",
"add-secure-apps/flows-stages/stages/email/index",
"add-secure-apps/flows-stages/stages/endpoint/index",
"add-secure-apps/flows-stages/stages/identification/index",
"add-secure-apps/flows-stages/stages/invitation/index",
"add-secure-apps/flows-stages/stages/mtls/index",
@@ -675,6 +676,138 @@ const items = [
{
//#endregion
//#region Endpoint Devices
type: "category",
label: "Endpoint Devices (Early Preview)",
collapsed: true,
link: {
type: "doc",
id: "endpoint-devices/index",
},
items: [
{
//#endregion
//#region authentik Agent
type: "category",
label: "authentik Agent",
collapsed: true,
link: {
type: "doc",
id: "endpoint-devices/authentik-agent/index",
},
items: [
"endpoint-devices/authentik-agent/configuration",
{
//#endregion
//#region authentik Agent Deployment
type: "category",
label: "Deployment",
collapsed: true,
link: {
type: "doc",
id: "endpoint-devices/authentik-agent/agent-deployment/index",
},
items: [
"endpoint-devices/authentik-agent/agent-deployment/mdm",
"endpoint-devices/authentik-agent/agent-deployment/linux",
"endpoint-devices/authentik-agent/agent-deployment/macos",
"endpoint-devices/authentik-agent/agent-deployment/windows",
],
},
"endpoint-devices/authentik-agent/authentik-cli",
"endpoint-devices/authentik-agent/development",
{
//#endregion
//#region authentik Agent Release Notes
type: "category",
label: "Release notes",
description: "Release Notes for recent authentik agent versions",
collapsed: true,
link: {
type: "doc",
id: "endpoint-devices/authentik-agent/release-notes/index",
},
items: ["endpoint-devices/authentik-agent/release-notes/temp"],
},
],
},
"endpoint-devices/manage-devices",
{
//#endregion
//#region Device Authentication
type: "category",
label: "Device authentication",
collapsed: true,
link: {
type: "doc",
id: "endpoint-devices/device-authentication/index",
},
items: [
"endpoint-devices/device-authentication/device-access-groups",
{
//#endregion
//#region local device login
type: "category",
label: "Local device login",
collapsed: true,
link: {
type: "doc",
id: "endpoint-devices/device-authentication/local-device-login/index",
},
items: [
"endpoint-devices/device-authentication/local-device-login/linux",
"endpoint-devices/device-authentication/local-device-login/windows",
],
},
"endpoint-devices/device-authentication/ssh-authentication",
"endpoint-devices/device-authentication/sudo-authorization",
{
//#endregion
//#region cli app authentication
type: "category",
label: "CLI application authentication",
collapsed: true,
link: {
type: "doc",
id: "endpoint-devices/device-authentication/cli-app-authentication/index",
},
items: [
"endpoint-devices/device-authentication/cli-app-authentication/aws",
"endpoint-devices/device-authentication/cli-app-authentication/k8s",
],
},
],
},
{
//#endregion
//#region Device Compliance
type: "category",
label: "Device compliance",
collapsed: true,
link: {
type: "doc",
id: "endpoint-devices/device-compliance/index",
},
items: [
"endpoint-devices/device-compliance/configuration",
"endpoint-devices/device-compliance/connectors",
"endpoint-devices/device-compliance/device-reporting",
"endpoint-devices/device-compliance/device-compliance-policy",
"endpoint-devices/device-compliance/browser-extension",
],
},
],
},
{
//#endregion
//#region Developer Documentation
type: "category",
label: "Developer Documentation",

View File

@@ -50,7 +50,7 @@ To assign or remove _object_ permissions for a specific role:
2. Select a specific role by clicking on the role's name.
3. Click the **Permissions** tab at the top of the page, then click the **Permissions on this object** tab
4. To assign permissions that another _role_ has on this specific role:
1. Click **Assign role permissions**.
1. Click **Assign Object Permission**.
2. In the **Role** drop-down, select the role object.
3. Use the toggles to set which permissions on that selected role object you want to grant to the specific role.
4. Click **Assign** to save your settings and close the box.

View File

@@ -1,6 +1,10 @@
import "./styles.css";
import { createVersionURL, parseBranchSemVer } from "#components/VersionPicker/utils.ts";
import {
createVersionURL,
normalizeReleaseName,
parseBranchSemVer,
} from "#components/VersionPicker/utils.ts";
import type {
AKReleaseFrontMatter,
@@ -74,15 +78,17 @@ export const VersionDropdown = memo<VersionDropdownProps>((props) => {
</>
) : null}
{visibleReleases.map((semVer, idx) => {
{visibleReleases.map((releaseName, idx) => {
const semVer = normalizeReleaseName(releaseName);
let label = semVer;
const frontmatter = frontMatterRecord[semVer];
if (frontmatter?.unlisted || frontmatter?.draft) {
if (frontmatter?.draft) {
return null;
}
if (idx === 0) {
if (idx === 0 && !frontmatter?.beta) {
label += " (Current Release)";
}

View File

@@ -1,4 +1,4 @@
import type { AKReleasesPluginData } from "@goauthentik/docusaurus-theme/releases/plugin";
import type { AKReleasesPluginData } from "@goauthentik/docusaurus-theme/releases/common";
import { usePluginData } from "@docusaurus/useGlobalData";
import useIsBrowser from "@docusaurus/useIsBrowser";
@@ -7,10 +7,15 @@ import { coerce } from "semver";
export const LocalhostAliases: ReadonlySet<string> = new Set(["localhost", "127.0.0.1"]);
export function normalizeReleaseName(name: string): string {
return name.split(" ")[0] || name;
}
/**
* Given a semver, create the URL for the version.
*/
export function createVersionURL(semver: string): string {
export function createVersionURL(releaseName: string): string {
const semver = normalizeReleaseName(releaseName);
const subdomain = `version-${semver.replace(".", "-")}`;
return `https://${subdomain}.goauthentik.io`;

View File

@@ -9,6 +9,7 @@
/**
* @typedef {object} AKReleaseFrontMatter
* @property {boolean} [beta] Whether the release is a beta.
* @property {boolean} [draft] Whether the release is a draft.
* @property {boolean} [unlisted] Whether the release is unlisted.
*/

View File

@@ -58,6 +58,10 @@ export function collectReleaseFiles(releasesParentDirectory) {
const { frontMatter } = parseFileContentFrontMatter(fileContent);
latestRelease.frontMatter = frontMatter;
if (latestRelease.frontMatter.beta) {
latestRelease.name += " (Release Candidate)";
}
}
return releaseFiles;
@@ -73,7 +77,14 @@ export function createReleaseSidebarEntries(releaseFiles) {
/**
* @type {SidebarItemConfig[]}
*/
let sidebarEntries = releaseFiles.map((fileEntry) => fileEntry.path);
let sidebarEntries = releaseFiles.map((fileEntry) => {
return {
type: "doc",
id: fileEntry.path,
label: fileEntry.name,
key: `release-${fileEntry.name}`,
};
});
if (releaseFiles.length > SUPPORTED_RELEASE_COUNT) {
// Then we add the rest of the releases as a category.

View File

@@ -1,60 +0,0 @@
import { useDoc } from "@docusaurus/plugin-content-docs/client";
import {
ThemeClassNames,
UnlistedBannerMessage,
UnlistedBannerTitle,
UnlistedMetadata,
} from "@docusaurus/theme-common";
import Translate from "@docusaurus/Translate";
import Admonition from "@theme/Admonition";
import type { Props } from "@theme/ContentVisibility/Unlisted";
import clsx from "clsx";
import React, { type ReactNode } from "react";
function UnlistedBanner({ className }: Props) {
const context = useDoc();
if (context.metadata.id?.startsWith("releases")) {
return (
<Admonition
type="note"
title={
<Translate
id="theme.contentVisibility.unlistedBanner.preRelease.title"
description="The unlisted content banner title"
>
Pre-Release Documentation
</Translate>
}
className={clsx(className, ThemeClassNames.common.unlistedBanner)}
>
<Translate
id="theme.contentVisibility.unlistedBanner.preRelease.message"
description="The unlisted content banner message"
>
This documentation is for an upcoming version of authentik. It may be incomplete
or subject to changes before the final release.
</Translate>
</Admonition>
);
}
return (
<Admonition
type="caution"
title={<UnlistedBannerTitle />}
className={clsx(className, ThemeClassNames.common.unlistedBanner)}
>
<UnlistedBannerMessage />
</Admonition>
);
}
export default function Unlisted(props: Props): ReactNode {
return (
<>
<UnlistedMetadata />
<UnlistedBanner {...props} />
</>
);
}

Some files were not shown because too many files have changed in this diff Show More