diff --git a/authentik/brands/api.py b/authentik/brands/api.py
index e8e1fc11719a..3089f86eabb0 100644
--- a/authentik/brands/api.py
+++ b/authentik/brands/api.py
@@ -49,6 +49,7 @@ class Meta:
"branding_title",
"branding_logo",
"branding_favicon",
+ "branding_custom_css",
"flow_authentication",
"flow_invalidation",
"flow_recovery",
@@ -86,6 +87,7 @@ class CurrentBrandSerializer(PassiveSerializer):
branding_title = CharField()
branding_logo = CharField(source="branding_logo_url")
branding_favicon = CharField(source="branding_favicon_url")
+ branding_custom_css = CharField()
ui_footer_links = ListField(
child=FooterLinkSerializer(),
read_only=True,
diff --git a/authentik/brands/migrations/0008_brand_branding_custom_css.py b/authentik/brands/migrations/0008_brand_branding_custom_css.py
new file mode 100644
index 000000000000..1e51d32a2d62
--- /dev/null
+++ b/authentik/brands/migrations/0008_brand_branding_custom_css.py
@@ -0,0 +1,37 @@
+# Generated by Django 5.0.12 on 2025-02-22 01:51
+
+from pathlib import Path
+from django.db import migrations, models
+from django.apps.registry import Apps
+
+from django.db.backends.base.schema import BaseDatabaseSchemaEditor
+
+
+def migrate_custom_css(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
+ Brand = apps.get_model("authentik_brands", "brand")
+
+ db_alias = schema_editor.connection.alias
+
+ path = Path("/web/dist/custom.css")
+ if not path.exists():
+ return
+ with path.read_text() as css:
+ for brand in Brand.objects.using(db_alias).all():
+ brand.branding_custom_css = css
+ brand.save()
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("authentik_brands", "0007_brand_default_application"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="brand",
+ name="branding_custom_css",
+ field=models.TextField(blank=True, default=""),
+ ),
+ migrations.RunPython(migrate_custom_css),
+ ]
diff --git a/authentik/brands/models.py b/authentik/brands/models.py
index 12e975b1de1e..ee4e73d52b5b 100644
--- a/authentik/brands/models.py
+++ b/authentik/brands/models.py
@@ -33,6 +33,7 @@ class Brand(SerializerModel):
branding_logo = models.TextField(default="/static/dist/assets/icons/icon_left_brand.svg")
branding_favicon = models.TextField(default="/static/dist/assets/icons/icon.png")
+ branding_custom_css = models.TextField(default="", blank=True)
flow_authentication = models.ForeignKey(
Flow, null=True, on_delete=models.SET_NULL, related_name="brand_authentication"
diff --git a/authentik/core/templates/base/skeleton.html b/authentik/core/templates/base/skeleton.html
index e2f063b071b7..43374ca0a5c3 100644
--- a/authentik/core/templates/base/skeleton.html
+++ b/authentik/core/templates/base/skeleton.html
@@ -14,7 +14,7 @@
{% block head_before %}
{% endblock %}
-
+
{% block head %}
diff --git a/blueprints/schema.json b/blueprints/schema.json
index bca4395e323d..cd34efefbd92 100644
--- a/blueprints/schema.json
+++ b/blueprints/schema.json
@@ -12997,6 +12997,10 @@
"minLength": 1,
"title": "Branding favicon"
},
+ "branding_custom_css": {
+ "type": "string",
+ "title": "Branding custom css"
+ },
"flow_authentication": {
"type": "string",
"format": "uuid",
diff --git a/internal/outpost/proxyv2/templates/error.html b/internal/outpost/proxyv2/templates/error.html
index b0e00b429279..6ef3917b56bd 100644
--- a/internal/outpost/proxyv2/templates/error.html
+++ b/internal/outpost/proxyv2/templates/error.html
@@ -8,7 +8,6 @@
-