diff --git a/packages/core/src/routes/well-known.test.ts b/packages/core/src/routes/well-known.test.ts index f8e19bf9b..788533786 100644 --- a/packages/core/src/routes/well-known.test.ts +++ b/packages/core/src/routes/well-known.test.ts @@ -1,5 +1,5 @@ import { ConnectorType, SignInMode } from '@logto/schemas'; -import { adminConsoleApplicationId, adminConsoleSignInMethods } from '@logto/schemas/lib/seeds'; +import { adminConsoleApplicationId, adminConsoleSignInExperience } from '@logto/schemas/lib/seeds'; import { Provider } from 'oidc-provider'; import { @@ -58,6 +58,10 @@ jest.mock('oidc-provider', () => ({ })), })); +jest.mock('i18next', () => ({ + t: (key: string) => key, +})); + describe('GET /.well-known/sign-in-exp', () => { afterEach(() => { jest.clearAllMocks(); @@ -112,13 +116,16 @@ describe('GET /.well-known/sign-in-exp', () => { it('should return admin console settings', async () => { interactionDetails.mockResolvedValue({ params: { client_id: adminConsoleApplicationId } }); const response = await sessionRequest.get('/.well-known/sign-in-exp'); - expect(signInExperienceQuerySpyOn).toHaveBeenCalledTimes(1); + expect(signInExperienceQuerySpyOn).not.toBeCalled(); expect(response.status).toEqual(200); expect(response.body).toMatchObject( expect.objectContaining({ - ...mockSignInExperience, - signInMethods: adminConsoleSignInMethods, + ...adminConsoleSignInExperience, + branding: { + ...adminConsoleSignInExperience.branding, + slogan: 'admin_console.welcome.title', + }, socialConnectors: [], signInMode: SignInMode.SignIn, }) diff --git a/packages/core/src/routes/well-known.ts b/packages/core/src/routes/well-known.ts index 28e6c0a25..fa1be88a9 100644 --- a/packages/core/src/routes/well-known.ts +++ b/packages/core/src/routes/well-known.ts @@ -2,7 +2,7 @@ import { ConnectorMetadata } from '@logto/connector-types'; import { SignInMode } from '@logto/schemas'; import { adminConsoleApplicationId, - adminConsoleSignInMethods, + adminConsoleSignInExperience, demoAppApplicationId, } from '@logto/schemas/lib/seeds'; import etag from 'etag'; @@ -19,17 +19,36 @@ export default function wellKnownRoutes(router: T, pr router.get( '/.well-known/sign-in-exp', async (ctx, next) => { - const [signInExperience, connectorInstances, interaction] = await Promise.all([ - findDefaultSignInExperience(), - getConnectorInstances(), - provider.interactionDetails(ctx.req, ctx.res).catch((error: unknown) => { + const interaction = await provider + .interactionDetails(ctx.req, ctx.res) + .catch((error: unknown) => { // Should not block if interaction is not found if (error instanceof errors.SessionNotFound) { return null; } throw error; - }), + }); + + // Hard code AdminConsole sign-in methods settings. + if (interaction?.params.client_id === adminConsoleApplicationId) { + ctx.body = { + ...adminConsoleSignInExperience, + branding: { + ...adminConsoleSignInExperience.branding, + slogan: i18next.t('admin_console.welcome.title'), + }, + signInMode: (await hasActiveUsers()) ? SignInMode.SignIn : SignInMode.Register, + socialConnectors: [], + }; + + return next(); + } + + // Custom Applications + const [signInExperience, connectorInstances] = await Promise.all([ + findDefaultSignInExperience(), + getConnectorInstances(), ]); const socialConnectors = signInExperience.socialSignInConnectorTargets.reduce< @@ -46,30 +65,12 @@ export default function wellKnownRoutes(router: T, pr ]; }, []); - // Hard code AdminConsole sign-in methods settings. - if (interaction?.params.client_id === adminConsoleApplicationId) { - ctx.body = { - ...signInExperience, - signInMethods: adminConsoleSignInMethods, - signInMode: (await hasActiveUsers()) ? SignInMode.SignIn : SignInMode.Register, - socialConnectors: [], - }; + const notification = + interaction?.params.client_id === demoAppApplicationId + ? i18next.t('demo_app.notification') + : undefined; - return next(); - } - - // Insert Notification Message to DemoApp - if (interaction?.params.client_id === demoAppApplicationId) { - ctx.body = { - ...signInExperience, - socialConnectors, - notification: i18next.t('demo_app.notification'), - }; - - return next(); - } - - ctx.body = { ...signInExperience, socialConnectors }; + ctx.body = { ...signInExperience, socialConnectors, notification }; return next(); }, diff --git a/packages/phrases/src/locales/en.ts b/packages/phrases/src/locales/en.ts index 701250ef8..4a1c238f5 100644 --- a/packages/phrases/src/locales/en.ts +++ b/packages/phrases/src/locales/en.ts @@ -498,7 +498,7 @@ const translation = { button: 'Sign in again', }, welcome: { - title: 'Welcome to Logto Admin Console', + title: 'Welcome to Admin Console', description: 'Admin console is a web app to manage Logto without coding requirements. Let’s first create an account. With this account, you can manage Logto by yourself or on behalf of your company.', create_account: 'Create Account', diff --git a/packages/phrases/src/locales/zh-cn.ts b/packages/phrases/src/locales/zh-cn.ts index 05656ff65..8a8fbbedf 100644 --- a/packages/phrases/src/locales/zh-cn.ts +++ b/packages/phrases/src/locales/zh-cn.ts @@ -479,7 +479,7 @@ const translation = { button: '重新登录', }, welcome: { - title: '欢迎使用管理控制台', + title: '欢迎来到管理控制台', description: '管理控制台是一个无需代码操作的应用。你可以用它来管理登录体验。让我们首先创建一个帐号。你可以用它以个人或公司的身份管理 Logto。', create_account: '创建帐号', diff --git a/packages/schemas/src/seeds/sign-in-experience.ts b/packages/schemas/src/seeds/sign-in-experience.ts index 4ebc2bfaa..d0c9db616 100644 --- a/packages/schemas/src/seeds/sign-in-experience.ts +++ b/packages/schemas/src/seeds/sign-in-experience.ts @@ -33,9 +33,11 @@ export const defaultSignInExperience: Readonly = { signInMode: SignInMode.SignInAndRegister, }; -export const adminConsoleSignInMethods = { - username: SignInMethodState.Primary, - email: SignInMethodState.Disabled, - sms: SignInMethodState.Disabled, - social: SignInMethodState.Disabled, +export const adminConsoleSignInExperience: CreateSignInExperience = { + ...defaultSignInExperience, + branding: { + style: BrandingStyle.Logo_Slogan, + logoUrl: '', + darkLogoUrl: '', + }, }; diff --git a/packages/ui/src/pages/SignIn/index.module.scss b/packages/ui/src/pages/SignIn/index.module.scss index 8fe6c278a..b697648ea 100644 --- a/packages/ui/src/pages/SignIn/index.module.scss +++ b/packages/ui/src/pages/SignIn/index.module.scss @@ -27,6 +27,14 @@ } } +.placeholderTop { + flex: 3; +} + +.placeholderBottom { + flex: 5; +} + :global(body.mobile) { .header { margin-top: _.unit(3); @@ -48,7 +56,6 @@ :global(body.desktop) { .header { - margin-top: _.unit(6); margin-bottom: _.unit(6); } diff --git a/packages/ui/src/pages/SignIn/index.tsx b/packages/ui/src/pages/SignIn/index.tsx index db2546660..ae8f73571 100644 --- a/packages/ui/src/pages/SignIn/index.tsx +++ b/packages/ui/src/pages/SignIn/index.tsx @@ -12,7 +12,7 @@ import * as styles from './index.module.scss'; import { PrimarySection, SecondarySection, CreateAccountLink } from './registry'; const SignIn = () => { - const { experienceSettings, theme } = useContext(PageContext); + const { experienceSettings, theme, platform } = useContext(PageContext); if (!experienceSettings) { return null; @@ -29,32 +29,36 @@ const SignIn = () => { }; return ( -
- - - - {experienceSettings.signInMode !== SignInMode.Register && ( - + {platform === 'web' &&
} +
+ + - )} - {experienceSettings.signInMode === SignInMode.SignInAndRegister && ( - - )} + {experienceSettings.signInMode !== SignInMode.Register && ( + + )} - -
+ {experienceSettings.signInMode === SignInMode.SignInAndRegister && ( + + )} + + +
+ {platform === 'web' &&
} + ); };