Compare commits

..

4 Commits

Author SHA1 Message Date
8128d8dab5 fix rac cache missing key
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-07-01 21:49:31 +02:00
f4a68c7878 use nested for RAC
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-07-01 21:46:35 +02:00
7ab17822e3 add support for nested routes
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-07-01 21:46:16 +02:00
76da77f26e fix ql schema
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-07-01 21:20:10 +02:00
35 changed files with 887 additions and 817 deletions

View File

@ -0,0 +1,67 @@
from rest_framework.routers import DefaultRouter as UpstreamDefaultRouter
from rest_framework.viewsets import ViewSet
from rest_framework_nested.routers import NestedMixin
class DefaultRouter(UpstreamDefaultRouter):
include_format_suffixes = False
class NestedRouter(DefaultRouter):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.nested_routers = []
class nested:
def __init__(self, parent: "NestedRouter", prefix: str):
self.parent = parent
self.prefix = prefix
self.inner = None
def nested(self, lookup: str, prefix: str, viewset: type[ViewSet]):
if not self.inner:
self.inner = NestedDefaultRouter(self.parent, self.prefix, lookup=lookup)
self.inner.register(prefix, viewset)
return self
@property
def urls(self):
return self.parent.urls
def register(self, prefix, viewset, basename=None):
super().register(prefix, viewset, basename)
nested_router = self.nested(self, prefix)
self.nested_routers.append(nested_router)
return nested_router
def get_urls(self):
urls = super().get_urls()
for nested in self.nested_routers:
if not nested.inner:
continue
urls.extend(nested.inner.urls)
return urls
class NestedDefaultRouter(NestedMixin, DefaultRouter):
...
# def __init__(self, *args, **kwargs):
# self.args = args
# self.kwargs = kwargs
# self.routes = []
# def register(self, *args, **kwargs):
# self.routes.append((args, kwargs))
# @property
# def urls(self):
# class r(NestedMixin, DefaultRouter):
# ...
# router = r(*self.args, **self.kwargs)
# for route_args, route_kwrags in self.routes:
# router.register(*route_args, **route_kwrags)
# return router
root_router = DefaultRouter()

View File

