# Configuring Multi-Brand Branding and Theming in authentik: Complete Implementation Guide

> Implement multi-brand branding and theming in authentik. Customize logos, titles, favicons, CSS, and map tiles for each domain with this complete guide.

- Repository: [Authentik Security/authentik](https://github.com/goauthentik/authentik)
- Tags: how-to-guide
- Published: 2026-08-14

---

**authentik supports multi-brand deployments where each domain can display its own logo, title, favicon, custom CSS, and map tiles through a combination of Python middleware, database models, and Lit-based web components.**

Multi-brand configuration in authentik allows organizations to host multiple tenants from a single instance, with each tenant presenting a fully customized visual identity. This guide examines the complete implementation across the Python backend and TypeScript frontend, referencing actual source paths and providing production-ready code examples.

## Understanding the Multi-Brand Architecture

The multi-brand system spans four layers: **data persistence**, **API exposure**, **request-time resolution**, and **UI rendering**. Each layer is engineered to keep brand data synchronized from database to pixel.

### Data Model and Storage

Brand-specific attributes reside in the database through the **`Brand`** model defined in [[`authentik/brands/models.py`](https://github.com/goauthentik/authentik/blob/main/authentik/brands/models.py)](https://github.com/goauthentik/authentik/blob/main/authentik/brands/models.py#L40):

- `branding_title` — Display name shown throughout the UI
- `branding_logo` — URL or path to the tenant's logo
- `branding_favicon` — Icon displayed in browser tabs
- `branding_custom_css` — Raw CSS injected into every page
- `branding_map_tiles` — Custom tile server URL for map components

The model also includes a **`domain`** field that determines which brand activates for incoming requests.

### API Layer

REST operations are exposed via [[`authentik/brands/api.py`](https://github.com/goauthentik/authentik/blob/main/authentik/brands/api.py)](https://github.com/goauthentik/authentik/blob/main/authentik/brands/api.py#L41), where `BrandSerializer` and `BrandViewSet` map the model to authentik's auto-generated OpenAPI schema. This enables both programmatic management and the built-in admin interface.

## How Brand Resolution Works at Runtime

Brand selection happens on every request through middleware, then propagates through the entire request lifecycle.

### Step 1: Middleware Host Matching

[[`authentik/brands/middleware.py`](https://github.com/goauthentik/authentik/blob/main/authentik/brands/middleware.py)](https://github.com/goauthentik/authentik/blob/main/authentik/brands/middleware.py#L12) implements `BrandMiddleware`, which:

1. Extracts the hostname from the incoming request
2. Queries for a `Brand` whose `domain` matches exactly
3. Falls back to the default brand if no match exists
4. Attaches the resolved brand to `request.brand`

```python

# Conceptual flow from authentik/brands/middleware.py

class BrandMiddleware:
    def __call__(self, request):
        host = request.get_host().split(":")[0]
        brand = Brand.objects.filter(domain=host).first()
        request.brand = brand or Brand.objects.get_default()
        return self.get_response(request)

```

### Step 2: Default Flow Redirection

The resolved brand drives navigation behavior. [[`authentik/core/views/interface.py`](https://github.com/goauthentik/authentik/blob/main/authentik/core/views/interface.py)](https://github.com/goauthentik/authentik/blob/main/authentik/core/views/interface.py#L71) contains `BrandDefaultRedirectView`, which reads `request.brand` to determine where to send users accessing the root URL:

```python

# From authentik/core/views/interface.py

class BrandDefaultRedirectView(InterfaceView):
    def get(self, request, *args, **kwargs):
        flow = request.brand.branding_default_flow
        return HttpResponseRedirect(flow.get_absolute_url())

```

## Frontend Branding with Lit Context

The authentik web UI uses Lit's context system to make brand data available globally without prop drilling.

### The BrandingMixin Pattern

[[`web/src/elements/mixins/branding.ts`](https://github.com/goauthentik/authentik/blob/main/web/src/elements/mixins/branding.ts)](https://github.com/goauthentik/authentik/blob/main/web/src/elements/mixins/branding.ts) defines `BrandingMixin`, which:

- Creates `BrandingContext` using Lit's `createContext`
- Exposes typed getters: `brandingTitle`, `brandingLogo`, `brandingFavicon`, `brandingCustomCss`, `brandingMapTiles`
- Can be applied to any LitElement via `WithBrandConfig`

Components consuming this mixin automatically re-render when brand data changes.

### Component Usage Example

[[`web/src/components/ak-page-navbar.ts`](https://github.com/goauthentik/authentik/blob/main/web/src/components/ak-page-navbar.ts)](https://github.com/goauthentik/authentik/blob/main/web/src/components/ak-page-navbar.ts#L97) demonstrates real-world consumption:

```typescript
import { WithBrandConfig } from "#elements/mixins/branding";

export class AkPageNavbar extends WithBrandConfig(LitElement) {
  render() {
    return html`
      <nav>
        <img src="${this.brandingLogo}" alt="${this.brandingTitle}" />
        <span>${this.brandingTitle}</span>
      </nav>
    `;
  }
}

```

### Custom CSS Injection

The `branding_custom_css` field is automatically converted to a stylesheet and applied to the root element. For programmatic injection in custom components:

```typescript
import { createStyleSheetUnsafe } from "lit";

if (this.brand?.brandingCustomCss) {
  const sheet = createStyleSheetUnsafe(this.brand.brandingCustomCss);
  this.shadowRoot?.adoptedStyleSheets.push(sheet);
}

```

## Creating and Managing Brands

### Via REST API (TypeScript)

Use the generated `@goauthentik/api` client for programmatic brand creation:

```typescript
import { BrandApi, BrandRequest } from "@goauthentik/api";

const api = new BrandApi();

const newBrand: BrandRequest = {
  domain: "example.org",
  branding_title: "Example Corp",
  branding_logo: "/static/custom/logo.svg",
  branding_favicon: "/static/custom/favicon.ico",
  branding_custom_css: `
    :root {
      --pf-global--primary-color--100: #0066cc;
    }
    .pf-c-button { border-radius: 4px; }
  `,
  branding_map_tiles: "https://my.tileserver.org/{z}/{x}/{y}.png",
};

await api.brandsCreate(newBrand);

```

### Via Admin UI

The brand management interface lives at [[`web/src/admin/brands/BrandForm.ts`](https://github.com/goauthentik/authentik/blob/main/web/src/admin/brands/BrandForm.ts)](https://github.com/goauthentik/authentik/blob/main/web/src/admin/brands/BrandForm.ts), providing fields for all branding attributes including file uploads for logos and favicons.

## Brand-Aware Custom Components

When building extensions that need brand data, apply the mixin pattern:

```typescript
import { WithBrandConfig } from "#elements/mixins/branding";
import { html, LitElement } from "lit";

export class MyCustomHeader extends WithBrandConfig(LitElement) {
  render() {
    const cssUrl = this.brandingCustomCss 
      ? undefined 
      : "/static/default-theme.css";

    return html`
      <header style="background: var(--brand-color)">
        <img 
          src="${this.brandingLogo}" 
          alt="${this.brandingTitle}"
          @error="${() => this.useFallbackLogo()}"
        />
        <h1>${this.brandingTitle}</h1>
      </header>
    `;
  }

  private useFallbackLogo() {
    // Handle missing or broken logo assets
  }
}

```

## Fallback Configuration

When no brand matches or the default brand lacks configuration, [[`web/src/common/ui/config.ts`](https://github.com/goauthentik/authentik/blob/main/web/src/common/ui/config.ts)](https://github.com/goauthentik/authentik/blob/main/web/src/common/ui/config.ts) supplies baseline values. This ensures the UI never renders without valid branding data.

## Summary

- **Multi-brand branding in authentik** works through domain-based `Brand` model resolution, middleware attachment to `request.brand`, and Lit context propagation to all UI components
- **Key files:** [[`authentik/brands/models.py`](https://github.com/goauthentik/authentik/blob/main/authentik/brands/models.py)](https://github.com/goauthentik/authentik/blob/main/authentik/brands/models.py#L40) (storage), [[`middleware.py`](https://github.com/goauthentik/authentik/blob/main/middleware.py)](https://github.com/goauthentik/authentik/blob/main/authentik/brands/middleware.py#L12) (resolution), [[`web/src/elements/mixins/branding.ts`](https://github.com/goauthentik/authentik/blob/main/web/src/elements/mixins/branding.ts)](https://github.com/goauthentik/authentik/blob/main/web/src/elements/mixins/branding.ts) (UI context)
- **Theming mechanisms** include per-brand CSS injection and configurable map tile servers
- **Management options** span REST API with `@goauthentik/api` and the built-in admin interface at [[`BrandForm.ts`](https://github.com/goauthentik/authentik/blob/main/BrandForm.ts)](https://github.com/goauthentik/authentik/blob/main/web/src/admin/brands/BrandForm.ts)

## Frequently Asked Questions

### What happens if no brand matches the incoming domain?

The middleware falls back to the default brand configured in the system. Default values from [[`web/src/common/ui/config.ts`](https://github.com/goauthentik/authentik/blob/main/web/src/common/ui/config.ts)](https://github.com/goauthentik/authentik/blob/main/web/src/common/ui/config.ts) ensure the UI remains functional even without custom branding.

### Can I use environment variables instead of the database for branding?

No. authentik's multi-brand system requires database storage to support runtime domain resolution and per-tenant isolation. The [`Brand`](https://github.com/goauthentik/authentik/blob/main/authentik/brands/models.py#L40) model is the single source of truth for all branding attributes.

### How do I apply brand-specific CSS to Shadow DOM components?

The `branding_custom_css` stylesheet is applied at the application root. For Shadow DOM isolation, components using `WithBrandConfig` can manually adopt the stylesheet using `createStyleSheetUnsafe` and `adoptedStyleSheets`, or inherit CSS custom properties defined in the global stylesheet.

### Are map tiles only used for geographic visualizations?

Yes. The `branding_map_tiles` field specifically configures tile server URLs for map components. This allows different tenants to use custom basemaps—corporate styles, satellite imagery, or regional tile servers—wherever authentik renders location data.