Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Controller, useFormContext } from 'react-hook-form';
import { useTranslation } from 'react-i18next';

import LogoAndFavicon from '@/components/ImageInputs/LogoAndFavicon';
import { isCloud, isDevFeaturesEnabled } from '@/consts/env';
import Button from '@/ds-components/Button';
import Card from '@/ds-components/Card';
import ColorPicker from '@/ds-components/ColorPicker';
Expand Down Expand Up @@ -49,6 +50,8 @@ function BrandingForm() {
}
}, [handleResetColor, isDarkModeEnabled, isDirty]);

const showHideLogtoBranding = isCloud && isDevFeaturesEnabled;

return (
<Card>
<FormSectionTitle title="branding.title" />
Expand Down Expand Up @@ -113,6 +116,14 @@ function BrandingForm() {
/>
</>
)}
{showHideLogtoBranding && (
<FormField title="sign_in_exp.branding.hide_logto_branding">
<Switch
label={t('sign_in_exp.branding.hide_logto_branding_description')}
{...register('hideLogtoBranding')}
/>
</FormField>
)}
</Card>
);
}
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/__mocks__/sign-in-experience.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const mockSignInExperience: SignInExperience = {
branding: {
logoUrl: 'http://logto.png',
},
hideLogtoBranding: false,
termsOfUseUrl: mockTermsOfUseUrl,
privacyPolicyUrl: mockPrivacyPolicyUrl,
languageInfo: {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/queries/sign-in-experience.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('sign-in-experience query', () => {
it('findDefaultSignInExperience', async () => {
/* eslint-disable sql/no-unsafe-query */
const expectSql = `
select "tenant_id", "id", "color", "branding", "language_info", "terms_of_use_url", "privacy_policy_url", "agree_to_terms_policy", "sign_in", "sign_up", "social_sign_in", "social_sign_in_connector_targets", "sign_in_mode", "custom_css", "custom_content", "custom_ui_assets", "password_policy", "mfa", "single_sign_on_enabled", "support_email", "support_website_url", "unknown_session_redirect_url", "captcha_policy", "sentinel_policy", "email_blocklist_policy", "forgot_password_methods"
select "tenant_id", "id", "color", "branding", "hide_logto_branding", "language_info", "terms_of_use_url", "privacy_policy_url", "agree_to_terms_policy", "sign_in", "sign_up", "social_sign_in", "social_sign_in_connector_targets", "sign_in_mode", "custom_css", "custom_content", "custom_ui_assets", "password_policy", "mfa", "single_sign_on_enabled", "support_email", "support_website_url", "unknown_session_redirect_url", "captcha_policy", "sentinel_policy", "email_blocklist_policy", "forgot_password_methods"
from "sign_in_experiences"
where "id"=$1
`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
"languageInfo": {
"description": "The language detection policy for the sign-in page."
},
"hideLogtoBranding": {
"description": "Cloud only. Whether to hide the Logto branding on hosted sign-in pages."
},
"signIn": {
"description": "Sign-in method settings."
},
Expand Down Expand Up @@ -112,6 +115,9 @@
"languageInfo": {
"description": "Control the language detection policy for the sign-in page."
},
"hideLogtoBranding": {
"description": "Cloud only. Whether to hide the Logto branding on hosted sign-in pages."
},
"signIn": {
"description": "Sign-in method settings"
},
Expand Down
8 changes: 7 additions & 1 deletion packages/core/src/routes/sign-in-experience/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
import { conditional, type Optional, tryThat } from '@silverhand/essentials';
import { literal, object, string, z } from 'zod';

import { EnvSet } from '#src/env-set/index.js';
import {
validateSignUp,
validateSignIn,
Expand Down Expand Up @@ -90,7 +91,7 @@ export default function signInExperiencesRoutes<T extends ManagementApiRouter>(
async (ctx, next) => {
const {
query: { removeUnusedDemoSocialConnector },
body: { socialSignInConnectorTargets, emailBlocklistPolicy, ...rest },
body: { socialSignInConnectorTargets, emailBlocklistPolicy, hideLogtoBranding, ...rest },
} = ctx.guard;
const {
languageInfo,
Expand Down Expand Up @@ -189,6 +190,11 @@ export default function signInExperiencesRoutes<T extends ManagementApiRouter>(

const payload = {
...rest,
...conditional(
EnvSet.values.isCloud &&
EnvSet.values.isDevFeaturesEnabled &&
hideLogtoBranding !== undefined && { hideLogtoBranding }
),
...conditional(
filteredSocialSignInConnectorTargets && {
socialSignInConnectorTargets: filteredSocialSignInConnectorTargets,
Expand Down
8 changes: 7 additions & 1 deletion packages/experience/src/Layout/AppLayout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import classNames from 'classnames';
import { useContext } from 'react';
import { Outlet } from 'react-router-dom';

import PageContext from '@/Providers/PageContextProvider/PageContext';
import LogtoSignature from '@/components/LogtoSignature';
import usePlatform from '@/hooks/use-platform';
import { layoutClassNames } from '@/utils/consts';
Expand All @@ -9,15 +11,19 @@ import CustomContent from './CustomContent';
import styles from './index.module.scss';

const AppLayout = () => {
const { experienceSettings } = useContext(PageContext);
const { isMobile } = usePlatform();
const hideLogtoBranding = experienceSettings?.hideLogtoBranding === true;

return (
<div className={styles.viewBox}>
<div className={classNames(styles.container, layoutClassNames.pageContainer)}>
{!isMobile && <CustomContent className={layoutClassNames.customContent} />}
<main className={classNames(styles.main, layoutClassNames.mainContent)}>
<Outlet />
<LogtoSignature className={classNames(styles.signature, layoutClassNames.signature)} />
{!hideLogtoBranding && (
<LogtoSignature className={classNames(styles.signature, layoutClassNames.signature)} />
)}
</main>
</div>
</div>
Expand Down
2 changes: 2 additions & 0 deletions packages/experience/src/__mocks__/logto.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ export const mockSignInExperience: SignInExperience = {
sentinelPolicy: {},
emailBlocklistPolicy: {},
forgotPasswordMethods: [],
hideLogtoBranding: false,
};

export const mockSignInExperienceSettings: SignInExperienceResponse = {
Expand All @@ -131,6 +132,7 @@ export const mockSignInExperienceSettings: SignInExperienceResponse = {
termsOfUseUrl: mockSignInExperience.termsOfUseUrl,
privacyPolicyUrl: mockSignInExperience.privacyPolicyUrl,
languageInfo: mockSignInExperience.languageInfo,
hideLogtoBranding: false,
signIn: mockSignInExperience.signIn,
signUp: {
identifiers: [SignInIdentifier.Username],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ const sign_in_exp = {
app_logo_and_favicon: 'شعار التطبيق ورمز الموقع',
company_logo_and_favicon: 'شعار الشركة ورمز الموقع',
organization_logo_and_favicon: 'شعار المنظمة ورمز الموقع',
hide_logto_branding: 'Hide Logto branding',
hide_logto_branding_description:
'Remove "Powered by Logto". Spotlight your brand exclusively with a clean, professional sign-in experience.',
},
branding_uploads: {
app_logo: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ const sign_in_exp = {
app_logo_and_favicon: 'App-Logo und Favicon',
company_logo_and_favicon: 'Firmenlogo und Favicon',
organization_logo_and_favicon: 'Organisationslogo und Favicon',
hide_logto_branding: 'Hide Logto branding',
hide_logto_branding_description:
'Remove "Powered by Logto". Spotlight your brand exclusively with a clean, professional sign-in experience.',
},
branding_uploads: {
app_logo: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ const sign_in_exp = {
app_logo_and_favicon: 'App logo and favicon',
company_logo_and_favicon: 'Company logo and favicon',
organization_logo_and_favicon: 'Organization logo and favicon',
hide_logto_branding: 'Hide Logto branding',
hide_logto_branding_description:
'Remove "Powered by Logto". Spotlight your brand exclusively with a clean, professional sign-in experience.',
},
branding_uploads: {
app_logo: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ const sign_in_exp = {
app_logo_and_favicon: 'Logotipo y favicon de la aplicación',
company_logo_and_favicon: 'Logotipo y favicon de la empresa',
organization_logo_and_favicon: 'Logotipo y favicon de la organización',
hide_logto_branding: 'Hide Logto branding',
hide_logto_branding_description:
'Remove "Powered by Logto". Spotlight your brand exclusively with a clean, professional sign-in experience.',
},
branding_uploads: {
app_logo: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ const sign_in_exp = {
app_logo_and_favicon: "Logo de l'app et favicon",
company_logo_and_favicon: 'Logo de la société et favicon',
organization_logo_and_favicon: "Logo de l'organisation et favicon",
hide_logto_branding: 'Hide Logto branding',
hide_logto_branding_description:
'Remove "Powered by Logto". Spotlight your brand exclusively with a clean, professional sign-in experience.',
},
branding_uploads: {
app_logo: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ const sign_in_exp = {
app_logo_and_favicon: "Logo dell'app e favicon",
company_logo_and_favicon: 'Logo aziendale e favicon',
organization_logo_and_favicon: "Logo dell'organizzazione e favicon",
hide_logto_branding: 'Hide Logto branding',
hide_logto_branding_description:
'Remove "Powered by Logto". Spotlight your brand exclusively with a clean, professional sign-in experience.',
},
branding_uploads: {
app_logo: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ const sign_in_exp = {
app_logo_and_favicon: 'アプリロゴとファビコン',
company_logo_and_favicon: '企業ロゴとファビコン',
organization_logo_and_favicon: '組織のロゴとファビコン',
hide_logto_branding: 'Hide Logto branding',
hide_logto_branding_description:
'Remove "Powered by Logto". Spotlight your brand exclusively with a clean, professional sign-in experience.',
},
branding_uploads: {
app_logo: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ const sign_in_exp = {
app_logo_and_favicon: '앱 로고 및 파비콘',
company_logo_and_favicon: '회사 로고 및 파비콘',
organization_logo_and_favicon: '조직 로고 및 파비콘',
hide_logto_branding: 'Hide Logto branding',
hide_logto_branding_description:
'Remove "Powered by Logto". Spotlight your brand exclusively with a clean, professional sign-in experience.',
},
branding_uploads: {
app_logo: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ const sign_in_exp = {
app_logo_and_favicon: 'Logo aplikacji i favicon',
company_logo_and_favicon: 'Logo firmy i favicon',
organization_logo_and_favicon: 'Logo organizacji i favicon',
hide_logto_branding: 'Hide Logto branding',
hide_logto_branding_description:
'Remove "Powered by Logto". Spotlight your brand exclusively with a clean, professional sign-in experience.',
},
branding_uploads: {
app_logo: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ const sign_in_exp = {
app_logo_and_favicon: 'Logo do aplicativo e favicon',
company_logo_and_favicon: 'Logo da empresa e favicon',
organization_logo_and_favicon: 'Logo da organização e favicon',
hide_logto_branding: 'Hide Logto branding',
hide_logto_branding_description:
'Remove "Powered by Logto". Spotlight your brand exclusively with a clean, professional sign-in experience.',
},
branding_uploads: {
app_logo: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ const sign_in_exp = {
app_logo_and_favicon: 'Logótipo e favicon do aplicativo',
company_logo_and_favicon: 'Logótipo e favicon da empresa',
organization_logo_and_favicon: 'Logótipo e favicon da organização',
hide_logto_branding: 'Hide Logto branding',
hide_logto_branding_description:
'Remove "Powered by Logto". Spotlight your brand exclusively with a clean, professional sign-in experience.',
},
branding_uploads: {
app_logo: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ const sign_in_exp = {
app_logo_and_favicon: 'Логотип приложения и иконка',
company_logo_and_favicon: 'Логотип компании и иконка',
organization_logo_and_favicon: 'Логотип организации и иконка',
hide_logto_branding: 'Hide Logto branding',
hide_logto_branding_description:
'Remove "Powered by Logto". Spotlight your brand exclusively with a clean, professional sign-in experience.',
},
branding_uploads: {
app_logo: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ const sign_in_exp = {
app_logo_and_favicon: 'โลโก้แอปและ Favicon',
company_logo_and_favicon: 'โลโก้บริษัทและ Favicon',
organization_logo_and_favicon: 'โลโก้องค์กรและ Favicon',
hide_logto_branding: 'Hide Logto branding',
hide_logto_branding_description:
'Remove "Powered by Logto". Spotlight your brand exclusively with a clean, professional sign-in experience.',
},
branding_uploads: {
app_logo: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ const sign_in_exp = {
app_logo_and_favicon: 'Uygulama logosu ve favicon',
company_logo_and_favicon: 'Şirket logosu ve favicon',
organization_logo_and_favicon: 'Organizasyon logosu ve favicon',
hide_logto_branding: 'Hide Logto branding',
hide_logto_branding_description:
'Remove "Powered by Logto". Spotlight your brand exclusively with a clean, professional sign-in experience.',
},
branding_uploads: {
app_logo: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ const sign_in_exp = {
app_logo_and_favicon: '应用 logo 和 favicon',
company_logo_and_favicon: '公司 logo 和 favicon',
organization_logo_and_favicon: '组织 logo 和 favicon',
hide_logto_branding: 'Hide Logto branding',
hide_logto_branding_description:
'Remove "Powered by Logto". Spotlight your brand exclusively with a clean, professional sign-in experience.',
},
branding_uploads: {
app_logo: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ const sign_in_exp = {
app_logo_and_favicon: '應用程式標誌和圖標',
company_logo_and_favicon: '公司標誌和圖標',
organization_logo_and_favicon: '組織標誌和圖標',
hide_logto_branding: 'Hide Logto branding',
hide_logto_branding_description:
'Remove "Powered by Logto". Spotlight your brand exclusively with a clean, professional sign-in experience.',
},
branding_uploads: {
app_logo: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ const sign_in_exp = {
app_logo_and_favicon: '應用圖標和 Favicon',
company_logo_and_favicon: '公司圖標和 Favicon',
organization_logo_and_favicon: '組織圖標和 Favicon',
hide_logto_branding: 'Hide Logto branding',
hide_logto_branding_description:
'Remove "Powered by Logto". Spotlight your brand exclusively with a clean, professional sign-in experience.',
},
branding_uploads: {
app_logo: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { sql } from '@silverhand/slonik';

import type { AlterationScript } from '../lib/types/alteration.js';

const alteration: AlterationScript = {
up: async (pool) => {
await pool.query(sql`
alter table sign_in_experiences
add column hide_logto_branding boolean not null default false;
`);
},
down: async (pool) => {
await pool.query(sql`
alter table sign_in_experiences
drop column hide_logto_branding;
`);
},
};

export default alteration;
1 change: 1 addition & 0 deletions packages/schemas/src/seeds/sign-in-experience.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const createDefaultSignInExperience = (
logoUrl: isCloud ? undefined : 'https://logto.io/logo.svg',
darkLogoUrl: isCloud ? undefined : 'https://logto.io/logo-dark.svg',
},
hideLogtoBranding: false,
languageInfo: {
autoDetect: true,
fallbackLanguage: 'en' as const,
Expand Down
1 change: 1 addition & 0 deletions packages/schemas/tables/sign_in_experiences.sql
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ create table sign_in_experiences (
id varchar(21) not null,
color jsonb /* @use Color */ not null,
branding jsonb /* @use Branding */ not null,
hide_logto_branding boolean not null default false,
language_info jsonb /* @use LanguageInfo */ not null,
terms_of_use_url varchar(2048),
privacy_policy_url varchar(2048),
Expand Down
Loading