authentik icon indicating copy to clipboard operation
authentik copied to clipboard

Make branding / customisation of UI more consistent, enhance possibilities

Open agrimpelhuber opened this issue 2 years ago โ€ข 24 comments

Is your feature request related to a problem? Please describe. The branding / customisation of authentik is somewhat inconsistent. While every flow can be branded in some way (wording, background image), a few things like the URL https://authentik.tld/if/session-end/my-app-name can't, as far as I can see.

Describe the solution you'd like Different stages may be possible:

  1. Make the hard-coded parts of an authentication workflow just as customizable as flows (on tenant level?): Background etc.
  2. Minor thing: As far as I can see, an uploaded background picture for X multiple flows ends up as X picture URLs. Re-use the same
  3. Make a type of branding possible to obscure the fact that the whole system is called "authentik" (e.g. in the above URL, button "go to authentik" is hard-coded). Even though I have no problem with this in my hobbyist use, that might be something to keep people from using Authentik in corporate environments. Therefore, this might be a paid feature (oops!)

Describe alternatives you've considered I might have overlooked something during the long hours I have looked into the customization details. In this case, please point me in the right direction, downgrade this to "question", and I'll share my findings here

agrimpelhuber avatar Feb 23 '22 08:02 agrimpelhuber

I would love to configure this via Django Templates!

benedikt-bartscher avatar Mar 16 '22 11:03 benedikt-bartscher

@agrimpelhuber

  1. Make the hard-coded parts of an authentication workflow just as customizable as flows (on tenant level?): Background etc.

There isn't really much hardcoded during authentication; and while I get having a tenant-level configurable background I think most environments that do change the background just change it to a URL and then update the picture behind that (at least thats how I would do it)

  1. Minor thing: As far as I can see, an uploaded background picture for X multiple flows ends up as X picture URLs. Re-use the same

Yes, this is a downside of how the whole uploading thing works, there could be a UI to show already uploaded files, allthough imo there's more important things before this

  1. Make a type of branding possible to obscure the fact that the whole system is called "authentik" (e.g. in the above URL, button "go to authentik" is hard-coded). Even though I have no problem with this in my hobbyist use, that might be something to keep people from using Authentik in corporate environments. Therefore, this might be a paid feature (oops!)

Yeah the logout screen is currently hardcoded, this will eventually be migrated to also use flows when I finish #2346 and the followup for OIDC, but besides that I recently cleaned up some of the default links, and it only shows Powered by authentik, which is equally hardcoded on things like Okta.

One thing I would like to eventually add for this is custom interface support, i.e. allow people to write their own frontend if they choose to do so, and host it under authentik.tld/if/<some name>/, and with this they'd obviously have full control of what is shown

@benedikt-bartscher authentik doesn't really use django templates for much, there are very few sites that are server-side rendered as most everything of the UI is an SPA nowadays

BeryJu avatar Mar 16 '22 11:03 BeryJu

Hi there @BeryJu thank you for the amazing project! Always nice to see polished Django in the nature. Would you be open to accepting a PR that enables the removal of the branding Powered by authentik via a config / env variable or is this something you would like to keep? If you pay Okta enough they give you full white-labeling including separate servers btw - but it still is not selfhosted and outside of the EU ๐Ÿ˜‡

matmair avatar Jul 21 '22 16:07 matmair

Hey @matmair ,

