feat: Totp account page fixed and completed
This commit is contained in:
@ -138,6 +138,7 @@ export declare namespace KcContext {
|
|||||||
export type Totp = Common & {
|
export type Totp = Common & {
|
||||||
pageId: "totp.ftl";
|
pageId: "totp.ftl";
|
||||||
totp: {
|
totp: {
|
||||||
|
enabled: boolean;
|
||||||
totpSecretEncoded: string;
|
totpSecretEncoded: string;
|
||||||
qrUrl: string;
|
qrUrl: string;
|
||||||
policy: {
|
policy: {
|
||||||
|
@ -204,6 +204,7 @@ export const kcContextMocks: KcContext[] = [
|
|||||||
...kcContextCommonMock,
|
...kcContextCommonMock,
|
||||||
"pageId": "totp.ftl",
|
"pageId": "totp.ftl",
|
||||||
totp: {
|
totp: {
|
||||||
|
enabled: true,
|
||||||
totpSecretEncoded: "KVVF G2BY N4YX S6LB IUYT K2LH IFYE 4SBV",
|
totpSecretEncoded: "KVVF G2BY N4YX S6LB IUYT K2LH IFYE 4SBV",
|
||||||
qrUrl: "#",
|
qrUrl: "#",
|
||||||
totpSecretQrCode:
|
totpSecretQrCode:
|
||||||
|
@ -12,7 +12,7 @@ export default function Totp(props: PageProps<Extract<KcContext, { pageId: "totp
|
|||||||
classes
|
classes
|
||||||
});
|
});
|
||||||
|
|
||||||
const { totp, mode, messagesPerField } = kcContext;
|
const { totp, mode, url, messagesPerField, stateChecker } = kcContext;
|
||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
@ -27,12 +27,52 @@ export default function Totp(props: PageProps<Extract<KcContext, { pageId: "totp
|
|||||||
<>
|
<>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-md-10">
|
<div className="col-md-10">
|
||||||
<h2>{msg("changePasswordHtmlTitle")}</h2>
|
<h2>{msg("authenticatorTitle")}</h2>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-2 subtitle">
|
{totp.otpCredentials.length === 0 && (
|
||||||
<span className="subtitle">{msg("allFieldsRequired")}</span>
|
<div className="subtitle col-md-2">
|
||||||
|
<span className="required">*</span>
|
||||||
|
{msg("requiredFields")}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{totp.enabled && (
|
||||||
|
<table className="table table-bordered table-striped">
|
||||||
|
<thead>
|
||||||
|
{totp.otpCredentials.length > 1 ? (
|
||||||
|
<tr>
|
||||||
|
<th colSpan={4}>{msg("configureAuthenticators")}</th>
|
||||||
|
</tr>
|
||||||
|
) : (
|
||||||
|
<tr>
|
||||||
|
<th colSpan={3}>{msg("configureAuthenticators")}</th>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{totp.otpCredentials.map((credential, index) => (
|
||||||
|
<tr key={index}>
|
||||||
|
<td className="provider">{msg("mobile")}</td>
|
||||||
|
{totp.otpCredentials.length > 1 && <td className="provider">{credential.id}</td>}
|
||||||
|
<td className="provider">{credential.userLabel || ""}</td>
|
||||||
|
<td className="action">
|
||||||
|
<form action={url.totpUrl} method="post" className="form-inline">
|
||||||
|
<input type="hidden" id="stateChecker" name="stateChecker" value={stateChecker} />
|
||||||
|
<input type="hidden" id="submitAction" name="submitAction" value="Delete" />
|
||||||
|
<input type="hidden" id="credentialId" name="credentialId" value={credential.id} />
|
||||||
|
<button id={`remove-mobile-${index}`} className="btn btn-default">
|
||||||
|
<i className="pficon pficon-delete"></i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
)}
|
||||||
|
{!totp.enabled && (
|
||||||
|
<div>
|
||||||
|
<hr />
|
||||||
<ol id="kc-totp-settings">
|
<ol id="kc-totp-settings">
|
||||||
<li>
|
<li>
|
||||||
<p>{msg("totpStep1")}</p>
|
<p>{msg("totpStep1")}</p>
|
||||||
@ -86,8 +126,13 @@ export default function Totp(props: PageProps<Extract<KcContext, { pageId: "totp
|
|||||||
) : (
|
) : (
|
||||||
<li>
|
<li>
|
||||||
<p>{msg("totpStep2")}</p>
|
<p>{msg("totpStep2")}</p>
|
||||||
<img id="kc-totp-secret-qr-code" src={`data:image/png;base64, ${totp.totpSecretQrCode}`} alt="Figure: Barcode" />
|
<p>
|
||||||
<br />
|
<img
|
||||||
|
id="kc-totp-secret-qr-code"
|
||||||
|
src={`data:image/png;base64, ${totp.totpSecretQrCode}`}
|
||||||
|
alt="Figure: Barcode"
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a href={totp.manualUrl} id="mode-manual">
|
<a href={totp.manualUrl} id="mode-manual">
|
||||||
{msg("totpUnableToScan")}
|
{msg("totpUnableToScan")}
|
||||||
@ -100,14 +145,14 @@ export default function Totp(props: PageProps<Extract<KcContext, { pageId: "totp
|
|||||||
<p>{msg("totpStep3DeviceName")}</p>
|
<p>{msg("totpStep3DeviceName")}</p>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
{/* <form action={url.loginAction} className={getClassName("kcFormClass")} id="kc-totp-settings-form" method="post"> */}
|
<hr />
|
||||||
|
<form action={url.totpUrl} className={getClassName("kcFormClass")} id="kc-totp-settings-form" method="post">
|
||||||
<form className={getClassName("kcFormClass")} id="kc-totp-settings-form" method="post">
|
<input type="hidden" id="stateChecker" name="stateChecker" value={stateChecker} />
|
||||||
<div className={getClassName("kcFormGroupClass")}>
|
<div className={getClassName("kcFormGroupClass")}>
|
||||||
<div className="col-sm-2 col-md-2">
|
<div className="col-sm-2 col-md-2">
|
||||||
<label htmlFor="totp" className="control-label">
|
<label htmlFor="totp" className="control-label">
|
||||||
{msg("authenticatorCode")}
|
{msg("authenticatorCode")}
|
||||||
</label>{" "}
|
</label>
|
||||||
<span className="required">*</span>
|
<span className="required">*</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-sm-10 col-md-10">
|
<div className="col-sm-10 col-md-10">
|
||||||
@ -134,7 +179,7 @@ export default function Totp(props: PageProps<Extract<KcContext, { pageId: "totp
|
|||||||
<div className="col-sm-2 col-md-2">
|
<div className="col-sm-2 col-md-2">
|
||||||
<label htmlFor="userLabel" className={getClassName("kcLabelClass")}>
|
<label htmlFor="userLabel" className={getClassName("kcLabelClass")}>
|
||||||
{msg("totpDeviceName")}
|
{msg("totpDeviceName")}
|
||||||
</label>{" "}
|
</label>
|
||||||
{totp.otpCredentials.length >= 1 && <span className="required">*</span>}
|
{totp.otpCredentials.length >= 1 && <span className="required">*</span>}
|
||||||
</div>
|
</div>
|
||||||
<div className="col-sm-10 col-md-10">
|
<div className="col-sm-10 col-md-10">
|
||||||
@ -154,10 +199,15 @@ export default function Totp(props: PageProps<Extract<KcContext, { pageId: "totp
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-right kcFormGroupClass">
|
<div id="kc-form-buttons" className={clsx(getClassName("kcFormGroupClass"), "text-right")}>
|
||||||
|
<div className={getClassName("kcInputWrapperClass")}>
|
||||||
<input
|
<input
|
||||||
type="submit"
|
type="submit"
|
||||||
className={clsx(getClassName("kcButtonClass"), getClassName("kcButtonPrimaryClass"), getClassName("kcButtonLargeClass"))}
|
className={clsx(
|
||||||
|
getClassName("kcButtonClass"),
|
||||||
|
getClassName("kcButtonPrimaryClass"),
|
||||||
|
getClassName("kcButtonLargeClass")
|
||||||
|
)}
|
||||||
id="saveTOTPBtn"
|
id="saveTOTPBtn"
|
||||||
value={msgStr("doSave")}
|
value={msgStr("doSave")}
|
||||||
/>
|
/>
|
||||||
@ -170,13 +220,16 @@ export default function Totp(props: PageProps<Extract<KcContext, { pageId: "totp
|
|||||||
getClassName("kcButtonLargeClass")
|
getClassName("kcButtonLargeClass")
|
||||||
)}
|
)}
|
||||||
id="cancelTOTPBtn"
|
id="cancelTOTPBtn"
|
||||||
name="cancel-aia"
|
name="submitAction"
|
||||||
value="true"
|
value="Cancel"
|
||||||
>
|
>
|
||||||
{msg("doCancel")}
|
{msg("doCancel")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
</Template>
|
</Template>
|
||||||
);
|
);
|
||||||
|
@ -25,6 +25,68 @@ export const Default = () => (
|
|||||||
<PageStory
|
<PageStory
|
||||||
kcContext={{
|
kcContext={{
|
||||||
totp: {
|
totp: {
|
||||||
|
enabled: false,
|
||||||
|
totpSecretEncoded: "HB2W ESCK KJKF K5DC GJQX S5RQ I5AX CZ2U",
|
||||||
|
totpSecret: "8ubHJRTUtb2ayv0GAqgT",
|
||||||
|
manualUrl: "http://localhost:8080/realms/master/account/totp?mode=manual",
|
||||||
|
supportedApplications: ["totpAppFreeOTPName", "totpAppMicrosoftAuthenticatorName", "totpAppGoogleName"],
|
||||||
|
totpSecretQrCode:
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAPYAAAD2AQAAAADNaUdlAAACoUlEQVR4Xu2YPW7DMAxGGWTImCP4JvbFAtiAL+bcxEfI6MEI+z3Kzl+BLh2oIRwEV08FJIr8SMX8T1vtc+bdvvxz5t2+/HPm3eA3Mzvc+jm+fDoPzXIQkPV18N79ejvOWnRpTlcf/XTVV4Aq+MU0uzZaZI12rZVml4YzVcQHDTrEYufBKuQaZEfX1TvDWhEv98+ty79dGX7HRx438ofQfB04Th08jNS+us+n+1l/XbfZKrgcumj/trRnpfak0Dw54Xp/nC+Xy5bOB7x6dDxt1sq5j/tP52vkd5Ee+Xc1JfnKmergxKdcOyOSCgLik5xXEtXBtXVVvTFcC+pdV6+YqIVT+rpNf4hKjqMgXdo9frO5ldAM1dFJfA7+1O9srqiM4W6otuYQIZ0pvivg8mWUvo6q14VImuTocf/JXFq4cP8lPifld/jXidQqOL0CX0V66L9a6Y9Pu34nc7XW8qoQQ9GfcghVwiq4kStqDdl1hGZpZ3f/ZnN9qyCHkTrWq4nl/l/8n8tVmieL1lpFhiDw0uQ84jeXl4ahp+tay/1r6/ai3/kchxKQI6njyCX64zg8n2s4RZEhIDkJ5ZD9b/mTzZnVq2mmMFP/RjJpwNObf7M5qa0Lj9K837/kvKuEu1ov6p9EiEXkd8ei+57f+VwJPSCSfSnNWkR8PvefytFvK/1riPiIEkXM1sGl39qrWlcEm9K3OXnXn2wO4qcFhWZ02uFk2dO/uTwMJx9ostn6Q4mq4LzvDmoSqXqzE5+8BHiOVsJ7arH661ZFuixCGp/+z+ZsWF+ROuR3I9faS39YBS82F9d2rpWkUzWchqFFdWitS5C+9F+5vC/3T/9Fkl8Y+H3BN/9mc/KHRwrxGa9iePnHGvhf9uWfM+/25Z8z7/Zv/gPV7u6J7fyCcQAAAABJRU5ErkJggg==",
|
||||||
|
qrUrl: "http://localhost:8080/realms/master/account/totp?mode=qr",
|
||||||
|
otpCredentials: []
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
resourcesPath: "/resources/ueycc/account/keycloakify-starter",
|
||||||
|
resourceUrl: "http://localhost:8080/realms/master/account/resource",
|
||||||
|
resourcesCommonPath: "/resources/ueycc/account/keycloakify-starter/resources-common",
|
||||||
|
logUrl: "http://localhost:8080/realms/master/account/log",
|
||||||
|
socialUrl: "http://localhost:8080/realms/master/account/identity",
|
||||||
|
accountUrl: "http://localhost:8080/realms/master/account/",
|
||||||
|
sessionsUrl: "http://localhost:8080/realms/master/account/sessions",
|
||||||
|
totpUrl: "http://localhost:8080/realms/master/account/totp",
|
||||||
|
applicationsUrl: "http://localhost:8080/realms/master/account/applications",
|
||||||
|
passwordUrl: "http://localhost:8080/realms/master/account/password"
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const WithTotpEnabled = () => (
|
||||||
|
<PageStory
|
||||||
|
kcContext={{
|
||||||
|
totp: {
|
||||||
|
enabled: true,
|
||||||
|
totpSecretEncoded: "HB2W ESCK KJKF K5DC GJQX S5RQ I5AX CZ2U",
|
||||||
|
totpSecret: "8ubHJRTUtb2ayv0GAqgT",
|
||||||
|
manualUrl: "http://localhost:8080/realms/master/account/totp?mode=manual",
|
||||||
|
supportedApplications: ["totpAppFreeOTPName", "totpAppMicrosoftAuthenticatorName", "totpAppGoogleName"],
|
||||||
|
totpSecretQrCode:
|
||||||
|
"iVBORw0KGgoAAAANSUhEUgAAAPYAAAD2AQAAAADNaUdlAAACoUlEQVR4Xu2YPW7DMAxGGWTImCP4JvbFAtiAL+bcxEfI6MEI+z3Kzl+BLh2oIRwEV08FJIr8SMX8T1vtc+bdvvxz5t2+/HPm3eA3Mzvc+jm+fDoPzXIQkPV18N79ejvOWnRpTlcf/XTVV4Aq+MU0uzZaZI12rZVml4YzVcQHDTrEYufBKuQaZEfX1TvDWhEv98+ty79dGX7HRx438ofQfB04Th08jNS+us+n+1l/XbfZKrgcumj/trRnpfak0Dw54Xp/nC+Xy5bOB7x6dDxt1sq5j/tP52vkd5Ee+Xc1JfnKmergxKdcOyOSCgLik5xXEtXBtXVVvTFcC+pdV6+YqIVT+rpNf4hKjqMgXdo9frO5ldAM1dFJfA7+1O9srqiM4W6otuYQIZ0pvivg8mWUvo6q14VImuTocf/JXFq4cP8lPifld/jXidQqOL0CX0V66L9a6Y9Pu34nc7XW8qoQQ9GfcghVwiq4kStqDdl1hGZpZ3f/ZnN9qyCHkTrWq4nl/l/8n8tVmieL1lpFhiDw0uQ84jeXl4ahp+tay/1r6/ai3/kchxKQI6njyCX64zg8n2s4RZEhIDkJ5ZD9b/mTzZnVq2mmMFP/RjJpwNObf7M5qa0Lj9K837/kvKuEu1ov6p9EiEXkd8ei+57f+VwJPSCSfSnNWkR8PvefytFvK/1riPiIEkXM1sGl39qrWlcEm9K3OXnXn2wO4qcFhWZ02uFk2dO/uTwMJx9ostn6Q4mq4LzvDmoSqXqzE5+8BHiOVsJ7arH661ZFuixCGp/+z+ZsWF+ROuR3I9faS39YBS82F9d2rpWkUzWchqFFdWitS5C+9F+5vC/3T/9Fkl8Y+H3BN/9mc/KHRwrxGa9iePnHGvhf9uWfM+/25Z8z7/Zv/gPV7u6J7fyCcQAAAABJRU5ErkJggg==",
|
||||||
|
qrUrl: "http://localhost:8080/realms/master/account/totp?mode=qr",
|
||||||
|
otpCredentials: []
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
resourcesPath: "/resources/ueycc/account/keycloakify-starter",
|
||||||
|
resourceUrl: "http://localhost:8080/realms/master/account/resource",
|
||||||
|
resourcesCommonPath: "/resources/ueycc/account/keycloakify-starter/resources-common",
|
||||||
|
logUrl: "http://localhost:8080/realms/master/account/log",
|
||||||
|
socialUrl: "http://localhost:8080/realms/master/account/identity",
|
||||||
|
accountUrl: "http://localhost:8080/realms/master/account/",
|
||||||
|
sessionsUrl: "http://localhost:8080/realms/master/account/sessions",
|
||||||
|
totpUrl: "http://localhost:8080/realms/master/account/totp",
|
||||||
|
applicationsUrl: "http://localhost:8080/realms/master/account/applications",
|
||||||
|
passwordUrl: "http://localhost:8080/realms/master/account/password"
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const WithManualMode = () => (
|
||||||
|
<PageStory
|
||||||
|
kcContext={{
|
||||||
|
mode: "manual",
|
||||||
|
totp: {
|
||||||
|
enabled: false,
|
||||||
totpSecretEncoded: "HB2W ESCK KJKF K5DC GJQX S5RQ I5AX CZ2U",
|
totpSecretEncoded: "HB2W ESCK KJKF K5DC GJQX S5RQ I5AX CZ2U",
|
||||||
totpSecret: "8ubHJRTUtb2ayv0GAqgT",
|
totpSecret: "8ubHJRTUtb2ayv0GAqgT",
|
||||||
manualUrl: "http://localhost:8080/realms/master/account/totp?mode=manual",
|
manualUrl: "http://localhost:8080/realms/master/account/totp?mode=manual",
|
||||||
|
Reference in New Issue
Block a user