@ -6,18 +6,15 @@ from django.urls import path
from django.urls.resolvers import URLPattern
from django.views.decorators.cache import cache_page
from drf_spectacular.views import SpectacularAPIView
from rest_framework import routers
from structlog.stdlib import get_logger
from authentik.api.v3.config import ConfigView
from authentik.api.v3.routers import root_router
from authentik.api.views import APIBrowserView
from authentik.lib.utils.reflection import get_apps
LOGGER = get_logger()
router = routers.DefaultRouter()
router.include_format_suffixes = False
_other_urls = []
for _authentik_app in get_apps():
try:
@ -38,7 +35,7 @@ for _authentik_app in get_apps():
if isinstance(url, URLPattern):
_other_urls.append(url)
else:
router.register(*url)
root_router.register(*url)
LOGGER.debug(
"Mounted API URLs",
app_name=_authentik_app.name,
@ -49,7 +46,7 @@ urlpatterns = (
[
path("", APIBrowserView.as_view(), name="schema-browser"),
]
+ router.urls
+ root_router.urls
+ _other_urls
+ [
path("root/config/", ConfigView.as_view(), name="config"),

View File

@ -6,7 +6,7 @@ from djangoql.ast import Name
from djangoql.exceptions import DjangoQLError
from djangoql.queryset import apply_search
from djangoql.schema import DjangoQLSchema
from rest_framework.filters import BaseFilterBackend, SearchFilter
from rest_framework.filters import SearchFilter
from rest_framework.request import Request
from structlog.stdlib import get_logger
@ -39,7 +39,8 @@ class BaseSchema(DjangoQLSchema):
return super().resolve_name(name)
class QLSearch(BaseFilterBackend):
# Inherits from SearchFilter to keep the schema correctly
class QLSearch(SearchFilter):
"""rest_framework search filter which uses DjangoQL"""
def __init__(self):

View File

@ -40,9 +40,16 @@ class ConnectionTokenViewSet(
):
"""ConnectionToken Viewset"""
queryset = ConnectionToken.objects.all().select_related("session", "endpoint")
queryset = ConnectionToken.objects.none()
serializer_class = ConnectionTokenSerializer
filterset_fields = ["endpoint", "session__user", "provider"]
search_fields = ["endpoint__name", "provider__name"]
ordering = ["endpoint__name", "provider__name"]
filterset_fields = ["endpoint", "session__user"]
search_fields = ["endpoint__name", "session__user__username"]
ordering = ["endpoint__name", "session__user__username"]
owner_field = "session__user"
def get_queryset(self):
return (
ConnectionToken.objects.all()
.select_related("session", "endpoint")
.filter(provider=self.kwargs["provider_pk"])
)

View File

@ -22,9 +22,9 @@ from authentik.rbac.filters import ObjectFilter
LOGGER = get_logger()
def user_endpoint_cache_key(user_pk: str) -> str:
def user_endpoint_cache_key(user_pk: str, provider_pk: str) -> str:
"""Cache key where endpoint list for user is saved"""
return f"goauthentik.io/providers/rac/endpoint_access/{user_pk}"
return f"goauthentik.io/providers/rac/endpoint_access/{user_pk}/{provider_pk}"
class EndpointSerializer(ModelSerializer):
@ -65,12 +65,15 @@ class EndpointSerializer(ModelSerializer):
class EndpointViewSet(UsedByMixin, ModelViewSet):
"""Endpoint Viewset"""
queryset = Endpoint.objects.all()
queryset = Endpoint.objects.none()
serializer_class = EndpointSerializer
filterset_fields = ["name", "provider"]
filterset_fields = ["name"]
search_fields = ["name", "protocol"]
ordering = ["name", "protocol"]
def get_queryset(self):
return Endpoint.objects.filter(provider=self.kwargs["provider_pk"])
def _filter_queryset_for_list(self, queryset: QuerySet) -> QuerySet:
"""Custom filter_queryset method which ignores guardian, but still supports sorting"""
for backend in list(self.filter_backends):
@ -120,14 +123,11 @@ class EndpointViewSet(UsedByMixin, ModelViewSet):
if not should_cache:
allowed_endpoints = self._get_allowed_endpoints(queryset)
if should_cache:
allowed_endpoints = cache.get(user_endpoint_cache_key(self.request.user.pk))
key = user_endpoint_cache_key(self.request.user.pk, self.kwargs["provider_pk"])
allowed_endpoints = cache.get(key)
if not allowed_endpoints:
LOGGER.debug("Caching allowed endpoint list")
allowed_endpoints = self._get_allowed_endpoints(queryset)
cache.set(
user_endpoint_cache_key(self.request.user.pk),
allowed_endpoints,
timeout=86400,
)
cache.set(key, allowed_endpoints, timeout=86400)
serializer = self.get_serializer(allowed_endpoints, many=True)
return self.get_paginated_response(serializer.data)

View File

@ -43,5 +43,5 @@ def pre_delete_connection_token_disconnect(sender, instance: ConnectionToken, **
@receiver([post_save, post_delete], sender=Endpoint)
def post_save_post_delete_endpoint(**_):
"""Clear user's endpoint cache upon endpoint creation or deletion"""
keys = cache.keys(user_endpoint_cache_key("*"))
keys = cache.keys(user_endpoint_cache_key("*", "*"))
cache.delete_many(keys)

View File

@ -2,6 +2,7 @@
from django.urls import path
from authentik.api.v3.routers import NestedRouter
from authentik.outposts.channels import TokenOutpostMiddleware
from authentik.providers.rac.api.connection_tokens import ConnectionTokenViewSet
from authentik.providers.rac.api.endpoints import EndpointViewSet
@ -38,8 +39,10 @@ websocket_urlpatterns = [
]
api_urlpatterns = [
("providers/rac", RACProviderViewSet),
*NestedRouter()
.register("providers/rac", RACProviderViewSet)
.nested("provider", "endpoints", EndpointViewSet)
.nested("provider", "connection_tokens", ConnectionTokenViewSet)
.urls,
("propertymappings/provider/rac", RACPropertyMappingViewSet),
("rac/endpoints", EndpointViewSet),
("rac/connection_tokens", ConnectionTokenViewSet),
]

View File

@ -28,6 +28,7 @@ dependencies = [
"djangorestframework-guardian==0.3.0",
"djangorestframework==3.16.0",
"docker==7.1.0",
"drf-nested-routers==0.94.2",
"drf-orjson-renderer==1.7.3",
"drf-spectacular==0.28.0",
"dumb-init==1.2.5.post1",

1092
schema.yml

File diff suppressed because it is too large Load Diff

17
uv.lock generated
View File

@ -191,6 +191,7 @@ dependencies = [
{ name = "djangorestframework" },
{ name = "djangorestframework-guardian" },
{ name = "docker" },
{ name = "drf-nested-routers" },
{ name = "drf-orjson-renderer" },
{ name = "drf-spectacular" },
{ name = "dumb-init" },
@ -290,6 +291,7 @@ requires-dist = [
{ name = "djangorestframework", git = "https://github.com/goauthentik/django-rest-framework?rev=896722bab969fabc74a08b827da59409cf9f1a4e" },
{ name = "djangorestframework-guardian", specifier = "==0.3.0" },
{ name = "docker", specifier = "==7.1.0" },
{ name = "drf-nested-routers", specifier = "==0.94.2" },
{ name = "drf-orjson-renderer", specifier = "==1.7.3" },
{ name = "drf-spectacular", specifier = "==0.28.0" },
{ name = "dumb-init", specifier = "==1.2.5.post1" },
@ -1190,6 +1192,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/2f/71/1f500097efe09e04c3be862ab26c997314237a8b0a16dc3e3047fee23f4c/drf_jsonschema_serializer-3.0.0-py3-none-any.whl", hash = "sha256:d0e5cce095a5638b0bb7867aa060ed59ab9eed2f54ba5058dd9b483c9c887ed5", size = 8994, upload-time = "2024-06-26T13:09:59.929Z" },
]
[[package]]
name = "drf-nested-routers"
version = "0.94.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "django" },
{ name = "djangorestframework" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f6/98/2d29f3ecd337255bc2775b9addef347b6fd30ff7b3757649d0e50602ba08/drf_nested_routers-0.94.2.tar.gz", hash = "sha256:aa70923b716dc47cd93b8129b06be6c15706b405cf5f718f59cb8eed01de59cc", size = 22845, upload-time = "2025-05-14T17:03:50.896Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/62/dc/6bdb857a631fe6558db18a009c93ae16c3ad94fef0b7be7a3aa35c3264fa/drf_nested_routers-0.94.2-py2.py3-none-any.whl", hash = "sha256:74dbdceeae2a32f8668ba0df8e3eeabeb9b1c64d2621d914901ae653e4e3bcff", size = 36367, upload-time = "2025-05-14T17:03:49.257Z" },
]
[[package]]
name = "drf-orjson-renderer"
version = "1.7.3"
@ -1831,8 +1846,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/79/21/6e7c060822a3c954ff085e5e1b94b4a25757c06529eac91e550f3f5cd8b8/lxml-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6da7cd4f405fd7db56e51e96bff0865b9853ae70df0e6720624049da76bde2da", size = 8414372, upload-time = "2025-06-26T16:26:39.079Z" },
{ url = "https://files.pythonhosted.org/packages/a4/f6/051b1607a459db670fc3a244fa4f06f101a8adf86cda263d1a56b3a4f9d5/lxml-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b34339898bb556a2351a1830f88f751679f343eabf9cf05841c95b165152c9e7", size = 4593940, upload-time = "2025-06-26T16:26:41.891Z" },
{ url = "https://files.pythonhosted.org/packages/8e/74/dd595d92a40bda3c687d70d4487b2c7eff93fd63b568acd64fedd2ba00fe/lxml-6.0.0-cp313-cp313-manylinux2010_i686.manylinux2014_i686.manylinux_2_12_i686.manylinux_2_17_i686.whl", hash = "sha256:51a5e4c61a4541bd1cd3ba74766d0c9b6c12d6a1a4964ef60026832aac8e79b3", size = 5214329, upload-time = "2025-06-26T16:26:44.669Z" },
{ url = "https://files.pythonhosted.org/packages/52/46/3572761efc1bd45fcafb44a63b3b0feeb5b3f0066886821e94b0254f9253/lxml-6.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d18a25b19ca7307045581b18b3ec9ead2b1db5ccd8719c291f0cd0a5cec6cb81", size = 4947559, upload-time = "2025-06-28T18:47:31.091Z" },
{ url = "https://files.pythonhosted.org/packages/94/8a/5e40de920e67c4f2eef9151097deb9b52d86c95762d8ee238134aff2125d/lxml-6.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d4f0c66df4386b75d2ab1e20a489f30dc7fd9a06a896d64980541506086be1f1", size = 5102143, upload-time = "2025-06-28T18:47:33.612Z" },
{ url = "https://files.pythonhosted.org/packages/7c/4b/20555bdd75d57945bdabfbc45fdb1a36a1a0ff9eae4653e951b2b79c9209/lxml-6.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f4b481b6cc3a897adb4279216695150bbe7a44c03daba3c894f49d2037e0a24", size = 5021931, upload-time = "2025-06-26T16:26:47.503Z" },
{ url = "https://files.pythonhosted.org/packages/d4/dd/39c8507c16db6031f8c1ddf70ed95dbb0a6d466a40002a3522c128aba472/lxml-6.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae06fbab4f1bb7db4f7c8ca9897dc8db4447d1a2b9bee78474ad403437bcc29", size = 5247467, upload-time = "2025-06-26T16:26:49.998Z" },
{ url = "https://files.pythonhosted.org/packages/4d/56/732d49def0631ad633844cfb2664563c830173a98d5efd9b172e89a4800d/lxml-6.0.0-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:1fa377b827ca2023244a06554c6e7dc6828a10aaf74ca41965c5d8a4925aebb4", size = 4720601, upload-time = "2025-06-26T16:26:52.564Z" },

View File

@ -12,7 +12,7 @@ import { customElement, property } from "lit/decorators.js";
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
import { ConnectionToken, RACProvider, RacApi } from "@goauthentik/api";
import { ConnectionToken, ProvidersApi, RACProvider } from "@goauthentik/api";
@customElement("ak-rac-connection-token-list")
export class ConnectionTokenListPage extends Table<ConnectionToken> {
@ -37,9 +37,9 @@ export class ConnectionTokenListPage extends Table<ConnectionToken> {
}
async apiEndpoint(): Promise<PaginatedResponse<ConnectionToken>> {
return new RacApi(DEFAULT_CONFIG).racConnectionTokensList({
return new ProvidersApi(DEFAULT_CONFIG).providersRacConnectionTokensList({
...(await this.defaultEndpointConfig()),
provider: this.provider?.pk,
providerPk: this.provider!.pk,
sessionUser: this.userId,
});
}
@ -56,12 +56,14 @@ export class ConnectionTokenListPage extends Table<ConnectionToken> {
];
}}
.usedBy=${(item: ConnectionToken) => {
return new RacApi(DEFAULT_CONFIG).racConnectionTokensUsedByList({
return new ProvidersApi(DEFAULT_CONFIG).providersRacConnectionTokensUsedByList({
providerPk: this.provider!.pk,
connectionTokenUuid: item.pk || "",
});
}}
.delete=${(item: ConnectionToken) => {
return new RacApi(DEFAULT_CONFIG).racConnectionTokensDestroy({
return new ProvidersApi(DEFAULT_CONFIG).providersRacConnectionTokensDestroy({
providerPk: this.provider!.pk,
connectionTokenUuid: item.pk || "",
});
}}

View File

@ -12,7 +12,7 @@ import { TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { AuthModeEnum, Endpoint, ProtocolEnum, RacApi } from "@goauthentik/api";
import { AuthModeEnum, Endpoint, ProtocolEnum, ProvidersApi } from "@goauthentik/api";
import { propertyMappingsProvider, propertyMappingsSelector } from "./RACProviderFormHelpers.js";
@ -22,7 +22,8 @@ export class EndpointForm extends ModelForm<Endpoint, string> {
providerID?: number;
loadInstance(pk: string): Promise<Endpoint> {
return new RacApi(DEFAULT_CONFIG).racEndpointsRetrieve({
return new ProvidersApi(DEFAULT_CONFIG).providersRacEndpointsRetrieve({
providerPk: this.providerID!,
pbmUuid: pk,
});
}
@ -41,12 +42,14 @@ export class EndpointForm extends ModelForm<Endpoint, string> {
data.provider = this.instance.provider;
}
if (this.instance) {
return new RacApi(DEFAULT_CONFIG).racEndpointsPartialUpdate({
return new ProvidersApi(DEFAULT_CONFIG).providersRacEndpointsPartialUpdate({
providerPk: this.providerID!,
pbmUuid: this.instance.pk || "",
patchedEndpointRequest: data,
});
}
return new RacApi(DEFAULT_CONFIG).racEndpointsCreate({
return new ProvidersApi(DEFAULT_CONFIG).providersRacEndpointsCreate({
providerPk: this.providerID!,
endpointRequest: data,
});
}

View File

@ -17,8 +17,8 @@ import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList
import {
Endpoint,
ProvidersApi,
RACProvider,
RacApi,
RbacPermissionsAssignedByUsersListModelEnum,
} from "@goauthentik/api";
@ -43,9 +43,9 @@ export class EndpointListPage extends Table<Endpoint> {
}
async apiEndpoint(): Promise<PaginatedResponse<Endpoint>> {
return new RacApi(DEFAULT_CONFIG).racEndpointsList({
return new ProvidersApi(DEFAULT_CONFIG).providersRacEndpointsList({
...(await this.defaultEndpointConfig()),
provider: this.provider?.pk,
providerPk: this.provider!.pk,
superuserFullList: true,
});
}
@ -70,12 +70,14 @@ export class EndpointListPage extends Table<Endpoint> {
];
}}
.usedBy=${(item: Endpoint) => {
return new RacApi(DEFAULT_CONFIG).racEndpointsUsedByList({
return new ProvidersApi(DEFAULT_CONFIG).providersRacEndpointsUsedByList({
providerPk: this.provider!.pk,
pbmUuid: item.pk,
});
}}
.delete=${(item: Endpoint) => {
return new RacApi(DEFAULT_CONFIG).racEndpointsDestroy({
return new ProvidersApi(DEFAULT_CONFIG).providersRacEndpointsDestroy({
providerPk: this.provider!.pk,
pbmUuid: item.pk,
});
}}

View File

@ -6,7 +6,7 @@ import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import { Application, Endpoint, RacApi } from "@goauthentik/api";
import { Application, Endpoint, ProvidersApi } from "@goauthentik/api";
@customElement("ak-library-rac-endpoint-launch")
export class RACLaunchEndpointModal extends TableModal<Endpoint> {
@ -30,9 +30,9 @@ export class RACLaunchEndpointModal extends TableModal<Endpoint> {
app?: Application;
async apiEndpoint(): Promise<PaginatedResponse<Endpoint>> {
const endpoints = await new RacApi(DEFAULT_CONFIG).racEndpointsList({
const endpoints = await new ProvidersApi(DEFAULT_CONFIG).providersRacEndpointsList({
...(await this.defaultEndpointConfig()),
provider: this.app?.provider || 0,
providerPk: this.app?.provider || 0,
});
if (this.open && endpoints.pagination.count === 1) {
this.clickHandler(endpoints.results[0]);

View File

@ -45,11 +45,11 @@ Configuration details such as credentials can be specified through _settings_, w
### Connection settings
Each connection is authorized through authentik policy objects that are bound to the application and the endpoint. Additional verification can be done with the authorization flow.
Each connection is authorized through authentik Policy objects that are bound to the application and the endpoint. Additional verification can be done with the authorization flow.
A new connection is created every time an endpoint is selected in the [User Interface](../../../customize/interfaces/user). After the user's authentik session expires, the connection is terminated. Additionally, the connection timeout can be specified in the provider, which applies even if the user is still authenticated. The connection can also be terminated manually from the **Connections** tab of the RAC provider.
A new connection is created every time an endpoint is selected in the [User Interface](../../../customize/interfaces/user/customization.mdx). Once the user's authentik session expires, the connection is terminated. Additionally, the connection timeout can be specified in the provider, which applies even if the user is still authenticated. The connection can also be terminated manually.
Additionally, it is possible to modify the connection settings through the authorization flow. Configuration set in `connection_settings` in the flow plan context will be merged with other settings as shown above.
Additionally it is possible to modify the connection settings through the authorization flow. Configuration set in `connection_settings` in the flow plan context will be merged with other settings as shown above.
The RAC provider utilises [Apache Guacamole](https://guacamole.apache.org/) for establishing SSH, RDP and VNC connections. RAC supports the use of Apache Guacamole connection configurations.

View File

@ -6,8 +6,8 @@ You can customize the behaviour, look, and available resources for your authenti
- [Policies](./policies/working_with_policies.md)
- Interfaces:
- [Flow interface](./interfaces/flow)
- [User interface](./interfaces/user)
- [Admin interface](./interfaces/admin)
- [Flows](./interfaces/flow/customization.mdx)
- [User interface](./interfaces/user/customization.mdx)
- [Admin interface](./interfaces/admin/customization.mdx)
- [Blueprints](./blueprints/index.mdx)
- [Branding](./branding.md)

View File

@ -1,19 +0,0 @@
### Enabling/disabling features
The features listed below can be enabled or disabled through attributes set on the Brand. By default, all of the listed features are enabled. To disable a specific feature, set its value to `false`.
#### `settings.enabledFeatures.apiDrawer`
Display the API Request drawer in the upper tool bar.
#### `settings.enabledFeatures.notificationDrawer`
Display the Notification drawer in the upper tool bar.
#### `settings.enabledFeatures.settings`
Display the Settings link in the upper tool bar.
#### `settings.enabledFeatures.search`
Display the Search bar in the upper tool bar.

View File

@ -1,36 +0,0 @@
### General settings (both Admin and User interfaces)
#### `settings.navbar.userDisplay`
Configure what is shown in the top right corner. Defaults to `username`. Available options: `username`, `name`, `email`
#### `settings.theme.base`
Configure the base color scheme or toggle between dark and light modes. The default setting is `automatic`, which adapts based on the user’s browser preference. Available options: `automatic`, `dark`, `light`.
**Example**:
```
settings:
theme:
base: dark
```
#### `settings.theme.background`
Optional CSS that is applied to the background of the User interface, for example to set a custom background color, gradient, or image.
```yaml
settings:
theme:
background: >
background: url('https://picsum.photos/1920/1080');
filter: blur(8px);
background-position: center;
background-repeat: no-repeat;
background-size: cover;
```
#### `settings.locale`
The locale which can be configured in the user settings by default. This can be used to preset locales for groups of users, but still let them choose their own preferred locale.

View File

@ -1,11 +1,3 @@
## Global customization
### Global customization
To customize the following brand settings, log in to the Admin interface and navigate to **System > Brands > Brand settings**.
- Title
- Logo
- Favicon
- Default flow background image
- Custom CSS
For more details, see the [Brand settings](../../../sys-mgmt/brands.md#branding-settings) documentation.
See [Brand Settings](../../../sys-mgmt/brands.md#branding-settings)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

View File

@ -0,0 +1,17 @@
# Customization
### `settings.pagination.perPage`
How many items should be retrieved per page. Defaults to 20.
### `settings.defaults.userPath`
Default user path which is opened when opening the user list. Defaults to `users`.
### `settings.theme.base`
Configure the base color scheme. Defaults to `automatic`, which switches between dark and light mode based on the users' browsers' preference. Choices: `automatic`, `dark`, `light`.
import Global from "../_global/global.mdx";
<Global />

View File

@ -1,46 +0,0 @@
---
title: Customize the Admin interface
sidebar_label: Admin interface
---
The Admin interface can be customized using attributes configured in [Brands](../../../sys-mgmt/brands.md)
To add, remove, or modify attributes for a brand, log in to the Admin interface and navigate to **System > Brands > Other global settings > Attributes**.
Most attributes defined in a brand apply to _both_ the User and Admin interfaces. However, any settings that are specific to only the Admin interface are explicitly noted as such below.
The following screenshot shows the syntax for setting several attributes for a brand: dark mode, a 3-column display of applications on **My applications** page of the User interface, and hiding the API and Notifications drawers from the Admin interface tool bar.
![](./admin-interface-attributes.png)
## Custom settings
The following settings for attributes are grouped by:
- `enabledFeatures` settings
- General settings (used on both the Admin interface and the User interface)
- Admin interface only
import Enabledfeatureslist from "../\_enabledfeatureslist.mdx";
<Enabledfeatureslist />
import Generalattributes from "../\_generalattributes.mdx";
<Generalattributes />
### Settings for the Admin interface only
The following settings can only be used to customize the Admin interface, not the User interface.
#### `settings.pagination.perPage`
How many items should be retrieved per page. Defaults to 20.
#### `settings.defaults.userPath`
Default user path which is used when opening the user list. Defaults to `users`.
import Global from "../_global/global.mdx";
<Global />

View File

@ -0,0 +1,11 @@
# Customization
Since flows can be executed authenticated or unauthenticated, the default settings can be set via brands _attributes_.
### `settings.theme.base`
Configure the base color scheme. Defaults to `automatic`, which switches between dark and light mode based on the users' browsers' preference. Choices: `automatic`, `dark`, `light`.
import Global from "../_global/global.mdx";
<Global />

View File

@ -1,19 +0,0 @@
---
title: Customize a flow
sidebar_label: Flow interface
---
Typically, settings for flows are defined as defaults in the [Brand settings](../../../sys-mgmt/brands.md). However, it’s important to note that some flows are executed before the specific user is authenticated and thus before authentik can determine which user is viewing the flow (for example, the `default-authentication-flow`!). Consequently, using default settings for all flows ensures a more consistent user experience.
Two settings that you can configure per flow are the _background image_ for the flow, and the _layout_.
## Customize a flow's background image
You can define a:
- Default background image for all flows, set in the instance's [brand](../../../sys-mgmt/brands.md)
- A background image for [one or more specific flows](../../../add-secure-apps/flows-stages/flow/index.md#flow-configuration-options) (overrides the default)
## Set the layout for a flow
To define the layout for a flow, edit the flow and under **Appearance settings > Layout** select how the UI displays the flow when it is executed; with stacked elements, content left or right, and sidebar left or right.

View File

@ -0,0 +1,64 @@
# Customization
The user interface can be customized through attributes, and will be inherited from a users' groups.
## Enabling/disabling features
The following features can be enabled/disabled. By default, all of them are enabled:
- `settings.enabledFeatures.apiDrawer`
API Request drawer in navbar
- `settings.enabledFeatures.notificationDrawer`
Notification drawer in navbar
- `settings.enabledFeatures.settings`
Settings link in navbar
- `settings.enabledFeatures.applicationEdit`
Application edit in library (only shown when user is superuser)
- `settings.enabledFeatures.search`
Search bar
## Other configuration
### `settings.navbar.userDisplay`
Configure what is shown in the top right corner. Defaults to `username`. Choices: `username`, `name`, `email`
### `settings.theme.base`
Configure the base color scheme. Defaults to `automatic`, which switches between dark and light mode based on the users' browsers' preference. Choices: `automatic`, `dark`, `light`.
### `settings.theme.background`
Optional CSS which is applied in the background of the background of the user interface; for example
```yaml
settings:
theme:
background: >
background: url('https://picsum.photos/1920/1080');
filter: blur(8px);
background-position: center;
background-repeat: no-repeat;
background-size: cover;
```
### `settings.layout.type`
Which layout to use for the _My applications_ view. Defaults to `row`. Choices: `row`, `2-column`, `3-column`
### `settings.locale`
The locale which can be configured in the user settings by default. This can be used to preset locales for groups of users, but still let them choose their own preferred locale
import Global from "../_global/global.mdx";
<Global />

View File

@ -1,44 +0,0 @@
---
title: Customize the User interface
sidebar_label: User interface
---
The User interface can be customized using attributes configured in [Brands](../../../sys-mgmt/brands.md).
To add, remove, or modify attributes for a brand, log in as an administrator and navigate to **System > Brands > Other global settings > Attributes**.
Most attributes defined in a brand apply to _both_ the User and Admin interfaces. However, any settings that are specific to only one interface are explicitly noted as such below.
The following screenshot shows the syntax for setting several attributes for a brand: light mode, a 3-column display of applications on **My applications** page, hiding the API drawer and the Notification drawer from the tool bar, and disallowing users to edit the applications on **My applications** page.
![](./user-interface-attributes.png)
## Custom settings
The following settings for attributes are grouped by:
- `enabledFeatures` settings
- General attributes (used on both the Admin interface and the User interface)
- User interface only
import Enabledfeatureslist from "../\_enabledfeatureslist.mdx";
<Enabledfeatureslist />
#### `settings.enabledFeatures.applicationEdit` (User interface only)
Display the Edit option for each application on the **My applications** page (only shown when user is superuser).
import Generalattributes from "../\_generalattributes.mdx";
<Generalattributes />
### Settings for the User interface only
#### `settings.layout.type`
Which layout to use for the **My applications** page. Defaults to `row`. Choices: `row`, `2-column`, `3-column`
import Global from "../_global/global.mdx";
<Global />

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

View File

@ -16,8 +16,8 @@ slug: "/releases/2023.3"
Documentation: [SCIM Provider](../../add-secure-apps/providers/scim/index.md)
- Theming improvements
- The custom.css file is now loaded in ShadowDOMs, allowing for much greater customization, as previously it was only possible to style elements outside of the ShadowDOM. See docs for the [User interface](../../customize/interfaces/user/index.mdx) and [Admin interface](../../customize/interfaces/admin/index.mdx).
- Previously, authentik would automatically switch between dark and light theme based on the users' browsers' settings. This can now be overridden to either force the light or dark theme, per user/group/brand. See docs for the [User interface](../../customize/interfaces/user/index.mdx) and [Admin interface](../../customize/interfaces/admin/index.mdx).
- The custom.css file is now loaded in ShadowDOMs, allowing for much greater customization, as previously it was only possible to style elements outside of the ShadowDOM. See docs for [Flow](../../customize/interfaces/flow/customization.mdx), [User](../../customize/interfaces/user/customization.mdx) and [Admin](../../customize/interfaces/admin/customization.mdx) interfaces.
- Previously, authentik would automatically switch between dark and light theme based on the users' browsers' settings. This can now be overridden to either force the light or dark theme, per user/group/tenant. See docs for [Flow](../../customize/interfaces/flow/customization.mdx), [User](../../customize/interfaces/user/customization.mdx) and [Admin](../../customize/interfaces/admin/customization.mdx) interfaces.
## Upgrading

View File

@ -3,7 +3,7 @@ title: Brands
slug: /brands
---
As an authentik administrator, you can customize your instance's appearance and behavior using brands. Brands apply to a single domain, a domain wildcard, or can be set as default, in which case the brand will be applied when no other brand matches the domain.
As an authentik admin, you can customize your instance's appearance and behavior using brands. Brands apply to a single domain, a domain wildcard or can be set as default, in which case the brand will be used when no other brand matches the domain.
For an overview of branding and other customization options in authentik refer to [Customize your instance](../customize/index.md).
@ -71,4 +71,4 @@ When using the [Mutual TLS Stage](../add-secure-apps/flows-stages/stages/mtls/in
#### Attributes
Attributes such as locale, theme settings (light/dark mode), and custom attributes can be set to a per-brand default value here. Any custom attributes can be retrieved via [`group_attributes()`](../users-sources/user/user_ref.mdx#object-properties).
Attributes such as locale, theme settings and custom attributes can be set to a per-brand default value here. Any custom attributes can be retrieved via [`group_attributes()`](../users-sources/user/user_ref.mdx#object-properties).

View File

@ -131,10 +131,6 @@ const config = createDocusaurusConfig({
],
],
},
gtag: {
trackingID: ["G-9MVR9WZFZH"],
anonymizeIP: true,
},
theme: {
customCss: require.resolve("@goauthentik/docusaurus-config/css/index.css"),
},

View File

@ -128,10 +128,6 @@ const config = createDocusaurusConfig({
],
],
},
gtag: {
trackingID: ["G-9MVR9WZFZH"],
anonymizeIP: true,
},
theme: {
customCss: require.resolve("@goauthentik/docusaurus-config/css/index.css"),
},

View File

@ -86,20 +86,6 @@ package = "netlify-plugin-debug-cache"
to = "/docs/customize/branding"
status = 302
[[redirects]]
from = "/docs/customize/interfaces/admin/customization"
to = "/docs/customize/interfaces/admin"
status = 302
[[redirects]]
from = "/docs/customize/interfaces/user/customization"
to = "/docs/customize/interfaces/user"
status = 302
[[redirects]]
from = "/docs/customize/interfaces/flow/customization"
to = "/docs/customize/interfaces/flow"
status = 302
# Migration to new structure with script Sept 2025
[[redirects]]

View File

@ -19,7 +19,7 @@
"@goauthentik/docusaurus-config": "^1.1.0",
"@goauthentik/tsconfig": "^1.0.4",
"@mdx-js/react": "^3.1.0",
"@swc/html-linux-x64-gnu": "1.12.9",
"@rspack/binding-linux-x64-gnu": "1.4.1",
"clsx": "^2.1.1",
"docusaurus-plugin-openapi-docs": "^4.4.0",
"docusaurus-theme-openapi-docs": "^4.4.0",
@ -62,15 +62,15 @@
"node": ">=22.14.0"
},
"optionalDependencies": {
"@rspack/binding-darwin-arm64": "1.4.2",
"@rspack/binding-linux-arm64-gnu": "1.4.2",
"@rspack/binding-linux-x64-gnu": "1.4.2",
"@swc/core-darwin-arm64": "1.12.9",
"@swc/core-linux-arm64-gnu": "1.12.9",
"@swc/core-linux-x64-gnu": "1.12.9",
"@swc/html-darwin-arm64": "1.12.9",
"@swc/html-linux-arm64-gnu": "1.12.9",
"@swc/html-linux-x64-gnu": "1.12.9",
"@rspack/binding-darwin-arm64": "1.4.1",
"@rspack/binding-linux-arm64-gnu": "1.4.1",
"@rspack/binding-linux-x64-gnu": "1.4.1",
"@swc/core-darwin-arm64": "1.12.7",
"@swc/core-linux-arm64-gnu": "1.12.7",
"@swc/core-linux-x64-gnu": "1.12.7",
"@swc/html-darwin-arm64": "1.12.7",
"@swc/html-linux-arm64-gnu": "1.12.7",
"@swc/html-linux-x64-gnu": "1.12.7",
"lightningcss-darwin-arm64": "1.30.1",
"lightningcss-linux-arm64-gnu": "1.30.1",
"lightningcss-linux-x64-gnu": "1.30.1"
@ -5034,9 +5034,9 @@
}
},
"node_modules/@rspack/binding-darwin-arm64": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.4.2.tgz",
"integrity": "sha512-0fPOew7D0l/x6qFZYdyUqutbw15K98VLvES2/7x2LPssTgypE4rVmnQSmVBnge3Nr8Qs/9qASPRpMWXBaqMfOA==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@rspack/binding-darwin-arm64/-/binding-darwin-arm64-1.4.1.tgz",
"integrity": "sha512-enh5DYbpaexdEmjbcxj3BJDauP3w+20jFKWvKROtAQV350PUw0bf2b4WOgngIH9hBzlfjpXNYAk6T5AhVAlY3Q==",
"cpu": [
"arm64"
],
@ -5060,9 +5060,9 @@
"peer": true
},
"node_modules/@rspack/binding-linux-arm64-gnu": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.4.2.tgz",
"integrity": "sha512-UHAzggS8Mc7b3Xguhj82HwujLqBZquCeo8qJj5XreNaMKGb6YRw/91dJOVmkNiLCB0bj71CRE1Cocd+Peq3N9A==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.4.1.tgz",
"integrity": "sha512-PJ5cHqvrj1bK7jH5DVrdKoR8Fy+p6l9baxXajq/6xWTxP+4YTdEtLsRZnpLMS1Ho2RRpkxDWJn+gdlKuleNioQ==",
"cpu": [
"arm64"
],
@ -5086,9 +5086,9 @@
"peer": true
},
"node_modules/@rspack/binding-linux-x64-gnu": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.4.2.tgz",
"integrity": "sha512-ucCCWdtH1tekZadrsYj6GNJ8EP21BM2uSE7MootbwLw8aBtgVTKUuRDQEps1h/rtrdthzd9XBX6Lc2N926gM+g==",
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@rspack/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.4.1.tgz",
"integrity": "sha512-jjTx53CpiYWK7fAv5qS8xHEytFK6gLfZRk+0kt2YII6uqez/xQ3SRcboreH8XbJcBoxINBzMNMf5/SeMBZ939A==",
"cpu": [
"x64"
],
@ -5586,12 +5586,13 @@
}
},
"node_modules/@swc/core-darwin-arm64": {
"version": "1.12.9",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.12.9.tgz",
"integrity": "sha512-GACFEp4nD6V+TZNR2JwbMZRHB+Yyvp14FrcmB6UCUYmhuNWjkxi+CLnEvdbuiKyQYv0zA+TRpCHZ+whEs6gwfA==",
"version": "1.12.7",
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.12.7.tgz",
"integrity": "sha512-w6BBT0hBRS56yS+LbReVym0h+iB7/PpCddqrn1ha94ra4rZ4R/A91A/rkv+LnQlPqU/+fhqdlXtCJU9mrhCBtA==",
"cpu": [
"arm64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"darwin"
@ -5633,12 +5634,13 @@
}
},
"node_modules/@swc/core-linux-arm64-gnu": {
"version": "1.12.9",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.12.9.tgz",
"integrity": "sha512-6qx1ka9LHcLzxIgn2Mros+CZLkHK2TawlXzi/h7DJeNnzi8F1Hw0Yzjp8WimxNCg6s2n+o3jnmin1oXB7gg8rw==",
"version": "1.12.7",
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.12.7.tgz",
"integrity": "sha512-N15hKizSSh+hkZ2x3TDVrxq0TDcbvDbkQJi2ZrLb9fK+NdFUV/x+XF16ZDPlbxtrGXl1CT7VD439SNaMN9F7qw==",
"cpu": [
"arm64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"linux"
@ -5664,12 +5666,13 @@
}
},
"node_modules/@swc/core-linux-x64-gnu": {
"version": "1.12.9",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.12.9.tgz",
"integrity": "sha512-SFUxyhWLZRNL8QmgGNqdi2Q43PNyFVkRZ2zIif30SOGFSxnxcf2JNeSeBgKIGVgaLSuk6xFVVCtJ3KIeaStgRg==",
"version": "1.12.7",
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.12.7.tgz",
"integrity": "sha512-PR4tPVwU1BQBfFDk2XfzXxsEIjF3x/bOV1BzZpYvrlkU0TKUDbR4t2wzvsYwD/coW7/yoQmlL70/qnuPtTp1Zw==",
"cpu": [
"x64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"linux"
@ -5821,12 +5824,13 @@
}
},
"node_modules/@swc/html-darwin-arm64": {
"version": "1.12.9",
"resolved": "https://registry.npmjs.org/@swc/html-darwin-arm64/-/html-darwin-arm64-1.12.9.tgz",
"integrity": "sha512-uQl0y9uOgqnYR6t+TgcwFeGv1TC48xHGBqw3MrOIQLc+tqavqhQsLkVEEz1yd1J0WW3cVAsNSQlbERiwQcXQXA==",
"version": "1.12.7",
"resolved": "https://registry.npmjs.org/@swc/html-darwin-arm64/-/html-darwin-arm64-1.12.7.tgz",
"integrity": "sha512-4rHV4lW8PXSc7YfJ/c9Cj0xZWSJArkD/Yuax4plH6f4VtEcEAluZI3ryBG3Vh4VawQ1RMkytPQ2S65BbCyDIXg==",
"cpu": [
"arm64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"darwin"
@ -5868,12 +5872,13 @@
}
},
"node_modules/@swc/html-linux-arm64-gnu": {
"version": "1.12.9",
"resolved": "https://registry.npmjs.org/@swc/html-linux-arm64-gnu/-/html-linux-arm64-gnu-1.12.9.tgz",
"integrity": "sha512-xX/S0galaqXMNc1olt1UOMcHXybDYGogGP90WheI6XD5zKVmbHdz9yU/nVeddZNUf5gZ011NCc5QSMB+2fh8EA==",
"version": "1.12.7",
"resolved": "https://registry.npmjs.org/@swc/html-linux-arm64-gnu/-/html-linux-arm64-gnu-1.12.7.tgz",
"integrity": "sha512-z66ejXsSwI0mKyDhLimG74+xZyvSQCrceSZv9jLHa23sn/di+07M9njZrj3SQKGfHoJqXsN1iPqDpvkVajNb9Q==",
"cpu": [
"arm64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"linux"
@ -5899,12 +5904,13 @@
}
},
"node_modules/@swc/html-linux-x64-gnu": {
"version": "1.12.9",
"resolved": "https://registry.npmjs.org/@swc/html-linux-x64-gnu/-/html-linux-x64-gnu-1.12.9.tgz",
"integrity": "sha512-9tRAsVsjjyEUFMH5uNrcLxb+5q0l2PCgTH7pe48hjcshKFoZamp1aiwvNnJMMBan3Ny9vFG5jKMJKG3ZkYPYxg==",
"version": "1.12.7",
"resolved": "https://registry.npmjs.org/@swc/html-linux-x64-gnu/-/html-linux-x64-gnu-1.12.7.tgz",
"integrity": "sha512-5KFLil4ELKzCLjjvKpt+SMEU6uBDR/EL4e7eleybtYi1cU8Jzv0xnTvabsVDfpT8fsvJF3Mvach4F/ggH5+CDQ==",
"cpu": [
"x64"
],
"license": "Apache-2.0 AND MIT",
"optional": true,
"os": [
"linux"

View File

@ -75,15 +75,15 @@
"typescript-eslint": "^8.35.1"
},
"optionalDependencies": {
"@rspack/binding-darwin-arm64": "1.4.2",
"@rspack/binding-linux-arm64-gnu": "1.4.2",
"@rspack/binding-linux-x64-gnu": "1.4.2",
"@swc/core-darwin-arm64": "1.12.9",
"@swc/core-linux-arm64-gnu": "1.12.9",
"@swc/core-linux-x64-gnu": "1.12.9",
"@swc/html-darwin-arm64": "1.12.9",
"@swc/html-linux-arm64-gnu": "1.12.9",
"@swc/html-linux-x64-gnu": "1.12.9",
"@rspack/binding-darwin-arm64": "1.4.1",
"@rspack/binding-linux-arm64-gnu": "1.4.1",
"@rspack/binding-linux-x64-gnu": "1.4.1",
"@swc/core-darwin-arm64": "1.12.7",
"@swc/core-linux-arm64-gnu": "1.12.7",
"@swc/core-linux-x64-gnu": "1.12.7",
"@swc/html-darwin-arm64": "1.12.7",
"@swc/html-linux-arm64-gnu": "1.12.7",
"@swc/html-linux-x64-gnu": "1.12.7",
"lightningcss-darwin-arm64": "1.30.1",
"lightningcss-linux-arm64-gnu": "1.30.1",
"lightningcss-linux-x64-gnu": "1.30.1"

View File

@ -408,9 +408,21 @@ const items = [
type: "category",
label: "Interfaces",
items: [
"customize/interfaces/flow/index",
"customize/interfaces/user/index",
"customize/interfaces/admin/index",
{
type: "category",
label: "Flow",
items: ["customize/interfaces/flow/customization"],
},
{
type: "category",
label: "User",
items: ["customize/interfaces/user/customization"],
},
{
type: "category",
label: "Admin",
items: ["customize/interfaces/admin/customization"],
},
],
},
{