2024-02-16 17:40:12 +02:00
|
|
|
import { clsx } from "keycloakify/tools/clsx";
|
2024-06-09 08:27:07 +02:00
|
|
|
import { getKcClsx } from "keycloakify/account/lib/kcClsx";
|
2024-06-05 21:13:58 +02:00
|
|
|
import type { PageProps } from "keycloakify/account/pages/PageProps";
|
|
|
|
import type { KcContext } from "../KcContext";
|
2024-06-09 11:20:45 +02:00
|
|
|
import type { I18n } from "../i18n";
|
2024-02-16 17:40:12 +02:00
|
|
|
|
2024-06-09 11:20:45 +02:00
|
|
|
export default function Totp(props: PageProps<Extract<KcContext, { pageId: "totp.ftl" }>, I18n>) {
|
|
|
|
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
|
2024-05-16 17:11:45 +03:00
|
|
|
|
2024-06-09 08:27:07 +02:00
|
|
|
const { kcClsx } = getKcClsx({
|
2024-02-16 17:40:12 +02:00
|
|
|
doUseDefaultCss,
|
|
|
|
classes
|
|
|
|
});
|
|
|
|
|
2024-02-19 11:57:03 +02:00
|
|
|
const { totp, mode, url, messagesPerField, stateChecker } = kcContext;
|
2024-02-16 17:40:12 +02:00
|
|
|
|
2024-06-09 11:20:45 +02:00
|
|
|
const { msg, msgStr, advancedMsg } = i18n;
|
2024-02-16 17:40:12 +02:00
|
|
|
|
|
|
|
return (
|
2024-06-09 11:20:45 +02:00
|
|
|
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} active="totp">
|
2024-02-16 17:40:12 +02:00
|
|
|
<>
|
2024-02-19 08:58:27 +02:00
|
|
|
<div className="row">
|
|
|
|
<div className="col-md-10">
|
2024-02-19 11:57:03 +02:00
|
|
|
<h2>{msg("authenticatorTitle")}</h2>
|
2024-02-19 08:58:27 +02:00
|
|
|
</div>
|
2024-02-19 11:57:03 +02:00
|
|
|
{totp.otpCredentials.length === 0 && (
|
|
|
|
<div className="subtitle col-md-2">
|
|
|
|
<span className="required">*</span>
|
|
|
|
{msg("requiredFields")}
|
|
|
|
</div>
|
|
|
|
)}
|
2024-02-19 08:58:27 +02:00
|
|
|
</div>
|
2024-02-19 11:57:03 +02:00
|
|
|
{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>
|
2024-02-16 17:40:12 +02:00
|
|
|
))}
|
2024-02-19 11:57:03 +02:00
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
)}
|
|
|
|
{!totp.enabled && (
|
|
|
|
<div>
|
|
|
|
<hr />
|
|
|
|
<ol id="kc-totp-settings">
|
2024-02-16 17:40:12 +02:00
|
|
|
<li>
|
2024-02-19 11:57:03 +02:00
|
|
|
<p>{msg("totpStep1")}</p>
|
|
|
|
|
2024-05-27 23:44:41 +02:00
|
|
|
<ul id="kc-totp-supported-apps">{totp.supportedApplications?.map(app => <li key={app}>{advancedMsg(app)}</li>)}</ul>
|
2024-02-16 17:40:12 +02:00
|
|
|
</li>
|
2024-02-19 11:57:03 +02:00
|
|
|
|
|
|
|
{mode && mode == "manual" ? (
|
|
|
|
<>
|
|
|
|
<li>
|
|
|
|
<p>{msg("totpManualStep2")}</p>
|
|
|
|
<p>
|
|
|
|
<span id="kc-totp-secret-key">{totp.totpSecretEncoded}</span>
|
|
|
|
</p>
|
|
|
|
<p>
|
|
|
|
<a href={totp.qrUrl} id="mode-barcode">
|
|
|
|
{msg("totpScanBarcode")}
|
|
|
|
</a>
|
|
|
|
</p>
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
<p>{msg("totpManualStep3")}</p>
|
2024-05-16 17:11:45 +03:00
|
|
|
<ul>
|
|
|
|
<li id="kc-totp-type">
|
|
|
|
{msg("totpType")}: {msg(`totp.${totp.policy.type}`)}
|
|
|
|
</li>
|
|
|
|
<li id="kc-totp-algorithm">
|
2024-06-15 17:33:27 +02:00
|
|
|
{msg("totpAlgorithm")}: {totp.policy.getAlgorithmKey()}
|
2024-05-16 17:11:45 +03:00
|
|
|
</li>
|
|
|
|
<li id="kc-totp-digits">
|
|
|
|
{msg("totpDigits")}: {totp.policy.digits}
|
|
|
|
</li>
|
|
|
|
{totp.policy.type === "totp" ? (
|
|
|
|
<li id="kc-totp-period">
|
|
|
|
{msg("totpInterval")}: {totp.policy.period}
|
2024-02-19 11:57:03 +02:00
|
|
|
</li>
|
2024-05-16 17:11:45 +03:00
|
|
|
) : (
|
|
|
|
<li id="kc-totp-counter">
|
|
|
|
{msg("totpCounter")}: {totp.policy.initialCounter}
|
2024-02-19 11:57:03 +02:00
|
|
|
</li>
|
2024-05-16 17:11:45 +03:00
|
|
|
)}
|
|
|
|
</ul>
|
2024-02-19 11:57:03 +02:00
|
|
|
</li>
|
|
|
|
</>
|
|
|
|
) : (
|
|
|
|
<li>
|
|
|
|
<p>{msg("totpStep2")}</p>
|
|
|
|
<p>
|
|
|
|
<img
|
|
|
|
id="kc-totp-secret-qr-code"
|
|
|
|
src={`data:image/png;base64, ${totp.totpSecretQrCode}`}
|
|
|
|
alt="Figure: Barcode"
|
|
|
|
/>
|
|
|
|
</p>
|
|
|
|
<p>
|
|
|
|
<a href={totp.manualUrl} id="mode-manual">
|
|
|
|
{msg("totpUnableToScan")}
|
|
|
|
</a>
|
|
|
|
</p>
|
|
|
|
</li>
|
|
|
|
)}
|
2024-02-16 17:40:12 +02:00
|
|
|
<li>
|
2024-02-19 11:57:03 +02:00
|
|
|
<p>{msg("totpStep3")}</p>
|
|
|
|
<p>{msg("totpStep3DeviceName")}</p>
|
2024-02-16 17:40:12 +02:00
|
|
|
</li>
|
2024-02-19 11:57:03 +02:00
|
|
|
</ol>
|
|
|
|
<hr />
|
2024-06-09 08:27:07 +02:00
|
|
|
<form action={url.totpUrl} className={kcClsx("kcFormClass")} id="kc-totp-settings-form" method="post">
|
2024-02-19 11:57:03 +02:00
|
|
|
<input type="hidden" id="stateChecker" name="stateChecker" value={stateChecker} />
|
2024-06-09 08:27:07 +02:00
|
|
|
<div className={kcClsx("kcFormGroupClass")}>
|
2024-02-19 11:57:03 +02:00
|
|
|
<div className="col-sm-2 col-md-2">
|
|
|
|
<label htmlFor="totp" className="control-label">
|
|
|
|
{msg("authenticatorCode")}
|
|
|
|
</label>
|
|
|
|
<span className="required">*</span>
|
|
|
|
</div>
|
|
|
|
<div className="col-sm-10 col-md-10">
|
|
|
|
<input
|
|
|
|
type="text"
|
|
|
|
id="totp"
|
|
|
|
name="totp"
|
|
|
|
autoComplete="off"
|
2024-06-09 08:27:07 +02:00
|
|
|
className={kcClsx("kcInputClass")}
|
2024-02-19 11:57:03 +02:00
|
|
|
aria-invalid={messagesPerField.existsError("totp")}
|
|
|
|
/>
|
2024-02-16 17:40:12 +02:00
|
|
|
|
2024-02-19 11:57:03 +02:00
|
|
|
{messagesPerField.existsError("totp") && (
|
2024-07-04 19:53:57 +02:00
|
|
|
<span
|
|
|
|
id="input-error-otp-code"
|
|
|
|
className={kcClsx("kcInputErrorMessageClass")}
|
|
|
|
aria-live="polite"
|
|
|
|
dangerouslySetInnerHTML={{
|
|
|
|
__html: messagesPerField.get("totp")
|
|
|
|
}}
|
|
|
|
/>
|
2024-02-19 11:57:03 +02:00
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
<input type="hidden" id="totpSecret" name="totpSecret" value={totp.totpSecret} />
|
|
|
|
{mode && <input type="hidden" id="mode" value={mode} />}
|
|
|
|
</div>
|
2024-02-16 17:40:12 +02:00
|
|
|
|
2024-06-09 08:27:07 +02:00
|
|
|
<div className={kcClsx("kcFormGroupClass")}>
|
2024-02-19 11:57:03 +02:00
|
|
|
<div className="col-sm-2 col-md-2">
|
2024-06-09 08:27:07 +02:00
|
|
|
<label htmlFor="userLabel" className={kcClsx("kcLabelClass")}>
|
2024-02-19 11:57:03 +02:00
|
|
|
{msg("totpDeviceName")}
|
|
|
|
</label>
|
|
|
|
{totp.otpCredentials.length >= 1 && <span className="required">*</span>}
|
|
|
|
</div>
|
|
|
|
<div className="col-sm-10 col-md-10">
|
|
|
|
<input
|
|
|
|
type="text"
|
|
|
|
id="userLabel"
|
|
|
|
name="userLabel"
|
|
|
|
autoComplete="off"
|
2024-06-09 08:27:07 +02:00
|
|
|
className={kcClsx("kcInputClass")}
|
2024-02-19 11:57:03 +02:00
|
|
|
aria-invalid={messagesPerField.existsError("userLabel")}
|
|
|
|
/>
|
|
|
|
{messagesPerField.existsError("userLabel") && (
|
2024-07-04 19:53:57 +02:00
|
|
|
<span
|
|
|
|
id="input-error-otp-label"
|
|
|
|
className={kcClsx("kcInputErrorMessageClass")}
|
|
|
|
aria-live="polite"
|
|
|
|
dangerouslySetInnerHTML={{
|
|
|
|
__html: messagesPerField.get("userLabel")
|
|
|
|
}}
|
|
|
|
/>
|
2024-02-19 11:57:03 +02:00
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</div>
|
2024-02-16 17:40:12 +02:00
|
|
|
|
2024-06-09 08:27:07 +02:00
|
|
|
<div id="kc-form-buttons" className={clsx(kcClsx("kcFormGroupClass"), "text-right")}>
|
|
|
|
<div className={kcClsx("kcInputWrapperClass")}>
|
2024-02-19 11:57:03 +02:00
|
|
|
<input
|
|
|
|
type="submit"
|
2024-06-09 08:27:07 +02:00
|
|
|
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonLargeClass")}
|
2024-02-19 11:57:03 +02:00
|
|
|
id="saveTOTPBtn"
|
|
|
|
value={msgStr("doSave")}
|
|
|
|
/>
|
|
|
|
<button
|
|
|
|
type="submit"
|
2024-06-09 08:27:07 +02:00
|
|
|
className={kcClsx("kcButtonClass", "kcButtonDefaultClass", "kcButtonLargeClass", "kcButtonLargeClass")}
|
2024-02-19 11:57:03 +02:00
|
|
|
id="cancelTOTPBtn"
|
|
|
|
name="submitAction"
|
|
|
|
value="Cancel"
|
|
|
|
>
|
|
|
|
{msg("doCancel")}
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</form>
|
2024-02-19 08:58:27 +02:00
|
|
|
</div>
|
2024-02-19 11:57:03 +02:00
|
|
|
)}
|
2024-02-16 17:40:12 +02:00
|
|
|
</>
|
|
|
|
</Template>
|
|
|
|
);
|
|
|
|
}
|