234 lines
13 KiB
TypeScript
Raw Normal View History

2024-02-16 17:40:12 +02:00
import { clsx } from "keycloakify/tools/clsx";
import type { PageProps } from "keycloakify/account/pages/PageProps";
import { useGetClassName } from "keycloakify/account/lib/useGetClassName";
import type { KcContext } from "../kcContext";
import type { I18n } from "../i18n";
2024-02-19 08:58:27 +02:00
import { MessageKey } from "keycloakify/account/i18n/i18n";
2024-02-16 17:40:12 +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-02-16 17:40:12 +02:00
const { getClassName } = useGetClassName({
doUseDefaultCss,
classes
});
const { totp, mode, url, messagesPerField, stateChecker } = kcContext;
2024-02-16 17:40:12 +02:00
const { msg, msgStr } = i18n;
const algToKeyUriAlg: Record<(typeof kcContext)["totp"]["policy"]["algorithm"], string> = {
2024-05-20 15:48:51 +02:00
HmacSHA1: "SHA1",
HmacSHA256: "SHA256",
HmacSHA512: "SHA512"
2024-02-16 17:40:12 +02:00
};
return (
<Template {...{ kcContext, i18n, doUseDefaultCss, classes }} active="totp">
<>
2024-02-19 08:58:27 +02:00
<div className="row">
<div className="col-md-10">
<h2>{msg("authenticatorTitle")}</h2>
2024-02-19 08:58:27 +02:00
</div>
{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>
{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
))}
</tbody>
</table>
)}
{!totp.enabled && (
<div>
<hr />
<ol id="kc-totp-settings">
2024-02-16 17:40:12 +02:00
<li>
<p>{msg("totpStep1")}</p>
<ul id="kc-totp-supported-apps">
2024-05-20 19:30:15 +02:00
{totp.supportedApplications?.map(app => <li key={app}>{msg(app as MessageKey)}</li>)}
</ul>
2024-02-16 17:40:12 +02:00
</li>
{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">
{msg("totpAlgorithm")}: {algToKeyUriAlg?.[totp.policy.algorithm] ?? totp.policy.algorithm}
</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}
</li>
2024-05-16 17:11:45 +03:00
) : (
<li id="kc-totp-counter">
{msg("totpCounter")}: {totp.policy.initialCounter}
</li>
2024-05-16 17:11:45 +03:00
)}
</ul>
</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>
<p>{msg("totpStep3")}</p>
<p>{msg("totpStep3DeviceName")}</p>
2024-02-16 17:40:12 +02:00
</li>
</ol>
<hr />
<form action={url.totpUrl} 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="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"
className={getClassName("kcInputClass")}
aria-invalid={messagesPerField.existsError("totp")}
/>
2024-02-16 17:40:12 +02:00
{messagesPerField.existsError("totp") && (
<span id="input-error-otp-code" className={getClassName("kcInputErrorMessageClass")} aria-live="polite">
{messagesPerField.get("totp")}
</span>
)}
</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
<div className={getClassName("kcFormGroupClass")}>
<div className="col-sm-2 col-md-2">
<label htmlFor="userLabel" className={getClassName("kcLabelClass")}>
{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"
className={getClassName("kcInputClass")}
aria-invalid={messagesPerField.existsError("userLabel")}
/>
{messagesPerField.existsError("userLabel") && (
<span id="input-error-otp-label" className={getClassName("kcInputErrorMessageClass")} aria-live="polite">
{messagesPerField.get("userLabel")}
</span>
)}
</div>
</div>
2024-02-16 17:40:12 +02:00
<div id="kc-form-buttons" className={clsx(getClassName("kcFormGroupClass"), "text-right")}>
<div className={getClassName("kcInputWrapperClass")}>
<input
type="submit"
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonPrimaryClass"),
getClassName("kcButtonLargeClass")
)}
id="saveTOTPBtn"
value={msgStr("doSave")}
/>
<button
type="submit"
className={clsx(
getClassName("kcButtonClass"),
getClassName("kcButtonDefaultClass"),
getClassName("kcButtonLargeClass"),
getClassName("kcButtonLargeClass")
)}
id="cancelTOTPBtn"
name="submitAction"
value="Cancel"
>
{msg("doCancel")}
</button>
</div>
</div>
</form>
2024-02-19 08:58:27 +02:00
</div>
)}
2024-02-16 17:40:12 +02:00
</>
</Template>
);
}