Fix LoginRecoveryAuthnCodeConfig
This commit is contained in:
parent
7e5abe8589
commit
28990a12da
@ -19,6 +19,7 @@ assert<KcContext.LoginPasskeysConditionalAuthenticate extends KcContextLike ? tr
|
|||||||
|
|
||||||
type I18nLike = {
|
type I18nLike = {
|
||||||
msgStr: (key: "webauthn-unsupported-browser-text" | "passkey-unsupported-browser-text") => string;
|
msgStr: (key: "webauthn-unsupported-browser-text" | "passkey-unsupported-browser-text") => string;
|
||||||
|
isFetchingTranslations: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useScript(params: { authButtonId: string; kcContext: KcContextLike; i18n: I18nLike }) {
|
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 { url, isUserIdentified, challenge, userVerification, rpId, createTimeout } = kcContext;
|
||||||
|
|
||||||
const { msgStr } = i18n;
|
const { msgStr, isFetchingTranslations } = i18n;
|
||||||
|
|
||||||
const { insertScriptTags } = useInsertScriptTags({
|
const { insertScriptTags } = useInsertScriptTags({
|
||||||
componentOrHookName: "LoginRecoveryAuthnCodeConfig",
|
componentOrHookName: "LoginRecoveryAuthnCodeConfig",
|
||||||
scriptTags: [
|
scriptTags: [
|
||||||
{
|
{
|
||||||
type: "module",
|
type: "module",
|
||||||
textContent: `
|
textContent: () => `
|
||||||
import { authenticateByWebAuthn } from "${url.resourcesPath}/js/webauthnAuthenticate.js";
|
import { authenticateByWebAuthn } from "${url.resourcesPath}/js/webauthnAuthenticate.js";
|
||||||
import { initAuthenticate } from "${url.resourcesPath}/js/passkeysConditionalAuth.js";
|
import { initAuthenticate } from "${url.resourcesPath}/js/passkeysConditionalAuth.js";
|
||||||
|
|
||||||
const authButton = document.getElementById(${JSON.stringify(authButtonId)});
|
const authButton = document.getElementById("${authButtonId}");
|
||||||
const input = {
|
const input = {
|
||||||
isUserIdentified : ${isUserIdentified},
|
isUserIdentified : ${isUserIdentified},
|
||||||
challenge : '${challenge}',
|
challenge : '${challenge}',
|
||||||
@ -62,6 +63,10 @@ export function useScript(params: { authButtonId: string; kcContext: KcContextLi
|
|||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (isFetchingTranslations) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
insertScriptTags();
|
insertScriptTags();
|
||||||
}, []);
|
}, [isFetchingTranslations]);
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { useEffect } from "react";
|
|
||||||
import { clsx } from "keycloakify/tools/clsx";
|
import { clsx } from "keycloakify/tools/clsx";
|
||||||
import { getKcClsx, type KcClsx } from "keycloakify/login/lib/kcClsx";
|
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 { PageProps } from "keycloakify/login/pages/PageProps";
|
||||||
import type { KcContext } from "../KcContext";
|
import type { KcContext } from "../KcContext";
|
||||||
import type { I18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
@ -18,130 +17,9 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
|
|||||||
|
|
||||||
const { msg, msgStr } = i18n;
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
const { insertScriptTags } = useInsertScriptTags({
|
const olRecoveryCodesListId = "kc-recovery-codes-list";
|
||||||
componentOrHookName: "LoginRecoveryAuthnCodeConfig",
|
|
||||||
scriptTags: [
|
|
||||||
{
|
|
||||||
type: "text/javascript",
|
|
||||||
textContent: `
|
|
||||||
|
|
||||||
/* copy recovery codes */
|
useScript({ olRecoveryCodesListId, i18n });
|
||||||
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();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
@ -164,7 +42,7 @@ export default function LoginRecoveryAuthnCodeConfig(props: PageProps<Extract<Kc
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ol id="kc-recovery-codes-list" className={kcClsx("kcRecoveryCodesList")}>
|
<ol id={olRecoveryCodesListId} className={kcClsx("kcRecoveryCodesList")}>
|
||||||
{recoveryAuthnCodesConfigBean.generatedRecoveryAuthnCodesList.map((code, index) => (
|
{recoveryAuthnCodesConfigBean.generatedRecoveryAuthnCodesList.map((code, index) => (
|
||||||
<li key={index}>
|
<li key={index}>
|
||||||
<span>{index + 1}:</span> {code.slice(0, 4)}-{code.slice(4, 8)}-{code.slice(8)}
|
<span>{index + 1}:</span> {code.slice(0, 4)}-{code.slice(4, 8)}-{code.slice(8)}
|
||||||
|
142
src/login/pages/LoginRecoveryAuthnCodeConfig.useScript.tsx
Normal file
142
src/login/pages/LoginRecoveryAuthnCodeConfig.useScript.tsx
Normal 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]);
|
||||||
|
}
|
@ -10,7 +10,7 @@ export namespace ScriptTag {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type TextContent = Common & {
|
export type TextContent = Common & {
|
||||||
textContent: string;
|
textContent: string | (() => string);
|
||||||
};
|
};
|
||||||
export type Src = Common & {
|
export type Src = Common & {
|
||||||
src: string;
|
src: string;
|
||||||
@ -69,7 +69,12 @@ export function useInsertScriptTags(params: {
|
|||||||
for (let i = 0; i < scripts.length; i++) {
|
for (let i = 0; i < scripts.length; i++) {
|
||||||
const script = scripts[i];
|
const script = scripts[i];
|
||||||
if ("textContent" in scriptTag) {
|
if ("textContent" in scriptTag) {
|
||||||
if (script.textContent === scriptTag.textContent) {
|
const textContent =
|
||||||
|
typeof scriptTag.textContent === "function"
|
||||||
|
? scriptTag.textContent()
|
||||||
|
: scriptTag.textContent;
|
||||||
|
|
||||||
|
if (script.textContent === textContent) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@ -90,7 +95,12 @@ export function useInsertScriptTags(params: {
|
|||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
if ("textContent" in scriptTag) {
|
if ("textContent" in scriptTag) {
|
||||||
htmlElement.textContent = scriptTag.textContent;
|
const textContent =
|
||||||
|
typeof scriptTag.textContent === "function"
|
||||||
|
? scriptTag.textContent()
|
||||||
|
: scriptTag.textContent;
|
||||||
|
|
||||||
|
htmlElement.textContent = textContent;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ("src" in scriptTag) {
|
if ("src" in scriptTag) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user