If you need a quick fix, this is what I did in my docker-compose file. It's a massive hack, but it removes the powered by links and makes the css apply in more areas:

  worker:
    image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-latest}
    user: root
    <<: *authentik-worker-environment
    entrypoint: /bin/bash
    command:
      - -c
      - |
        DIST_DIR=web/dist

        #---CUSTOM BACKGROUND
        URL=https://picsum.photos/1920/1080/?blur&grayscale
        curl -o $$DIST_DIR/assets/images/flow_background.jpg $$URL

        #---CUSTOM_CSS_APPEND
        read -r -d '' CSS << EOM
        input {border-radius: 3px;border: 1px solid transparent;border-top: none;border-bottom: 1px solid #DDD;box-shadow: inset 0 1px 2px rgba(0,0,0,.39), 0 -1px 1px #FFF, 0 1px 0 #FFF;}
        a[href^="https://goauthentik.io"] {display:none !important}
        a[href^="https://unsplash.com"] {display:none !important}
        EOM
        echo "$$CSS" >> $$DIST_DIR/custom.css

        #---CUSTOM_CSS_HEADER
        for FILE in $$DIST_DIR/flow/*; do
            sed -i "s|</header>|<link rel="stylesheet" type="text/css" href="/static/dist/custom.css"></header>|g" $$FILE
        done

        cp -R $$DIST_DIR/* /dist

        /usr/local/bin/dumb-init -- /lifecycle/ak worker
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - geoip:/geoip
      - certs:/certs
      - custom-templates:/templates
      - media:/media
      - dist:/dist

NOTE: This only works because I have my worker and server sharing a volume. The worker (running as root), modifies the files, and then copies them to the shared /dist folder on every boot. It then adds a link to the custom css where possible using sed in the template files.

regbo avatar Dec 07 '22 21:12 regbo

Thanks @regbo, I have a similar solution. Mainly asked to get the go-ahead to address this upstream if there was interest from the maintainer but it does not seem so - which I respect.

matmair avatar Dec 07 '22 23:12 matmair

Yes, this is a downside of how the whole uploading thing works, there could be a UI to show already uploaded files, allthough imo there's more important things before this

The easiest way would probably be to allow free text entry even if a /media folder is mounted. Then I could upload a picture once using the upload button and local file selector and then just paste the same web path in other places, such as flow backgrounds.

BTW, an idea for other people who want to use a custom background without uploading the same file multiple times:

I worked around the image doubling issue by just not mounting /media and instead mounting an external folder to /web/dist/extra in the container. I can then manually upload files to that folder from the host. That folder is served to /static/dist/extra.

As /media isn't mounted, I get free text fields for media to enter any URL. E.g. for flow backgrounds I can then enter something like /static/dist/extra/mypicture.jpg. Now I can reuse that link without uploading the same picture again and again.

That's a little more work and authentik doesn't detect a different picture is being used and still shows the "Background image" link to unsplash โ€“ even though that image isn't used anymore. But I can live with that.

Eisfunke avatar Dec 08 '22 17:12 Eisfunke

@matmair and @Eisfunke ,

I simplified a lot of this by creating the following:

https://github.com/regbo/public-html/tree/master/authentik

It assumes that /dist is mounted on the worker, and that same volume is mounted to /web/dist on the servers. To use it, replace the entrypoint and command with this on the worker:

    entrypoint: /bin/bash
    command:
      - -c
      - |
        curl -fsSL https://raw.githubusercontent.com/regbo/public-html/master/authentik/run-worker.sh | bash

The environment variable 'AUTHENTIK_FLOW_BACKGROUND_URL' can be used to set a background. Also, it auto injects .js and .css files by reading 'AUTHENTIK_INJECT_URL_*" environment variables. CSS files are injected into all doms, including the shadow roots. So for example, this is what I use in my docker config:

    environment:
      AUTHENTIK_FLOW_BACKGROUND_URL: ${AUTHENTIK_FLOW_BACKGROUND_URL:-https://raw.githubusercontent.com/regbo/public-html/master/background.png}
      AUTHENTIK_INJECT_URL_0: https://rawcdn.githack.com/regbo/public-html/9dd48564ee00433ceced7177c6376731899e8147/authentik/input.css
      AUTHENTIK_INJECT_URL_1: https://rawcdn.githack.com/regbo/public-html/444807d39c657ffef12ae1a8ea882ed7667eca3e/authentik/remove-branding.js

The above allows me to externalize some of the styling.

regbo avatar Dec 09 '22 02:12 regbo

Authentik lack of white labeling / customization ๐Ÿคจ

nothing serious seems to be on the roadmap against that isn't it @BeryJu ?

rroblik avatar Jan 11 '23 21:01 rroblik

Hi @regbo, I can't get your custumization working, my docker-compose.yml looks like this:

version: '3.4'

services:
  postgresql:
    image: docker.io/library/postgres:12-alpine
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
      start_period: 20s
      interval: 30s
      retries: 5
      timeout: 5s
    volumes:
      - database:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD=${PG_PASS:?database password required}
      - POSTGRES_USER=${PG_USER:-authentik}
      - POSTGRES_DB=${PG_DB:-authentik}
    env_file:
      - .env
  redis:
    image: docker.io/library/redis:alpine
    command: --save 60 1 --loglevel warning
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
      start_period: 20s
      interval: 30s
      retries: 5
      timeout: 3s
    volumes:
      - redis:/data
  server:
    image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.1.2}
    restart: unless-stopped
    command: server
    environment:
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
      AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
      AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
    volumes:
      - ./media:/media
      - ./custom-templates:/templates
    env_file:
      - .env
    ports:
      - "${AUTHENTIK_PORT_HTTP:-9000}:9000"
      - "${AUTHENTIK_PORT_HTTPS:-9443}:9443"
  worker:
    image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.1.2}
    restart: unless-stopped
    entrypoint: /bin/bash
    command:
      - -c
      - |
        curl -fsSL https://raw.githubusercontent.com/regbo/public-html/master/authentik/run-worker.sh | bash
    environment:
      AUTHENTIK_FLOW_BACKGROUND_URL: ${AUTHENTIK_FLOW_BACKGROUND_URL:-https://raw.githubusercontent.com/regbo/public-html/master/background.png}
      AUTHENTIK_INJECT_URL_0: https://rawcdn.githack.com/regbo/public-html/9dd48564ee00433ceced7177c6376731899e8147/authentik/input.css
      AUTHENTIK_INJECT_URL_1: https://rawcdn.githack.com/regbo/public-html/444807d39c657ffef12ae1a8ea882ed7667eca3e/authentik/remove-branding.js
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgresql
      AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik}
      AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik}
      AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS}
    # `user: root` and the docker socket volume are optional.
    # See more for the docker socket integration here:
    # https://goauthentik.io/docs/outposts/integrations/docker
    # Removing `user: root` also prevents the worker from fixing the permissions
    # on the mounted folders, so when removing this make sure the folders have the correct UID/GID
    # (1000:1000 by default)
    user: root
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./media:/media
      - ./certs:/certs
      - ./custom-templates:/templates
    env_file:
      - .env

volumes:
  database:
    driver: local
  redis:
    driver: local

did I missed something or did I pasted it to the wrong place?

Thanks in advanced pupazze

pupazze avatar Jan 23 '23 16:01 pupazze

for all who are also interessted in this, I still got no success with my customised docker-compose.yml file, but the solution working for me is nevertheless found in the details @regbo posted:

for FILE in /web/dist/flow/*; do
     sed -i "s|</header>|<link rel="stylesheet" type="text/css" href="/static/dist/custom.css"></header>|g" $FILE
done

this bash part makes it possible for me to apply my custom css as wanted

pupazze avatar Jan 26 '23 14:01 pupazze

So so so dirty hack ๐Ÿ˜ฎโ€๐Ÿ’จ I've switched back to Keycloak

rroblik avatar Jan 26 '23 19:01 rroblik

Hey all,

Sorry for the delay. This is indeed a dirty nasty hack. But, FWIW, below is my compose file. The key is to make the server share the same dist directory as the worker instance. It won't work if they don't share a common volume across all instances (in my case I'm using gluster):

---
version: '3.4'

x-authentik-env: &authentik-env
      AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY?Variable not set}

      AUTHENTIK_POSTGRESQL__HOST: ${AUTHENTIK_POSTGRESQL__HOST?Variable not set}
      AUTHENTIK_POSTGRESQL__PORT: ${AUTHENTIK_POSTGRESQL__PORT:-5432}
      AUTHENTIK_POSTGRESQL__NAME: ${AUTHENTIK_POSTGRESQL__NAME?Variable not set}
      AUTHENTIK_POSTGRESQL__USER: ${AUTHENTIK_POSTGRESQL__USER?Variable not set}
      AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_POSTGRESQL__PASSWORD?Variable not set}
      AUTHENTIK_POSTGRESQL__USE_PGBOUNCER: ${AUTHENTIK_POSTGRESQL__USE_PGBOUNCER:-False}

      AUTHENTIK_EMAIL__HOST: postfix
      AUTHENTIK_EMAIL__PORT: 25
      AUTHENTIK_EMAIL__FROM: ${AUTHENTIK_EMAIL__FROM_NAME} <auth@${DOMAIN?Variable not set}>

      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_AUTHENTIK__GEOIP: /geoip/GeoLite2-City.mmdb
      AUTHENTIK_ERROR_REPORTING__ENABLED: "true"
      AUTHENTIK_FOOTER_LINKS: "[]"

x-authentik-server-environment: &authentik-server-environment
    environment:
      <<: *authentik-env

x-authentik-worker-environment: &authentik-worker-environment
    environment:
      <<: *authentik-env
      AUTHENTIK_FLOW_BACKGROUND_URL: ${AUTHENTIK_FLOW_BACKGROUND_URL:-https://raw.githubusercontent.com/regbo/public-html/master/authentik/background.png}
      AUTHENTIK_BRAND_ICON_URL: ${AUTHENTIK_BRAND_ICON_URL?Variable nto set}
      AUTHENTIK_INJECT_CSS_URL_0: https://raw.githubusercontent.com/regbo/public-html/master/authentik/input.css
      AUTHENTIK_INJECT_CSS_URL_1: https://rawcdn.githack.com/regbo/public-html/2561d77f76d55b9f91afa697f2570f6ed74e6b94/authentik/hide-branding.css

services:

  redis:
    image: docker.io/library/redis:alpine
    command: --save 60 1 --loglevel warning
    healthcheck:
      test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
      start_period: 20s
      interval: 30s
      retries: 5
      timeout: 3s
    volumes:
      - redis:/data
    networks:
      authentik:
        aliases: [redis]
    logging:
      driver: "json-file"
      options:
          max-file: 5
          max-size: 10m
    deploy:
      mode: replicated
      replicas: 1
      restart_policy:
        condition: any
      placement:
        constraints: [node.role == manager]

  server:
    image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-latest}
    hostname: authentik-server
    <<: *authentik-server-environment
    command: server
    volumes:
      - geoip:/geoip
      - certs:/certs
      - custom-templates:/templates
      - media:/media
      - dist:/web/dist
    networks:
      traefik-public: {}
      authentik:
        aliases: [authentik-server]
    logging:
      driver: "json-file"
      options:
          max-file: 5
          max-size: 10m
    deploy:
      mode: global
      restart_policy:
        condition: any
      placement:
        constraints: [node.role == manager]
      labels:
        #required or treafik may select the correct ip
        - traefik.docker.network=traefik-public
        - traefik.enable=true
        ## Individual Application forwardAuth regex (catch any subdomain using individual application forwardAuth)
        - traefik.http.routers.authentik-rtr-outpost.rule=Path(`/outpost.goauthentik.io`) || PathPrefix(`/outpost.goauthentik.io/`)
        - traefik.http.routers.authentik-rtr-outpost.entrypoints=tcps
        - traefik.http.routers.authentik-rtr-outpost.tls=true
        - traefik.http.routers.authentik-rtr-outpost.priority=9223372036854775807
        - traefik.http.routers.authentik-rtr-outpost.middlewares=authentik-mdw-outpost
        - traefik.http.middlewares.authentik-mdw-outpost.headers.customresponseheaders.X-Authentik-Outpost=true
        ## HTTP Routers
        - traefik.http.routers.authentik-rtr.rule=HostRegexp(`auth.{domain:\S+}`)
        - traefik.http.routers.authentik-rtr.entrypoints=tcps
        - traefik.http.routers.authentik-rtr.tls=true
        # `authentik-server` refers to the service name in the compose file.
        - traefik.http.middlewares.authentik.forwardauth.address=http://authentik-server:9000/outpost.goauthentik.io/auth/traefik
        - traefik.http.middlewares.authentik.forwardauth.trustForwardHeader=true
        - traefik.http.middlewares.authentik.forwardauth.authResponseHeadersRegex=^X-Authentik-
        ## HTTP Services
        - traefik.http.routers.authentik-rtr.service=authentik-svc
        - traefik.http.services.authentik-svc.loadBalancer.server.port=9000
        - traefik.http.services.authentik-svc.loadBalancer.sticky.cookie=true
        - traefik.http.services.authentik-svc.loadBalancer.sticky.cookie.name=authentik_lb

  worker:
    image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-latest}
    user: root
    <<: *authentik-worker-environment
    entrypoint: /bin/bash
    command:
      - -c
      - |
        command -v jq >/dev/null 2>&1 || { echo "installing jq"; curl -fsSL https://glare.vercel.app/stedolan/jq/linux64 -o /usr/bin/jq; chmod +x /usr/bin/jq; }
        echo "starting worker";
        curl -fsSL https://api.github.com/repos/regbo/public-html/contents/authentik/run-worker.sh | jq -r ".content" | base64 --decode | bash
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - geoip:/geoip
      - certs:/certs
      - custom-templates:/templates
      - media:/media
      - dist:/dist
    networks:
      authentik: {}
    logging:
      driver: "json-file"
      options:
          max-file: 5
          max-size: 10m
    healthcheck:
      test: ["CMD", "/lifecycle/ak", "healthcheck"]
      interval: 30s
      timeout: 30s
      start_period: 60s
      retries: 10
    deploy:
      mode: replicated
      replicas: 1
      placement:
        constraints: [node.role == manager]

  geoipupdate:
    image: "maxmindinc/geoipupdate:latest"
    volumes:
      - "geoip:/usr/share/GeoIP"
    environment:
      GEOIPUPDATE_ACCOUNT_ID: ${GEOIPUPDATE_ACCOUNT_ID?Variable not set}
      GEOIPUPDATE_LICENSE_KEY: ${GEOIPUPDATE_LICENSE_KEY?Variable not set}
      GEOIPUPDATE_EDITION_IDS: "GeoLite2-City"
      GEOIPUPDATE_FREQUENCY: "8"
    logging:
      driver: "json-file"
      options:
          max-file: 5
          max-size: 10m
    deploy:
      mode: replicated
      replicas: 1
      restart_policy:
        condition: on-failure
      placement:
        constraints: [node.role == manager]

  postfix:
    image: juanluisbaptiste/postfix:latest
    environment:
      SMTP_SERVER: ${POSTFIX_SMTP_HOST?Variable not set}
      SMTP_PORT: ${POSTFIX_SMTP_PORT:-587}
      SMTP_USERNAME: ${POSTFIX_SMTP_USERNAME}
      SMTP_PASSWORD: ${POSTFIX_SMTP_PASSWORD}
      SERVER_HOSTNAME: ${DOMAIN?Variable not set}
      LOG_SUBJECT: "true"
      DEBUG: ${POSTFIX_DEBUG:-false}
    networks:
      authentik:
        aliases: [postfix]
    logging:
      driver: "json-file"
      options:
          max-file: 5
          max-size: 10m
    deploy:
      mode: replicated
      replicas: 1
      restart_policy:
        condition: on-failure
      placement:
        constraints:
          - ${POSTFIX_DEPLOY_PLACEMENT_CONSTRAINTS:-node.role == manager}

volumes:
  redis:
    driver: "local"
    driver_opts:
      type: "none"
      o: "bind"
      device: "/mnt/gluster/authentik/redis"
  geoip:
    driver: "local"
    driver_opts:
      type: "none"
      o: "bind"
      device: "/mnt/gluster/authentik/geoip"
  certs:
    driver: "local"
    driver_opts:
      type: "none"
      o: "bind"
      device: "/mnt/gluster/authentik/certs"
  custom-templates:
    driver: "local"
    driver_opts:
      type: "none"
      o: "bind"
      device: "/mnt/gluster/authentik/custom-templates"
  media:
    driver: "local"
    driver_opts:
      type: "none"
      o: "bind"
      device: "/mnt/gluster/authentik/media"
  dist:
    driver: "local"
    driver_opts:
      type: "none"
      o: "bind"
      device: "/mnt/gluster/authentik/dist"

networks:
  authentik: {}
  traefik-public:
    external: true

regbo avatar Jan 26 '23 19:01 regbo

I want to know if I can use docker mount volume to replace the flow_background.jpg and branding logo, sidebar icon directly? Don't care about the footer links and others?

a3linux avatar Feb 16 '23 01:02 a3linux

I want to know if I can use docker mount volume to replace the flow_background.jpg and branding logo, sidebar icon directly? Don't care about the footer links and others?

I do this in my environment:

- /myimg.jpg:/web/dist/assets/images/flow_background.jpg

It might be cached, so do a hard refresh after changing.

sevmonster avatar Mar 01 '23 02:03 sevmonster

This might be closed by #4804.

@regbo @pupazze Does this solve your situation so you don't have to futz so much with the container? I have had great success using pure CSS so far. Specifically on updating the background: https://github.com/goauthentik/authentik/issues/4586#issuecomment-1452051178

sevmonster avatar Mar 02 '23 18:03 sevmonster

were you smh able to make the cards rounded with a custom.css?

ne0YT avatar Mar 08 '23 18:03 ne0YT

https://github.com/goauthentik/authentik/discussions/4831

sevmonster avatar Mar 08 '23 18:03 sevmonster

#4831

youre not on "2023.2.3" right? stuff like this is still wrong for me (the orange part): image

ne0YT avatar Mar 08 '23 18:03 ne0YT

You need to update to gh-main (step three) or the PR branch until #4804 lands in a release.

sevmonster avatar Mar 08 '23 18:03 sevmonster

We'll be finalising #5048 hopefully soon-ish, the whitelabelling will be an option for enterprise licenses. For the file upload, as a relatively simple option, we're considering giving people that run authentik in docker-compose the option to set files to URLs (which is the default behaviour when no persistent storage is mounted, like on K8s), which would slightly improve this

BeryJu avatar Jun 15 '23 15:06 BeryJu

It would also be great if the tenant logo input would have the same file-picker logic as application logos have (i.e. show a file-picker if a volume is mounted under /media and also offer the possibility to revert back to the default again)

septatrix avatar Jul 03 '23 15:07 septatrix

Another bug-ish issue is that Authentik offers to delete images even when once under /dist/static are referenced which it definitely should not. In general media/file-input handling seems like it would benefit from a bit of a cleanup, either making it more powerful (separate media section similar to certificates, fa-icon overview etc.) or keeping it simpler but avoiding inconsistencies (get rid of the delete option, only always offer text field etc.).

septatrix avatar Jul 03 '23 15:07 septatrix

As a heads up to @BeryJu comment above, it appears setting URLs, e.g. for a Tenant's Logo or Favicon, is now also possible when /media is mounted into the 2023.8.1 container.

almereyda avatar Aug 31 '23 22:08 almereyda

To update this issue:

  • The static logout page will be customisable with #5048, which I'll resurrect
  • We will consider allow the removal of the Powered by authentik footer for enterprise customers
  • The ability to set a default flow background image on a "tenant" (which has since been renamed to "Brand") will be added to our roadmap

BeryJu avatar Mar 21 '24 17:03 BeryJu