diff --git a/packages/experience/src/App.tsx b/packages/experience/src/App.tsx
index 56e03faa5..3ab55e763 100644
--- a/packages/experience/src/App.tsx
+++ b/packages/experience/src/App.tsx
@@ -14,6 +14,7 @@ import Continue from './pages/Continue';
import DirectSignIn from './pages/DirectSignIn';
import ErrorPage from './pages/ErrorPage';
import ForgotPassword from './pages/ForgotPassword';
+import IdentifierRegister from './pages/IdentifierRegister';
import IdentifierSignIn from './pages/IdentifierSignIn';
import MfaBinding from './pages/MfaBinding';
import BackupCodeBinding from './pages/MfaBinding/BackupCodeBinding';
@@ -129,6 +130,12 @@ const App = () => {
path={experience.routes.identifierSignIn}
element={}
/>
+
+ {/* Identifier register */}
+ }
+ />
>
)}
diff --git a/packages/experience/src/pages/IdentifierRegister/index.tsx b/packages/experience/src/pages/IdentifierRegister/index.tsx
new file mode 100644
index 000000000..a7ab58c66
--- /dev/null
+++ b/packages/experience/src/pages/IdentifierRegister/index.tsx
@@ -0,0 +1,41 @@
+import { AgreeToTermsPolicy, experience } from '@logto/schemas';
+import { useTranslation } from 'react-i18next';
+import { Navigate } from 'react-router-dom';
+
+import IdentifierPageLayout from '@/Layout/IdentifierPageLayout';
+import { identifierInputDescriptionMap } from '@/utils/form';
+
+import IdentifierRegisterForm from '../Register/IdentifierRegisterForm';
+
+import useIdentifierSignUpMethods from './use-identifier-sign-up-methods';
+
+const IdentifierRegister = () => {
+ const { t } = useTranslation();
+ const signUpMethods = useIdentifierSignUpMethods();
+
+ /**
+ * Fallback to sign-in page if no sign up methods are available (not allowed to create an account).
+ */
+ if (signUpMethods.length === 0) {
+ return ;
+ }
+
+ return (
+ t(identifierInputDescriptionMap[identifier])),
+ })}
+ footerTermsDisplayPolicies={[AgreeToTermsPolicy.Automatic]}
+ authOptionsLink={{
+ to: `/${experience.routes.register}`,
+ text: 'description.all_account_creation_options',
+ }}
+ >
+
+
+ );
+};
+
+export default IdentifierRegister;
diff --git a/packages/experience/src/pages/IdentifierRegister/use-identifier-sign-up-methods.ts b/packages/experience/src/pages/IdentifierRegister/use-identifier-sign-up-methods.ts
new file mode 100644
index 000000000..1a08d0553
--- /dev/null
+++ b/packages/experience/src/pages/IdentifierRegister/use-identifier-sign-up-methods.ts
@@ -0,0 +1,35 @@
+import { useMemo } from 'react';
+
+import useIdentifierParams from '@/hooks/use-identifier-params';
+import { useSieMethods } from '@/hooks/use-sie';
+
+/**
+ * Read sign-up methods from sign-in experience config and URL identifier parameters.
+ *
+ * Sign-up methods fallback logic:
+ * 1. If no identifiers are provided in the URL, return all sign-up methods from sign-in experience config.
+ * 2. If identifiers are provided in the URL but all of them are not supported by the sign-in experience config, return all sign-up methods from sign-in experience config.
+ * 3. If identifiers are provided in the URL and supported by the sign-in experience config, return the intersection of the two.
+ */
+const useIdentifierSignUpMethods = () => {
+ const { signUpMethods: signUpMethodsFromSie } = useSieMethods();
+ const { identifiers } = useIdentifierParams();
+
+ return useMemo(() => {
+ // Fallback to all sign up methods if no identifiers are provided
+ if (identifiers.length === 0) {
+ return signUpMethodsFromSie;
+ }
+
+ const methods = signUpMethodsFromSie.filter((identifier) => identifiers.includes(identifier));
+
+ // Fallback to all sign up methods if no identifiers are supported
+ if (methods.length === 0) {
+ return signUpMethodsFromSie;
+ }
+
+ return methods;
+ }, [identifiers, signUpMethodsFromSie]);
+};
+
+export default useIdentifierSignUpMethods;
diff --git a/packages/phrases-experience/src/locales/de/description.ts b/packages/phrases-experience/src/locales/de/description.ts
index bde66dd9c..3d72bc3aa 100644
--- a/packages/phrases-experience/src/locales/de/description.ts
+++ b/packages/phrases-experience/src/locales/de/description.ts
@@ -105,6 +105,9 @@ const description = {
identifier_sign_in_description:
'Geben Sie Ihre {{types, list(type: disjunction;)}} ein, um sich anzumelden.',
all_sign_in_options: 'Alle Anmeldeoptionen',
+ identifier_register_description:
+ 'Geben Sie Ihre {{types, list(type: disjunction;)}} ein, um ein neues Konto zu erstellen.',
+ all_account_creation_options: 'Alle Kontoerstellungsoptionen',
};
export default Object.freeze(description);
diff --git a/packages/phrases-experience/src/locales/en/description.ts b/packages/phrases-experience/src/locales/en/description.ts
index 8689e7e1e..62ad75401 100644
--- a/packages/phrases-experience/src/locales/en/description.ts
+++ b/packages/phrases-experience/src/locales/en/description.ts
@@ -90,6 +90,9 @@ const description = {
auto_agreement: 'By continuing, you agree to the .',
identifier_sign_in_description: 'Enter you {{types, list(type: disjunction;)}} to sign in.',
all_sign_in_options: 'All sign-in options',
+ identifier_register_description:
+ 'Enter you {{types, list(type: disjunction;)}} to create a new account.',
+ all_account_creation_options: 'All account creation options',
};
export default Object.freeze(description);
diff --git a/packages/phrases-experience/src/locales/es/description.ts b/packages/phrases-experience/src/locales/es/description.ts
index fbec1edf2..ad6341bc2 100644
--- a/packages/phrases-experience/src/locales/es/description.ts
+++ b/packages/phrases-experience/src/locales/es/description.ts
@@ -105,6 +105,9 @@ const description = {
identifier_sign_in_description:
'Ingrese su {{types, list(type: disjunction;)}} para iniciar sesión.',
all_sign_in_options: 'Todas las opciones de inicio de sesión',
+ identifier_register_description:
+ 'Ingrese su {{types, list(type: disjunction;)}} para crear una nueva cuenta.',
+ all_account_creation_options: 'Todas las opciones de creación de cuenta',
};
export default Object.freeze(description);
diff --git a/packages/phrases-experience/src/locales/fr/description.ts b/packages/phrases-experience/src/locales/fr/description.ts
index 867e96ec7..748074b7b 100644
--- a/packages/phrases-experience/src/locales/fr/description.ts
+++ b/packages/phrases-experience/src/locales/fr/description.ts
@@ -105,6 +105,9 @@ const description = {
identifier_sign_in_description:
'Entrez votre {{types, list(type: disjunction;)}} pour vous connecter.',
all_sign_in_options: 'Toutes les options de connexion',
+ identifier_register_description:
+ 'Entrez votre {{types, list(type: disjunction;)}} pour créer un nouveau compte.',
+ all_account_creation_options: 'Toutes les options de création de compte',
};
export default Object.freeze(description);
diff --git a/packages/phrases-experience/src/locales/it/description.ts b/packages/phrases-experience/src/locales/it/description.ts
index 8c5fa999e..3a3eed40c 100644
--- a/packages/phrases-experience/src/locales/it/description.ts
+++ b/packages/phrases-experience/src/locales/it/description.ts
@@ -102,6 +102,9 @@ const description = {
identifier_sign_in_description:
'Inserisci il tuo {{types, list(type: disjunction;)}} per accedere.',
all_sign_in_options: 'Tutte le opzioni di accesso',
+ identifier_register_description:
+ 'Inserisci il tuo {{types, list(type: disjunction;)}} per creare un nuovo account.',
+ all_account_creation_options: 'Tutte le opzioni di creazione account',
};
export default Object.freeze(description);
diff --git a/packages/phrases-experience/src/locales/ja/description.ts b/packages/phrases-experience/src/locales/ja/description.ts
index 4bf415507..6a68e572b 100644
--- a/packages/phrases-experience/src/locales/ja/description.ts
+++ b/packages/phrases-experience/src/locales/ja/description.ts
@@ -101,6 +101,9 @@ const description = {
auto_agreement: '続行することで、に同意したことになります。',
identifier_sign_in_description: '{{types, list(type: disjunction;)}}を入力してサインインします。',
all_sign_in_options: 'すべてのサインインオプション',
+ identifier_register_description:
+ '{{types, list(type: disjunction;)}}を入力して新しいアカウントを作成します。',
+ all_account_creation_options: 'すべてのアカウント作成オプション',
};
export default Object.freeze(description);
diff --git a/packages/phrases-experience/src/locales/ko/description.ts b/packages/phrases-experience/src/locales/ko/description.ts
index 460663a2c..6a952a4fd 100644
--- a/packages/phrases-experience/src/locales/ko/description.ts
+++ b/packages/phrases-experience/src/locales/ko/description.ts
@@ -96,6 +96,9 @@ const description = {
identifier_sign_in_description:
'로그인하려면 {{types, list(type: disjunction;)}}을(를) 입력하세요.',
all_sign_in_options: '모든 로그인 옵션',
+ identifier_register_description:
+ '새 계정을 만들려면 {{types, list(type: disjunction;)}}을(를) 입력하세요.',
+ all_account_creation_options: '모든 계정 생성 옵션',
};
export default Object.freeze(description);
diff --git a/packages/phrases-experience/src/locales/pl-pl/description.ts b/packages/phrases-experience/src/locales/pl-pl/description.ts
index 8ec9d862d..8f1172498 100644
--- a/packages/phrases-experience/src/locales/pl-pl/description.ts
+++ b/packages/phrases-experience/src/locales/pl-pl/description.ts
@@ -103,6 +103,9 @@ const description = {
identifier_sign_in_description:
'Wprowadź swoje {{types, list(type: disjunction;)}} aby się zalogować.',
all_sign_in_options: 'Wszystkie opcje logowania',
+ identifier_register_description:
+ 'Wprowadź swoje {{types, list(type: disjunction;)}} aby utworzyć nowe konto.',
+ all_account_creation_options: 'Wszystkie opcje tworzenia konta',
};
export default Object.freeze(description);
diff --git a/packages/phrases-experience/src/locales/pt-br/description.ts b/packages/phrases-experience/src/locales/pt-br/description.ts
index 2bfa37561..d9e0cb9a7 100644
--- a/packages/phrases-experience/src/locales/pt-br/description.ts
+++ b/packages/phrases-experience/src/locales/pt-br/description.ts
@@ -99,6 +99,9 @@ const description = {
auto_agreement: 'Ao continuar, você concorda com os .',
identifier_sign_in_description: 'Digite seu {{types, list(type: disjunction;)}} para entrar.',
all_sign_in_options: 'Todas as opções de login',
+ identifier_register_description:
+ 'Digite seu {{types, list(type: disjunction;)}} para criar uma nova conta.',
+ all_account_creation_options: 'Todas as opções de criação de conta',
};
export default Object.freeze(description);
diff --git a/packages/phrases-experience/src/locales/pt-pt/description.ts b/packages/phrases-experience/src/locales/pt-pt/description.ts
index 70a3d64ad..1991d3825 100644
--- a/packages/phrases-experience/src/locales/pt-pt/description.ts
+++ b/packages/phrases-experience/src/locales/pt-pt/description.ts
@@ -100,6 +100,9 @@ const description = {
identifier_sign_in_description:
'Introduza o seu {{types, list(type: disjunction;)}} para iniciar sessão.',
all_sign_in_options: 'Todas as opções de início de sessão',
+ identifier_register_description:
+ 'Introduza o seu {{types, list(type: disjunction;)}} para criar uma nova conta.',
+ all_account_creation_options: 'Todas as opções de criação de conta',
};
export default Object.freeze(description);
diff --git a/packages/phrases-experience/src/locales/ru/description.ts b/packages/phrases-experience/src/locales/ru/description.ts
index dab9966c3..1f8f6ca8b 100644
--- a/packages/phrases-experience/src/locales/ru/description.ts
+++ b/packages/phrases-experience/src/locales/ru/description.ts
@@ -103,6 +103,9 @@ const description = {
auto_agreement: 'Продолжая, вы соглашаетесь с .',
identifier_sign_in_description: 'Введите свои {{types, list(type: disjunction;)}} для входа.',
all_sign_in_options: 'Все варианты входа',
+ identifier_register_description:
+ 'Введите свои {{types, list(type: disjunction;)}} чтобы создать новую учётную запись.',
+ all_account_creation_options: 'Все варианты создания учётной записи',
};
export default Object.freeze(description);
diff --git a/packages/phrases-experience/src/locales/tr-tr/description.ts b/packages/phrases-experience/src/locales/tr-tr/description.ts
index cecb87590..6189a4b26 100644
--- a/packages/phrases-experience/src/locales/tr-tr/description.ts
+++ b/packages/phrases-experience/src/locales/tr-tr/description.ts
@@ -99,6 +99,9 @@ const description = {
auto_agreement: 'Devam ederek kabul etmiş oluyorsunuz.',
identifier_sign_in_description: 'Oturum açmak için {{types, list(type: disjunction;)}} girin.',
all_sign_in_options: 'Tüm oturum açma seçenekleri',
+ identifier_register_description:
+ 'Yeni bir hesap oluşturmak için {{types, list(type: disjunction;)}} girin.',
+ all_account_creation_options: 'Tüm hesap oluşturma seçenekleri',
};
export default Object.freeze(description);
diff --git a/packages/phrases-experience/src/locales/zh-cn/description.ts b/packages/phrases-experience/src/locales/zh-cn/description.ts
index f0c9b23ed..a24ae4f85 100644
--- a/packages/phrases-experience/src/locales/zh-cn/description.ts
+++ b/packages/phrases-experience/src/locales/zh-cn/description.ts
@@ -79,6 +79,8 @@ const description = {
auto_agreement: '继续即表示您同意。',
identifier_sign_in_description: '输入您的{{types, list(type: disjunction;)}}以登录。',
all_sign_in_options: '所有登录选项',
+ identifier_register_description: '输入您的{{types, list(type: disjunction;)}}以创建新账户。',
+ all_account_creation_options: '所有账户创建选项',
};
export default Object.freeze(description);
diff --git a/packages/phrases-experience/src/locales/zh-hk/description.ts b/packages/phrases-experience/src/locales/zh-hk/description.ts
index 1ef2ce8bd..fc9a12330 100644
--- a/packages/phrases-experience/src/locales/zh-hk/description.ts
+++ b/packages/phrases-experience/src/locales/zh-hk/description.ts
@@ -91,6 +91,8 @@ const description = {
auto_agreement: '繼續即表示您同意。',
identifier_sign_in_description: '輸入您的{{types, list(type: disjunction;)}}以登入。',
all_sign_in_options: '所有登入選項',
+ identifier_register_description: '輸入您的{{types, list(type: disjunction;)}}以建立新帳戶。',
+ all_account_creation_options: '所有帳戶創建選項',
};
export default Object.freeze(description);
diff --git a/packages/phrases-experience/src/locales/zh-tw/description.ts b/packages/phrases-experience/src/locales/zh-tw/description.ts
index 23c706afd..89a0e0805 100644
--- a/packages/phrases-experience/src/locales/zh-tw/description.ts
+++ b/packages/phrases-experience/src/locales/zh-tw/description.ts
@@ -91,6 +91,8 @@ const description = {
auto_agreement: '繼續即表示您同意。',
identifier_sign_in_description: '輸入您的{{types, list(type: disjunction;)}}以登入。',
all_sign_in_options: '所有登入選項',
+ identifier_register_description: '輸入您的{{types, list(type: disjunction;)}}以建立新帳戶。',
+ all_account_creation_options: '所有帳戶創建選項',
};
export default Object.freeze(description);
diff --git a/packages/schemas/src/consts/experience.ts b/packages/schemas/src/consts/experience.ts
index c992636b6..2fab4d053 100644
--- a/packages/schemas/src/consts/experience.ts
+++ b/packages/schemas/src/consts/experience.ts
@@ -4,6 +4,7 @@ const routes = Object.freeze({
sso: 'single-sign-on',
consent: 'consent',
identifierSignIn: 'identifier-sign-in',
+ identifierRegister: 'identifier-register',
});
export const experience = Object.freeze({