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:
packages/experience/src/Providers/SettingsProvider/index.tsx– Initializes the UI context with sign-in experience data and handles preview modespackages/experience/src/Providers/SettingsProvider/use-sign-in-experience.ts– Fetches the experience JSON from the well-known endpoint and sets the themepackages/schemas/src/types/sign-in-experience.ts– TypeScript definitions for theFullSignInExperienceconfiguration objectpackages/core/src/routes/well-known/well-known.experience.ts– Server-side endpoint that aggregates and returns the tenant's configurationpackages/experience/src/pages/SignIn/Main.tsx– Core sign-in page that decides which method components (password, social, passkey) to renderpackages/core/src/routes/experience/classes/libraries/adaptive-mfa-validator/– Contains rule implementations for untrusted IP detection and other adaptive MFA logic
Summary
- Logto's sign-in UI is purely reactive, rendering based on JSON configuration from the
/api/.well-known/experienceendpoint rather than hardcoded components. - Changes apply immediately without code modifications or redeployment, as the
SettingsProviderfetches fresh configuration on page load. - The
FullSignInExperiencetype inpackages/schemas/src/types/sign-in-experience.tsdefines 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.tsxconditionally rendering them. - Programmatic updates use the
PATCH /api/sign-in-expendpoint, 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →