Fix LoginRecoveryAuthnCodeConfig

This commit is contained in:
Joseph Garrone 2024-09-09 07:39:14 +02:00
parent 7e5abe8589
commit 28990a12da
4 changed files with 168 additions and 133 deletions

View File

@ -19,6 +19,7 @@ assert<KcContext.LoginPasskeysConditionalAuthenticate extends KcContextLike ? tr
type I18nLike = {
msgStr: (key: "webauthn-unsupported-browser-text" | "passkey-unsupported-browser-text") => string;
isFetchingTranslations: boolean;
};
export function useScript(params: { authButtonId: string; kcContext: KcContextLike; i18n: I18nLike }) {
@ -26,18 +27,18 @@ export function useScript(params: { authButtonId: string; kcContext: KcContextLi
const { url, isUserIdentified, challenge, userVerification, rpId, createTimeout } = kcContext;
const { msgStr } = i18n;
const { msgStr, isFetchingTranslations } = i18n;
const { insertScriptTags } = useInsertScriptTags({
componentOrHookName: "LoginRecoveryAuthnCodeConfig",
scriptTags: [
{
type: "module",
textContent: `
textContent: () => `
import { authenticateByWebAuthn } from "${url.resourcesPath}/js/webauthnAuthenticate.js";
import { initAuthenticate } from "${url.resourcesPath}/js/passkeysConditionalAuth.js";
const authButton = document.getElementById(${JSON.stringify(authButtonId)});
const authButton = document.getElementById("${authButtonId}");
const input = {
isUserIdentified : ${isUserIdentified},
challenge : '${challenge}',
@ -62,6 +63,10 @@ export function useScript(params: { authButtonId: string; kcContext: KcContextLi
});
useEffect(() => {
if (isFetchingTranslations) {
return;
}
insertScriptTags();
}, []);
}, [isFetchingTranslations]);
}

View File

@ -1,7 +1,6 @@
import { useEffect } from "react";
import { clsx } from "keycloakify/tools/clsx";
import { getKcClsx, type KcClsx } from "keycloakify/login/lib/kcClsx";
import { useInsertScriptTags } from "keycloakify/tools/useInsertScriptTags";
import { useScript } from "keycloakify/login/pages/LoginRecoveryAuthnCodeConfig.useScript";
import type { PageProps } from "keycloakify/login/pages/PageProps";
import type { KcContext } from "../KcContext";
import type { I18n } from "../i18n";
@ -18,130 +17,9 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
const { msg, msgStr } = i18n;
const { insertScriptTags } = useInsertScriptTags({
componentOrHookName: "LoginRecoveryAuthnCodeConfig",
scriptTags: [
{
type: "text/javascript",
textContent: `
const olRecoveryCodesListId = "kc-recovery-codes-list";
/* copy recovery codes */
function copyRecoveryCodes() {
var tmpTextarea = document.createElement("textarea");
var codes = document.getElementById("kc-recovery-codes-list").getElementsByTagName("li");
for (i = 0; i < codes.length; i++) {
tmpTextarea.value = tmpTextarea.value + codes[i].innerText + "\\n";
}
document.body.appendChild(tmpTextarea);
tmpTextarea.select();
document.execCommand("copy");
document.body.removeChild(tmpTextarea);
}
var copyButton = document.getElementById("copyRecoveryCodes");
copyButton && copyButton.addEventListener("click", function () {
copyRecoveryCodes();
});
/* download recovery codes */
function formatCurrentDateTime() {
var dt = new Date();
var options = {
month: 'long',
day: 'numeric',
year: 'numeric',
hour: 'numeric',
minute: 'numeric',
timeZoneName: 'short'
};
return dt.toLocaleString('en-US', options);
}
function parseRecoveryCodeList() {
var recoveryCodes = document.querySelectorAll(".kc-recovery-codes-list li");
var recoveryCodeList = "";
for (var i = 0; i < recoveryCodes.length; i++) {
var recoveryCodeLiElement = recoveryCodes[i].innerText;
recoveryCodeList += recoveryCodeLiElement + "\\r\\n";
}
return recoveryCodeList;
}
function buildDownloadContent() {
var recoveryCodeList = parseRecoveryCodeList();
var dt = new Date();
var options = {
month: 'long',
day: 'numeric',
year: 'numeric',
hour: 'numeric',
minute: 'numeric',
timeZoneName: 'short'
};
return fileBodyContent =
"${msgStr("recovery-codes-download-file-header")}\\n\\n" +
recoveryCodeList + "\\n" +
"${msgStr("recovery-codes-download-file-description")}\\n\\n" +
"${msgStr("recovery-codes-download-file-date")} " + formatCurrentDateTime();
}
function setUpDownloadLinkAndDownload(filename, text) {
var el = document.createElement('a');
el.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
el.setAttribute('download', filename);
el.style.display = 'none';
document.body.appendChild(el);
el.click();
document.body.removeChild(el);
}
function downloadRecoveryCodes() {
setUpDownloadLinkAndDownload('kc-download-recovery-codes.txt', buildDownloadContent());
}
var downloadButton = document.getElementById("downloadRecoveryCodes");
downloadButton && downloadButton.addEventListener("click", downloadRecoveryCodes);
/* print recovery codes */
function buildPrintContent() {
var recoveryCodeListHTML = document.getElementById('kc-recovery-codes-list').innerHTML;
var styles =
\`@page { size: auto; margin-top: 0; }
body { width: 480px; }
div { list-style-type: none; font-family: monospace }
p:first-of-type { margin-top: 48px }\`;
return printFileContent =
"<html><style>" + styles + "</style><body>" +
"<title>kc-download-recovery-codes</title>" +
"<p>${msgStr("recovery-codes-download-file-header")}</p>" +
"<div>" + recoveryCodeListHTML + "</div>" +
"<p>${msgStr("recovery-codes-download-file-description")}</p>" +
"<p>${msgStr("recovery-codes-download-file-date")} " + formatCurrentDateTime() + "</p>" +
"</body></html>";
}
function printRecoveryCodes() {
var w = window.open();
w.document.write(buildPrintContent());
w.print();
w.close();
}
var printButton = document.getElementById("printRecoveryCodes");
printButton && printButton.addEventListener("click", printRecoveryCodes);
`
}
]
});
useEffect(() => {
insertScriptTags();
}, []);
useScript({ olRecoveryCodesListId, i18n });
return (
<Template
@ -164,7 +42,7 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
</div>
</div>
<ol id="kc-recovery-codes-list" className={kcClsx("kcRecoveryCodesList")}>
<ol id={olRecoveryCodesListId} className={kcClsx("kcRecoveryCodesList")}>
{recoveryAuthnCodesConfigBean.generatedRecoveryAuthnCodesList.map((code, index) => (
<li key={index}>
<span>{index + 1}:</span> {code.slice(0, 4)}-{code.slice(4, 8)}-{code.slice(8)}

View File

@ -0,0 +1,142 @@
import { useEffect } from "react";
import { useInsertScriptTags } from "keycloakify/tools/useInsertScriptTags";
type I18nLike = {
msgStr: (key: "recovery-codes-download-file-header" | "recovery-codes-download-file-description" | "recovery-codes-download-file-date") => string;
isFetchingTranslations: boolean;
};
export function useScript(params: { olRecoveryCodesListId: string; i18n: I18nLike }) {
const { olRecoveryCodesListId, i18n } = params;
const { msgStr, isFetchingTranslations } = i18n;
const { insertScriptTags } = useInsertScriptTags({
componentOrHookName: "LoginRecoveryAuthnCodeConfig",
scriptTags: [
{
type: "text/javascript",
textContent: () => `
/* copy recovery codes */
function copyRecoveryCodes() {
var tmpTextarea = document.createElement("textarea");
var codes = document.querySelectorAll("#${olRecoveryCodesListId} li");
for (i = 0; i < codes.length; i++) {
tmpTextarea.value = tmpTextarea.value + codes[i].innerText + "\\n";
}
document.body.appendChild(tmpTextarea);
tmpTextarea.select();
document.execCommand("copy");
document.body.removeChild(tmpTextarea);
}
var copyButton = document.getElementById("copyRecoveryCodes");
copyButton && copyButton.addEventListener("click", function () {
copyRecoveryCodes();
});
/* download recovery codes */
function formatCurrentDateTime() {
var dt = new Date();
var options = {
month: 'long',
day: 'numeric',
year: 'numeric',
hour: 'numeric',
minute: 'numeric',
timeZoneName: 'short'
};
return dt.toLocaleString('en-US', options);
}
function parseRecoveryCodeList() {
var recoveryCodes = document.querySelectorAll("#${olRecoveryCodesListId} li");
var recoveryCodeList = "";
for (var i = 0; i < recoveryCodes.length; i++) {
var recoveryCodeLiElement = recoveryCodes[i].innerText;
recoveryCodeList += recoveryCodeLiElement + "\\r\\n";
}
return recoveryCodeList;
}
function buildDownloadContent() {
var recoveryCodeList = parseRecoveryCodeList();
var dt = new Date();
var options = {
month: 'long',
day: 'numeric',
year: 'numeric',
hour: 'numeric',
minute: 'numeric',
timeZoneName: 'short'
};
return fileBodyContent =
${JSON.stringify(msgStr("recovery-codes-download-file-header"))} + "\\n\\n" +
recoveryCodeList + "\\n" +
${JSON.stringify(msgStr("recovery-codes-download-file-description"))} + "\\n\\n" +
${JSON.stringify(msgStr("recovery-codes-download-file-date"))} + " " + formatCurrentDateTime();
}
function setUpDownloadLinkAndDownload(filename, text) {
var el = document.createElement('a');
el.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
el.setAttribute('download', filename);
el.style.display = 'none';
document.body.appendChild(el);
el.click();
document.body.removeChild(el);
}
function downloadRecoveryCodes() {
setUpDownloadLinkAndDownload('kc-download-recovery-codes.txt', buildDownloadContent());
}
var downloadButton = document.getElementById("downloadRecoveryCodes");
downloadButton && downloadButton.addEventListener("click", downloadRecoveryCodes);
/* print recovery codes */
function buildPrintContent() {
var recoveryCodeListHTML = document.getElementById('${olRecoveryCodesListId}').innerHTML;
var styles =
\`@page { size: auto; margin-top: 0; }
body { width: 480px; }
div { list-style-type: none; font-family: monospace }
p:first-of-type { margin-top: 48px }\`;
return printFileContent =
"<html><style>" + styles + "</style><body>" +
"<title>kc-download-recovery-codes</title>" +
"<p>" + ${JSON.stringify(msgStr("recovery-codes-download-file-header"))} + "</p>" +
"<div>" + recoveryCodeListHTML + "</div>" +
"<p>" + ${JSON.stringify(msgStr("recovery-codes-download-file-description"))} + "</p>" +
"<p>" + ${JSON.stringify(msgStr("recovery-codes-download-file-date"))} + " " + formatCurrentDateTime() + "</p>" +
"</body></html>";
}
function printRecoveryCodes() {
var w = window.open();
w.document.write(buildPrintContent());
w.print();
w.close();
}
var printButton = document.getElementById("printRecoveryCodes");
printButton && printButton.addEventListener("click", printRecoveryCodes);
`
}
]
});
useEffect(() => {
if (isFetchingTranslations) {
return;
}
insertScriptTags();
}, [isFetchingTranslations]);
}

View File

@ -10,7 +10,7 @@ export namespace ScriptTag {
};
export type TextContent = Common & {
textContent: string;
textContent: string | (() => string);
};
export type Src = Common & {
src: string;
@ -69,7 +69,12 @@ export function useInsertScriptTags(params: {
for (let i = 0; i < scripts.length; i++) {
const script = scripts[i];
if ("textContent" in scriptTag) {
if (script.textContent === scriptTag.textContent) {
const textContent =
typeof scriptTag.textContent === "function"
? scriptTag.textContent()
: scriptTag.textContent;
if (script.textContent === textContent) {
return;
}
continue;
@ -90,7 +95,12 @@ export function useInsertScriptTags(params: {
(() => {
if ("textContent" in scriptTag) {
htmlElement.textContent = scriptTag.textContent;
const textContent =
typeof scriptTag.textContent === "function"
? scriptTag.textContent()
: scriptTag.textContent;
htmlElement.textContent = textContent;
return;
}
if ("src" in scriptTag) {