Skip to main content

Advanced Branding with Themes

Customize SecureAuth identity experiences per workspace by creating and binding custom themes. Themes allow advanced control over HTML structure, messages, and components.

Use advanced branding to build tenant-specific or organization-specific login flows, messages, and layouts using templates and reusable components.


What Can Be Customized​

  • Login, Consent, MFA, and Error Pages
  • Messages (email/SMS)
  • Reusable Components (e.g., headers, footers)
  • Full HTML layout, structure, and behavior via Golang templates

Create a Theme​

Steps to Create​

  1. Go to Tenant Settings > Appearance, and open the Themes tab.
  2. Click + CREATE THEME.
  3. Enter a unique theme name.
  4. (Optional) Add a logo/icon URL to identify this theme in the UI.
  5. Click Create.

Result: Your new theme appears in the list.

Want to Duplicate?​

Use the three-dot menu next to any theme > Duplicate Theme to reuse or iterate from an existing one.


Edit Templates​

  1. In the Themes list, click the theme you want to customize to open the theme editor.
  2. Navigate the three template categories:
Template TypeDescription
PagesUI pages (Login, Consent, MFA, Error)
MessagesOutgoing email or SMS messages
ComponentsCommon HTML blocks reused across templates

Use the live preview panel to test page and message templates as you edit.

Tip: You can insert OTPs or variables using [[OTP]], [[User.Email]], etc.

Scripts in page templates​

Add nonce="{{ .Config.Nonce }}" to every <script> tag in a page template, both inline and external. A nonce-based Content Security Policy only runs scripts that carry the per-request nonce:

<script nonce="{{ .Config.Nonce }}">
document.getElementById("sign-in-form").addEventListener("submit", function (event) {
// your logic
});
</script>

A nonce-based policy also blocks inline event handler attributes such as onclick or onsubmit, and javascript: links. Attach handlers with addEventListener from a script that carries the nonce.

Where the nonce lives depends on the data the template receives:

TemplateNonce expression
Page templates (pages/...) and partials called with the whole page, as in {{ template "my_partial" . }}{{ .Config.Nonce }}
shared/header.tmpl, which receives .Config{{ .Nonce }}
Partials called with a dictPass the nonce in: {{ template "my_partial" dict "Nonce" .Config.Nonce }}, then use {{ .Nonce }} inside

If you override shared/header.tmpl, both ways of calling the language selector get the nonce: {{ template "language_selector" .I18n }} and the built-in header's call:

{{ template "language_selector" dict "I18n" .I18n "Nonce" .Nonce "CurrentLocale" .I18n.CurrentLocale "SupportedLocales" .I18n.SupportedLocales "SelectorEnabled" .I18n.SelectorEnabled }}

If you add your own scripts to the header, use {{ .Nonce }} or {{ .I18n.Nonce }}.


Bind a Theme to a Workspace​

Themes are scoped per workspace.

  1. From the Themes list, click the three dots next to your theme.
  2. Select Bind to Workspace.
  3. Choose the workspace and click Save.

Each workspace can have only one active theme at a time.

To see which workspaces use a theme, click Show workspaces in the theme's row.


CI/CD Integration​

For production use, you can:

  • Store and manage themes in source control
  • Use the CI/CD repo integration to promote changes across environments

Preview Themes​

  • View live updates inside the preview editor.
  • Switch between page types (Login, Consent, MFA, etc.)
  • Visual validation before publishing.

Summary of Advanced Branding Options​

AreaWhat You Control
PagesHTML layout and styles
MessagesEmail/SMS content and branding
ComponentsShared UI parts across templates
Workspace BindingAssign different themes per org
CI/CDAutomate and version-control themes

Troubleshooting​

IssueCauseSolution
Save disabledTemplate errorOpen browser console for syntax issues
Preview brokenMalformed HTML or tokensValidate placeholders and layout
Changes not appliedTheme not boundEnsure workspace has correct theme
Script does not runMissing CSP nonceAdd nonce="{{ .Config.Nonce }}" to the <script> tag