How to Customize the Logto UI: A Complete Guide to Custom Sign-In Experiences

Logto enables full customization of the authentication interface by allowing developers to upload static HTML, CSS, and JavaScript bundles that replace the default sign-in experience.

The logto-io/logto repository provides a comprehensive custom UI system that lets you completely replace the built-in sign-in flow with your own branded interface. This guide walks through the exact implementation details found in the source code, from preparing your asset bundle to configuring security policies.

Architecture Overview of Logto Custom UI

The custom UI system follows a specific deployment and serving pipeline. When you customize the Logto UI, your static assets are stored in Azure Blob Storage and served through dedicated Koa middleware that handles routing and security.

The flow works as follows:

  1. You create a ZIP archive containing your static files (index.html, CSS, JavaScript).
  2. The bundle uploads via the Management API endpoint defined in packages/core/src/routes/sign-in-experience/custom-ui-assets/index.openapi.json.
  3. Logto stores the assets in Azure Blob Storage using the driver in packages/core/src/utils/storage/azure-storage.js.
  4. During authentication requests, the koa-serve-custom-ui-assets.ts middleware streams files directly from Blob Storage, automatically redirecting directory requests to index.html.

Preparing Your Custom UI Bundle

Before you can customize the Logto UI, you must structure your static assets correctly. The bundle must include an index.html entry point that handles the authentication form and posts to /callback.

Create a directory structure like this:

my-custom-ui/
├─ index.html        # Must expose a form posting to /callback

├─ style.css
└─ script.js

Package these files into a ZIP archive:

cd my-custom-ui
zip -r ../my-custom-ui.zip .

Deploying Custom UI Assets

Logto offers two methods to upload your custom UI bundle: the Tunnel CLI for developer convenience, or direct Management API calls for automated pipelines.

Using the Tunnel CLI

The Tunnel CLI provides the smoothest developer experience for customizing the Logto UI. The deploy command in packages/tunnel/src/commands/deploy/index.ts handles authentication, packaging, and uploading in a single operation.

Deploy your custom UI using:

npx @logto/tunnel deploy \
  --auth <M2M_APP_ID>:<M2M_APP_SECRET> \
  --endpoint https://<tenant-id>.logto.app \
  --experience-path ./my-custom-ui

If you have already created a ZIP file, replace --experience-path with --zip-path:

npx @logto/tunnel deploy \
  --auth <M2M_APP_ID>:<M2M_APP_SECRET> \
  --endpoint https://<tenant-id>.logto.app \
  --zip-path ./my-custom-ui.zip

The CLI validates the endpoint URL, creates the ZIP if needed, and POSTs it to the upload API. It then stores the returned customUiAssetId and updates your tenant's Sign-in Experience configuration automatically.

Manual Upload via Management API

For CI/CD pipelines or custom automation, call the upload endpoint directly. First obtain a Machine-to-Machine (M2M) access token, then POST to the custom UI assets endpoint:

curl -X POST "https://<tenant>.logto.app/api/sign-in-experience/custom-ui-assets" \
  -H "Authorization: Bearer <M2M_ACCESS_TOKEN>" \
  -F "file=@my-custom-ui.zip"

The API returns a JSON response containing the asset identifier:

{
  "customUiAssetId": "abc123"
}

You must then update your tenant's Sign-in Experience settings to reference this customUiAssetId through the admin console or Management API.

Configuring Content Security Policy

When you customize the Logto UI with external scripts or API calls, you must define a Content Security Policy (CSP) to whitelist permitted sources. The CSP schema and validator live in packages/toolkit/core-kit/src/custom-ui-csp.ts.

The system validates only two directives: scriptSrc for JavaScript sources and connectSrc for API endpoints. Configure these in your Sign-in Experience settings:

{
  "customUiCsp": {
    "scriptSrc": ["https://cdn.example.com"],
    "connectSrc": ["https://api.example.com"]
  }
}

The customUiCspGuard validator ensures no other CSP directives are accepted, maintaining a strict security boundary for custom authentication interfaces.

How Logto Serves Custom UI Assets

Once uploaded, Logto serves your custom UI through specialized middleware. The koa-serve-custom-ui-assets.ts file in packages/core/src/middleware/ handles every aspect of asset delivery.

This middleware:

  • Looks up the customUiAssetId associated with the current tenant.
  • Streams files directly from Azure Blob Storage using the utilities in packages/core/src/utils/storage/azure-storage.js.
  • Supports HTTP range requests for efficient video and audio delivery.
  • Automatically redirects directory paths (like /) to index.html.
  • Applies the custom CSP headers you configured to prevent XSS attacks.

If no custom UI asset is configured for the tenant, Logto falls back to the default built-in sign-in experience.

Summary

  • Logto custom UI replaces the default sign-in experience with your own static HTML, CSS, and JavaScript.
  • Assets must be packaged as a ZIP file with an index.html entry point that posts to /callback.
  • Upload via the Tunnel CLI (@logto/tunnel deploy) or the Management API endpoint at /sign-in-experience/custom-ui-assets.
  • Files are stored in Azure Blob Storage and served by the koa-serve-custom-ui-assets.ts middleware.
  • Configure CSP rules through customUiCsp to whitelist external scripts (scriptSrc) and API endpoints (connectSrc).
  • The middleware handles range requests and automatically serves index.html for directory paths.

Frequently Asked Questions

What file formats are supported for custom UI bundles?

Logto accepts only ZIP archives containing static web assets. Your bundle must include HTML, CSS, and JavaScript files, with index.html serving as the mandatory entry point. The system does not support server-side rendering or dynamic file generation.

Where are custom UI assets stored after upload?

All custom UI assets are stored in Azure Blob Storage, which is currently the only supported storage provider in the Logto architecture. The koa-serve-custom-ui-assets.ts middleware streams files directly from Blob Storage rather than local disk, enabling efficient multi-tenant isolation and CDN integration.

How do I update my custom UI after the initial deployment?

To update your custom UI, create a new ZIP bundle and deploy it using the same method as the initial upload. Whether using the Tunnel CLI or the Management API, the new upload generates a fresh customUiAssetId that you must apply to your Sign-in Experience configuration. The old assets remain available until you explicitly update the configuration reference.

Can I use external CDNs and APIs in my custom UI?

Yes, but you must explicitly whitelist them in the Content Security Policy. Define scriptSrc arrays for external JavaScript libraries and connectSrc arrays for API endpoints in your Sign-in Experience settings. The customUiCspGuard validator in packages/toolkit/core-kit/src/custom-ui-csp.ts enforces these restrictions to prevent unauthorized script execution or data exfiltration.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →