diff --git a/package.json b/package.json index a3ac40b3..660d8a83 100755 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" }, "devDependencies": { + "@babel/core": "^7.0.0", "@emotion/react": "^11.4.1", "@types/memoizee": "^0.4.7", "@types/minimist": "^1.2.2", diff --git a/src/bin/keycloakify/generateFtl/generateFtl.ts b/src/bin/keycloakify/generateFtl/generateFtl.ts index 3674f5d8..e1bfb3b9 100644 --- a/src/bin/keycloakify/generateFtl/generateFtl.ts +++ b/src/bin/keycloakify/generateFtl/generateFtl.ts @@ -13,6 +13,7 @@ import { Reflect } from "tsafe/Reflect"; // https://github.com/keycloak/keycloak/blob/main/services/src/main/java/org/keycloak/forms/login/freemarker/Templates.java export const pageIds = [ "login.ftl", + "login-username.ftl", "register.ftl", "register-user-profile.ftl", "info.ftl", diff --git a/src/lib/components/KcApp.tsx b/src/lib/components/KcApp.tsx index 51f7a662..f18f64cb 100644 --- a/src/lib/components/KcApp.tsx +++ b/src/lib/components/KcApp.tsx @@ -13,6 +13,7 @@ const LoginResetPassword = lazy(() => import("./LoginResetPassword")); const LoginVerifyEmail = lazy(() => import("./LoginVerifyEmail")); const Terms = lazy(() => import("./Terms")); const LoginOtp = lazy(() => import("./LoginOtp")); +const LoginUsername = lazy(() => import("./LoginUsername")); const LoginUpdatePassword = lazy(() => import("./LoginUpdatePassword")); const LoginUpdateProfile = lazy(() => import("./LoginUpdateProfile")); const LoginIdpLinkConfirm = lazy(() => import("./LoginIdpLinkConfirm")); @@ -67,6 +68,8 @@ const KcApp = memo( return ; case "login-otp.ftl": return ; + case "login-username.ftl": + return ; case "login-update-password.ftl": return ; case "login-update-profile.ftl": diff --git a/src/lib/components/LoginUsername.tsx b/src/lib/components/LoginUsername.tsx new file mode 100644 index 00000000..4c8415a0 --- /dev/null +++ b/src/lib/components/LoginUsername.tsx @@ -0,0 +1,168 @@ +import React, { useState, memo } from "react"; +import Template from "./Template"; +import type { KcProps } from "./KcProps"; +import type { KcContextBase } from "../getKcContext/KcContextBase"; +import { useCssAndCx } from "../tools/useCssAndCx"; +import { useConstCallback } from "powerhooks/useConstCallback"; +import type { FormEventHandler } from "react"; +import type { I18n } from "../i18n"; + +const LoginUsername = memo( + ({ + kcContext, + i18n, + doFetchDefaultThemeResources = true, + ...props + }: { kcContext: KcContextBase.LoginUsername; i18n: I18n; doFetchDefaultThemeResources?: boolean } & KcProps) => { + const { social, realm, url, usernameHidden, login, registrationDisabled } = kcContext; + + const { msg, msgStr } = i18n; + + const { cx } = useCssAndCx(); + + const [isLoginButtonDisabled, setIsLoginButtonDisabled] = useState(false); + + const onSubmit = useConstCallback>(e => { + e.preventDefault(); + + setIsLoginButtonDisabled(true); + + const formElement = e.target as HTMLFormElement; + + //NOTE: Even if we login with email Keycloak expect username and password in + //the POST request. + formElement.querySelector("input[name='email']")?.setAttribute("name", "username"); + + formElement.submit(); + }); + + return ( +