From 7e5abe8589e1d61999023a95ca203253277918ed Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Mon, 9 Sep 2024 06:59:11 +0200 Subject: [PATCH] Fix LoginPasskesConditionalAuthenticate --- .../build/createPublicDotKeycloakifyDir.ts | 21 ----- .../shared/downloadKeycloakDefaultTheme.ts | 12 +-- src/login/KcContext/KcContext.ts | 6 +- src/login/Template.tsx | 4 +- ...ate.ts => Template.useStylesAndScripts.ts} | 53 +++++------ .../pages/LoginIdpLinkConfirmOverride.tsx | 1 - .../LoginPasskeysConditionalAuthenticate.tsx | 87 ++----------------- ...skeysConditionalAuthenticate.useScript.tsx | 67 ++++++++++++++ src/tools/useInsertScriptTags.ts | 2 +- 9 files changed, 102 insertions(+), 151 deletions(-) rename src/login/{useInitTemplate.ts => Template.useStylesAndScripts.ts} (65%) create mode 100644 src/login/pages/LoginPasskeysConditionalAuthenticate.useScript.tsx diff --git a/scripts/build/createPublicDotKeycloakifyDir.ts b/scripts/build/createPublicDotKeycloakifyDir.ts index b2e19d71..667124ea 100644 --- a/scripts/build/createPublicDotKeycloakifyDir.ts +++ b/scripts/build/createPublicDotKeycloakifyDir.ts @@ -68,27 +68,6 @@ export async function createPublicDotKeycloakifyDir() { WELL_KNOWN_DIRECTORY_BASE_NAME.RESOURCES_COMMON ) }); - - copy_v24_js: { - if (themeType !== "login") { - break copy_v24_js; - } - - const { extractedDirPath } = await downloadKeycloakDefaultTheme({ - keycloakVersionId: "LAST_24" - }); - - transformCodebase({ - srcDirPath: pathJoin( - extractedDirPath, - "base", - "login", - "resources", - "js" - ), - destDirPath: pathJoin(destDirPath, "js", "v24") - }); - } }) ); } diff --git a/scripts/shared/downloadKeycloakDefaultTheme.ts b/scripts/shared/downloadKeycloakDefaultTheme.ts index a93b9dd4..76c5a51c 100644 --- a/scripts/shared/downloadKeycloakDefaultTheme.ts +++ b/scripts/shared/downloadKeycloakDefaultTheme.ts @@ -7,8 +7,7 @@ import { assert, type Equals } from "tsafe/assert"; const KEYCLOAK_VERSION = { FOR_LOGIN_THEME: "25.0.4", - FOR_ACCOUNT_MULTI_PAGE: "21.1.2", - LAST_24: "24.0.4" + FOR_ACCOUNT_MULTI_PAGE: "21.1.2" } as const; export async function downloadKeycloakDefaultTheme(params: { @@ -73,15 +72,6 @@ export async function downloadKeycloakDefaultTheme(params: { return; } - break; - case KEYCLOAK_VERSION.LAST_24: - if ( - !fileRelativePath.startsWith( - pathJoin("base", "login", "resources", "js") - ) - ) { - return; - } break; default: assert>(false); diff --git a/src/login/KcContext/KcContext.ts b/src/login/KcContext/KcContext.ts index 819f8c21..e64ba30f 100644 --- a/src/login/KcContext/KcContext.ts +++ b/src/login/KcContext/KcContext.ts @@ -149,10 +149,6 @@ export declare namespace KcContext { getFirstError: (...fieldNames: string[]) => string; }; - authenticationSession?: { - authSessionId: string; - tabId: string; - }; properties: {}; "x-keycloakify": { messages: Record; @@ -593,7 +589,7 @@ export declare namespace KcContext { challenge: string; userVerification: string; rpId: string; - createTimeout: number | string; + createTimeout: number; authenticators?: { authenticators: WebauthnAuthenticate.WebauthnAuthenticator[]; diff --git a/src/login/Template.tsx b/src/login/Template.tsx index ee94ab79..ade06dee 100644 --- a/src/login/Template.tsx +++ b/src/login/Template.tsx @@ -4,9 +4,9 @@ import { clsx } from "keycloakify/tools/clsx"; import type { TemplateProps } from "keycloakify/login/TemplateProps"; import { getKcClsx } from "keycloakify/login/lib/kcClsx"; import { useSetClassName } from "keycloakify/tools/useSetClassName"; +import { useStylesAndScripts } from "keycloakify/login/Template.useStylesAndScripts"; import type { I18n } from "./i18n"; import type { KcContext } from "./KcContext"; -import { useInitTemplate } from "keycloakify/login/useInitTemplate"; export default function Template(props: TemplateProps) { const { @@ -45,7 +45,7 @@ export default function Template(props: TemplateProps) { className: bodyClassName ?? kcClsx("kcBodyClass") }); - const { isReadyToRender } = useInitTemplate({ kcContext, doUseDefaultCss }); + const { isReadyToRender } = useStylesAndScripts({ kcContext, doUseDefaultCss }); if (!isReadyToRender) { return null; diff --git a/src/login/useInitTemplate.ts b/src/login/Template.useStylesAndScripts.ts similarity index 65% rename from src/login/useInitTemplate.ts rename to src/login/Template.useStylesAndScripts.ts index 5da30ef1..72933100 100644 --- a/src/login/useInitTemplate.ts +++ b/src/login/Template.useStylesAndScripts.ts @@ -14,22 +14,18 @@ export type KcContextLike = { currentLanguageTag: string; }; scripts: string[]; - authenticationSession?: { - authSessionId: string; - tabId: string; - }; }; assert(); assert(); -export function useInitTemplate(params: { +export function useStylesAndScripts(params: { kcContext: KcContextLike; doUseDefaultCss: boolean; }) { const { kcContext, doUseDefaultCss } = params; - const { url, locale, scripts, authenticationSession } = kcContext; + const { url, locale, scripts } = kcContext; useEffect(() => { const { currentLanguageTag } = locale ?? {}; @@ -59,33 +55,32 @@ export function useInitTemplate(params: { const { insertScriptTags } = useInsertScriptTags({ componentOrHookName: "Template", scriptTags: [ + { + type: "importmap", + textContent: JSON.stringify({ + imports: { + rfc4648: `${url.resourcesCommonPath}/node_modules/rfc4648/lib/rfc4648.js` + } + }) + }, { type: "module", src: `${url.resourcesPath}/js/menu-button-links.js` }, - ...(authenticationSession === undefined - ? [] - : [ - { - type: "module", - textContent: [ - `import { checkCookiesAndSetTimer } from "${url.resourcesPath}/js/authChecker.js";`, - ``, - `checkCookiesAndSetTimer(`, - ` "${authenticationSession.authSessionId}",`, - ` "${authenticationSession.tabId}",`, - ` "${url.ssoLoginInOtherTabsUrl}"`, - `);` - ].join("\n") - } as const - ]), - ...scripts.map( - script => - ({ - type: "text/javascript", - src: script - }) as const - ) + ...scripts.map(src => ({ + type: "text/javascript" as const, + src + })), + { + type: "module", + textContent: ` + import { checkCookiesAndSetTimer } from "${url.resourcesPath}/js/authChecker.js"; + + checkCookiesAndSetTimer( + "${url.ssoLoginInOtherTabsUrl}" + ); + ` + } ] }); diff --git a/src/login/pages/LoginIdpLinkConfirmOverride.tsx b/src/login/pages/LoginIdpLinkConfirmOverride.tsx index dbad8a32..ec4186cd 100644 --- a/src/login/pages/LoginIdpLinkConfirmOverride.tsx +++ b/src/login/pages/LoginIdpLinkConfirmOverride.tsx @@ -3,7 +3,6 @@ import type { PageProps } from "keycloakify/login/pages/PageProps"; import type { KcContext } from "../KcContext"; import type { I18n } from "../i18n"; -// NOTE: Added with Keycloak 25 export default function LoginIdpLinkConfirmOverride(props: PageProps, I18n>) { const { kcContext, i18n, doUseDefaultCss, Template, classes } = props; diff --git a/src/login/pages/LoginPasskeysConditionalAuthenticate.tsx b/src/login/pages/LoginPasskeysConditionalAuthenticate.tsx index bb3f8b70..e5aa5b2f 100644 --- a/src/login/pages/LoginPasskeysConditionalAuthenticate.tsx +++ b/src/login/pages/LoginPasskeysConditionalAuthenticate.tsx @@ -1,33 +1,17 @@ -import { useEffect, Fragment } from "react"; +import { Fragment } from "react"; import { clsx } from "keycloakify/tools/clsx"; import type { PageProps } from "keycloakify/login/pages/PageProps"; -import { useInsertScriptTags } from "keycloakify/tools/useInsertScriptTags"; import { getKcClsx } from "keycloakify/login/lib/kcClsx"; -import { assert } from "keycloakify/tools/assert"; +import { useScript } from "keycloakify/login/pages/LoginPasskeysConditionalAuthenticate.useScript"; import type { KcContext } from "../KcContext"; import type { I18n } from "../i18n"; -// NOTE: From Keycloak 25.0.4 export default function LoginPasskeysConditionalAuthenticate( props: PageProps, I18n> ) { const { kcContext, i18n, doUseDefaultCss, Template, classes } = props; - const { - messagesPerField, - login, - url, - usernameHidden, - shouldDisplayAuthenticators, - authenticators, - registrationDisabled, - realm, - isUserIdentified, - challenge, - userVerification, - rpId, - createTimeout - } = kcContext; + const { messagesPerField, login, url, usernameHidden, shouldDisplayAuthenticators, authenticators, registrationDisabled, realm } = kcContext; const { msg, msgStr, advancedMsg } = i18n; @@ -36,46 +20,9 @@ export default function LoginPasskeysConditionalAuthenticate( classes }); - const { insertScriptTags } = useInsertScriptTags({ - componentOrHookName: "LoginRecoveryAuthnCodeConfig", - scriptTags: [ - { - type: "module", - textContent: ` - import { authenticateByWebAuthn } from "${url.resourcesPath}/js/webauthnAuthenticate.js"; - import { initAuthenticate } from "${url.resourcesPath}/js/passkeysConditionalAuth.js"; + const authButtonId = "authenticateWebAuthnButton"; - const authButton = document.getElementById('authenticateWebAuthnButton'); - const input = { - isUserIdentified : ${isUserIdentified}, - challenge : '${challenge}', - userVerification : '${userVerification}', - rpId : '${rpId}', - createTimeout : ${createTimeout}, - errmsg : "${msgStr("webauthn-unsupported-browser-text")}" - }; - authButton.addEventListener("click", () => { - authenticateByWebAuthn(input); - }); - - const args = { - isUserIdentified : ${isUserIdentified}, - challenge : '${challenge}', - userVerification : '${userVerification}', - rpId : '${rpId}', - createTimeout : ${createTimeout}, - errmsg : "${msgStr("passkey-unsupported-browser-text")}" - }; - - document.addEventListener("DOMContentLoaded", (event) => initAuthenticate(args)); - ` - } - ] - }); - - useEffect(() => { - insertScriptTags(); - }, []); + useScript({ authButtonId, kcContext, i18n }); return (