diff --git a/src/bin/keycloakify/generateFtl/pageId.ts b/src/bin/keycloakify/generateFtl/pageId.ts index efe852c9..a44f4527 100644 --- a/src/bin/keycloakify/generateFtl/pageId.ts +++ b/src/bin/keycloakify/generateFtl/pageId.ts @@ -31,7 +31,8 @@ export const loginThemePageIds = [ "delete-account-confirm.ftl", "frontchannel-logout.ftl", "login-recovery-authn-code-config.ftl", - "login-recovery-authn-code-input.ftl" + "login-recovery-authn-code-input.ftl", + "login-reset-otp.ftl" ] as const; export const accountThemePageIds = ["password.ftl", "account.ftl", "sessions.ftl", "totp.ftl", "applications.ftl", "log.ftl"] as const; diff --git a/src/login/Fallback.tsx b/src/login/Fallback.tsx index 32036e4b..0a1f1d3f 100644 --- a/src/login/Fallback.tsx +++ b/src/login/Fallback.tsx @@ -37,6 +37,7 @@ const DeleteAccountConfirm = lazy(() => import("keycloakify/login/pages/DeleteAc const FrontchannelLogout = lazy(() => import("keycloakify/login/pages/FrontchannelLogout")); const LoginRecoveryAuthnCodeConfig = lazy(() => import("keycloakify/login/pages/LoginRecoveryAuthnCodeConfig")); const LoginRecoveryAuthnCodeInput = lazy(() => import("keycloakify/login/pages/LoginRecoveryAuthnCodeInput")); +const LoginResetOtp = lazy(() => import("keycloakify/login/pages/LoginResetOtp")); type FallbackProps = PageProps & { UserProfileFormFields: LazyOrNot<(props: UserProfileFormFieldsProps) => JSX.Element>; @@ -113,6 +114,8 @@ export default function Fallback(props: FallbackProps) { return ; case "login-recovery-authn-code-input.ftl": return ; + case "login-reset-otp.ftl": + return ; } assert>(false); })()} diff --git a/src/login/kcContext/KcContext.ts b/src/login/kcContext/KcContext.ts index 0a0c5b3f..7b6fde05 100644 --- a/src/login/kcContext/KcContext.ts +++ b/src/login/kcContext/KcContext.ts @@ -41,7 +41,8 @@ export type KcContext = | KcContext.DeleteAccountConfirm | KcContext.FrontchannelLogout | KcContext.LoginRecoveryAuthnCodeConfig - | KcContext.LoginRecoveryAuthnCodeInput; + | KcContext.LoginRecoveryAuthnCodeInput + | KcContext.LoginResetOtp; assert(); @@ -546,6 +547,17 @@ export declare namespace KcContext { codeNumber: number; }; }; + + export type LoginResetOtp = Common & { + pageId: "login-reset-otp.ftl"; + configuredOtpCredentials: { + userOtpCredentials: { + id: string; + userLabel: string; + }[]; + selectedCredentialId: string; + }; + }; } export type UserProfile = { diff --git a/src/login/pages/LoginResetOtp.tsx b/src/login/pages/LoginResetOtp.tsx new file mode 100644 index 00000000..94de40b9 --- /dev/null +++ b/src/login/pages/LoginResetOtp.tsx @@ -0,0 +1,70 @@ +import { Fragment } from "react"; +import { clsx } from "keycloakify/tools/clsx"; +import type { PageProps } from "keycloakify/login/pages/PageProps"; +import { useGetClassName } from "keycloakify/login/lib/useGetClassName"; +import type { KcContext } from "../kcContext"; +import type { I18n } from "../i18n"; + +export default function LoginResetOtp(props: PageProps, I18n>) { + const { kcContext, i18n, doUseDefaultCss, Template, classes } = props; + + const { getClassName } = useGetClassName({ + doUseDefaultCss, + classes + }); + + const { url, messagesPerField, configuredOtpCredentials } = kcContext; + + const { msg, msgStr } = i18n; + + return ( + + ); +}