Support OTP #4

This commit is contained in:
garronej 2021-05-01 14:55:58 +02:00
parent 7800d125b2
commit 0163459ad6
6 changed files with 211 additions and 2 deletions

View File

@ -14,7 +14,8 @@ import { ftlValuesGlobalName } from "../ftlValuesGlobalName";
export const pageIds = [ export const pageIds = [
"login.ftl", "register.ftl", "info.ftl", "login.ftl", "register.ftl", "info.ftl",
"error.ftl", "login-reset-password.ftl", "error.ftl", "login-reset-password.ftl",
"login-verify-email.ftl", "terms.ftl" "login-verify-email.ftl", "terms.ftl",
"login-otp.ftl"
] as const; ] as const;
export type PageId = typeof pageIds[number]; export type PageId = typeof pageIds[number];

View 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>

View File

@ -9,6 +9,7 @@ import { Error } from "./Error";
import { LoginResetPassword } from "./LoginResetPassword"; import { LoginResetPassword } from "./LoginResetPassword";
import { LoginVerifyEmail } from "./LoginVerifyEmail"; import { LoginVerifyEmail } from "./LoginVerifyEmail";
import { Terms } from "./Terms"; import { Terms } from "./Terms";
import { LoginOtp } from "./LoginOtp";
export const KcApp = memo(({ kcContext, ...props }: { kcContext: KcContext; } & KcProps ) => { export const KcApp = memo(({ kcContext, ...props }: { kcContext: KcContext; } & KcProps ) => {
switch (kcContext.pageId) { 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-reset-password.ftl": return <LoginResetPassword {...{ kcContext, ...props }} />;
case "login-verify-email.ftl": return <LoginVerifyEmail {...{ kcContext, ...props }} />; case "login-verify-email.ftl": return <LoginVerifyEmail {...{ kcContext, ...props }} />;
case "terms.ftl": return <Terms {...{ kcContext, ...props }}/>; case "terms.ftl": return <Terms {...{ kcContext, ...props }}/>;
case "login-otp.ftl": return <LoginOtp {...{ kcContext, ...props }}/>;
} }
}); });

View 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();
}
});
}

View File

@ -17,7 +17,7 @@ type ExtractAfterStartingWith<Prefix extends string, StrEnum> =
export type KcContext = export type KcContext =
KcContext.Login | KcContext.Register | KcContext.Info | KcContext.Login | KcContext.Register | KcContext.Info |
KcContext.Error | KcContext.LoginResetPassword | KcContext.LoginVerifyEmail | KcContext.Error | KcContext.LoginResetPassword | KcContext.LoginVerifyEmail |
KcContext.Terms; KcContext.Terms | KcContext.LoginOtp;
export declare namespace KcContext { export declare namespace KcContext {
@ -165,6 +165,13 @@ export declare namespace KcContext {
pageId: "terms.ftl"; pageId: "terms.ftl";
}; };
export type LoginOtp = Common & {
pageId: "login-otp.ftl";
otpLogin: {
userOtpCredentials: { id: string; userLabel: string; }[];
}
};
} }
doExtends<KcContext["pageId"], PageId>(); doExtends<KcContext["pageId"], PageId>();

View File

@ -211,3 +211,20 @@ export const kcTermsContext: KcContext.Terms = {
"pageId": "terms.ftl" "pageId": "terms.ftl"
}; };
export const kcLoginOtcContext: KcContext.LoginOtp = {
...kcCommonContext,
"pageId": "login-otp.ftl",
"otpLogin": {
"userOtpCredentials": [
{
"id": "id1",
"userLabel": "label1"
},
{
"id": "id2",
"userLabel": "label2"
}
]
}
};