Customizing the Logto Sign-In Experience and UI: A Complete Guide to Runtime Configuration

Logto provides a runtime-configurable sign-in UI that renders authentication flows based on tenant-specific settings fetched from the /api/.well-known/experience endpoint, requiring no code changes to update branding, connectors, or security policies.

Logto is an open-source identity infrastructure that separates authentication logic from presentation. The logto-io/logto repository implements a dynamic sign-in experience where all UI elements—from logos to multi-factor authentication rules—are controlled via a JSON configuration stored in the database and consumed by the Experience package at runtime.

How Runtime Configuration Drives the UI

The sign-in experience initializes through a React context hierarchy that fetches tenant settings before rendering any authentication components.

In packages/experience/src/Providers/SettingsProvider/use-sign-in-experience.ts, the SettingsProvider loads the configuration and initializes internationalization:

// packages/experience/src/Providers/SettingsProvider/use-sign-in-experience.ts
const [settings] = await Promise.all([getSignInExperienceSettings(), initI18n()]);
setExperienceSettings(settings);

These settings propagate through PageContext and are consumed throughout the application. The provider in packages/experience/src/Providers/SettingsProvider/index.tsx conditionally renders children only after configuration loads:

// packages/experience/src/Providers/SettingsProvider/index.tsx
const { isPreview, experienceSettings } = useContext(PageContext);
const usePageLoad = useMemo(() => (isPreview ? usePreview : useSignInExperience), [isPreview]);
usePageLoad();
return experienceSettings ? children : null;

Individual pages then read this context to determine which components to display. For example, packages/experience/src/pages/SignIn/Main.tsx uses the settings to conditionally render social login buttons, password fields, or passkey options based on the tenant's enabled methods.

The FullSignInExperience Data Model

The configuration schema defined in packages/schemas/src/types/sign-in-experience.ts (lines 34-63) determines every UI-visible aspect of authentication. The FullSignInExperience type includes:

  • Branding assets – Logo URLs, primary colors, dark mode preferences
  • Connector configurations – Social identity providers and SSO settings
  • Security policies – Password requirements, captcha settings, and adaptive MFA rules
  • Registration flows – Custom profile fields and required user data
  • Google One-Tap – Optional seamless authentication configuration

The server aggregates this data at packages/core/src/routes/well-known/well-known.experience.ts (lines 11-13), exposing it via the /api/.well-known/experience endpoint. The Experience package fetches this payload on load, caches it, and supplies it to the UI context.

Customization Points Available Without Code Changes

Logto's architecture allows complete UI customization through API calls or the admin console, with changes reflecting immediately on the next page load.

Branding and Theming The settings.color object controls primary colors, while logo assets and dark-mode toggles are configured via the branding field. These values flow directly into the CSS variables of the Experience package.

Authentication Methods Social connectors render dynamically based on the socialConnectors array. Adding a new connector automatically generates the corresponding button in packages/experience/src/pages/SignIn/Main.tsx without frontend redeployment.

Adaptive MFA Security rules defined in packages/core/src/routes/experience/classes/libraries/adaptive-mfa-validator/ (such as untrusted-ip.ts) determine when to challenge users for additional factors. The UI reads these rules from the context to conditionally render TOTP or WebAuthn prompts.

Custom Profile Fields Registration flows support custom profile fields chosen in the admin console, rendered through the customProfileFields configuration in the sign-in experience settings.

Programmatically Updating the Sign-In Experience

While the admin console provides a GUI, you can programmatically modify the sign-in experience using the REST API endpoint PATCH /api/sign-in-exp.

Adding a Custom Social Connector

The following example demonstrates fetching connector metadata and updating the sign-in experience to include a new social provider:

import { updateSignInExperience } from '#src/api/sign-in-experience.js';
import { fetchConnectorMetadata } from '#src/api/connector.js';

// 1️⃣ Fetch the connector metadata (only the fields needed for UI)
const connector = await fetchConnectorMetadata('my-google-connector');

// 2️⃣ Update the tenant’s sign-in experience
await updateSignInExperience({
  socialConnectors: [
    // Preserve existing connectors (simplified)
    ...existing.socialConnectors,
    {
      id: connector.id,
      name: connector.name,
      logo: connector.logo,
      // other fields required by ExperienceSocialConnector
    },
  ],
});

// 3️⃣ The next page load will automatically render the new button

Enabling Google One-Tap

To enable seamless authentication, update the configuration with your Google client credentials:

await updateSignInExperience({
  googleOneTap: {
    clientId: 'YOUR_GOOGLE_CLIENT_ID',
    connectorId: 'google-connector-id',
    // Additional Google One-Tap config (if any)
    ...googleOneTapConfig,
  },
});

Once the payload includes googleOneTap, the sign-in page conditionally injects the One-Tap script. The implementation in packages/experience/src/pages/SignIn/index.tsx reads this value from context to determine whether to initialize the Google SDK.

Key Implementation Files

Understanding these source files clarifies how configuration transforms into UI:

Summary

  • Logto's sign-in UI is purely reactive, rendering based on JSON configuration from the /api/.well-known/experience endpoint rather than hardcoded components.
  • Changes apply immediately without code modifications or redeployment, as the SettingsProvider fetches fresh configuration on page load.
  • The FullSignInExperience type in packages/schemas/src/types/sign-in-experience.ts defines all customizable elements, from branding to MFA rules.
  • Social connectors and authentication methods appear automatically when added to the configuration array, with the UI in packages/experience/src/pages/SignIn/Main.tsx conditionally rendering them.
  • Programmatic updates use the PATCH /api/sign-in-exp endpoint, allowing CI/CD pipelines to modify authentication flows alongside infrastructure changes.

Frequently Asked Questions

How do I change the logo and brand colors without redeploying Logto?

Update the tenant's sign-in experience via the admin console or API. The branding object in the configuration accepts logo URLs and color hex codes. When you save changes to PATCH /api/sign-in-exp, the SettingsProvider automatically fetches the new settings on the next user request, applying the updated CSS variables immediately without server restart.

Can I enable passkey authentication programmatically?

Yes. Passkey (WebAuthn) support is controlled through the sign-in experience settings. Use the updateSignInExperience API to enable the feature, and ensure the user's browser supports WebAuthn. The UI components in packages/experience/src/pages/SignIn/Main.tsx check the context settings to determine whether to render the passkey button and handle the authentication flow accordingly.

What is the well-known endpoint and how does it work?

The /api/.well-known/experience endpoint (implemented in packages/core/src/routes/well-known/well-known.experience.ts) aggregates the tenant's complete sign-in configuration, including connectors, branding, and MFA policies. The Experience frontend calls this endpoint during initialization via getSignInExperienceSettings(), caching the result to render the appropriate UI components for that specific tenant.

How does Logto handle adaptive MFA in the UI?

Adaptive MFA rules defined in packages/core/src/routes/experience/classes/libraries/adaptive-mfa-validator/ evaluate risk factors like IP trust status. When these rules trigger, the backend includes MFA requirements in the authentication response. The UI then conditionally renders TOTP input fields or WebAuthn prompts based on the experienceSettings context, ensuring users only see additional verification steps when security policies require them.

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 →