Support OTP #4
This commit is contained in:
parent
7800d125b2
commit
0163459ad6
@ -14,7 +14,8 @@ import { ftlValuesGlobalName } from "../ftlValuesGlobalName";
|
||||
export const pageIds = [
|
||||
"login.ftl", "register.ftl", "info.ftl",
|
||||
"error.ftl", "login-reset-password.ftl",
|
||||
"login-verify-email.ftl", "terms.ftl"
|
||||
"login-verify-email.ftl", "terms.ftl",
|
||||
"login-otp.ftl"
|
||||
] as const;
|
||||
|
||||
export type PageId = typeof pageIds[number];
|
||||
|
37
src/bin/build-keycloak-theme/generateFtl/login-otp.ftl
Normal file
37
src/bin/build-keycloak-theme/generateFtl/login-otp.ftl
Normal file
@ -0,0 +1,37 @@
|
||||
<script>const _=
|
||||
{
|
||||
"otpLogin": {
|
||||
"userOtpCredentials": (function(){
|
||||
|
||||
var out = [];
|
||||
|
||||
<#attempt>
|
||||
<#list otpLogin.userOtpCredentials as otpCredential>
|
||||
out.push({
|
||||
"id": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${otpCredential.id}";
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})(),
|
||||
"userLabel": (function (){
|
||||
|
||||
<#attempt>
|
||||
return "${otpCredential.userLabel}";
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
})()
|
||||
});
|
||||
</#list>
|
||||
<#recover>
|
||||
</#attempt>
|
||||
|
||||
return out;
|
||||
|
||||
})()
|
||||
}
|
||||
}
|
||||
</script>
|
@ -9,6 +9,7 @@ import { Error } from "./Error";
|
||||
import { LoginResetPassword } from "./LoginResetPassword";
|
||||
import { LoginVerifyEmail } from "./LoginVerifyEmail";
|
||||
import { Terms } from "./Terms";
|
||||
import { LoginOtp } from "./LoginOtp";
|
||||
|
||||
export const KcApp = memo(({ kcContext, ...props }: { kcContext: KcContext; } & KcProps ) => {
|
||||
switch (kcContext.pageId) {
|
||||
@ -19,5 +20,6 @@ export const KcApp = memo(({ kcContext, ...props }: { kcContext: KcContext; } &
|
||||
case "login-reset-password.ftl": return <LoginResetPassword {...{ kcContext, ...props }} />;
|
||||
case "login-verify-email.ftl": return <LoginVerifyEmail {...{ kcContext, ...props }} />;
|
||||
case "terms.ftl": return <Terms {...{ kcContext, ...props }}/>;
|
||||
case "login-otp.ftl": return <LoginOtp {...{ kcContext, ...props }}/>;
|
||||
}
|
||||
});
|
145
src/lib/components/LoginOtp.tsx
Normal file
145
src/lib/components/LoginOtp.tsx
Normal file
@ -0,0 +1,145 @@
|
||||
|
||||
|
||||
import { useEffect, memo } from "react";
|
||||
import { Template } from "./Template";
|
||||
import type { KcProps } from "./KcProps";
|
||||
import type { KcContext } from "../KcContext";
|
||||
import { useKcMessage } from "../i18n/useKcMessage";
|
||||
import { appendHead } from "../tools/appendHead";
|
||||
import { join as pathJoin } from "path";
|
||||
import { cx } from "tss-react";
|
||||
|
||||
export const LoginOtp = memo(({ kcContext, ...props }: { kcContext: KcContext.LoginOtp; } & KcProps) => {
|
||||
|
||||
const { otpLogin, url } = kcContext;
|
||||
|
||||
const { msg, msgStr } = useKcMessage();
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
|
||||
let isCleanedUp = false;
|
||||
|
||||
appendHead({
|
||||
"type": "javascript",
|
||||
"src": pathJoin(
|
||||
kcContext.url.resourcesCommonPath,
|
||||
"node_modules/jquery/dist/jquery.min.js"
|
||||
)
|
||||
}).then(() => {
|
||||
|
||||
if (isCleanedUp) return;
|
||||
|
||||
evaluateInlineScript();
|
||||
|
||||
});
|
||||
|
||||
return () => { isCleanedUp = true };
|
||||
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<Template
|
||||
{...{ kcContext, ...props }}
|
||||
headerNode={msg("doLogIn")}
|
||||
formNode={
|
||||
|
||||
<form
|
||||
id="kc-otp-login-form"
|
||||
className={cx(props.kcFormClass)}
|
||||
action={url.loginAction}
|
||||
method="post"
|
||||
>
|
||||
{
|
||||
otpLogin.userOtpCredentials.length > 1 &&
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
{
|
||||
otpLogin.userOtpCredentials.map(otpCredential =>
|
||||
<div className={cx(props.kcSelectOTPListClass)}>
|
||||
<input type="hidden" value="${otpCredential.id}" />
|
||||
<div className={cx(props.kcSelectOTPListItemClass)}>
|
||||
<span className={cx(props.kcAuthenticatorOtpCircleClass)} />
|
||||
<h2 className={cx(props.kcSelectOTPItemHeadingClass)}>
|
||||
{otpCredential.userLabel}
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
<div className={cx(props.kcLabelWrapperClass)}>
|
||||
<label htmlFor="otp" className={cx(props.kcLabelClass)}>
|
||||
{msg("loginOtpOneTime")}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className={cx(props.kcInputWrapperClass)}>
|
||||
<input
|
||||
id="otp"
|
||||
name="otp"
|
||||
autoComplete="off"
|
||||
type="text"
|
||||
className={cx(props.kcInputClass)}
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={cx(props.kcFormGroupClass)}>
|
||||
<div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
|
||||
<div className={cx(props.kcFormOptionsWrapperClass)} />
|
||||
</div>
|
||||
|
||||
<div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
|
||||
<input
|
||||
className={cx(
|
||||
props.kcButtonClass,
|
||||
props.kcButtonPrimaryClass,
|
||||
props.kcButtonBlockClass,
|
||||
props.kcButtonLargeClass
|
||||
)}
|
||||
name="login"
|
||||
id="kc-login"
|
||||
type="submit"
|
||||
value={msgStr("doLogIn")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form >
|
||||
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
declare const $: any;
|
||||
|
||||
function evaluateInlineScript() {
|
||||
|
||||
$(document).ready(function () {
|
||||
// Card Single Select
|
||||
$('.card-pf-view-single-select').click(function (this: any) {
|
||||
if ($(this).hasClass('active')) { $(this).removeClass('active'); $(this).children().removeAttr('name'); }
|
||||
else {
|
||||
$('.card-pf-view-single-select').removeClass('active');
|
||||
$('.card-pf-view-single-select').children().removeAttr('name');
|
||||
$(this).addClass('active'); $(this).children().attr('name', 'selectedCredentialId');
|
||||
}
|
||||
});
|
||||
|
||||
var defaultCred = $('.card-pf-view-single-select')[0];
|
||||
if (defaultCred) {
|
||||
defaultCred.click();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
@ -17,7 +17,7 @@ type ExtractAfterStartingWith<Prefix extends string, StrEnum> =
|
||||
export type KcContext =
|
||||
KcContext.Login | KcContext.Register | KcContext.Info |
|
||||
KcContext.Error | KcContext.LoginResetPassword | KcContext.LoginVerifyEmail |
|
||||
KcContext.Terms;
|
||||
KcContext.Terms | KcContext.LoginOtp;
|
||||
|
||||
export declare namespace KcContext {
|
||||
|
||||
@ -165,6 +165,13 @@ export declare namespace KcContext {
|
||||
pageId: "terms.ftl";
|
||||
};
|
||||
|
||||
export type LoginOtp = Common & {
|
||||
pageId: "login-otp.ftl";
|
||||
otpLogin: {
|
||||
userOtpCredentials: { id: string; userLabel: string; }[];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
doExtends<KcContext["pageId"], PageId>();
|
||||
|
@ -211,3 +211,20 @@ export const kcTermsContext: KcContext.Terms = {
|
||||
"pageId": "terms.ftl"
|
||||
};
|
||||
|
||||
export const kcLoginOtcContext: KcContext.LoginOtp = {
|
||||
...kcCommonContext,
|
||||
"pageId": "login-otp.ftl",
|
||||
"otpLogin": {
|
||||
"userOtpCredentials": [
|
||||
{
|
||||
"id": "id1",
|
||||
"userLabel": "label1"
|
||||
},
|
||||
{
|
||||
"id": "id2",
|
||||
"userLabel": "label2"
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user