From c9d7fc1b6ed5a0721095cef6d856462876ac5d26 Mon Sep 17 00:00:00 2001 From: Nima Shkouhfar Date: Sun, 29 Sep 2024 04:35:02 -0400 Subject: [PATCH 01/77] Changes: - First draft of test coverage improvement for storybooks - code's page html rendering issue fixed --- src/login/pages/Code.tsx | 10 +- stories/login/pages/Code.stories.tsx | 39 ++++++ .../pages/DeleteAccountConfirm.stories.tsx | 30 ++++ .../login/pages/DeleteCredential.stories.tsx | 10 ++ stories/login/pages/Error.stories.tsx | 35 +++++ .../pages/FrontchannelLogout.stories.tsx | 11 ++ .../pages/IdpReviewUserProfile.stories.tsx | 44 ++++++ stories/login/pages/Info.stories.tsx | 39 ++++++ stories/login/pages/Login.stories.tsx | 128 ++++++++++++++++++ .../login/pages/LoginConfigTotp.stories.tsx | 21 +++ .../login/pages/LoginIdpLinkEmail.stories.tsx | 46 +++++++ stories/login/pages/Register.stories.tsx | 62 +++++++++ stories/login/pages/Terms.stories.tsx | 29 ++++ 13 files changed, 503 insertions(+), 1 deletion(-) diff --git a/src/login/pages/Code.tsx b/src/login/pages/Code.tsx index 576a2ccd..4bca62f6 100644 --- a/src/login/pages/Code.tsx +++ b/src/login/pages/Code.tsx @@ -2,6 +2,7 @@ import { getKcClsx } from "keycloakify/login/lib/kcClsx"; import type { PageProps } from "keycloakify/login/pages/PageProps"; import type { KcContext } from "../KcContext"; import type { I18n } from "../i18n"; +import { kcSanitize } from "keycloakify/lib/kcSanitize"; export default function Code(props: PageProps, I18n>) { const { kcContext, i18n, doUseDefaultCss, Template, classes } = props; @@ -30,7 +31,14 @@ export default function Code(props: PageProps ) : ( -

{code.error}

+ code.error && ( +

+ ) )} diff --git a/stories/login/pages/Code.stories.tsx b/stories/login/pages/Code.stories.tsx index 0fcdf87f..b93125bf 100644 --- a/stories/login/pages/Code.stories.tsx +++ b/stories/login/pages/Code.stories.tsx @@ -16,3 +16,42 @@ type Story = StoryObj; export const Default: Story = { render: () => }; +export const WithErrorCode: Story = { + render: () => ( + + ) +}; +export const WithFrenchLanguage: Story = { + render: () => ( + + ) +}; +export const WithHtmlErrorMessage: Story = { + render: () => ( + Try again" + } + }} + /> + ) +}; diff --git a/stories/login/pages/DeleteAccountConfirm.stories.tsx b/stories/login/pages/DeleteAccountConfirm.stories.tsx index 501fa0c4..0d865dfe 100644 --- a/stories/login/pages/DeleteAccountConfirm.stories.tsx +++ b/stories/login/pages/DeleteAccountConfirm.stories.tsx @@ -16,3 +16,33 @@ type Story = StoryObj; export const Default: Story = { render: () => }; +export const WithAIAFlow: Story = { + render: () => ( + + ) +}; +export const WithoutAIAFlow: Story = { + render: () => ( + + ) +}; +export const WithCustomButtonStyle: Story = { + render: () => ( + + ) +}; diff --git a/stories/login/pages/DeleteCredential.stories.tsx b/stories/login/pages/DeleteCredential.stories.tsx index 7b211a52..38619d5b 100644 --- a/stories/login/pages/DeleteCredential.stories.tsx +++ b/stories/login/pages/DeleteCredential.stories.tsx @@ -16,3 +16,13 @@ type Story = StoryObj; export const Default: Story = { render: () => }; +export const WithCustomCredentialLabel: Story = { + render: () => ( + + ) +}; diff --git a/stories/login/pages/Error.stories.tsx b/stories/login/pages/Error.stories.tsx index 7b12410e..e64ea3f8 100644 --- a/stories/login/pages/Error.stories.tsx +++ b/stories/login/pages/Error.stories.tsx @@ -26,3 +26,38 @@ export const WithAnotherMessage: Story = { /> ) }; + +export const WithHtmlErrorMessage: Story = { + render: () => ( + Error: Something went wrong. Go back" + } + }} + /> + ) +}; +export const FrenchError: Story = { + render: () => ( + + ) +}; +export const WithSkipLink: Story = { + render: () => ( + + ) +}; diff --git a/stories/login/pages/FrontchannelLogout.stories.tsx b/stories/login/pages/FrontchannelLogout.stories.tsx index 9dacf0b0..cdef7c2c 100644 --- a/stories/login/pages/FrontchannelLogout.stories.tsx +++ b/stories/login/pages/FrontchannelLogout.stories.tsx @@ -16,3 +16,14 @@ type Story = StoryObj; export const Default: Story = { render: () => }; +export const WithoutRedirectUrl: Story = { + render: () => ( + + ) +}; diff --git a/stories/login/pages/IdpReviewUserProfile.stories.tsx b/stories/login/pages/IdpReviewUserProfile.stories.tsx index 6778e77d..93eb5b30 100644 --- a/stories/login/pages/IdpReviewUserProfile.stories.tsx +++ b/stories/login/pages/IdpReviewUserProfile.stories.tsx @@ -16,3 +16,47 @@ type Story = StoryObj; export const Default: Story = { render: () => }; +export const WithFormValidationErrors: Story = { + render: () => ( + ["email", "firstName"].includes(fieldName), + get: (fieldName: string) => { + if (fieldName === "email") return "Invalid email format."; + if (fieldName === "firstName") return "First name is required."; + } + } + }} + /> + ) +}; +export const WithReadOnlyFields: Story = { + render: () => ( + + ) +}; +export const WithPrefilledFormFields: Story = { + render: () => ( + + ) +}; diff --git a/stories/login/pages/Info.stories.tsx b/stories/login/pages/Info.stories.tsx index 195061f3..81884dce 100644 --- a/stories/login/pages/Info.stories.tsx +++ b/stories/login/pages/Info.stories.tsx @@ -55,3 +55,42 @@ export const WithRequiredActions: Story = { /> ) }; +export const WithPageRedirect: Story = { + render: () => ( + + ) +}; +export const WithoutClientBaseUrl: Story = { + render: () => ( + + ) +}; +export const WithMessageHeader: Story = { + render: () => ( + + ) +}; +export const WithAdvancedMessage: Story = { + render: () => ( + important information." } + }} + /> + ) +}; diff --git a/stories/login/pages/Login.stories.tsx b/stories/login/pages/Login.stories.tsx index 075d1f98..a3743adb 100644 --- a/stories/login/pages/Login.stories.tsx +++ b/stories/login/pages/Login.stories.tsx @@ -231,3 +231,131 @@ export const WithErrorMessage: Story = { /> ) }; + +export const WithOneSocialProvider: Story = { + render: args => ( + + ) +}; + +export const WithTwoSocialProviders: Story = { + render: args => ( + + ) +}; +export const WithNoSocialProviders: Story = { + render: args => ( + + ) +}; +export const WithMoreThanTwoSocialProviders: Story = { + render: args => ( + + ) +}; +export const WithSocialProvidersAndWithoutRememberMe: Story = { + render: args => ( + + ) +}; diff --git a/stories/login/pages/LoginConfigTotp.stories.tsx b/stories/login/pages/LoginConfigTotp.stories.tsx index 5d38df9b..743d38c2 100644 --- a/stories/login/pages/LoginConfigTotp.stories.tsx +++ b/stories/login/pages/LoginConfigTotp.stories.tsx @@ -41,3 +41,24 @@ export const WithError: Story = { /> ) }; +export const WithAppInitiatedAction: Story = { + render: () => ( + + ) +}; + +export const WithPreFilledUserLabel: Story = { + render: () => ( + + ) +}; diff --git a/stories/login/pages/LoginIdpLinkEmail.stories.tsx b/stories/login/pages/LoginIdpLinkEmail.stories.tsx index a58ae2d3..e855eed5 100644 --- a/stories/login/pages/LoginIdpLinkEmail.stories.tsx +++ b/stories/login/pages/LoginIdpLinkEmail.stories.tsx @@ -16,3 +16,49 @@ type Story = StoryObj; export const Default: Story = { render: () => }; +export const WithIdpAlias: Story = { + render: () => ( + + ) +}; +export const WithoutIdpAlias: Story = { + render: () => ( + + ) +}; + +export const WithCustomRealmDisplayName: Story = { + render: () => ( + + ) +}; diff --git a/stories/login/pages/Register.stories.tsx b/stories/login/pages/Register.stories.tsx index 0a8c395f..11e0a8a7 100644 --- a/stories/login/pages/Register.stories.tsx +++ b/stories/login/pages/Register.stories.tsx @@ -185,3 +185,65 @@ export const WithTermsAcceptance: Story = { /> ) }; +export const WithTermsNotAccepted: Story = { + render: args => ( + fieldName === "termsAccepted", + get: (fieldName: string) => (fieldName === "termsAccepted" ? "You must accept the terms." : undefined) + } + }} + /> + ) +}; +export const WithFieldErrors: Story = { + render: () => ( + ["username", "email"].includes(fieldName), + get: fieldName => { + if (fieldName === "username") return "Username is required."; + if (fieldName === "email") return "Invalid email format."; + } + } + }} + /> + ) +}; +export const WithReadOnlyFields: Story = { + render: () => ( + + ) +}; +export const WithAutoGeneratedUsername: Story = { + render: () => ( + + ) +}; diff --git a/stories/login/pages/Terms.stories.tsx b/stories/login/pages/Terms.stories.tsx index f5837b07..631fe685 100644 --- a/stories/login/pages/Terms.stories.tsx +++ b/stories/login/pages/Terms.stories.tsx @@ -45,3 +45,32 @@ export const French: Story = { /> ) }; +export const WithErrorMessage: Story = { + render: () => ( + true, + get: () => "An error occurred while processing your request." + } + }} + /> + ) +}; + +export const Spanish: Story = { + render: () => ( + Mis tΓ©rminos en EspaΓ±ol

" + } + } + }} + /> + ) +}; From da3023cf5e7ddfa70df33d4d0f888fb3bfdcdf8d Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Mon, 30 Sep 2024 00:31:27 +0200 Subject: [PATCH 02/77] Refactor: Make ClassKey importable without having react as a dependency --- src/account/TemplateProps.ts | 16 +---- src/account/lib/kcClsx.ts | 18 ++++- src/login/TemplateProps.ts | 127 +--------------------------------- src/login/lib/kcClsx.ts | 129 ++++++++++++++++++++++++++++++++++- 4 files changed, 145 insertions(+), 145 deletions(-) diff --git a/src/account/TemplateProps.ts b/src/account/TemplateProps.ts index 0e5196d2..d8f26cbd 100644 --- a/src/account/TemplateProps.ts +++ b/src/account/TemplateProps.ts @@ -1,4 +1,5 @@ import type { ReactNode } from "react"; +import type { ClassKey } from "keycloakify/account/lib/kcClsx"; export type TemplateProps = { kcContext: KcContext; @@ -10,17 +11,4 @@ export type TemplateProps = { active: string; }; -export type ClassKey = - | "kcHtmlClass" - | "kcBodyClass" - | "kcButtonClass" - | "kcButtonPrimaryClass" - | "kcButtonLargeClass" - | "kcButtonDefaultClass" - | "kcContentWrapperClass" - | "kcFormClass" - | "kcFormGroupClass" - | "kcInputWrapperClass" - | "kcLabelClass" - | "kcInputClass" - | "kcInputErrorMessageClass"; +export type { ClassKey }; diff --git a/src/account/lib/kcClsx.ts b/src/account/lib/kcClsx.ts index 330e0c4f..33df4515 100644 --- a/src/account/lib/kcClsx.ts +++ b/src/account/lib/kcClsx.ts @@ -1,5 +1,19 @@ import { createGetKcClsx } from "keycloakify/lib/getKcClsx"; -import type { ClassKey } from "keycloakify/account/TemplateProps"; + +export type ClassKey = + | "kcHtmlClass" + | "kcBodyClass" + | "kcButtonClass" + | "kcButtonPrimaryClass" + | "kcButtonLargeClass" + | "kcButtonDefaultClass" + | "kcContentWrapperClass" + | "kcFormClass" + | "kcFormGroupClass" + | "kcInputWrapperClass" + | "kcLabelClass" + | "kcInputClass" + | "kcInputErrorMessageClass"; export const { getKcClsx } = createGetKcClsx({ defaultClasses: { @@ -20,6 +34,4 @@ export const { getKcClsx } = createGetKcClsx({ } }); -export type { ClassKey }; - export type KcClsx = ReturnType["kcClsx"]; diff --git a/src/login/TemplateProps.ts b/src/login/TemplateProps.ts index 0e3b2895..b3b8f7c6 100644 --- a/src/login/TemplateProps.ts +++ b/src/login/TemplateProps.ts @@ -1,4 +1,5 @@ import type { ReactNode } from "react"; +import type { ClassKey } from "keycloakify/login/lib/kcClsx"; export type TemplateProps = { kcContext: KcContext; @@ -18,128 +19,4 @@ export type TemplateProps = { bodyClassName?: string; }; -export type ClassKey = - | "kcBodyClass" - | "kcHeaderWrapperClass" - | "kcLocaleWrapperClass" - | "kcInfoAreaWrapperClass" - | "kcFormButtonsWrapperClass" - | "kcFormOptionsWrapperClass" - | "kcCheckboxInputClass" - | "kcLocaleDropDownClass" - | "kcLocaleListItemClass" - | "kcContentWrapperClass" - | "kcLogoIdP-facebook" - | "kcAuthenticatorOTPClass" - | "kcLogoIdP-bitbucket" - | "kcAuthenticatorWebAuthnClass" - | "kcWebAuthnDefaultIcon" - | "kcLogoIdP-stackoverflow" - | "kcSelectAuthListItemClass" - | "kcLogoIdP-microsoft" - | "kcLoginOTPListItemHeaderClass" - | "kcLocaleItemClass" - | "kcLoginOTPListItemIconBodyClass" - | "kcInputHelperTextAfterClass" - | "kcFormClass" - | "kcSelectAuthListClass" - | "kcInputClassRadioCheckboxLabelDisabled" - | "kcSelectAuthListItemIconClass" - | "kcRecoveryCodesWarning" - | "kcFormSettingClass" - | "kcWebAuthnBLE" - | "kcInputWrapperClass" - | "kcSelectAuthListItemArrowIconClass" - | "kcFeedbackAreaClass" - | "kcFormPasswordVisibilityButtonClass" - | "kcLogoIdP-google" - | "kcCheckLabelClass" - | "kcSelectAuthListItemFillClass" - | "kcAuthenticatorDefaultClass" - | "kcLogoIdP-gitlab" - | "kcFormAreaClass" - | "kcFormButtonsClass" - | "kcInputClassRadioLabel" - | "kcAuthenticatorWebAuthnPasswordlessClass" - | "kcSelectAuthListItemHeadingClass" - | "kcInfoAreaClass" - | "kcLogoLink" - | "kcContainerClass" - | "kcSelectAuthListItemTitle" - | "kcHtmlClass" - | "kcLoginOTPListItemTitleClass" - | "kcLogoIdP-openshift-v4" - | "kcWebAuthnUnknownIcon" - | "kcFormSocialAccountNameClass" - | "kcLogoIdP-openshift-v3" - | "kcLoginOTPListInputClass" - | "kcWebAuthnUSB" - | "kcInputClassRadio" - | "kcWebAuthnKeyIcon" - | "kcFeedbackInfoIcon" - | "kcCommonLogoIdP" - | "kcRecoveryCodesActions" - | "kcFormGroupHeader" - | "kcFormSocialAccountSectionClass" - | "kcLogoIdP-instagram" - | "kcAlertClass" - | "kcHeaderClass" - | "kcLabelWrapperClass" - | "kcFormPasswordVisibilityIconShow" - | "kcFormSocialAccountLinkClass" - | "kcLocaleMainClass" - | "kcInputGroup" - | "kcTextareaClass" - | "kcButtonBlockClass" - | "kcButtonClass" - | "kcWebAuthnNFC" - | "kcLocaleClass" - | "kcInputClassCheckboxInput" - | "kcFeedbackErrorIcon" - | "kcInputLargeClass" - | "kcInputErrorMessageClass" - | "kcRecoveryCodesList" - | "kcFormSocialAccountListClass" - | "kcAlertTitleClass" - | "kcAuthenticatorPasswordClass" - | "kcCheckInputClass" - | "kcLogoIdP-linkedin" - | "kcLogoIdP-twitter" - | "kcFeedbackWarningIcon" - | "kcResetFlowIcon" - | "kcSelectAuthListItemIconPropertyClass" - | "kcFeedbackSuccessIcon" - | "kcLoginOTPListClass" - | "kcSrOnlyClass" - | "kcFormSocialAccountListGridClass" - | "kcButtonDefaultClass" - | "kcFormGroupErrorClass" - | "kcSelectAuthListItemDescriptionClass" - | "kcSelectAuthListItemBodyClass" - | "kcWebAuthnInternal" - | "kcSelectAuthListItemArrowClass" - | "kcCheckClass" - | "kcContentClass" - | "kcLogoClass" - | "kcLoginOTPListItemIconClass" - | "kcLoginClass" - | "kcSignUpClass" - | "kcButtonLargeClass" - | "kcFormCardClass" - | "kcLocaleListClass" - | "kcInputClass" - | "kcFormGroupClass" - | "kcLogoIdP-paypal" - | "kcInputClassCheckbox" - | "kcRecoveryCodesConfirmation" - | "kcFormPasswordVisibilityIconHide" - | "kcInputClassRadioInput" - | "kcFormSocialAccountListButtonClass" - | "kcInputClassCheckboxLabel" - | "kcFormOptionsClass" - | "kcFormHeaderClass" - | "kcFormSocialAccountGridItem" - | "kcButtonPrimaryClass" - | "kcInputHelperTextBeforeClass" - | "kcLogoIdP-github" - | "kcLabelClass"; +export type { ClassKey }; diff --git a/src/login/lib/kcClsx.ts b/src/login/lib/kcClsx.ts index a140671f..9198f778 100644 --- a/src/login/lib/kcClsx.ts +++ b/src/login/lib/kcClsx.ts @@ -1,5 +1,130 @@ import { createGetKcClsx } from "keycloakify/lib/getKcClsx"; -import type { ClassKey } from "keycloakify/login/TemplateProps"; + +export type ClassKey = + | "kcBodyClass" + | "kcHeaderWrapperClass" + | "kcLocaleWrapperClass" + | "kcInfoAreaWrapperClass" + | "kcFormButtonsWrapperClass" + | "kcFormOptionsWrapperClass" + | "kcCheckboxInputClass" + | "kcLocaleDropDownClass" + | "kcLocaleListItemClass" + | "kcContentWrapperClass" + | "kcLogoIdP-facebook" + | "kcAuthenticatorOTPClass" + | "kcLogoIdP-bitbucket" + | "kcAuthenticatorWebAuthnClass" + | "kcWebAuthnDefaultIcon" + | "kcLogoIdP-stackoverflow" + | "kcSelectAuthListItemClass" + | "kcLogoIdP-microsoft" + | "kcLoginOTPListItemHeaderClass" + | "kcLocaleItemClass" + | "kcLoginOTPListItemIconBodyClass" + | "kcInputHelperTextAfterClass" + | "kcFormClass" + | "kcSelectAuthListClass" + | "kcInputClassRadioCheckboxLabelDisabled" + | "kcSelectAuthListItemIconClass" + | "kcRecoveryCodesWarning" + | "kcFormSettingClass" + | "kcWebAuthnBLE" + | "kcInputWrapperClass" + | "kcSelectAuthListItemArrowIconClass" + | "kcFeedbackAreaClass" + | "kcFormPasswordVisibilityButtonClass" + | "kcLogoIdP-google" + | "kcCheckLabelClass" + | "kcSelectAuthListItemFillClass" + | "kcAuthenticatorDefaultClass" + | "kcLogoIdP-gitlab" + | "kcFormAreaClass" + | "kcFormButtonsClass" + | "kcInputClassRadioLabel" + | "kcAuthenticatorWebAuthnPasswordlessClass" + | "kcSelectAuthListItemHeadingClass" + | "kcInfoAreaClass" + | "kcLogoLink" + | "kcContainerClass" + | "kcSelectAuthListItemTitle" + | "kcHtmlClass" + | "kcLoginOTPListItemTitleClass" + | "kcLogoIdP-openshift-v4" + | "kcWebAuthnUnknownIcon" + | "kcFormSocialAccountNameClass" + | "kcLogoIdP-openshift-v3" + | "kcLoginOTPListInputClass" + | "kcWebAuthnUSB" + | "kcInputClassRadio" + | "kcWebAuthnKeyIcon" + | "kcFeedbackInfoIcon" + | "kcCommonLogoIdP" + | "kcRecoveryCodesActions" + | "kcFormGroupHeader" + | "kcFormSocialAccountSectionClass" + | "kcLogoIdP-instagram" + | "kcAlertClass" + | "kcHeaderClass" + | "kcLabelWrapperClass" + | "kcFormPasswordVisibilityIconShow" + | "kcFormSocialAccountLinkClass" + | "kcLocaleMainClass" + | "kcInputGroup" + | "kcTextareaClass" + | "kcButtonBlockClass" + | "kcButtonClass" + | "kcWebAuthnNFC" + | "kcLocaleClass" + | "kcInputClassCheckboxInput" + | "kcFeedbackErrorIcon" + | "kcInputLargeClass" + | "kcInputErrorMessageClass" + | "kcRecoveryCodesList" + | "kcFormSocialAccountListClass" + | "kcAlertTitleClass" + | "kcAuthenticatorPasswordClass" + | "kcCheckInputClass" + | "kcLogoIdP-linkedin" + | "kcLogoIdP-twitter" + | "kcFeedbackWarningIcon" + | "kcResetFlowIcon" + | "kcSelectAuthListItemIconPropertyClass" + | "kcFeedbackSuccessIcon" + | "kcLoginOTPListClass" + | "kcSrOnlyClass" + | "kcFormSocialAccountListGridClass" + | "kcButtonDefaultClass" + | "kcFormGroupErrorClass" + | "kcSelectAuthListItemDescriptionClass" + | "kcSelectAuthListItemBodyClass" + | "kcWebAuthnInternal" + | "kcSelectAuthListItemArrowClass" + | "kcCheckClass" + | "kcContentClass" + | "kcLogoClass" + | "kcLoginOTPListItemIconClass" + | "kcLoginClass" + | "kcSignUpClass" + | "kcButtonLargeClass" + | "kcFormCardClass" + | "kcLocaleListClass" + | "kcInputClass" + | "kcFormGroupClass" + | "kcLogoIdP-paypal" + | "kcInputClassCheckbox" + | "kcRecoveryCodesConfirmation" + | "kcFormPasswordVisibilityIconHide" + | "kcInputClassRadioInput" + | "kcFormSocialAccountListButtonClass" + | "kcInputClassCheckboxLabel" + | "kcFormOptionsClass" + | "kcFormHeaderClass" + | "kcFormSocialAccountGridItem" + | "kcButtonPrimaryClass" + | "kcInputHelperTextBeforeClass" + | "kcLogoIdP-github" + | "kcLabelClass"; export const { getKcClsx } = createGetKcClsx({ defaultClasses: { @@ -138,6 +263,4 @@ export const { getKcClsx } = createGetKcClsx({ } }); -export type { ClassKey }; - export type KcClsx = ReturnType["kcClsx"]; From a0c95207cfed164215072b369ad00233cd9ea891 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Mon, 30 Sep 2024 01:10:45 +0200 Subject: [PATCH 03/77] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 42f474a3..f28b02d8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.2.0", + "version": "11.2.1", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From 8cacb21f1baf4086cde20975b4938b9816a80bde Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Mon, 30 Sep 2024 01:22:37 +0200 Subject: [PATCH 04/77] Remove unessesary reference to react specific construct in KcContext --- src/login/KcContext/KcContext.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/login/KcContext/KcContext.ts b/src/login/KcContext/KcContext.ts index 4b9c91e6..14d1169f 100644 --- a/src/login/KcContext/KcContext.ts +++ b/src/login/KcContext/KcContext.ts @@ -2,7 +2,7 @@ import type { ThemeType, LoginThemePageId } from "keycloakify/bin/shared/constan import type { ValueOf } from "keycloakify/tools/ValueOf"; import { assert } from "tsafe/assert"; import type { Equals } from "tsafe"; -import type { ClassKey } from "keycloakify/login/TemplateProps"; +import type { ClassKey } from "keycloakify/login/lib/kcClsx"; export type ExtendKcContext< KcContextExtension extends { properties?: Record }, From 47ca811878cbec21662229b0c638d7b0f0c4be7b Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Mon, 30 Sep 2024 01:22:49 +0200 Subject: [PATCH 05/77] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f28b02d8..ef9b65d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.2.1", + "version": "11.2.2", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From 9f8a36fe9361ba81d792c1e2b97a97a641872b4c Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Mon, 30 Sep 2024 11:48:57 +0200 Subject: [PATCH 06/77] Fix allegated vulnerability --- .../keycloakify/generateFtl/kcContextDeclarationTemplate.ftl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/keycloakify/generateFtl/kcContextDeclarationTemplate.ftl b/src/bin/keycloakify/generateFtl/kcContextDeclarationTemplate.ftl index 6b74955f..5cfc2755 100644 --- a/src/bin/keycloakify/generateFtl/kcContextDeclarationTemplate.ftl +++ b/src/bin/keycloakify/generateFtl/kcContextDeclarationTemplate.ftl @@ -166,7 +166,7 @@ function decodeHtmlEntities(htmlStr){ areSamePath(path, []) && ["login-idp-link-confirm.ftl", "login-idp-link-email.ftl" ]?seq_contains(xKeycloakify.pageId) ) || ( - ["masterAdminClient", "delegateForUpdate", "defaultRole"]?seq_contains(key) && + ["masterAdminClient", "delegateForUpdate", "defaultRole", "smtpConfig"]?seq_contains(key) && areSamePath(path, ["realm"]) ) || ( xKeycloakify.pageId == "error.ftl" && From d7103b1ad914eea6821f478e9a4b0bd2699505dd Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Mon, 30 Sep 2024 11:49:33 +0200 Subject: [PATCH 07/77] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ef9b65d9..880782d1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.2.2", + "version": "11.2.3", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From 2ea5e34e81b554024ed470b87fa366ab2ad773c0 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Mon, 30 Sep 2024 17:57:41 +0200 Subject: [PATCH 08/77] update ci --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 9fca0a23..6ec87504 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -112,7 +112,7 @@ jobs: registry-url: https://registry.npmjs.org/ - uses: bahmutov/npm-install@v1 - run: npm run build - - run: npx -y -p denoify@1.6.12 enable_short_npm_import_path + - run: npx -y -p denoify@1.6.13 enable_short_npm_import_path env: DRY_RUN: "0" - uses: garronej/ts-ci@v2.1.2 From 302fe8d7cde5529d84f7d6ff6736d854feeeed7c Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Mon, 30 Sep 2024 18:10:09 +0200 Subject: [PATCH 09/77] Update tsafe (provide ESM distribution) --- package.json | 2 +- yarn.lock | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 880782d1..4b2ad41f 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ ], "homepage": "https://www.keycloakify.dev", "dependencies": { - "tsafe": "^1.6.6" + "tsafe": "^1.7.4" }, "devDependencies": { "@babel/core": "^7.24.5", diff --git a/yarn.lock b/yarn.lock index abab9a2c..d05e77af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12579,6 +12579,11 @@ tsafe@^1.6.6: resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-1.6.6.tgz#fd93e64d6eb13ef83ed1650669cc24bad4f5df9f" integrity sha512-gzkapsdbMNwBnTIjgO758GujLCj031IgHK/PKr2mrmkCSJMhSOR5FeOuSxKLMUoYc0vAA4RGEYYbjt/v6afD3g== +tsafe@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-1.7.4.tgz#7dd288b1a1be8d9c25e84ab8dd8a7df6094168d7" + integrity sha512-4BrLklZMJ14dEtA+CkhY9OtID3al4+/GJhaeocWPtUuoZPr4SJkaqoPemyFgkLC1Y3LRNXF9zxa94SwssRGMaQ== + tsc-alias@^1.8.10: version "1.8.10" resolved "https://registry.yarnpkg.com/tsc-alias/-/tsc-alias-1.8.10.tgz#279f9bf0dd8bc10fb27820393d4881db5a303938" From 6716fcb88102cb1ee7d13eb3a2fa7e5d4c6594f4 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Mon, 30 Sep 2024 18:10:26 +0200 Subject: [PATCH 10/77] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4b2ad41f..2aac84f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.2.3", + "version": "11.2.4", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From 77952337c594e396b277a1057dfc8bf62cda0c92 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Tue, 1 Oct 2024 11:52:40 +0200 Subject: [PATCH 11/77] Avoid modifying BASE_URL for App context --- src/vite-plugin/vite-plugin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vite-plugin/vite-plugin.ts b/src/vite-plugin/vite-plugin.ts index 1ccfaf89..f22cb62a 100644 --- a/src/vite-plugin/vite-plugin.ts +++ b/src/vite-plugin/vite-plugin.ts @@ -166,7 +166,7 @@ export function keycloakify(params: keycloakify.Params) { [ `(`, `(window.kcContext === undefined || import.meta.env.MODE === "development")?`, - `"${urlPathname ?? "/"}":`, + `import.meta.env.BASE_URL:`, `(window.kcContext["x-keycloakify"].resourcesPath + "/${WELL_KNOWN_DIRECTORY_BASE_NAME.DIST}/")`, `)` ].join("") From 1177d6770ceb44e859955e1a32bf561cb96da10d Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Tue, 1 Oct 2024 11:59:39 +0200 Subject: [PATCH 12/77] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2aac84f8..760e7453 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.2.4", + "version": "11.2.5", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From 2e10ec80734c5ab7097518d23d00363c92b1500c Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 08:59:48 +0000 Subject: [PATCH 13/77] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ddc36c80..82e44eba 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Omid
Omid

⚠️ πŸ’» + Katharina Eiserfey
Katharina Eiserfey

πŸ’» ⚠️ πŸ“– From c21d072231ca0e5fe272ff861507caa5873ad8e7 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 08:59:49 +0000 Subject: [PATCH 14/77] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 864f118b..b6496bfb 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -269,6 +269,17 @@ "test", "code" ] + }, + { + "login": "kathari00", + "name": "Katharina Eiserfey", + "avatar_url": "https://avatars.githubusercontent.com/u/42547712?v=4", + "profile": "https://github.com/kathari00", + "contributions": [ + "code", + "test", + "doc" + ] } ], "contributorsPerLine": 7, From 939e3ca7ea8f6c542983451972c24968bd790aa0 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Wed, 2 Oct 2024 11:02:25 +0200 Subject: [PATCH 15/77] Put Kathi as first contributor --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 82e44eba..50c221ce 100644 --- a/README.md +++ b/README.md @@ -97,13 +97,13 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d + - @@ -134,7 +134,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d - +
Katharina Eiserfey
Katharina Eiserfey

πŸ’» ⚠️ πŸ“–
Waldemar Reusch
Waldemar Reusch

πŸ’»
William Will
William Will

πŸ’»
Bystrova Ann
Bystrova Ann

πŸ’»
Michael Kreuzmayr
Michael Kreuzmayr

πŸ’»
Mary
Mary

πŸ’»
German Γ–ΓΆ
German Γ–ΓΆ

πŸ’»
Julien Bouquillon
Julien Bouquillon

πŸ’»
Aidan Gilmore
Aidan Gilmore

πŸ’»
Omid
Omid

⚠️ πŸ’»
Katharina Eiserfey
Katharina Eiserfey

πŸ’» ⚠️ πŸ“–
Julien Bouquillon
Julien Bouquillon

πŸ’»
From 153a99d63ff0b9c7fdb72c60864b186b37c5a38b Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 09:05:34 +0000 Subject: [PATCH 16/77] docs: update README.md [skip ci] --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 50c221ce..78872df3 100644 --- a/README.md +++ b/README.md @@ -97,13 +97,13 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d - + @@ -134,7 +134,8 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d - + +
Katharina Eiserfey
Katharina Eiserfey

πŸ’» ⚠️ πŸ“–
Waldemar Reusch
Waldemar Reusch

πŸ’»
William Will
William Will

πŸ’»
Bystrova Ann
Bystrova Ann

πŸ’»
Michael Kreuzmayr
Michael Kreuzmayr

πŸ’»
Mary
Mary

πŸ’»
German Γ–ΓΆ
German Γ–ΓΆ

πŸ’»
Julien Bouquillon
Julien Bouquillon

πŸ’»
Aidan Gilmore
Aidan Gilmore

πŸ’»
Omid
Omid

⚠️ πŸ’»
Julien Bouquillon
Julien Bouquillon

πŸ’»
Katharina Eiserfey
Katharina Eiserfey

πŸ’» ⚠️ πŸ“–
Luca Peruzzo
Luca Peruzzo

πŸ’» ⚠️
From 8c8d2fd6a8b0861b9ee74b6865a21ff3ad2c205d Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 09:05:35 +0000 Subject: [PATCH 17/77] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index b6496bfb..fdede216 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -280,6 +280,16 @@ "test", "doc" ] + }, + { + "login": "luca-peruzzo", + "name": "Luca Peruzzo", + "avatar_url": "https://avatars.githubusercontent.com/u/69015314?v=4", + "profile": "https://github.com/luca-peruzzo", + "contributions": [ + "code", + "test" + ] } ], "contributorsPerLine": 7, From 09abc73068c8b66ed86d434bca9ca5c2c5eff8bd Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Wed, 2 Oct 2024 13:42:38 +0200 Subject: [PATCH 18/77] Update tsafe --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 760e7453..14a5622b 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ ], "homepage": "https://www.keycloakify.dev", "dependencies": { - "tsafe": "^1.7.4" + "tsafe": "^1.7.5" }, "devDependencies": { "@babel/core": "^7.24.5", diff --git a/yarn.lock b/yarn.lock index d05e77af..c8b469f3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12579,10 +12579,10 @@ tsafe@^1.6.6: resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-1.6.6.tgz#fd93e64d6eb13ef83ed1650669cc24bad4f5df9f" integrity sha512-gzkapsdbMNwBnTIjgO758GujLCj031IgHK/PKr2mrmkCSJMhSOR5FeOuSxKLMUoYc0vAA4RGEYYbjt/v6afD3g== -tsafe@^1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-1.7.4.tgz#7dd288b1a1be8d9c25e84ab8dd8a7df6094168d7" - integrity sha512-4BrLklZMJ14dEtA+CkhY9OtID3al4+/GJhaeocWPtUuoZPr4SJkaqoPemyFgkLC1Y3LRNXF9zxa94SwssRGMaQ== +tsafe@^1.7.5: + version "1.7.5" + resolved "https://registry.yarnpkg.com/tsafe/-/tsafe-1.7.5.tgz#0d3a31202b5ef87c7ba997e66e03fd80801278ef" + integrity sha512-tbNyyBSbwfbilFfiuXkSOj82a6++ovgANwcoqBAcO9/REPoZMEQoE8kWPeO0dy5A2D/2Lajr8Ohue5T0ifIvLQ== tsc-alias@^1.8.10: version "1.8.10" From ce3135c83be2b6d2cbc4a97313c6b84b0780353b Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Wed, 2 Oct 2024 13:44:22 +0200 Subject: [PATCH 19/77] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 14a5622b..e6c02daa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.2.5", + "version": "11.2.6", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From 80eaa77acc26a5196e487bb81ce9136d42331653 Mon Sep 17 00:00:00 2001 From: johanjk Date: Wed, 2 Oct 2024 16:16:16 +0200 Subject: [PATCH 20/77] ['select-radiobuttons'/'multiselect-checkboxes'] fixed 'inputOptionLabels' --- src/login/UserProfileFormFields.tsx | 38 ++++++++++++------------ stories/login/pages/Register.stories.tsx | 32 ++++++++++++++++++++ 2 files changed, 51 insertions(+), 19 deletions(-) diff --git a/src/login/UserProfileFormFields.tsx b/src/login/UserProfileFormFields.tsx index 445bda6f..93a99ad7 100644 --- a/src/login/UserProfileFormFields.tsx +++ b/src/login/UserProfileFormFields.tsx @@ -434,9 +434,7 @@ function AddRemoveButtonsMultiValuedAttribute(props: { } function InputTagSelects(props: InputFieldByTypeProps) { - const { attribute, dispatchFormAction, kcClsx, valueOrValues } = props; - - const { advancedMsg } = props.i18n; + const { attribute, dispatchFormAction, kcClsx, i18n, valueOrValues } = props; const { classDiv, classInput, classLabel, inputType } = (() => { const { inputType } = attribute.annotations; @@ -533,7 +531,7 @@ function InputTagSelects(props: InputFieldByTypeProps) { htmlFor={`${attribute.name}-${option}`} className={`${classLabel}${attribute.readOnly ? ` ${kcClsx("kcInputClassRadioCheckboxLabelDisabled")}` : ""}`} > - {advancedMsg(option)} + {inputLabel(i18n, attribute, option)} ))} @@ -580,8 +578,6 @@ function TextareaTag(props: InputFieldByTypeProps) { function SelectTag(props: InputFieldByTypeProps) { const { attribute, dispatchFormAction, kcClsx, displayableErrors, i18n, valueOrValues } = props; - const { advancedMsgStr } = i18n; - const isMultiple = attribute.annotations.inputType === "multiselect"; return ( @@ -645,22 +641,26 @@ function SelectTag(props: InputFieldByTypeProps) { return options.map(option => ( )); })()} ); } + +function inputLabel(i18n: I18n, attribute: Attribute, option: string) { + const { advancedMsg } = i18n; + + if (attribute.annotations.inputOptionLabels !== undefined) { + const { inputOptionLabels } = attribute.annotations; + + return advancedMsg(inputOptionLabels[option] ?? option); + } + + if (attribute.annotations.inputOptionLabelsI18nPrefix !== undefined) { + return advancedMsg(`${attribute.annotations.inputOptionLabelsI18nPrefix}.${option}`); + } + + return option; +} diff --git a/stories/login/pages/Register.stories.tsx b/stories/login/pages/Register.stories.tsx index 0a8c395f..f25568cc 100644 --- a/stories/login/pages/Register.stories.tsx +++ b/stories/login/pages/Register.stories.tsx @@ -115,6 +115,38 @@ export const WithFavoritePet: Story = { ) }; + +export const WithNewsletter: Story = { + render: () => ( + + ) +}; + + export const WithEmailAsUsername: Story = { render: () => ( Date: Wed, 2 Oct 2024 23:36:58 +0200 Subject: [PATCH 21/77] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e6c02daa..ff7fbe2a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.2.6", + "version": "11.2.8", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From dd217e8a469a625f1cd66a1fdca719ee9882d72e Mon Sep 17 00:00:00 2001 From: pnzrr <93841792+pnzrr@users.noreply.github.com> Date: Thu, 3 Oct 2024 21:04:02 -0600 Subject: [PATCH 22/77] Fix link in CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 733042ec..25b079a7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,3 +1,3 @@ Looking to contribute? Thank you! PR are more than welcome. -Please refers to [this documentation page](https://docs.keycloakify.dev/contributing) that will help you get started. +Please refers to [this documentation page](https://docs.keycloakify.dev/faq-and-help/contributing) that will help you get started. From 39ad1eb8d13717c063133110fbd90c0bb88c8e38 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Fri, 4 Oct 2024 12:17:08 +0200 Subject: [PATCH 23/77] Update version target range --- .../getKeycloakVersionRangeForJar.ts | 4 +- src/bin/shared/KeycloakVersionRange.ts | 2 +- src/bin/shared/buildContext.ts | 36 +- src/bin/start-keycloak/myrealm-realm-26.json | 2400 +++++++++++++++++ 4 files changed, 2431 insertions(+), 11 deletions(-) create mode 100644 src/bin/start-keycloak/myrealm-realm-26.json diff --git a/src/bin/keycloakify/buildJars/getKeycloakVersionRangeForJar.ts b/src/bin/keycloakify/buildJars/getKeycloakVersionRangeForJar.ts index 3999ce8f..6c301223 100644 --- a/src/bin/keycloakify/buildJars/getKeycloakVersionRangeForJar.ts +++ b/src/bin/keycloakify/buildJars/getKeycloakVersionRangeForJar.ts @@ -75,9 +75,9 @@ export function getKeycloakVersionRangeForJar(params: { } switch (keycloakThemeAdditionalInfoExtensionVersion) { case null: - return "21-and-below"; + return "all-other-versions"; case "1.1.5": - return "22-and-above"; + return "22-to-25"; } assert>( false diff --git a/src/bin/shared/KeycloakVersionRange.ts b/src/bin/shared/KeycloakVersionRange.ts index 2827d34e..1e118ea1 100644 --- a/src/bin/shared/KeycloakVersionRange.ts +++ b/src/bin/shared/KeycloakVersionRange.ts @@ -3,7 +3,7 @@ export type KeycloakVersionRange = | KeycloakVersionRange.WithoutAccountV1Theme; export namespace KeycloakVersionRange { - export type WithoutAccountV1Theme = "21-and-below" | "22-and-above"; + export type WithoutAccountV1Theme = "22-to-25" | "all-other-versions"; export type WithAccountV1Theme = "21-and-below" | "23" | "24" | "25-and-above"; } diff --git a/src/bin/shared/buildContext.ts b/src/bin/shared/buildContext.ts index b4a998b7..852bd1fd 100644 --- a/src/bin/shared/buildContext.ts +++ b/src/bin/shared/buildContext.ts @@ -25,6 +25,7 @@ import { type ThemeType } from "./constants"; import { id } from "tsafe/id"; import chalk from "chalk"; import { getProxyFetchOptions, type ProxyFetchOptions } from "../tools/fetchProxyOptions"; +import { is } from "tsafe/is"; export type BuildContext = { themeVersion: string; @@ -297,8 +298,8 @@ export function getBuildContext(params: { ]), keycloakVersionTargets: z .object({ - "21-and-below": z.union([z.boolean(), z.string()]), - "22-and-above": z.union([z.boolean(), z.string()]) + "22-to-25": z.union([z.boolean(), z.string()]), + "all-other-versions": z.union([z.boolean(), z.string()]) }) .optional() }); @@ -779,11 +780,14 @@ export function getBuildContext(params: { return keycloakVersionRange; } else { const keycloakVersionRange = (() => { - if (buildForKeycloakMajorVersionNumber <= 21) { - return "21-and-below" as const; + if ( + buildForKeycloakMajorVersionNumber <= 21 || + buildForKeycloakMajorVersionNumber >= 26 + ) { + return "all-other-versions" as const; } - return "22-and-above" as const; + return "22-to-25" as const; })(); assert< @@ -801,6 +805,12 @@ export function getBuildContext(params: { use_custom_jar_basename: { const { keycloakVersionTargets } = buildOptions; + assert( + is>( + keycloakVersionTargets + ) + ); + if (keycloakVersionTargets === undefined) { break use_custom_jar_basename; } @@ -861,8 +871,8 @@ export function getBuildContext(params: { } } else { for (const keycloakVersionRange of [ - "21-and-below", - "22-and-above" + "22-to-25", + "all-other-versions" ] as const) { assert< Equals< @@ -888,7 +898,17 @@ export function getBuildContext(params: { const jarTargets: BuildContext["jarTargets"] = []; for (const [keycloakVersionRange, jarNameOrBoolean] of objectEntries( - buildOptions.keycloakVersionTargets + (() => { + const { keycloakVersionTargets } = buildOptions; + + assert( + is>( + keycloakVersionTargets + ) + ); + + return keycloakVersionTargets; + })() )) { if (jarNameOrBoolean === false) { continue; diff --git a/src/bin/start-keycloak/myrealm-realm-26.json b/src/bin/start-keycloak/myrealm-realm-26.json new file mode 100644 index 00000000..21b95023 --- /dev/null +++ b/src/bin/start-keycloak/myrealm-realm-26.json @@ -0,0 +1,2400 @@ +{ + "id": "5d0dd960-0478-4ca6-b64a-810a3f6f4071", + "realm": "myrealm", + "notBefore": 0, + "defaultSignatureAlgorithm": "RS256", + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 300, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "oauth2DeviceCodeLifespan": 600, + "oauth2DevicePollingInterval": 5, + "enabled": true, + "sslRequired": "external", + "registrationAllowed": true, + "registrationEmailAsUsername": false, + "rememberMe": true, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": true, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxTemporaryLockouts": 0, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "roles": { + "realm": [ + { + "id": "cc4b5045-3bff-4aa7-889e-1492630c3002", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "5d0dd960-0478-4ca6-b64a-810a3f6f4071", + "attributes": {} + }, + { + "id": "e92017b2-18a0-49cd-956c-fad64f16b26b", + "name": "default-roles-myrealm", + "description": "${role_default-roles}", + "composite": true, + "composites": { + "realm": ["offline_access", "uma_authorization"], + "client": { + "account": ["delete-account", "manage-account", "view-profile"] + } + }, + "clientRole": false, + "containerId": "5d0dd960-0478-4ca6-b64a-810a3f6f4071", + "attributes": {} + }, + { + "id": "e8616113-e302-4abe-bd5c-d51f8221046b", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "5d0dd960-0478-4ca6-b64a-810a3f6f4071", + "attributes": {} + } + ], + "client": { + "myclient": [], + "realm-management": [ + { + "id": "b27b272d-d153-4ae7-9fe7-fd96582f057d", + "name": "manage-events", + "description": "${role_manage-events}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "40fdfec8-f1b9-4c2b-81c5-a775bc047840", + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "5f446f9a-d008-4067-8325-f4658a32d964", + "name": "view-authorization", + "description": "${role_view-authorization}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "82bf956d-1fd1-4d20-a5a9-62b3e77e9d88", + "name": "create-client", + "description": "${role_create-client}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "b41e1ce8-d63f-4cf4-9966-e6c9eab5da11", + "name": "manage-clients", + "description": "${role_manage-clients}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "3198743d-fdfa-4a9c-a229-5fb979847ec2", + "name": "view-users", + "description": "${role_view-users}", + "composite": true, + "composites": { + "client": { + "realm-management": ["query-users", "query-groups"] + } + }, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "e83c21cb-c84c-4824-9f7d-ce3574921800", + "name": "query-users", + "description": "${role_query-users}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "3f6e2e81-e40d-40ff-a5f3-12ba2614fba5", + "name": "query-groups", + "description": "${role_query-groups}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "63111288-7f3d-4570-838f-48405d70e212", + "name": "view-realm", + "description": "${role_view-realm}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "a7f8f8ad-057b-485e-abfa-8a98e5e0c4ea", + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "7783b160-2f1a-48c9-89fb-623a29f26c9a", + "name": "query-realms", + "description": "${role_query-realms}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "b8b5341f-f44f-40a2-9ba4-e2d621b11b2f", + "name": "impersonation", + "description": "${role_impersonation}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "6b9d72e9-949f-4897-b11a-c8aa9252f3f2", + "name": "query-clients", + "description": "${role_query-clients}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "bfa94ba9-1d70-4259-b928-906e8bb815b2", + "name": "view-events", + "description": "${role_view-events}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "96bb9322-5c1f-48f0-aa05-65521c77e742", + "name": "realm-admin", + "description": "${role_realm-admin}", + "composite": true, + "composites": { + "client": { + "realm-management": [ + "manage-users", + "view-authorization", + "manage-events", + "create-client", + "view-users", + "manage-clients", + "query-users", + "query-groups", + "view-realm", + "manage-realm", + "query-realms", + "query-clients", + "impersonation", + "view-events", + "manage-authorization", + "manage-identity-providers", + "view-identity-providers", + "view-clients" + ] + } + }, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "6e0ca5ce-f5db-4580-90e5-27c35804fc34", + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "7499eb46-cf4a-4813-9bf9-42b1bbcadc0d", + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "fcc99ef9-347d-4c21-b25c-8229e906a1a3", + "name": "view-clients", + "description": "${role_view-clients}", + "composite": true, + "composites": { + "client": { + "realm-management": ["query-clients"] + } + }, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + }, + { + "id": "7b024069-57d8-4368-9942-8790507c156d", + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "attributes": {} + } + ], + "security-admin-console": [], + "admin-cli": [], + "account-console": [], + "broker": [ + { + "id": "3050eb8a-9a47-4a27-aece-be2e60fc7f73", + "name": "read-token", + "description": "${role_read-token}", + "composite": false, + "clientRole": true, + "containerId": "f5e032da-c8ab-48c2-959c-8466ad1e6a09", + "attributes": {} + } + ], + "account": [ + { + "id": "d554d15b-d098-47a0-bdd5-d656b20f5643", + "name": "delete-account", + "description": "${role_delete-account}", + "composite": false, + "clientRole": true, + "containerId": "7221ef76-9d96-49ad-88a6-9f72eeeb0aa7", + "attributes": {} + }, + { + "id": "aaf4946d-2cd4-43ba-ad7d-86be56b9ad2c", + "name": "view-applications", + "description": "${role_view-applications}", + "composite": false, + "clientRole": true, + "containerId": "7221ef76-9d96-49ad-88a6-9f72eeeb0aa7", + "attributes": {} + }, + { + "id": "b417b187-18b7-41fa-9537-3313cf9b8ed4", + "name": "manage-account", + "description": "${role_manage-account}", + "composite": true, + "composites": { + "client": { + "account": ["manage-account-links"] + } + }, + "clientRole": true, + "containerId": "7221ef76-9d96-49ad-88a6-9f72eeeb0aa7", + "attributes": {} + }, + { + "id": "8bb5480d-83a3-4ea2-8e91-237b8870acec", + "name": "view-consent", + "description": "${role_view-consent}", + "composite": false, + "clientRole": true, + "containerId": "7221ef76-9d96-49ad-88a6-9f72eeeb0aa7", + "attributes": {} + }, + { + "id": "e341c1b8-eaf7-467d-9986-d3f2356a60b9", + "name": "view-profile", + "description": "${role_view-profile}", + "composite": false, + "clientRole": true, + "containerId": "7221ef76-9d96-49ad-88a6-9f72eeeb0aa7", + "attributes": {} + }, + { + "id": "98ccac20-3906-436f-8dc3-ae8d8ae25cbc", + "name": "view-groups", + "description": "${role_view-groups}", + "composite": false, + "clientRole": true, + "containerId": "7221ef76-9d96-49ad-88a6-9f72eeeb0aa7", + "attributes": {} + }, + { + "id": "adfba539-826f-4fa7-86f5-8c1287152ed6", + "name": "manage-account-links", + "description": "${role_manage-account-links}", + "composite": false, + "clientRole": true, + "containerId": "7221ef76-9d96-49ad-88a6-9f72eeeb0aa7", + "attributes": {} + }, + { + "id": "2516ab58-490c-444c-9e7d-0dd8b87a69f0", + "name": "manage-consent", + "description": "${role_manage-consent}", + "composite": true, + "composites": { + "client": { + "account": ["view-consent"] + } + }, + "clientRole": true, + "containerId": "7221ef76-9d96-49ad-88a6-9f72eeeb0aa7", + "attributes": {} + } + ] + } + }, + "groups": [], + "defaultRole": { + "id": "e92017b2-18a0-49cd-956c-fad64f16b26b", + "name": "default-roles-myrealm", + "description": "${role_default-roles}", + "composite": true, + "clientRole": false, + "containerId": "5d0dd960-0478-4ca6-b64a-810a3f6f4071" + }, + "requiredCredentials": ["password"], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpPolicyCodeReusable": false, + "otpSupportedApplications": [ + "totpAppFreeOTPName", + "totpAppGoogleName", + "totpAppMicrosoftAuthenticatorName" + ], + "localizationTexts": { + "de": { + "profile.attributes.favourite_pet": "" + }, + "no": { + "profile.attributes.favourite_pet": "" + }, + "fi": { + "profile.attributes.favourite_pet": "" + }, + "ru": { + "profile.attributes.favourite_pet": "" + }, + "pt": { + "profile.attributes.favourite_pet": "" + }, + "lt": { + "profile.attributes.favourite_pet": "" + }, + "lv": { + "profile.attributes.favourite_pet": "" + }, + "fr": { + "profile.attributes.favourite_pet": "Animal de compagnie prΓ©fΓ©rΓ©", + "profile.attributes.favourite_pet.cat": "Chat", + "profile.attributes.favourite_pet.dog": "Chien", + "profile.attributes.favourite_pet.bird": "Oiseau" + }, + "hu": { + "profile.attributes.favourite_pet": "" + }, + "zh-CN": { + "profile.attributes.favourite_pet": "" + }, + "uk": { + "profile.attributes.favourite_pet": "" + }, + "sk": { + "profile.attributes.favourite_pet": "" + }, + "ca": { + "profile.attributes.favourite_pet": "" + }, + "sv": { + "profile.attributes.favourite_pet": "" + }, + "zh-TW": { + "profile.attributes.favourite_pet": "" + }, + "pt-BR": { + "profile.attributes.favourite_pet": "" + }, + "en": { + "profile.attributes.favourite_pet": "Favourite Pet", + "profile.attributes.favourite_pet.cat": "Cat", + "profile.attributes.favourite_pet.dog": "Dog", + "profile.attributes.favourite_pet.bird": "Bird" + }, + "it": { + "profile.attributes.favourite_pet": "" + }, + "es": { + "profile.attributes.favourite_pet": "Mascota favorita", + "profile.attributes.favourite_pet.cat": "Gato", + "profile.attributes.favourite_pet.dog": "Perro", + "profile.attributes.favourite_pet.bird": "PΓ‘jaro" + }, + "cs": { + "profile.attributes.favourite_pet": "" + }, + "ar": { + "profile.attributes.favourite_pet": "" + }, + "th": { + "profile.attributes.favourite_pet": "" + }, + "ja": { + "profile.attributes.favourite_pet": "" + }, + "fa": { + "profile.attributes.favourite_pet": "" + }, + "pl": { + "profile.attributes.favourite_pet": "" + }, + "da": { + "profile.attributes.favourite_pet": "" + }, + "nl": { + "profile.attributes.favourite_pet": "" + }, + "tr": { + "profile.attributes.favourite_pet": "" + } + }, + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": ["ES256"], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyExtraOrigins": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": ["ES256"], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "webAuthnPolicyPasswordlessExtraOrigins": [], + "users": [ + { + "id": "d93e1772-4916-4243-850f-a6d9b2615716", + "username": "testuser", + "firstName": "Test", + "lastName": "User", + "email": "testuser@gmail.com", + "emailVerified": true, + "attributes": { + "additional_emails": ["test.user@protonmail.com", "testuser@hotmail.com"], + "gender": ["prefer_not_to_say"], + "favorite_pet": ["cats"], + "favourite_pet": ["cat"], + "bio": ["Hello I'm Test User and I do not exist."], + "phone_number": ["1111111111"], + "locale": ["en"], + "favorite_media": ["movies", "series"] + }, + "createdTimestamp": 1716183898408, + "enabled": true, + "totp": false, + "credentials": [ + { + "id": "576982e2-6fb3-4752-8724-5ff390ea8301", + "type": "password", + "userLabel": "My password", + "createdDate": 1716183916529, + "secretData": "{\"value\":\"9hwJ989FAr0UgT0MfffNYSI6Zf/3qT/y17DTUcwbiEM=\",\"salt\":\"C3ZnHzgPd+0Lemw4olCOgA==\",\"additionalParameters\":{}}", + "credentialData": "{\"hashIterations\":5,\"algorithm\":\"argon2\",\"additionalParameters\":{\"hashLength\":[\"32\"],\"memory\":[\"7168\"],\"type\":[\"id\"],\"version\":[\"1.3\"],\"parallelism\":[\"1\"]}}" + } + ], + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": ["default-roles-myrealm"], + "notBefore": 0, + "groups": [] + } + ], + "scopeMappings": [ + { + "clientScope": "offline_access", + "roles": ["offline_access"] + } + ], + "clientScopeMappings": { + "account": [ + { + "client": "account-console", + "roles": ["manage-account", "view-groups"] + } + ] + }, + "clients": [ + { + "id": "7221ef76-9d96-49ad-88a6-9f72eeeb0aa7", + "clientId": "account", + "name": "${client_account}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/myrealm/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": ["/realms/myrealm/account/*"], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "d8f14dc4-5f0f-4a1d-8c0b-cfe78ee55cb3", + "clientId": "account-console", + "name": "${client_account-console}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/myrealm/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": ["/realms/myrealm/account/*"], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+", + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "08d7bc08-2ff3-44ea-9d65-fa1c4ca35646", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "953c597f-faef-4abc-88dc-4fbc9501170c", + "clientId": "admin-cli", + "name": "${client_admin-cli}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "f5e032da-c8ab-48c2-959c-8466ad1e6a09", + "clientId": "broker", + "name": "${client_broker}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "8fba88fa-61e9-45a4-893d-ab102973ebf6", + "clientId": "myclient", + "name": "", + "description": "", + "rootUrl": "https://my-theme.keycloakify.dev", + "adminUrl": "https://my-theme.keycloakify.dev", + "baseUrl": "https://my-theme.keycloakify.dev", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [ + "https://my-theme.keycloakify.dev/*", + "http://localhost*", + "http://127.0.0.1*" + ], + "webOrigins": ["*"], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": true, + "protocol": "openid-connect", + "attributes": { + "oidc.ciba.grant.enabled": "false", + "backchannel.logout.session.required": "true", + "login_theme": "keycloakify-starter", + "post.logout.redirect.uris": "https://my-theme.keycloakify.dev/*##http://localhost*##http://127.0.0.1*", + "oauth2.device.authorization.grant.enabled": "false", + "display.on.consent.screen": "false", + "backchannel.logout.revoke.offline.tokens": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "id": "91a196c1-f93c-48a5-aced-b8d60fb09b62", + "name": "Favourite Pet", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "favourite_pet", + "id.token.claim": "true", + "lightweight.claim": "false", + "access.token.claim": "true", + "claim.name": "favourite_pet", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "e05cc68c-5e53-4796-ae3a-a1bfbf5c51bb", + "clientId": "realm-management", + "name": "${client_realm-management}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + }, + { + "id": "fce8a109-6f32-4814-9a20-2ff2435d2da6", + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "rootUrl": "${authAdminUrl}", + "baseUrl": "/admin/myrealm/console/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "redirectUris": ["/admin/myrealm/console/*"], + "webOrigins": ["+"], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "post.logout.redirect.uris": "+", + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "52192d19-0406-41b7-b995-b099bdbaa448", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "profile", + "roles", + "basic", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] + } + ], + "clientScopes": [ + { + "id": "6a955b1e-f0e2-49fa-b3c9-bd59ed1fcd4f", + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "consent.screen.text": "", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "3a392f70-ed70-424a-b60b-82db32b83df8", + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "access.token.claim": "true" + } + } + ] + }, + { + "id": "9cda058d-9935-4c8b-844d-c163d10f7c3c", + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${addressScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "a053d8ec-b267-4e5a-a424-3b14bef9cd15", + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "introspection.token.claim": "true", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "id": "6225f4c7-ad5c-42ea-b7d4-5bb4e7c77459", + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${phoneScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "5052be82-243f-41b0-a214-4f01935180e5", + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + }, + { + "id": "4d31d278-e6ef-4b8b-97cb-4da9626d0e93", + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "id": "9357440c-6200-41a1-a447-0ec97895763e", + "name": "basic", + "description": "OpenID Connect scope for add all basic claims to the token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "bf9cb6c6-71a4-4bf9-8c60-ed58adcc2258", + "name": "auth_time", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "AUTH_TIME", + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "auth_time", + "jsonType.label": "long" + } + }, + { + "id": "679c8292-1abb-4d96-bacc-671303765f9b", + "name": "sub", + "protocol": "openid-connect", + "protocolMapper": "oidc-sub-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "access.token.claim": "true" + } + } + ] + }, + { + "id": "0ec225e7-253b-4a01-85e1-68daf3df3eba", + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "a55cf74e-ce68-4ebd-9c24-dc3fd6a9cfa5", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "id": "e2f1dd86-00a2-4374-b888-7211f748c58d", + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "id": "e86456b8-0663-448e-ad16-7d520d0c448e", + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${profileScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "569c799d-79f2-4b2b-a1ec-3661e3d8d433", + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "id": "2d01eb48-77c3-4c83-a864-755699cb7e7c", + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "long" + } + }, + { + "id": "a9700270-006f-4a85-8458-f39644659029", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "id": "3a7bca96-0839-4d1e-b37d-6e624f37facb", + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "id": "2a41be1c-872a-4b3e-9051-71ebd5d140c1", + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "id": "9fe5e57d-ee79-4b8b-9ab2-345093a1fdbf", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "bda9e4e7-4de0-455d-bace-4e94b1dab5ad", + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "id": "312a0b4d-46b8-42e0-b162-e5869b317b36", + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + }, + { + "id": "4f8ac9bc-e32d-4ebb-bb85-b9a94a459aa1", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "bebdf0c7-6f0f-4b08-a327-50af837c82b9", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "d96d9686-f4e0-479a-9855-cfc526a35294", + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + }, + { + "id": "66ad8239-e1df-4f9d-9cb7-d35f23f95f37", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "ece8245b-16ae-4322-bc78-f8d5f671640a", + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "id": "384cf049-0fed-47e2-8b11-06cf6c03465d", + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "49e85de9-edd1-4a9e-a2b0-e9c663d4dd9a", + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "consent.screen.text": "${emailScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "d458e6fc-b414-4b45-b9e1-99342d7d2bba", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "id": "2b73ce63-0443-46dc-b35c-1148edb976ab", + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "id": "71303f6d-348a-4892-9d6f-dc9a2d2e4b14", + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "498cbff6-a650-4a09-8192-5defaa50f33b", + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + }, + { + "id": "eb8585bc-ca30-410e-9f92-0d63665f5ed6", + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "multivalued": "true", + "userinfo.token.claim": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "62b8c264-2c10-48c6-803f-b7606a89e0d9", + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "consent.screen.text": "${rolesScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "0c18ca55-df63-4071-81f9-43f5d077c015", + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "id": "6de6510d-d7f3-4289-a10f-4c21289313a4", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": { + "introspection.token.claim": "true", + "access.token.claim": "true" + } + }, + { + "id": "a5851eb2-bfc5-4a0a-8a49-92f4fc8c5041", + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "introspection.token.claim": "true", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String", + "multivalued": "true" + } + } + ] + }, + { + "id": "bfc69775-83af-4816-82fd-d1c42687fb5e", + "name": "acr", + "description": "OpenID Connect scope for add acr (authentication context class reference) to the token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "8e2027d5-32dd-4a87-a7ec-00e5316c5617", + "name": "acr loa level", + "protocol": "openid-connect", + "protocolMapper": "oidc-acr-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "introspection.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + } + ] + } + ], + "defaultDefaultClientScopes": [ + "role_list", + "profile", + "email", + "roles", + "web-origins", + "acr", + "basic" + ], + "defaultOptionalClientScopes": [ + "offline_access", + "address", + "phone", + "microprofile-jwt" + ], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "referrerPolicy": "no-referrer", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection": "1; mode=block", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "loginTheme": "keycloakify-starter", + "accountTheme": "keycloakify-starter", + "adminTheme": "", + "emailTheme": "", + "eventsEnabled": false, + "eventsListeners": ["jboss-logging"], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "identityProviders": [], + "identityProviderMappers": [], + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "id": "67526992-f0ce-42ff-a0fb-af267192ff70", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": ["true"] + } + }, + { + "id": "64a2f718-da10-45d9-a75a-69c156a7ccd8", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-full-name-mapper", + "oidc-usermodel-attribute-mapper", + "oidc-address-mapper", + "saml-user-attribute-mapper", + "oidc-usermodel-property-mapper", + "saml-user-property-mapper", + "saml-role-list-mapper", + "oidc-sha256-pairwise-sub-mapper" + ] + } + }, + { + "id": "4d3e104f-6fdf-45eb-b756-5fef6840fbed", + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "c647e85f-6700-4d66-84f2-4a869e467735", + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": ["200"] + } + }, + { + "id": "51f41974-f7e5-4e7d-b486-5bd652a98e93", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-sha256-pairwise-sub-mapper", + "oidc-usermodel-property-mapper", + "oidc-address-mapper", + "oidc-usermodel-attribute-mapper", + "oidc-full-name-mapper", + "saml-user-attribute-mapper", + "saml-user-property-mapper", + "saml-role-list-mapper" + ] + } + }, + { + "id": "8f7d6ece-e956-4e48-95ab-5ab72b2b7c9a", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": ["true"] + } + }, + { + "id": "e60b1167-cdee-4173-be99-3dad6a536b4a", + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": ["true"], + "client-uris-must-match": ["true"] + } + }, + { + "id": "5ba8b893-ab01-430b-9092-32646a50a662", + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + } + ], + "org.keycloak.userprofile.UserProfileProvider": [ + { + "id": "237022c6-9443-46b3-902e-210e14c3c9a8", + "providerId": "declarative-user-profile", + "subComponents": {}, + "config": { + "kc.user.profile.config": [ + "{\"attributes\":[{\"name\":\"username\",\"displayName\":\"${username}\",\"validations\":{\"length\":{\"min\":3,\"max\":255},\"username-prohibited-characters\":{},\"up-username-not-idn-homograph\":{}},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"email\",\"displayName\":\"${email}\",\"validations\":{\"email\":{},\"length\":{\"max\":255}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"firstName\",\"displayName\":\"${firstName}\",\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"lastName\",\"displayName\":\"${lastName}\",\"validations\":{\"length\":{\"max\":255},\"person-name-prohibited-characters\":{}},\"required\":{\"roles\":[\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false},{\"name\":\"favourite_pet\",\"displayName\":\"${profile.attributes.favourite_pet}\",\"validations\":{\"options\":{\"options\":[\"cat\",\"dog\",\"bird\"]}},\"annotations\":{\"inputType\":\"select\",\"inputOptionLabelsI18nPrefix\":\"profile.attributes.favourite_pet\"},\"required\":{\"roles\":[\"admin\",\"user\"]},\"permissions\":{\"view\":[\"admin\",\"user\"],\"edit\":[\"admin\",\"user\"]},\"multivalued\":false}],\"groups\":[{\"name\":\"user-metadata\",\"displayHeader\":\"User metadata\",\"displayDescription\":\"Attributes, which refer to user metadata\"}]}" + ] + } + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "id": "5f3c1765-8810-419f-9c18-4a2db0e874e7", + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "privateKey": [ + "MIIEowIBAAKCAQEAsYUWzVfZMd6ywpBmLJYeF1U9Mgd/z3xWvl1Yq76oRPPfpcqQitN+cktWqu0hPerCVSl2ltwXDMrUwFzswG9MiM9hb+BLEld7kYiYkcFNt3lCtmmeRQEae7JwWimzeNV96Qlz0tHY8f9Zh0ffPDsLTN1HGAeRJJhI7mNQm6qCJNMCfVA/O5SWumsIn2XLnSMiQ05AACVHOLUq6rAZ2zCCaYmXTmJkuSOb8e26V303P6l63DSe5HSNXDdI00tjfFFf37q870zhvfsotrjjx0RMijy9Kjj8OZF+pFHpDRaGEi8tpQxZDnCTofTieB/Vp3QP+aTlvAyD3Q1ZnJxGQCLygwIDAQABAoIBABUJ9XMJGNQzamiVwuOWN7ht4UP8ezYvgdEA8NaLUO0PIYVIKyD7l4OwkHPPM9PfRACM2qG0MZp8sCyg4WxIeepy+D979oRqJYUmNRLSipqWlASuItRXIPjiY99uYXdjh2R8Os5pvCD+MZxPX9KHGuaVXmzSJMO7YAAPeYkMHcLYTp/U0c65Ztaaz1zz1FeyvpjkLr9SHiMcIN51zFmhvT1tcRIqy4zidisjrTSUr/KPVxeJtrEfyhTGk3z41yJf5YbeaxaMjJR5x0WXzt1fWVmA/V1bWa2Zlj9d8AxDReA1p7Lpstz34PRoCMj9bmFguI2+RTw6K0D++Jydfxmh8vUCgYEA5Zwk2r3TFO3i3V70LOn6CLzn15yLeuSIJ9p2os70jQOmFMCreLdcUbCaiUe7UV/IIVftbcxhFm9zECXZXX0wubcmHZqyptlbuAn1de4QkLJixXo1A7ZQXBEZk22WN2naXHQF5oK6lh/VSLcZBajTsyvBm5JWXrd8djjG06MugA8CgYEAxexKI5IwcLhpMDV9UPQb/+lDWHVqCT2xwYxnZ85y+5gmrOyyT7mIChz3DFYiaw4CHJWmBkIDBaiDgLEgQk4QXWzYshXawShBHnv1h08bVMMw98Ivec7ZRkV+/ET30YRwC2Uyk4bm4HpwVV5GCFhC4aAvRcCA1CIJk3MwcOwksk0CgYEAqxyaOomMbOR7VQ4WWgJkW26sOHppV8RH06tzDhG9HfnCI2USZHwBSL+b6wKSDiqbMn4cat8M23NjBH2wZ4OMdFqRBS7sRHtnZtfFHYW0wqCuCwzvxTxw1qvHq57Xe6RfHtc4LnjuJELE59PLyfPvEG9jcVS1GREUp+XYBpBtbvECgYAMhWBDU9JAr0noRNoCrw6+Z9Fc3UCyCPcf2XQJOyRHCl8X/XliVchna2GtpB1VTHORv13bc32hdAGtuIbj6vBaGLK0wXEvWw6TkR/9SWHfQOHuKpi6Sf2w1mCsMOjElm5IKkTC1Hvyo4xLukUP7hV9FJcpAH6l7OlSLK1Z13aS2QKBgB6w4gvmVEQruHV5+K60OatuFojr+kxJwmzCb5uKOULUFezT2pA3p3l6IWxGL2XtM+LD0SiZE3KZJUzf+LatYlBU9ek4F1krkVNUTRZpzUa0oADbymCL1chM4oPIs7sISQlFIH2wOSZt6Blvcw0E0wfjd9Gv/LHxcMnlRb1t1sLk" + ], + "keyUse": ["SIG"], + "certificate": [ + "MIICnTCCAYUCBgGQBsyplzANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdteXJlYWxtMB4XDTI0MDYxMTEwMTQ1NFoXDTM0MDYxMTEwMTYzNFowEjEQMA4GA1UEAwwHbXlyZWFsbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALGFFs1X2THessKQZiyWHhdVPTIHf898Vr5dWKu+qETz36XKkIrTfnJLVqrtIT3qwlUpdpbcFwzK1MBc7MBvTIjPYW/gSxJXe5GImJHBTbd5QrZpnkUBGnuycFops3jVfekJc9LR2PH/WYdH3zw7C0zdRxgHkSSYSO5jUJuqgiTTAn1QPzuUlrprCJ9ly50jIkNOQAAlRzi1KuqwGdswgmmJl05iZLkjm/Htuld9Nz+petw0nuR0jVw3SNNLY3xRX9+6vO9M4b37KLa448dETIo8vSo4/DmRfqRR6Q0WhhIvLaUMWQ5wk6H04ngf1ad0D/mk5bwMg90NWZycRkAi8oMCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAVS+gJshIFX6cmBGI8UaOOI/9+XFb4Gi+DHaHVWVVHTd14MoqNK1bmmyTHbGIZbvK8UqgJ9+FhJX1ejx17d4KBzkZI3tYvPnVacHvaw1CIUMZ1Ini6u+UGUTnIlnQzCG0pcTKjOZXf3ih1B2CKdwyC7XeXyEJHicAIG7XfzYfYd9DYHvA+h6hrXaQcNJMW7WFNbtb3fJhtlv5P1Iw+ZEGdj15ukMI0bg2OEQA0F3jIw6QZpigSAGuai3HOY6OgoPO82d7TyTYlNhuwyutWr9izl6QMc2R7BmRfW9XQj4ICR2VWJiL9nqz+SOyqnjQiOObuw8Vywb8c36R1Ym1aaGjOw==" + ], + "priority": ["100"] + } + }, + { + "id": "e586f825-a25a-4833-a38e-4c6484ad17fd", + "name": "rsa-enc-generated", + "providerId": "rsa-enc-generated", + "subComponents": {}, + "config": { + "privateKey": [ + "MIIEogIBAAKCAQEAkQtefHy82e8d5dVWN00LnGI5YmBOTKh0tgqayVRjqLH6u3NfgJVVIe0tFnxa7Wka/ySHrn1KSsW52czZ4uPXLUo4sXBkQxyyFXeZiWN8H+9WiUQ+0hefZF4es5ZPhY2VpeMK9XAnphC362LFLVycXulkpJcQ+4DjI99To4LLyJmjQvsVaJ7amoVJ5xd62eUv+D7f2+jwuaTwjGE3+MWZADXjVxsUY1qJuGLGKnLkNNxJNMDhvnKYw+aa3Z4V90fQVyjN1Volgw3DdA59o4wrWEy+2xHc6j2ESi8+cM60fWzZU9sp2XkyJoCnV7nmwk7pZkDy3zvAkeOWzrr3OWeR3wIDAQABAoIBACWMcet8R0+L7YuATQ+H7IeRjhV/pQWHXp9541RXem1DlgtM9N5Oynk78z4s90Uavphqlo1/deohgdl2hLmODjh1THPzCqGtHhUcnyzICmwiA58JgdHVt7e9/eiz8uY6HxGQ01dyr3D4RwSyzyTNItYXSayqRwU0+phgykA8LhFCAQM/UkRXDf6UCFKBhDyE7VPBaDv0xyxNb7dKtE7C6Qo5t5D40xCfQ8ni8OcD5RvshQq5xOWcw7igxAhlmXCu1fuO2CDiSiqXLMENs4NlwilQ3caMXAIzUiblaKwCrrK2noBoitx6vuOR2tKmIZSlTyDAG4vLQQtOHk53hBoupGECgYEAx4jSmLM9uUzNwNY1zfs8iNswxbU3YibNe2Q+IFmOQofvTaq1jBBxdPWX5ifIbuTvOAA33pmJRh+BtWzOBBQC7Z4i9mdfvyWB6s8t9nnTnWIY5Hj+hV5gaqae59MjdudsORR887fxzPIeAwwaETfKaZnYpC6zLaE3BXwhIcjlFTcCgYEAuhcKf16JkEYNIwanVHpUXjFxwAThAogHWZAngRokmai67Iulx+rSUhhtOIXtmjj/EaObsrqo5yCKAVZ5EbPTOajdd9RtFzH6q3bRjRdp8o8ZVx4c1vMNaOnLbvK4YzJlKSZN9N7m255Mg+/ea3veKVZsSVHDMnuYmH8GjncjPJkCgYAOIUlQmPjZA3BapJDA2nbJ9kO47IFUiQzqHQotPkpNudSfemRK2+s87htoqA6Qk9PA8nsCX3sSJS8JSwA317bxXs55Bo8IOT6/AxbtKmlq7sR2gX78sNdBFjWQkyoixHasgB/tHmyYJ9kqPBQoffvuiH+H+OqlY5JC6CxseQ6H9wKBgF69Hj4MDjLiRwve9k9+2/b8azHcCgX05PEG/+WtPpbwHQIScnseJKdhAjH1lSqf+9OqHLlYaGcK3Nejg42spEvFmcLI5iUZ78lde3++PNUdX0RH81zHbrtL06MPdSojXPcfJi8VUCjdJY1CEFVeQZOACS8mrh7EZ8KzYM4k/055AoGAYqjBv3WS8ul7kAsjpZKpIw1QZZaTjBSmLpjB6X8InF+Zihjgm80Dd4RMFnMnEawhFBvnpklvyw5Ce6NSwcC137kN3NVpJypykkXuYkimg7OxgJjR7YFdbQWJWlc+1eB81WTHcEOHVI/DmeV2yVJcv6kA2iC+3/JA0VoJxvrRBKc=" + ], + "keyUse": ["ENC"], + "certificate": [ + "MIICnTCCAYUCBgGQBsyq0jANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdteXJlYWxtMB4XDTI0MDYxMTEwMTQ1NFoXDTM0MDYxMTEwMTYzNFowEjEQMA4GA1UEAwwHbXlyZWFsbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJELXnx8vNnvHeXVVjdNC5xiOWJgTkyodLYKmslUY6ix+rtzX4CVVSHtLRZ8Wu1pGv8kh659SkrFudnM2eLj1y1KOLFwZEMcshV3mYljfB/vVolEPtIXn2ReHrOWT4WNlaXjCvVwJ6YQt+tixS1cnF7pZKSXEPuA4yPfU6OCy8iZo0L7FWie2pqFSecXetnlL/g+39vo8Lmk8IxhN/jFmQA141cbFGNaibhixipy5DTcSTTA4b5ymMPmmt2eFfdH0FcozdVaJYMNw3QOfaOMK1hMvtsR3Oo9hEovPnDOtH1s2VPbKdl5MiaAp1e55sJO6WZA8t87wJHjls669zlnkd8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAD9wQ+CJ0FRgls3JrUzxwHLgrJ3Yo4+mDFpSe1rh2XYK5FEIWDWSqxaXI3p0cOZq75RZmI2xV8oaiJMUz9WMZkbNe/KtGRzHY1N9AZooicGIsnFu1t++b8taFxxpvKWZgnbOum2PZlfcNiXL0QeMv0wwhfn9zKA9W1DRcqYGbIamoyVlumvbNyIjqXJKwGYIOW6GNt7v3wJl5AJw8qAU/O/DQwWwmzcnFGNRxRxAwI7we8EiQ5JlG0Wi+nyAQn74o3RhNr3zsY0ndmFx9bFV4BBo2AiYGozCDOCCG5HvrmoDbrm//wmGRv0tCwueBzWHL2mhtbZ6sGWmMWfiTJ2HPpg==" + ], + "priority": ["100"], + "algorithm": ["RSA-OAEP"] + } + }, + { + "id": "d85dae25-3728-46a0-980b-46171ba50cdd", + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "kid": ["1c1d0c8a-6f0b-48a9-a66f-488489137d85"], + "secret": ["N4wzheVYYBWxFn9VGWTPQQ"], + "priority": ["100"] + } + }, + { + "id": "8c3bb039-6f5b-4bdc-9faa-e0f6038d9e6b", + "name": "hmac-generated-hs512", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "kid": ["ce43821c-6cfd-4ea9-a29a-a724a37e6955"], + "secret": [ + "j_8WeQHYt5R6coay0IOUeu9hGvCoJsgnENSoYm0gDlDx6IHOg-f6p17QIaesNmgrzXtJDRpYMhSjpTMHOnHCHLxwUM4eVg9TcszffndB850Yj3PHPeCc5aoHcpYzWN9NDZZ02nBYA04nfbkdlLXiGlpS3I3e502e4DX3rFtbFZ0" + ], + "priority": ["100"], + "algorithm": ["HS512"] + } + } + ] + }, + "internationalizationEnabled": true, + "supportedLocales": ["en", "fr", "es"], + "defaultLocale": "en", + "authenticationFlows": [ + { + "id": "0e1abbbe-40e3-4754-9fe2-8a7d1f82354e", + "alias": "Account verification options", + "description": "Method with which to verity the existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-email-verification", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false + } + ] + }, + { + "id": "f279cc4d-ebed-4390-a5d4-0cbb6dd662ae", + "alias": "Browser - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "6926f455-0fd0-4ac6-9fc1-333b86c4150f", + "alias": "Direct Grant - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "b11840e7-21ec-4200-bf3c-c7853646a908", + "alias": "First broker login - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "615b4d0e-e71e-4c96-aed3-b03b34b61808", + "alias": "Handle Existing Account", + "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Account verification options", + "userSetupAllowed": false + } + ] + }, + { + "id": "36958ec5-62d7-4d51-8b30-7a6709476aec", + "alias": "Reset - Conditional OTP", + "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "aa4a7ac2-ec63-48ea-a70f-b3f18992b99a", + "alias": "User creation or linking", + "description": "Flow for the existing/non-existing user alternatives", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false + } + ] + }, + { + "id": "dafdfc68-72eb-49b2-a8f4-495ee25fba21", + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "First broker login - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "6a39b6db-c81e-4de4-92a8-a9e504593f2e", + "alias": "browser", + "description": "browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "identity-provider-redirector", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 25, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "forms", + "userSetupAllowed": false + } + ] + }, + { + "id": "6fa840df-bc04-4045-9e33-8901d183b165", + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-secret-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-x509", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 40, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "4aa24ca0-ad09-4f30-806b-4c699724d731", + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "Direct Grant - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "0a914ba4-f662-4b85-af64-74738a222b7f", + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "9b40f15f-b690-4fe2-9fe8-07e77d965297", + "alias": "first broker login", + "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "User creation or linking", + "userSetupAllowed": false + } + ] + }, + { + "id": "c8a9848f-8dd8-4e13-b521-0a537d92ec36", + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Browser - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "603957f8-b0a5-4885-aafd-e2757e431954", + "alias": "registration", + "description": "registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": true, + "flowAlias": "registration form", + "userSetupAllowed": false + } + ] + }, + { + "id": "f41632f9-7fad-427d-ae7a-78ac9b1f51d0", + "alias": "registration form", + "description": "registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-password-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 50, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-recaptcha-action", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 60, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-terms-and-conditions", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 70, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "27a133ca-e05e-4c93-a3b7-ffe14b4e62ec", + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-credential-email", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 40, + "autheticatorFlow": true, + "flowAlias": "Reset - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "06cd7382-4944-4499-94dc-9908544e291b", + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + } + ], + "authenticatorConfig": [ + { + "id": "5f953def-6f7c-430f-a33f-440ec2d2dddd", + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "id": "b3dad9a1-5b82-4e91-a250-157a45694e24", + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" + } + } + ], + "requiredActions": [ + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "priority": 10, + "config": {} + }, + { + "alias": "TERMS_AND_CONDITIONS", + "name": "Terms and Conditions", + "providerId": "TERMS_AND_CONDITIONS", + "enabled": true, + "defaultAction": true, + "priority": 20, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "priority": 30, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 40, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "priority": 50, + "config": {} + }, + { + "alias": "delete_account", + "name": "Delete Account", + "providerId": "delete_account", + "enabled": true, + "defaultAction": false, + "priority": 60, + "config": {} + }, + { + "alias": "webauthn-register", + "name": "Webauthn Register", + "providerId": "webauthn-register", + "enabled": true, + "defaultAction": false, + "priority": 70, + "config": {} + }, + { + "alias": "webauthn-register-passwordless", + "name": "Webauthn Register Passwordless", + "providerId": "webauthn-register-passwordless", + "enabled": true, + "defaultAction": false, + "priority": 80, + "config": {} + }, + { + "alias": "VERIFY_PROFILE", + "name": "Verify Profile", + "providerId": "VERIFY_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 90, + "config": {} + }, + { + "alias": "delete_credential", + "name": "Delete Credential", + "providerId": "delete_credential", + "enabled": true, + "defaultAction": false, + "priority": 100, + "config": {} + }, + { + "alias": "update_user_locale", + "name": "Update User Locale", + "providerId": "update_user_locale", + "enabled": true, + "defaultAction": false, + "priority": 1000, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "firstBrokerLoginFlow": "first broker login", + "attributes": { + "cibaBackchannelTokenDeliveryMode": "poll", + "cibaAuthRequestedUserHint": "login_hint", + "clientOfflineSessionMaxLifespan": "0", + "oauth2DevicePollingInterval": "5", + "clientSessionIdleTimeout": "0", + "clientOfflineSessionIdleTimeout": "0", + "cibaInterval": "5", + "realmReusableOtpCode": "false", + "cibaExpiresIn": "120", + "oauth2DeviceCodeLifespan": "600", + "parRequestUriLifespan": "60", + "clientSessionMaxLifespan": "0", + "organizationsEnabled": "false" + }, + "keycloakVersion": "25.0.0", + "userManagedAccessAllowed": false, + "organizationsEnabled": false, + "clientProfiles": { + "profiles": [] + }, + "clientPolicies": { + "policies": [] + } +} From cfdad8d71d5a373eb470efc642a25bb4d40b7da5 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Fri, 4 Oct 2024 12:17:54 +0200 Subject: [PATCH 24/77] Release candidate --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ff7fbe2a..fe130725 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.2.8", + "version": "11.2.9-rc.0", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From bcb514ae9c7aa80910d20af82c1cfda2d8aab1b2 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Fri, 4 Oct 2024 12:44:03 +0200 Subject: [PATCH 25/77] Aditional context exclusion --- .../keycloakify/generateFtl/kcContextDeclarationTemplate.ftl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bin/keycloakify/generateFtl/kcContextDeclarationTemplate.ftl b/src/bin/keycloakify/generateFtl/kcContextDeclarationTemplate.ftl index 5cfc2755..4139df7c 100644 --- a/src/bin/keycloakify/generateFtl/kcContextDeclarationTemplate.ftl +++ b/src/bin/keycloakify/generateFtl/kcContextDeclarationTemplate.ftl @@ -235,6 +235,9 @@ function decodeHtmlEntities(htmlStr){ "identityFederationEnabled", "userManagedAccessAllowed" ]?seq_contains(key) + ) || ( + ["flowContext", "session", "realm"]?seq_contains(key) && + areSamePath(path, ["social"]) ) > <#-- <#local outSeq += ["/*" + path?join(".") + "." + key + " excluded*/"]> --> From 4e982ee898b3df57bc0b24bd77010e17126e86fc Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Fri, 4 Oct 2024 12:44:22 +0200 Subject: [PATCH 26/77] Release candidate --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fe130725..f6a41f99 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.2.9-rc.0", + "version": "11.2.9-rc.1", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From 6f416ad33508ca2e4106025ede077a1c711adc6c Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Fri, 4 Oct 2024 12:58:31 +0200 Subject: [PATCH 27/77] Update version ranges for Multi-Page account theme --- .../buildJars/getKeycloakVersionRangeForJar.ts | 4 ++-- src/bin/shared/KeycloakVersionRange.ts | 2 +- src/bin/shared/buildContext.ts | 12 +++++++++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/bin/keycloakify/buildJars/getKeycloakVersionRangeForJar.ts b/src/bin/keycloakify/buildJars/getKeycloakVersionRangeForJar.ts index 6c301223..89c42ef7 100644 --- a/src/bin/keycloakify/buildJars/getKeycloakVersionRangeForJar.ts +++ b/src/bin/keycloakify/buildJars/getKeycloakVersionRangeForJar.ts @@ -52,9 +52,9 @@ export function getKeycloakVersionRangeForJar(params: { case "0.6": switch (keycloakThemeAdditionalInfoExtensionVersion) { case null: - return undefined; + return "26-and-above" as const; case "1.1.5": - return "25-and-above" as const; + return "25" as const; } } assert>(false); diff --git a/src/bin/shared/KeycloakVersionRange.ts b/src/bin/shared/KeycloakVersionRange.ts index 1e118ea1..860f42f0 100644 --- a/src/bin/shared/KeycloakVersionRange.ts +++ b/src/bin/shared/KeycloakVersionRange.ts @@ -5,5 +5,5 @@ export type KeycloakVersionRange = export namespace KeycloakVersionRange { export type WithoutAccountV1Theme = "22-to-25" | "all-other-versions"; - export type WithAccountV1Theme = "21-and-below" | "23" | "24" | "25-and-above"; + export type WithAccountV1Theme = "21-and-below" | "23" | "24" | "25" | "26-and-above"; } diff --git a/src/bin/shared/buildContext.ts b/src/bin/shared/buildContext.ts index 852bd1fd..5fc09c3a 100644 --- a/src/bin/shared/buildContext.ts +++ b/src/bin/shared/buildContext.ts @@ -277,7 +277,8 @@ export function getBuildContext(params: { "21-and-below": z.union([z.boolean(), z.string()]), "23": z.union([z.boolean(), z.string()]), "24": z.union([z.boolean(), z.string()]), - "25-and-above": z.union([z.boolean(), z.string()]) + "25": z.union([z.boolean(), z.string()]), + "26-and-above": z.union([z.boolean(), z.string()]) }) .optional() }); @@ -767,7 +768,11 @@ export function getBuildContext(params: { return "24" as const; } - return "25-and-above" as const; + if (buildForKeycloakMajorVersionNumber === 25) { + return "25" as const; + } + + return "26-and-above" as const; })(); assert< @@ -855,7 +860,8 @@ export function getBuildContext(params: { "21-and-below", "23", "24", - "25-and-above" + "25", + "26-and-above" ] as const) { assert< Equals< From 8d9b80f54943c4f5e73e475e6b38ac7ccd0407db Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Fri, 4 Oct 2024 12:59:56 +0200 Subject: [PATCH 28/77] Update readme, support keycloak 26 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 78872df3..2c133223 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@

-Keycloakify is fully compatible with Keycloak from version 11 to 25...[and beyond](https://github.com/keycloakify/keycloakify/discussions/346#discussioncomment-5889791) +Keycloakify is fully compatible with Keycloak from version 11 to 26...[and beyond](https://github.com/keycloakify/keycloakify/discussions/346#discussioncomment-5889791) ## Sponsors From da495b90aeb6776457e5f1d07fae16d186bc139b Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Fri, 4 Oct 2024 13:00:15 +0200 Subject: [PATCH 29/77] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f6a41f99..9a704e0e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.2.9-rc.1", + "version": "11.2.9", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From e14f187fc037cf9d1df61c66a36a09b2828795d7 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Fri, 4 Oct 2024 16:56:02 +0200 Subject: [PATCH 30/77] Fix cache issue --- src/bin/keycloakify/buildJars/buildJar.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/keycloakify/buildJars/buildJar.ts b/src/bin/keycloakify/buildJars/buildJar.ts index cdbc3f22..4a952343 100644 --- a/src/bin/keycloakify/buildJars/buildJar.ts +++ b/src/bin/keycloakify/buildJars/buildJar.ts @@ -197,7 +197,7 @@ export async function buildJar(params: { await new Promise((resolve, reject) => child_process.exec( - `mvn install -Dmaven.repo.local="${pathJoin(keycloakifyBuildCacheDirPath, ".m2")}"`, + `mvn clean install -Dmaven.repo.local="${pathJoin(keycloakifyBuildCacheDirPath, ".m2")}"`, { cwd: keycloakifyBuildCacheDirPath }, error => { if (error !== null) { From e3bd7f3bc56f8ad3b078be897343a65d451f61d9 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Fri, 4 Oct 2024 16:56:17 +0200 Subject: [PATCH 31/77] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9a704e0e..0564ddf0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.2.9", + "version": "11.2.10", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From 35b012b93753605dadfe83295a05ad2a5bb24d24 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sat, 5 Oct 2024 20:30:09 +0200 Subject: [PATCH 32/77] Implement custom handler cli hook --- package.json | 1 + src/bin/add-story.ts | 11 +- src/bin/copy-keycloak-resources-to-public.ts | 9 +- src/bin/eject-page.ts | 11 +- .../initialize-account-theme.ts | 9 +- src/bin/initialize-email-theme.ts | 9 +- src/bin/keycloakify/keycloakify.ts | 9 +- src/bin/main.ts | 297 +++++++++++------- src/bin/shared/buildContext.ts | 9 +- src/bin/shared/constants.ts | 5 + src/bin/shared/customHandler.ts | 35 +++ src/bin/shared/customHandler_caller.ts | 47 +++ src/bin/start-keycloak/start-keycloak.ts | 22 +- src/bin/update-kc-gen.ts | 11 +- src/vite-plugin/vite-plugin.ts | 4 +- 15 files changed, 300 insertions(+), 189 deletions(-) create mode 100644 src/bin/shared/customHandler.ts create mode 100644 src/bin/shared/customHandler_caller.ts diff --git a/package.json b/package.json index 0564ddf0..70ca8dae 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "dist/bin/*.index.js", "dist/bin/*.node", "dist/bin/shared/constants.js", + "dist/bin/shared/customHandler.js", "dist/bin/shared/*.d.ts", "dist/bin/shared/*.js.map", "!dist/vite-plugin/", diff --git a/src/bin/add-story.ts b/src/bin/add-story.ts index b3ed5be7..b2be6d65 100644 --- a/src/bin/add-story.ts +++ b/src/bin/add-story.ts @@ -13,16 +13,11 @@ import * as fs from "fs"; import { join as pathJoin, relative as pathRelative, dirname as pathDirname } from "path"; import { kebabCaseToCamelCase } from "./tools/kebabCaseToSnakeCase"; import { assert, Equals } from "tsafe/assert"; -import type { CliCommandOptions } from "./main"; -import { getBuildContext } from "./shared/buildContext"; +import type { BuildContext } from "./shared/buildContext"; import chalk from "chalk"; -export async function command(params: { cliCommandOptions: CliCommandOptions }) { - const { cliCommandOptions } = params; - - const buildContext = getBuildContext({ - cliCommandOptions - }); +export async function command(params: { buildContext: BuildContext }) { + const { buildContext } = params; console.log(chalk.cyan("Theme type:")); diff --git a/src/bin/copy-keycloak-resources-to-public.ts b/src/bin/copy-keycloak-resources-to-public.ts index b245076a..ba944068 100644 --- a/src/bin/copy-keycloak-resources-to-public.ts +++ b/src/bin/copy-keycloak-resources-to-public.ts @@ -1,11 +1,8 @@ import { copyKeycloakResourcesToPublic } from "./shared/copyKeycloakResourcesToPublic"; -import { getBuildContext } from "./shared/buildContext"; -import type { CliCommandOptions } from "./main"; +import type { BuildContext } from "./shared/buildContext"; -export async function command(params: { cliCommandOptions: CliCommandOptions }) { - const { cliCommandOptions } = params; - - const buildContext = getBuildContext({ cliCommandOptions }); +export async function command(params: { buildContext: BuildContext }) { + const { buildContext } = params; copyKeycloakResourcesToPublic({ buildContext diff --git a/src/bin/eject-page.ts b/src/bin/eject-page.ts index 404483ec..8874fd99 100644 --- a/src/bin/eject-page.ts +++ b/src/bin/eject-page.ts @@ -20,16 +20,11 @@ import { } from "path"; import { kebabCaseToCamelCase } from "./tools/kebabCaseToSnakeCase"; import { assert, Equals } from "tsafe/assert"; -import type { CliCommandOptions } from "./main"; -import { getBuildContext } from "./shared/buildContext"; +import type { BuildContext } from "./shared/buildContext"; import chalk from "chalk"; -export async function command(params: { cliCommandOptions: CliCommandOptions }) { - const { cliCommandOptions } = params; - - const buildContext = getBuildContext({ - cliCommandOptions - }); +export async function command(params: { buildContext: BuildContext }) { + const { buildContext } = params; console.log(chalk.cyan("Theme type:")); diff --git a/src/bin/initialize-account-theme/initialize-account-theme.ts b/src/bin/initialize-account-theme/initialize-account-theme.ts index e15b95ba..e29b6733 100644 --- a/src/bin/initialize-account-theme/initialize-account-theme.ts +++ b/src/bin/initialize-account-theme/initialize-account-theme.ts @@ -1,5 +1,4 @@ -import { getBuildContext } from "../shared/buildContext"; -import type { CliCommandOptions } from "../main"; +import type { BuildContext } from "../shared/buildContext"; import cliSelect from "cli-select"; import child_process from "child_process"; import chalk from "chalk"; @@ -8,10 +7,8 @@ import * as fs from "fs"; import { updateAccountThemeImplementationInConfig } from "./updateAccountThemeImplementationInConfig"; import { generateKcGenTs } from "../shared/generateKcGenTs"; -export async function command(params: { cliCommandOptions: CliCommandOptions }) { - const { cliCommandOptions } = params; - - const buildContext = getBuildContext({ cliCommandOptions }); +export async function command(params: { buildContext: BuildContext }) { + const { buildContext } = params; const accountThemeSrcDirPath = pathJoin(buildContext.themeSrcDirPath, "account"); diff --git a/src/bin/initialize-email-theme.ts b/src/bin/initialize-email-theme.ts index f2520645..dfa0287c 100644 --- a/src/bin/initialize-email-theme.ts +++ b/src/bin/initialize-email-theme.ts @@ -1,15 +1,12 @@ import { join as pathJoin, relative as pathRelative } from "path"; import { transformCodebase } from "./tools/transformCodebase"; import { promptKeycloakVersion } from "./shared/promptKeycloakVersion"; -import { getBuildContext } from "./shared/buildContext"; +import type { BuildContext } from "./shared/buildContext"; import * as fs from "fs"; -import type { CliCommandOptions } from "./main"; import { downloadAndExtractArchive } from "./tools/downloadAndExtractArchive"; -export async function command(params: { cliCommandOptions: CliCommandOptions }) { - const { cliCommandOptions } = params; - - const buildContext = getBuildContext({ cliCommandOptions }); +export async function command(params: { buildContext: BuildContext }) { + const { buildContext } = params; const emailThemeSrcDirPath = pathJoin(buildContext.themeSrcDirPath, "email"); diff --git a/src/bin/keycloakify/keycloakify.ts b/src/bin/keycloakify/keycloakify.ts index b39dcad8..0d027103 100644 --- a/src/bin/keycloakify/keycloakify.ts +++ b/src/bin/keycloakify/keycloakify.ts @@ -2,19 +2,16 @@ import { generateResources } from "./generateResources"; import { join as pathJoin, relative as pathRelative, sep as pathSep } from "path"; import * as child_process from "child_process"; import * as fs from "fs"; -import { getBuildContext } from "../shared/buildContext"; +import type { BuildContext } from "../shared/buildContext"; import { VITE_PLUGIN_SUB_SCRIPTS_ENV_NAMES } from "../shared/constants"; import { buildJars } from "./buildJars"; -import type { CliCommandOptions } from "../main"; import chalk from "chalk"; import { readThisNpmPackageVersion } from "../tools/readThisNpmPackageVersion"; import * as os from "os"; import { rmSync } from "../tools/fs.rmSync"; -export async function command(params: { cliCommandOptions: CliCommandOptions }) { - const { cliCommandOptions } = params; - - const buildContext = getBuildContext({ cliCommandOptions }); +export async function command(params: { buildContext: BuildContext }) { + const { buildContext } = params; exit_if_maven_not_installed: { let commandOutput: Buffer | undefined = undefined; diff --git a/src/bin/main.ts b/src/bin/main.ts index 9f54242f..7ef2a9b8 100644 --- a/src/bin/main.ts +++ b/src/bin/main.ts @@ -4,8 +4,10 @@ import { termost } from "termost"; import { readThisNpmPackageVersion } from "./tools/readThisNpmPackageVersion"; import * as child_process from "child_process"; import { assertNoPnpmDlx } from "./tools/assertNoPnpmDlx"; +import { callHandlerIfAny } from "./shared/customHandler_caller"; +import { getBuildContext } from "./shared/buildContext"; -export type CliCommandOptions = { +type CliCommandOptions = { projectDirPath: string | undefined; }; @@ -69,115 +71,154 @@ program }) .task({ skip, - handler: async cliCommandOptions => { + handler: async ({ projectDirPath }) => { + const buildContext = getBuildContext({ projectDirPath }); + const { command } = await import("./keycloakify"); - await command({ cliCommandOptions }); + await command({ buildContext }); } }); -program - .command<{ - port: number | undefined; - keycloakVersion: string | undefined; - realmJsonFilePath: string | undefined; - }>({ - name: "start-keycloak", - description: - "Spin up a pre configured Docker image of Keycloak to test your theme." - }) - .option({ - key: "port", - name: (() => { - const name = "port"; +{ + const commandName = "start-keycloak"; - optionsKeys.push(name); + program + .command<{ + port: number | undefined; + keycloakVersion: string | undefined; + realmJsonFilePath: string | undefined; + }>({ + name: commandName, + description: + "Spin up a pre configured Docker image of Keycloak to test your theme." + }) + .option({ + key: "port", + name: (() => { + const name = "port"; - return name; - })(), - description: ["Keycloak server port.", "Example `--port 8085`"].join(" "), - defaultValue: undefined - }) - .option({ - key: "keycloakVersion", - name: (() => { - const name = "keycloak-version"; + optionsKeys.push(name); - optionsKeys.push(name); + return name; + })(), + description: ["Keycloak server port.", "Example `--port 8085`"].join(" "), + defaultValue: undefined + }) + .option({ + key: "keycloakVersion", + name: (() => { + const name = "keycloak-version"; - return name; - })(), - description: [ - "Use a specific version of Keycloak.", - "Example `--keycloak-version 21.1.1`" - ].join(" "), - defaultValue: undefined - }) - .option({ - key: "realmJsonFilePath", - name: (() => { - const name = "import"; + optionsKeys.push(name); - optionsKeys.push(name); + return name; + })(), + description: [ + "Use a specific version of Keycloak.", + "Example `--keycloak-version 21.1.1`" + ].join(" "), + defaultValue: undefined + }) + .option({ + key: "realmJsonFilePath", + name: (() => { + const name = "import"; - return name; - })(), - defaultValue: undefined, - description: [ - "Import your own realm configuration file", - "Example `--import path/to/myrealm-realm.json`" - ].join(" ") - }) - .task({ - skip, - handler: async cliCommandOptions => { - const { command } = await import("./start-keycloak"); + optionsKeys.push(name); - await command({ cliCommandOptions }); - } - }); + return name; + })(), + defaultValue: undefined, + description: [ + "Import your own realm configuration file", + "Example `--import path/to/myrealm-realm.json`" + ].join(" ") + }) + .task({ + skip, + handler: async ({ + projectDirPath, + keycloakVersion, + port, + realmJsonFilePath + }) => { + const buildContext = getBuildContext({ projectDirPath }); -program - .command({ - name: "eject-page", - description: "Eject a Keycloak page." - }) - .task({ - skip, - handler: async cliCommandOptions => { - const { command } = await import("./eject-page"); + const { command } = await import("./start-keycloak"); - await command({ cliCommandOptions }); - } - }); + await command({ + buildContext, + cliCommandOptions: { keycloakVersion, port, realmJsonFilePath } + }); + } + }); +} -program - .command({ - name: "add-story", - description: "Add *.stories.tsx file for a specific page to in your Storybook." - }) - .task({ - skip, - handler: async cliCommandOptions => { - const { command } = await import("./add-story"); +{ + const commandName = "eject-page"; - await command({ cliCommandOptions }); - } - }); + program + .command({ + name: commandName, + description: "Eject a Keycloak page." + }) + .task({ + skip, + handler: async ({ projectDirPath }) => { + const buildContext = getBuildContext({ projectDirPath }); -program - .command({ - name: "initialize-email-theme", - description: "Initialize an email theme." - }) - .task({ - skip, - handler: async cliCommandOptions => { - const { command } = await import("./initialize-email-theme"); + callHandlerIfAny({ buildContext, commandName }); - await command({ cliCommandOptions }); - } - }); + const { command } = await import("./eject-page"); + + await command({ buildContext }); + } + }); +} + +{ + const commandName = "add-story"; + + program + .command({ + name: commandName, + description: + "Add *.stories.tsx file for a specific page to in your Storybook." + }) + .task({ + skip, + handler: async ({ projectDirPath }) => { + const buildContext = getBuildContext({ projectDirPath }); + + callHandlerIfAny({ buildContext, commandName }); + + const { command } = await import("./add-story"); + + await command({ buildContext }); + } + }); +} + +{ + const comandName = "initialize-login-theme"; + + program + .command({ + name: comandName, + description: "Initialize an email theme." + }) + .task({ + skip, + handler: async ({ projectDirPath }) => { + const buildContext = getBuildContext({ projectDirPath }); + + const { command } = await import("./initialize-email-theme"); + + await command({ buildContext }); + } + }); +} program .command({ @@ -186,42 +227,58 @@ program }) .task({ skip, - handler: async cliCommandOptions => { + handler: async ({ projectDirPath }) => { + const buildContext = getBuildContext({ projectDirPath }); + const { command } = await import("./initialize-account-theme"); - await command({ cliCommandOptions }); + await command({ buildContext }); } }); -program - .command({ - name: "copy-keycloak-resources-to-public", - description: - "(Webpack/Create-React-App only) Copy Keycloak default theme resources to the public directory." - }) - .task({ - skip, - handler: async cliCommandOptions => { - const { command } = await import("./copy-keycloak-resources-to-public"); +{ + const commandName = "copy-keycloak-resources-to-public"; - await command({ cliCommandOptions }); - } - }); + program + .command({ + name: commandName, + description: + "(Webpack/Create-React-App only) Copy Keycloak default theme resources to the public directory." + }) + .task({ + skip, + handler: async ({ projectDirPath }) => { + const buildContext = getBuildContext({ projectDirPath }); -program - .command({ - name: "update-kc-gen", - description: - "(Webpack/Create-React-App only) Create/update the kc.gen.ts file in your project." - }) - .task({ - skip, - handler: async cliCommandOptions => { - const { command } = await import("./update-kc-gen"); + const { command } = await import("./copy-keycloak-resources-to-public"); - await command({ cliCommandOptions }); - } - }); + await command({ buildContext }); + } + }); +} + +{ + const commandName = "update-kc-gen"; + + program + .command({ + name: commandName, + description: + "(Webpack/Create-React-App only) Create/update the kc.gen.ts file in your project." + }) + .task({ + skip, + handler: async ({ projectDirPath }) => { + const buildContext = getBuildContext({ projectDirPath }); + + callHandlerIfAny({ buildContext, commandName }); + + const { command } = await import("./update-kc-gen"); + + await command({ buildContext }); + } + }); +} // Fallback to build command if no command is provided { diff --git a/src/bin/shared/buildContext.ts b/src/bin/shared/buildContext.ts index 5fc09c3a..fa658097 100644 --- a/src/bin/shared/buildContext.ts +++ b/src/bin/shared/buildContext.ts @@ -7,7 +7,6 @@ import { dirname as pathDirname } from "path"; import { getAbsoluteAndInOsFormatPath } from "../tools/getAbsoluteAndInOsFormatPath"; -import type { CliCommandOptions } from "../main"; import { z } from "zod"; import * as fs from "fs"; import { assert, type Equals } from "tsafe/assert"; @@ -129,14 +128,12 @@ export type ResolvedViteConfig = { }; export function getBuildContext(params: { - cliCommandOptions: CliCommandOptions; + projectDirPath: string | undefined; }): BuildContext { - const { cliCommandOptions } = params; - const projectDirPath = - cliCommandOptions.projectDirPath !== undefined + params.projectDirPath !== undefined ? getAbsoluteAndInOsFormatPath({ - pathIsh: cliCommandOptions.projectDirPath, + pathIsh: params.projectDirPath, cwd: process.cwd() }) : process.cwd(); diff --git a/src/bin/shared/constants.ts b/src/bin/shared/constants.ts index d0707a90..d2137b14 100644 --- a/src/bin/shared/constants.ts +++ b/src/bin/shared/constants.ts @@ -71,3 +71,8 @@ export type AccountThemePageId = (typeof ACCOUNT_THEME_PAGE_IDS)[number]; export const CONTAINER_NAME = "keycloak-keycloakify"; export const FALLBACK_LANGUAGE_TAG = "en"; + +export const CUSTOM_HANDLER_ENV_NAMES = { + COMMAND_NAME: "KEYCLOAKIFY_COMMAND_NAME", + BUILD_CONTEXT: "KEYCLOAKIFY_BUILD_CONTEXT" +}; diff --git a/src/bin/shared/customHandler.ts b/src/bin/shared/customHandler.ts new file mode 100644 index 00000000..d858aaa5 --- /dev/null +++ b/src/bin/shared/customHandler.ts @@ -0,0 +1,35 @@ +import { assert } from "tsafe/assert"; +import type { BuildContext } from "./buildContext"; +import { CUSTOM_HANDLER_ENV_NAMES } from "./constants"; + +export const BIN_NAME = "_keycloakify-custom-handler"; + +export const NOT_IMPLEMENTED_EXIT_CODE = 78; + +export type CommandName = "update-kc-gen" | "eject-page" | "add-story"; + +export type ApiVersion = "v1"; + +export function readParams(params: { apiVersion: ApiVersion }) { + const { apiVersion } = params; + + assert(apiVersion === "v1"); + + const commandName = (() => { + const envValue = process.env[CUSTOM_HANDLER_ENV_NAMES.COMMAND_NAME]; + + assert(envValue !== undefined); + + return envValue as CommandName; + })(); + + const buildContext = (() => { + const envValue = process.env[CUSTOM_HANDLER_ENV_NAMES.BUILD_CONTEXT]; + + assert(envValue !== undefined); + + return JSON.parse(envValue) as BuildContext; + })(); + + return { commandName, buildContext }; +} diff --git a/src/bin/shared/customHandler_caller.ts b/src/bin/shared/customHandler_caller.ts new file mode 100644 index 00000000..38f25972 --- /dev/null +++ b/src/bin/shared/customHandler_caller.ts @@ -0,0 +1,47 @@ +import { assert, type Equals } from "tsafe/assert"; +import type { BuildContext } from "./buildContext"; +import { CUSTOM_HANDLER_ENV_NAMES } from "./constants"; +import { + NOT_IMPLEMENTED_EXIT_CODE, + type CommandName, + BIN_NAME, + ApiVersion +} from "./customHandler"; +import * as child_process from "child_process"; +import { is } from "tsafe/is"; +import { dirname as pathDirname } from "path"; +import * as fs from "fs"; + +assert>(); + +export function callHandlerIfAny(params: { + commandName: CommandName; + buildContext: BuildContext; +}) { + const { commandName, buildContext } = params; + + if (!fs.readdirSync(pathDirname(process.argv[1])).includes(BIN_NAME)) { + return; + } + + try { + child_process.execSync(`npx ${BIN_NAME}`, { + stdio: "inherit", + env: { + ...process.env, + [CUSTOM_HANDLER_ENV_NAMES.COMMAND_NAME]: commandName, + [CUSTOM_HANDLER_ENV_NAMES.BUILD_CONTEXT]: JSON.stringify(buildContext) + } + }); + } catch (error) { + assert(is(error)); + + if (error.code === NOT_IMPLEMENTED_EXIT_CODE) { + return; + } + + process.exit(error.code); + } + + process.exit(0); +} diff --git a/src/bin/start-keycloak/start-keycloak.ts b/src/bin/start-keycloak/start-keycloak.ts index c85b09dc..e243fe2b 100644 --- a/src/bin/start-keycloak/start-keycloak.ts +++ b/src/bin/start-keycloak/start-keycloak.ts @@ -1,6 +1,5 @@ -import { getBuildContext } from "../shared/buildContext"; +import type { BuildContext } from "../shared/buildContext"; import { exclude } from "tsafe/exclude"; -import type { CliCommandOptions as CliCommandOptions_common } from "../main"; import { promptKeycloakVersion } from "../shared/promptKeycloakVersion"; import { CONTAINER_NAME } from "../shared/constants"; import { SemVer } from "../tools/SemVer"; @@ -29,13 +28,14 @@ import { existsAsync } from "../tools/fs.existsAsync"; import { rm } from "../tools/fs.rm"; import { downloadAndExtractArchive } from "../tools/downloadAndExtractArchive"; -export type CliCommandOptions = CliCommandOptions_common & { - port: number | undefined; - keycloakVersion: string | undefined; - realmJsonFilePath: string | undefined; -}; - -export async function command(params: { cliCommandOptions: CliCommandOptions }) { +export async function command(params: { + buildContext: BuildContext; + cliCommandOptions: { + port: number | undefined; + keycloakVersion: string | undefined; + realmJsonFilePath: string | undefined; + }; +}) { exit_if_docker_not_installed: { let commandOutput: string | undefined = undefined; @@ -88,9 +88,7 @@ export async function command(params: { cliCommandOptions: CliCommandOptions }) process.exit(1); } - const { cliCommandOptions } = params; - - const buildContext = getBuildContext({ cliCommandOptions }); + const { cliCommandOptions, buildContext } = params; const { dockerImageTag } = await (async () => { if (cliCommandOptions.keycloakVersion !== undefined) { diff --git a/src/bin/update-kc-gen.ts b/src/bin/update-kc-gen.ts index 585b0f22..b3d9ce15 100644 --- a/src/bin/update-kc-gen.ts +++ b/src/bin/update-kc-gen.ts @@ -1,13 +1,8 @@ -import type { CliCommandOptions } from "./main"; -import { getBuildContext } from "./shared/buildContext"; +import type { BuildContext } from "./shared/buildContext"; import { generateKcGenTs } from "./shared/generateKcGenTs"; -export async function command(params: { cliCommandOptions: CliCommandOptions }) { - const { cliCommandOptions } = params; - - const buildContext = getBuildContext({ - cliCommandOptions - }); +export async function command(params: { buildContext: BuildContext }) { + const { buildContext } = params; await generateKcGenTs({ buildContext }); } diff --git a/src/vite-plugin/vite-plugin.ts b/src/vite-plugin/vite-plugin.ts index f22cb62a..3a7f3d26 100644 --- a/src/vite-plugin/vite-plugin.ts +++ b/src/vite-plugin/vite-plugin.ts @@ -122,9 +122,7 @@ export function keycloakify(params: keycloakify.Params) { } const buildContext = getBuildContext({ - cliCommandOptions: { - projectDirPath - } + projectDirPath }); copyKeycloakResourcesToPublic({ buildContext }), From 7f608ad8add9113aec8457b213c21d5657dc6623 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sat, 5 Oct 2024 20:31:41 +0200 Subject: [PATCH 33/77] Release candidate --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 70ca8dae..bebb3ed3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.2.10", + "version": "11.3.0-rc.0", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From 590de7a67b4d13889b43dcfb63d7d6d91ff1ab3b Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sat, 5 Oct 2024 21:22:00 +0200 Subject: [PATCH 34/77] Fix missing exports --- package.json | 2 +- src/bin/shared/buildContext.ts | 4 ++-- src/bin/tools/fetchProxyOptions.ts | 14 ++++++++------ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index bebb3ed3..9a799b8f 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "dist/bin/*.node", "dist/bin/shared/constants.js", "dist/bin/shared/customHandler.js", - "dist/bin/shared/*.d.ts", + "dist/bin/*.d.ts", "dist/bin/shared/*.js.map", "!dist/vite-plugin/", "dist/vite-plugin/index.js", diff --git a/src/bin/shared/buildContext.ts b/src/bin/shared/buildContext.ts index fa658097..26c746b8 100644 --- a/src/bin/shared/buildContext.ts +++ b/src/bin/shared/buildContext.ts @@ -23,7 +23,7 @@ import { objectEntries } from "tsafe/objectEntries"; import { type ThemeType } from "./constants"; import { id } from "tsafe/id"; import chalk from "chalk"; -import { getProxyFetchOptions, type ProxyFetchOptions } from "../tools/fetchProxyOptions"; +import { getProxyFetchOptions, type FetchOptionsLike } from "../tools/fetchProxyOptions"; import { is } from "tsafe/is"; export type BuildContext = { @@ -42,7 +42,7 @@ export type BuildContext = { * In this case the urlPathname will be "/my-app/" */ urlPathname: string | undefined; assetsDirPath: string; - fetchOptions: ProxyFetchOptions; + fetchOptions: FetchOptionsLike; kcContextExclusionsFtlCode: string | undefined; environmentVariables: { name: string; default: string }[]; themeSrcDirPath: string; diff --git a/src/bin/tools/fetchProxyOptions.ts b/src/bin/tools/fetchProxyOptions.ts index e6f8f497..c607feb9 100644 --- a/src/bin/tools/fetchProxyOptions.ts +++ b/src/bin/tools/fetchProxyOptions.ts @@ -1,16 +1,18 @@ -import { type FetchOptions } from "make-fetch-happen"; import * as child_process from "child_process"; import * as fs from "fs"; import { exclude } from "tsafe/exclude"; -export type ProxyFetchOptions = Pick< - FetchOptions, - "proxy" | "noProxy" | "strictSSL" | "cert" | "ca" ->; +export type FetchOptionsLike = { + proxy: string | undefined; + noProxy: string | string[]; + strictSSL: boolean; + cert: string | string[] | undefined; + ca: string[] | undefined; +}; export function getProxyFetchOptions(params: { npmConfigGetCwd: string; -}): ProxyFetchOptions { +}): FetchOptionsLike { const { npmConfigGetCwd } = params; const cfg = (() => { From 052936f769da2db75fe7fd4bf147897b452b9c89 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sat, 5 Oct 2024 21:23:57 +0200 Subject: [PATCH 35/77] Release candidate --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9a799b8f..39c31f79 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.3.0-rc.0", + "version": "11.3.0-rc.1", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From cf18f9d06cd120fcbac37d5886f246cc6de90951 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sat, 5 Oct 2024 21:39:14 +0200 Subject: [PATCH 36/77] Add other missing declaration files --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 39c31f79..542b7aff 100644 --- a/package.json +++ b/package.json @@ -38,13 +38,14 @@ "dist/", "!dist/tsconfig.tsbuildinfo", "!dist/bin/", + "dist/bin/**/*.d.ts", "dist/bin/main.js", "dist/bin/*.index.js", "dist/bin/*.node", "dist/bin/shared/constants.js", + "dist/bin/shared/constants.js.map", "dist/bin/shared/customHandler.js", - "dist/bin/*.d.ts", - "dist/bin/shared/*.js.map", + "dist/bin/shared/customHandler.js.map", "!dist/vite-plugin/", "dist/vite-plugin/index.js", "dist/vite-plugin/index.d.ts", From 7b9aec4ed070be2791033eba4fc71187c08050cb Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sat, 5 Oct 2024 21:39:32 +0200 Subject: [PATCH 37/77] Release candidate --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 542b7aff..a472aa99 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.3.0-rc.1", + "version": "11.3.0-rc.2", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From 881386a123a46494e142789206faa03a35a2f0ec Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sat, 5 Oct 2024 22:28:36 +0200 Subject: [PATCH 38/77] add debug logs --- src/bin/main.ts | 4 ++++ src/bin/shared/customHandler_caller.ts | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/bin/main.ts b/src/bin/main.ts index 7ef2a9b8..836bda9b 100644 --- a/src/bin/main.ts +++ b/src/bin/main.ts @@ -168,8 +168,12 @@ program handler: async ({ projectDirPath }) => { const buildContext = getBuildContext({ projectDirPath }); + console.log("before callHandlerIfAny"); + callHandlerIfAny({ buildContext, commandName }); + console.log("after callHandlerIfAny"); + const { command } = await import("./eject-page"); await command({ buildContext }); diff --git a/src/bin/shared/customHandler_caller.ts b/src/bin/shared/customHandler_caller.ts index 38f25972..c4a49e7a 100644 --- a/src/bin/shared/customHandler_caller.ts +++ b/src/bin/shared/customHandler_caller.ts @@ -33,7 +33,10 @@ export function callHandlerIfAny(params: { [CUSTOM_HANDLER_ENV_NAMES.BUILD_CONTEXT]: JSON.stringify(buildContext) } }); - } catch (error) { + } catch (error: any) { + console.log(error.message); + console.log(error.status); + assert(is(error)); if (error.code === NOT_IMPLEMENTED_EXIT_CODE) { From 0fe49e3d6ee4342986639204de57bbc890e300cc Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sat, 5 Oct 2024 22:29:13 +0200 Subject: [PATCH 39/77] Release candidate --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a472aa99..d6ffdaf8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.3.0-rc.2", + "version": "11.3.0-rc.3", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From 77f12a940d5ad04fe1b417417dcda9e08fdd8b46 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sun, 6 Oct 2024 06:41:51 +0200 Subject: [PATCH 40/77] Fix not handling correctly exit cause --- src/bin/main.ts | 4 ---- src/bin/shared/customHandler_caller.ts | 10 +++------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/bin/main.ts b/src/bin/main.ts index 836bda9b..7ef2a9b8 100644 --- a/src/bin/main.ts +++ b/src/bin/main.ts @@ -168,12 +168,8 @@ program handler: async ({ projectDirPath }) => { const buildContext = getBuildContext({ projectDirPath }); - console.log("before callHandlerIfAny"); - callHandlerIfAny({ buildContext, commandName }); - console.log("after callHandlerIfAny"); - const { command } = await import("./eject-page"); await command({ buildContext }); diff --git a/src/bin/shared/customHandler_caller.ts b/src/bin/shared/customHandler_caller.ts index c4a49e7a..88d5a41c 100644 --- a/src/bin/shared/customHandler_caller.ts +++ b/src/bin/shared/customHandler_caller.ts @@ -8,7 +8,6 @@ import { ApiVersion } from "./customHandler"; import * as child_process from "child_process"; -import { is } from "tsafe/is"; import { dirname as pathDirname } from "path"; import * as fs from "fs"; @@ -34,16 +33,13 @@ export function callHandlerIfAny(params: { } }); } catch (error: any) { - console.log(error.message); - console.log(error.status); + const status = error.status; - assert(is(error)); - - if (error.code === NOT_IMPLEMENTED_EXIT_CODE) { + if (status === NOT_IMPLEMENTED_EXIT_CODE) { return; } - process.exit(error.code); + process.exit(status); } process.exit(0); From e62aa89d72830605427543dd5517732231ec0891 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sun, 6 Oct 2024 06:42:04 +0200 Subject: [PATCH 41/77] Release candidate --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d6ffdaf8..eb36617a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.3.0-rc.3", + "version": "11.3.0-rc.4", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From e8609de7b4904d31b0dc02f860af3ff9f9fdd8f2 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sun, 6 Oct 2024 06:44:53 +0200 Subject: [PATCH 42/77] No need to handle non react environement with custom handler support --- src/bin/shared/generateKcGenTs.ts | 104 ++++++++---------------------- 1 file changed, 28 insertions(+), 76 deletions(-) diff --git a/src/bin/shared/generateKcGenTs.ts b/src/bin/shared/generateKcGenTs.ts index 7191a55c..e5bb7e86 100644 --- a/src/bin/shared/generateKcGenTs.ts +++ b/src/bin/shared/generateKcGenTs.ts @@ -1,10 +1,8 @@ -import { assert, type Equals } from "tsafe/assert"; -import { id } from "tsafe/id"; +import { assert } from "tsafe/assert"; import type { BuildContext } from "./buildContext"; import * as fs from "fs/promises"; import { join as pathJoin } from "path"; import { existsAsync } from "../tools/fs.existsAsync"; -import { z } from "zod"; export type BuildContextLike = { projectDirPath: string; @@ -25,45 +23,7 @@ export async function generateKcGenTs(params: { }): Promise { const { buildContext } = params; - const isReactProject: boolean = await (async () => { - const parsedPackageJson = await (async () => { - type ParsedPackageJson = { - dependencies?: Record; - devDependencies?: Record; - }; - - const zParsedPackageJson = (() => { - type TargetType = ParsedPackageJson; - - const zTargetType = z.object({ - dependencies: z.record(z.string()).optional(), - devDependencies: z.record(z.string()).optional() - }); - - assert, TargetType>>(); - - return id>(zTargetType); - })(); - - return zParsedPackageJson.parse( - JSON.parse( - (await fs.readFile(buildContext.packageJsonFilePath)).toString("utf8") - ) - ); - })(); - - return ( - { - ...parsedPackageJson.dependencies, - ...parsedPackageJson.devDependencies - }.react !== undefined - ); - })(); - - const filePath = pathJoin( - buildContext.themeSrcDirPath, - `kc.gen.ts${isReactProject ? "x" : ""}` - ); + const filePath = pathJoin(buildContext.themeSrcDirPath, `kc.gen.tsx`); const currentContent = (await existsAsync(filePath)) ? await fs.readFile(filePath) @@ -84,7 +44,7 @@ export async function generateKcGenTs(params: { ``, `// This file is auto-generated by Keycloakify`, ``, - isReactProject && `import { lazy, Suspense, type ReactNode } from "react";`, + `import { lazy, Suspense, type ReactNode } from "react";`, ``, `export type ThemeName = ${buildContext.themeNames.map(themeName => `"${themeName}"`).join(" | ")};`, ``, @@ -115,35 +75,31 @@ export async function generateKcGenTs(params: { ` }`, `}`, ``, - ...(!isReactProject - ? [] - : [ - hasLoginTheme && - `export const KcLoginPage = lazy(() => import("./login/KcPage"));`, - hasAccountTheme && - `export const KcAccountPage = lazy(() => import("./account/KcPage"));`, - ``, - `export function KcPage(`, - ` props: {`, - ` kcContext: KcContext;`, - ` fallback?: ReactNode;`, - ` }`, - `) {`, - ` const { kcContext, fallback } = props;`, - ` return (`, - ` `, - ` {(() => {`, - ` switch (kcContext.themeType) {`, - hasLoginTheme && - ` case "login": return ;`, - hasAccountTheme && - ` case "account": return ;`, - ` }`, - ` })()}`, - ` `, - ` );`, - `}` - ]), + hasLoginTheme && + `export const KcLoginPage = lazy(() => import("./login/KcPage"));`, + hasAccountTheme && + `export const KcAccountPage = lazy(() => import("./account/KcPage"));`, + ``, + `export function KcPage(`, + ` props: {`, + ` kcContext: KcContext;`, + ` fallback?: ReactNode;`, + ` }`, + `) {`, + ` const { kcContext, fallback } = props;`, + ` return (`, + ` `, + ` {(() => {`, + ` switch (kcContext.themeType) {`, + hasLoginTheme && + ` case "login": return ;`, + hasAccountTheme && + ` case "account": return ;`, + ` }`, + ` })()}`, + ` `, + ` );`, + `}`, ``, `/* prettier-ignore-end */`, `` @@ -160,10 +116,6 @@ export async function generateKcGenTs(params: { await fs.writeFile(filePath, newContent); delete_legacy_file: { - if (!isReactProject) { - break delete_legacy_file; - } - const legacyFilePath = filePath.replace(/tsx$/, "ts"); if (!(await existsAsync(legacyFilePath))) { From 63775b2866bcf208284330d338f31e9aff4b96e4 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sun, 6 Oct 2024 06:45:06 +0200 Subject: [PATCH 43/77] Release candidate --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eb36617a..133ddec1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.3.0-rc.4", + "version": "11.3.0-rc.5", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From a04f07d149c646b1c2f8bb7a7f7ba52548d057c3 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sun, 6 Oct 2024 09:03:15 +0200 Subject: [PATCH 44/77] Make sure the update-kc-gen command is delegated when building with vite --- .../initialize-account-theme.ts | 4 +- src/bin/main.ts | 293 +++++++----------- ...er_caller.ts => customHandler_delegate.ts} | 2 +- src/bin/shared/generateKcGenTs.ts | 127 -------- src/bin/update-kc-gen.ts | 112 ++++++- src/vite-plugin/vite-plugin.ts | 7 +- stories/login/pages/Register.stories.tsx | 6 +- 7 files changed, 239 insertions(+), 312 deletions(-) rename src/bin/shared/{customHandler_caller.ts => customHandler_delegate.ts} (95%) delete mode 100644 src/bin/shared/generateKcGenTs.ts diff --git a/src/bin/initialize-account-theme/initialize-account-theme.ts b/src/bin/initialize-account-theme/initialize-account-theme.ts index e29b6733..3caed327 100644 --- a/src/bin/initialize-account-theme/initialize-account-theme.ts +++ b/src/bin/initialize-account-theme/initialize-account-theme.ts @@ -5,7 +5,7 @@ import chalk from "chalk"; import { join as pathJoin, relative as pathRelative } from "path"; import * as fs from "fs"; import { updateAccountThemeImplementationInConfig } from "./updateAccountThemeImplementationInConfig"; -import { generateKcGenTs } from "../shared/generateKcGenTs"; +import { command as updateKcGenCommand } from "../update-kc-gen"; export async function command(params: { buildContext: BuildContext }) { const { buildContext } = params; @@ -94,7 +94,7 @@ export async function command(params: { buildContext: BuildContext }) { updateAccountThemeImplementationInConfig({ buildContext, accountThemeType }); - await generateKcGenTs({ + await updateKcGenCommand({ buildContext: { ...buildContext, implementedThemeTypes: { diff --git a/src/bin/main.ts b/src/bin/main.ts index 7ef2a9b8..89b03d7c 100644 --- a/src/bin/main.ts +++ b/src/bin/main.ts @@ -4,7 +4,6 @@ import { termost } from "termost"; import { readThisNpmPackageVersion } from "./tools/readThisNpmPackageVersion"; import * as child_process from "child_process"; import { assertNoPnpmDlx } from "./tools/assertNoPnpmDlx"; -import { callHandlerIfAny } from "./shared/customHandler_caller"; import { getBuildContext } from "./shared/buildContext"; type CliCommandOptions = { @@ -72,153 +71,117 @@ program .task({ skip, handler: async ({ projectDirPath }) => { - const buildContext = getBuildContext({ projectDirPath }); - const { command } = await import("./keycloakify"); - await command({ buildContext }); + await command({ buildContext: getBuildContext({ projectDirPath }) }); } }); -{ - const commandName = "start-keycloak"; +program + .command<{ + port: number | undefined; + keycloakVersion: string | undefined; + realmJsonFilePath: string | undefined; + }>({ + name: "start-keycloak", + description: + "Spin up a pre configured Docker image of Keycloak to test your theme." + }) + .option({ + key: "port", + name: (() => { + const name = "port"; - program - .command<{ - port: number | undefined; - keycloakVersion: string | undefined; - realmJsonFilePath: string | undefined; - }>({ - name: commandName, - description: - "Spin up a pre configured Docker image of Keycloak to test your theme." - }) - .option({ - key: "port", - name: (() => { - const name = "port"; + optionsKeys.push(name); - optionsKeys.push(name); + return name; + })(), + description: ["Keycloak server port.", "Example `--port 8085`"].join(" "), + defaultValue: undefined + }) + .option({ + key: "keycloakVersion", + name: (() => { + const name = "keycloak-version"; - return name; - })(), - description: ["Keycloak server port.", "Example `--port 8085`"].join(" "), - defaultValue: undefined - }) - .option({ - key: "keycloakVersion", - name: (() => { - const name = "keycloak-version"; + optionsKeys.push(name); - optionsKeys.push(name); + return name; + })(), + description: [ + "Use a specific version of Keycloak.", + "Example `--keycloak-version 21.1.1`" + ].join(" "), + defaultValue: undefined + }) + .option({ + key: "realmJsonFilePath", + name: (() => { + const name = "import"; - return name; - })(), - description: [ - "Use a specific version of Keycloak.", - "Example `--keycloak-version 21.1.1`" - ].join(" "), - defaultValue: undefined - }) - .option({ - key: "realmJsonFilePath", - name: (() => { - const name = "import"; + optionsKeys.push(name); - optionsKeys.push(name); + return name; + })(), + defaultValue: undefined, + description: [ + "Import your own realm configuration file", + "Example `--import path/to/myrealm-realm.json`" + ].join(" ") + }) + .task({ + skip, + handler: async ({ projectDirPath, keycloakVersion, port, realmJsonFilePath }) => { + const { command } = await import("./start-keycloak"); - return name; - })(), - defaultValue: undefined, - description: [ - "Import your own realm configuration file", - "Example `--import path/to/myrealm-realm.json`" - ].join(" ") - }) - .task({ - skip, - handler: async ({ - projectDirPath, - keycloakVersion, - port, - realmJsonFilePath - }) => { - const buildContext = getBuildContext({ projectDirPath }); + await command({ + buildContext: getBuildContext({ projectDirPath }), + cliCommandOptions: { keycloakVersion, port, realmJsonFilePath } + }); + } + }); - const { command } = await import("./start-keycloak"); +program + .command({ + name: "eject-page", + description: "Eject a Keycloak page." + }) + .task({ + skip, + handler: async ({ projectDirPath }) => { + const { command } = await import("./eject-page"); - await command({ - buildContext, - cliCommandOptions: { keycloakVersion, port, realmJsonFilePath } - }); - } - }); -} + await command({ buildContext: getBuildContext({ projectDirPath }) }); + } + }); -{ - const commandName = "eject-page"; +program + .command({ + name: "add-story", + description: "Add *.stories.tsx file for a specific page to in your Storybook." + }) + .task({ + skip, + handler: async ({ projectDirPath }) => { + const { command } = await import("./add-story"); - program - .command({ - name: commandName, - description: "Eject a Keycloak page." - }) - .task({ - skip, - handler: async ({ projectDirPath }) => { - const buildContext = getBuildContext({ projectDirPath }); + await command({ buildContext: getBuildContext({ projectDirPath }) }); + } + }); - callHandlerIfAny({ buildContext, commandName }); +program + .command({ + name: "initialize-login-theme", + description: "Initialize an email theme." + }) + .task({ + skip, + handler: async ({ projectDirPath }) => { + const { command } = await import("./initialize-email-theme"); - const { command } = await import("./eject-page"); - - await command({ buildContext }); - } - }); -} - -{ - const commandName = "add-story"; - - program - .command({ - name: commandName, - description: - "Add *.stories.tsx file for a specific page to in your Storybook." - }) - .task({ - skip, - handler: async ({ projectDirPath }) => { - const buildContext = getBuildContext({ projectDirPath }); - - callHandlerIfAny({ buildContext, commandName }); - - const { command } = await import("./add-story"); - - await command({ buildContext }); - } - }); -} - -{ - const comandName = "initialize-login-theme"; - - program - .command({ - name: comandName, - description: "Initialize an email theme." - }) - .task({ - skip, - handler: async ({ projectDirPath }) => { - const buildContext = getBuildContext({ projectDirPath }); - - const { command } = await import("./initialize-email-theme"); - - await command({ buildContext }); - } - }); -} + await command({ buildContext: getBuildContext({ projectDirPath }) }); + } + }); program .command({ @@ -228,57 +191,41 @@ program .task({ skip, handler: async ({ projectDirPath }) => { - const buildContext = getBuildContext({ projectDirPath }); - const { command } = await import("./initialize-account-theme"); - await command({ buildContext }); + await command({ buildContext: getBuildContext({ projectDirPath }) }); } }); -{ - const commandName = "copy-keycloak-resources-to-public"; +program + .command({ + name: "copy-keycloak-resources-to-public", + description: + "(Webpack/Create-React-App only) Copy Keycloak default theme resources to the public directory." + }) + .task({ + skip, + handler: async ({ projectDirPath }) => { + const { command } = await import("./copy-keycloak-resources-to-public"); - program - .command({ - name: commandName, - description: - "(Webpack/Create-React-App only) Copy Keycloak default theme resources to the public directory." - }) - .task({ - skip, - handler: async ({ projectDirPath }) => { - const buildContext = getBuildContext({ projectDirPath }); + await command({ buildContext: getBuildContext({ projectDirPath }) }); + } + }); - const { command } = await import("./copy-keycloak-resources-to-public"); +program + .command({ + name: "update-kc-gen", + description: + "(Webpack/Create-React-App only) Create/update the kc.gen.ts file in your project." + }) + .task({ + skip, + handler: async ({ projectDirPath }) => { + const { command } = await import("./update-kc-gen"); - await command({ buildContext }); - } - }); -} - -{ - const commandName = "update-kc-gen"; - - program - .command({ - name: commandName, - description: - "(Webpack/Create-React-App only) Create/update the kc.gen.ts file in your project." - }) - .task({ - skip, - handler: async ({ projectDirPath }) => { - const buildContext = getBuildContext({ projectDirPath }); - - callHandlerIfAny({ buildContext, commandName }); - - const { command } = await import("./update-kc-gen"); - - await command({ buildContext }); - } - }); -} + await command({ buildContext: getBuildContext({ projectDirPath }) }); + } + }); // Fallback to build command if no command is provided { diff --git a/src/bin/shared/customHandler_caller.ts b/src/bin/shared/customHandler_delegate.ts similarity index 95% rename from src/bin/shared/customHandler_caller.ts rename to src/bin/shared/customHandler_delegate.ts index 88d5a41c..a41cbc7b 100644 --- a/src/bin/shared/customHandler_caller.ts +++ b/src/bin/shared/customHandler_delegate.ts @@ -13,7 +13,7 @@ import * as fs from "fs"; assert>(); -export function callHandlerIfAny(params: { +export function maybeDelegateCommandToCustomHandler(params: { commandName: CommandName; buildContext: BuildContext; }) { diff --git a/src/bin/shared/generateKcGenTs.ts b/src/bin/shared/generateKcGenTs.ts deleted file mode 100644 index e5bb7e86..00000000 --- a/src/bin/shared/generateKcGenTs.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { assert } from "tsafe/assert"; -import type { BuildContext } from "./buildContext"; -import * as fs from "fs/promises"; -import { join as pathJoin } from "path"; -import { existsAsync } from "../tools/fs.existsAsync"; - -export type BuildContextLike = { - projectDirPath: string; - themeNames: string[]; - environmentVariables: { name: string; default: string }[]; - themeSrcDirPath: string; - implementedThemeTypes: Pick< - BuildContext["implementedThemeTypes"], - "login" | "account" - >; - packageJsonFilePath: string; -}; - -assert(); - -export async function generateKcGenTs(params: { - buildContext: BuildContextLike; -}): Promise { - const { buildContext } = params; - - const filePath = pathJoin(buildContext.themeSrcDirPath, `kc.gen.tsx`); - - const currentContent = (await existsAsync(filePath)) - ? await fs.readFile(filePath) - : undefined; - - const hasLoginTheme = buildContext.implementedThemeTypes.login.isImplemented; - const hasAccountTheme = buildContext.implementedThemeTypes.account.isImplemented; - - const newContent = Buffer.from( - [ - `/* prettier-ignore-start */`, - ``, - `/* eslint-disable */`, - ``, - `// @ts-nocheck`, - ``, - `// noinspection JSUnusedGlobalSymbols`, - ``, - `// This file is auto-generated by Keycloakify`, - ``, - `import { lazy, Suspense, type ReactNode } from "react";`, - ``, - `export type ThemeName = ${buildContext.themeNames.map(themeName => `"${themeName}"`).join(" | ")};`, - ``, - `export const themeNames: ThemeName[] = [${buildContext.themeNames.map(themeName => `"${themeName}"`).join(", ")}];`, - ``, - `export type KcEnvName = ${buildContext.environmentVariables.length === 0 ? "never" : buildContext.environmentVariables.map(({ name }) => `"${name}"`).join(" | ")};`, - ``, - `export const kcEnvNames: KcEnvName[] = [${buildContext.environmentVariables.map(({ name }) => `"${name}"`).join(", ")}];`, - ``, - `export const kcEnvDefaults: Record = ${JSON.stringify( - Object.fromEntries( - buildContext.environmentVariables.map( - ({ name, default: defaultValue }) => [name, defaultValue] - ) - ), - null, - 2 - )};`, - ``, - `export type KcContext =`, - hasLoginTheme && ` | import("./login/KcContext").KcContext`, - hasAccountTheme && ` | import("./account/KcContext").KcContext`, - ` ;`, - ``, - `declare global {`, - ` interface Window {`, - ` kcContext?: KcContext;`, - ` }`, - `}`, - ``, - hasLoginTheme && - `export const KcLoginPage = lazy(() => import("./login/KcPage"));`, - hasAccountTheme && - `export const KcAccountPage = lazy(() => import("./account/KcPage"));`, - ``, - `export function KcPage(`, - ` props: {`, - ` kcContext: KcContext;`, - ` fallback?: ReactNode;`, - ` }`, - `) {`, - ` const { kcContext, fallback } = props;`, - ` return (`, - ` `, - ` {(() => {`, - ` switch (kcContext.themeType) {`, - hasLoginTheme && - ` case "login": return ;`, - hasAccountTheme && - ` case "account": return ;`, - ` }`, - ` })()}`, - ` `, - ` );`, - `}`, - ``, - `/* prettier-ignore-end */`, - `` - ] - .filter(item => typeof item === "string") - .join("\n"), - "utf8" - ); - - if (currentContent !== undefined && currentContent.equals(newContent)) { - return; - } - - await fs.writeFile(filePath, newContent); - - delete_legacy_file: { - const legacyFilePath = filePath.replace(/tsx$/, "ts"); - - if (!(await existsAsync(legacyFilePath))) { - break delete_legacy_file; - } - - await fs.unlink(legacyFilePath); - } -} diff --git a/src/bin/update-kc-gen.ts b/src/bin/update-kc-gen.ts index b3d9ce15..f126bcb1 100644 --- a/src/bin/update-kc-gen.ts +++ b/src/bin/update-kc-gen.ts @@ -1,8 +1,116 @@ import type { BuildContext } from "./shared/buildContext"; -import { generateKcGenTs } from "./shared/generateKcGenTs"; +import * as fs from "fs/promises"; +import { join as pathJoin } from "path"; +import { existsAsync } from "./tools/fs.existsAsync"; +import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate"; export async function command(params: { buildContext: BuildContext }) { const { buildContext } = params; - await generateKcGenTs({ buildContext }); + maybeDelegateCommandToCustomHandler({ + commandName: "update-kc-gen", + buildContext + }); + + const filePath = pathJoin(buildContext.themeSrcDirPath, `kc.gen.tsx`); + + const currentContent = (await existsAsync(filePath)) + ? await fs.readFile(filePath) + : undefined; + + const hasLoginTheme = buildContext.implementedThemeTypes.login.isImplemented; + const hasAccountTheme = buildContext.implementedThemeTypes.account.isImplemented; + + const newContent = Buffer.from( + [ + `/* prettier-ignore-start */`, + ``, + `/* eslint-disable */`, + ``, + `// @ts-nocheck`, + ``, + `// noinspection JSUnusedGlobalSymbols`, + ``, + `// This file is auto-generated by Keycloakify`, + ``, + `import { lazy, Suspense, type ReactNode } from "react";`, + ``, + `export type ThemeName = ${buildContext.themeNames.map(themeName => `"${themeName}"`).join(" | ")};`, + ``, + `export const themeNames: ThemeName[] = [${buildContext.themeNames.map(themeName => `"${themeName}"`).join(", ")}];`, + ``, + `export type KcEnvName = ${buildContext.environmentVariables.length === 0 ? "never" : buildContext.environmentVariables.map(({ name }) => `"${name}"`).join(" | ")};`, + ``, + `export const kcEnvNames: KcEnvName[] = [${buildContext.environmentVariables.map(({ name }) => `"${name}"`).join(", ")}];`, + ``, + `export const kcEnvDefaults: Record = ${JSON.stringify( + Object.fromEntries( + buildContext.environmentVariables.map( + ({ name, default: defaultValue }) => [name, defaultValue] + ) + ), + null, + 2 + )};`, + ``, + `export type KcContext =`, + hasLoginTheme && ` | import("./login/KcContext").KcContext`, + hasAccountTheme && ` | import("./account/KcContext").KcContext`, + ` ;`, + ``, + `declare global {`, + ` interface Window {`, + ` kcContext?: KcContext;`, + ` }`, + `}`, + ``, + hasLoginTheme && + `export const KcLoginPage = lazy(() => import("./login/KcPage"));`, + hasAccountTheme && + `export const KcAccountPage = lazy(() => import("./account/KcPage"));`, + ``, + `export function KcPage(`, + ` props: {`, + ` kcContext: KcContext;`, + ` fallback?: ReactNode;`, + ` }`, + `) {`, + ` const { kcContext, fallback } = props;`, + ` return (`, + ` `, + ` {(() => {`, + ` switch (kcContext.themeType) {`, + hasLoginTheme && + ` case "login": return ;`, + hasAccountTheme && + ` case "account": return ;`, + ` }`, + ` })()}`, + ` `, + ` );`, + `}`, + ``, + `/* prettier-ignore-end */`, + `` + ] + .filter(item => typeof item === "string") + .join("\n"), + "utf8" + ); + + if (currentContent !== undefined && currentContent.equals(newContent)) { + return; + } + + await fs.writeFile(filePath, newContent); + + delete_legacy_file: { + const legacyFilePath = filePath.replace(/tsx$/, "ts"); + + if (!(await existsAsync(legacyFilePath))) { + break delete_legacy_file; + } + + await fs.unlink(legacyFilePath); + } } diff --git a/src/vite-plugin/vite-plugin.ts b/src/vite-plugin/vite-plugin.ts index 3a7f3d26..e6dfb054 100644 --- a/src/vite-plugin/vite-plugin.ts +++ b/src/vite-plugin/vite-plugin.ts @@ -15,7 +15,7 @@ import { type ResolvedViteConfig } from "../bin/shared/buildContext"; import MagicString from "magic-string"; -import { generateKcGenTs } from "../bin/shared/generateKcGenTs"; +import { command as updateKcGenCommand } from "../bin/update-kc-gen"; export namespace keycloakify { export type Params = BuildOptions & { @@ -125,8 +125,9 @@ export function keycloakify(params: keycloakify.Params) { projectDirPath }); - copyKeycloakResourcesToPublic({ buildContext }), - await generateKcGenTs({ buildContext }); + copyKeycloakResourcesToPublic({ buildContext }); + + await updateKcGenCommand({ buildContext }); }, transform: (code, id) => { assert(command !== undefined); diff --git a/stories/login/pages/Register.stories.tsx b/stories/login/pages/Register.stories.tsx index f25568cc..09cdf249 100644 --- a/stories/login/pages/Register.stories.tsx +++ b/stories/login/pages/Register.stories.tsx @@ -115,7 +115,6 @@ export const WithFavoritePet: Story = { ) }; - export const WithNewsletter: Story = { render: () => ( ) }; - export const WithEmailAsUsername: Story = { render: () => ( Date: Sun, 6 Oct 2024 09:07:10 +0200 Subject: [PATCH 45/77] Release candidate --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 133ddec1..791e13e6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.3.0-rc.5", + "version": "11.3.0-rc.6", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From 9de2ed9eaf17a4d75d84b7b096c0b0699d4f56c6 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sun, 6 Oct 2024 12:44:46 +0200 Subject: [PATCH 46/77] Fix dead code --- .../updateAccountThemeImplementationInConfig.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bin/initialize-account-theme/updateAccountThemeImplementationInConfig.ts b/src/bin/initialize-account-theme/updateAccountThemeImplementationInConfig.ts index 976beb9a..1c60ef03 100644 --- a/src/bin/initialize-account-theme/updateAccountThemeImplementationInConfig.ts +++ b/src/bin/initialize-account-theme/updateAccountThemeImplementationInConfig.ts @@ -8,12 +8,14 @@ import { id } from "tsafe/id"; export type BuildContextLike = { bundler: BuildContext["bundler"]; + projectDirPath: string; + packageJsonFilePath: string; }; assert(); export function updateAccountThemeImplementationInConfig(params: { - buildContext: BuildContext; + buildContext: BuildContextLike; accountThemeType: "Single-Page" | "Multi-Page"; }) { const { buildContext, accountThemeType } = params; From 5045c5e8bfdb82da169acb742de1d12791bb95c5 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sun, 6 Oct 2024 13:18:12 +0200 Subject: [PATCH 47/77] Add initialize-email-theme, initialize-account-theme and copy-keycloak-resources-to-public to commands that can be delegated to a custom handler --- src/bin/copy-keycloak-resources-to-public.ts | 6 ++++++ .../initialize-account-theme/initialize-account-theme.ts | 6 ++++++ src/bin/initialize-email-theme.ts | 6 ++++++ src/bin/shared/customHandler.ts | 8 +++++++- 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/bin/copy-keycloak-resources-to-public.ts b/src/bin/copy-keycloak-resources-to-public.ts index ba944068..b48f9d0e 100644 --- a/src/bin/copy-keycloak-resources-to-public.ts +++ b/src/bin/copy-keycloak-resources-to-public.ts @@ -1,9 +1,15 @@ import { copyKeycloakResourcesToPublic } from "./shared/copyKeycloakResourcesToPublic"; import type { BuildContext } from "./shared/buildContext"; +import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate"; export async function command(params: { buildContext: BuildContext }) { const { buildContext } = params; + maybeDelegateCommandToCustomHandler({ + commandName: "copy-keycloak-resources-to-public", + buildContext + }); + copyKeycloakResourcesToPublic({ buildContext }); diff --git a/src/bin/initialize-account-theme/initialize-account-theme.ts b/src/bin/initialize-account-theme/initialize-account-theme.ts index 3caed327..e7ae9223 100644 --- a/src/bin/initialize-account-theme/initialize-account-theme.ts +++ b/src/bin/initialize-account-theme/initialize-account-theme.ts @@ -6,10 +6,16 @@ import { join as pathJoin, relative as pathRelative } from "path"; import * as fs from "fs"; import { updateAccountThemeImplementationInConfig } from "./updateAccountThemeImplementationInConfig"; import { command as updateKcGenCommand } from "../update-kc-gen"; +import { maybeDelegateCommandToCustomHandler } from "../shared/customHandler_delegate"; export async function command(params: { buildContext: BuildContext }) { const { buildContext } = params; + maybeDelegateCommandToCustomHandler({ + commandName: "initialize-account-theme", + buildContext + }); + const accountThemeSrcDirPath = pathJoin(buildContext.themeSrcDirPath, "account"); if ( diff --git a/src/bin/initialize-email-theme.ts b/src/bin/initialize-email-theme.ts index dfa0287c..d5ce0fd8 100644 --- a/src/bin/initialize-email-theme.ts +++ b/src/bin/initialize-email-theme.ts @@ -4,10 +4,16 @@ import { promptKeycloakVersion } from "./shared/promptKeycloakVersion"; import type { BuildContext } from "./shared/buildContext"; import * as fs from "fs"; import { downloadAndExtractArchive } from "./tools/downloadAndExtractArchive"; +import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate"; export async function command(params: { buildContext: BuildContext }) { const { buildContext } = params; + maybeDelegateCommandToCustomHandler({ + commandName: "initialize-email-theme", + buildContext + }); + const emailThemeSrcDirPath = pathJoin(buildContext.themeSrcDirPath, "email"); if ( diff --git a/src/bin/shared/customHandler.ts b/src/bin/shared/customHandler.ts index d858aaa5..7c0b9e1c 100644 --- a/src/bin/shared/customHandler.ts +++ b/src/bin/shared/customHandler.ts @@ -6,7 +6,13 @@ export const BIN_NAME = "_keycloakify-custom-handler"; export const NOT_IMPLEMENTED_EXIT_CODE = 78; -export type CommandName = "update-kc-gen" | "eject-page" | "add-story"; +export type CommandName = + | "update-kc-gen" + | "eject-page" + | "add-story" + | "initialize-account-theme" + | "initialize-email-theme" + | "copy-keycloak-resources-to-public"; export type ApiVersion = "v1"; From 7156665684de3df18e49f9be3d60f671c70f0c81 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sun, 6 Oct 2024 13:19:12 +0200 Subject: [PATCH 48/77] Release candidate --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 791e13e6..ae774e39 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.3.0-rc.6", + "version": "11.3.0-rc.7", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From 8d31866a0be2a37c99e6fb9ea0d98c98bbcccb0c Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sun, 6 Oct 2024 15:09:53 +0200 Subject: [PATCH 49/77] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ae774e39..0fec7284 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.3.0-rc.7", + "version": "11.3.0", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From d63e5f4e542c2e3ac7d3abecb20b7d543a14d927 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sun, 6 Oct 2024 15:37:32 +0200 Subject: [PATCH 50/77] Fix litle inconsistency --- src/bin/eject-page.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/eject-page.ts b/src/bin/eject-page.ts index 8874fd99..bacafe6f 100644 --- a/src/bin/eject-page.ts +++ b/src/bin/eject-page.ts @@ -239,12 +239,12 @@ export async function command(params: { buildContext: BuildContext }) { )} copy pasted from the Keycloakify source code into your project` ); - edit_KcApp: { + edit_KcPage: { if ( pageIdOrComponent !== templateValue && pageIdOrComponent !== userProfileFormFieldsValue ) { - break edit_KcApp; + break edit_KcPage; } const kcAppTsxPath = pathJoin( From 52e33bba2d77f13c32aadecb935c861f1af7ec99 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sun, 6 Oct 2024 22:08:43 +0200 Subject: [PATCH 51/77] Fix: check for delegation of the eject-page command --- src/bin/eject-page.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/bin/eject-page.ts b/src/bin/eject-page.ts index bacafe6f..ee4a3581 100644 --- a/src/bin/eject-page.ts +++ b/src/bin/eject-page.ts @@ -22,10 +22,16 @@ import { kebabCaseToCamelCase } from "./tools/kebabCaseToSnakeCase"; import { assert, Equals } from "tsafe/assert"; import type { BuildContext } from "./shared/buildContext"; import chalk from "chalk"; +import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate"; export async function command(params: { buildContext: BuildContext }) { const { buildContext } = params; + maybeDelegateCommandToCustomHandler({ + commandName: "eject-page", + buildContext + }); + console.log(chalk.cyan("Theme type:")); const themeType = await (async () => { From 12534e57ad8cec04d36fe9f3f1d9b462369f1bc1 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sun, 6 Oct 2024 22:09:21 +0200 Subject: [PATCH 52/77] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0fec7284..0ac79b18 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.3.0", + "version": "11.3.1", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From f6b48c88b9b0b9891c0259bad7092ec32937e25d Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sun, 6 Oct 2024 22:55:18 +0200 Subject: [PATCH 53/77] Fix vite quitting if custom handler implemented --- src/bin/copy-keycloak-resources-to-public.ts | 90 +++++++++++++++++- src/bin/eject-page.ts | 6 +- .../initialize-account-theme.ts | 6 +- src/bin/initialize-email-theme.ts | 6 +- .../shared/copyKeycloakResourcesToPublic.ts | 95 ------------------- src/bin/shared/customHandler_delegate.ts | 8 +- src/bin/update-kc-gen.ts | 6 +- src/vite-plugin/vite-plugin.ts | 4 +- 8 files changed, 111 insertions(+), 110 deletions(-) delete mode 100644 src/bin/shared/copyKeycloakResourcesToPublic.ts diff --git a/src/bin/copy-keycloak-resources-to-public.ts b/src/bin/copy-keycloak-resources-to-public.ts index b48f9d0e..1925860d 100644 --- a/src/bin/copy-keycloak-resources-to-public.ts +++ b/src/bin/copy-keycloak-resources-to-public.ts @@ -1,16 +1,96 @@ -import { copyKeycloakResourcesToPublic } from "./shared/copyKeycloakResourcesToPublic"; -import type { BuildContext } from "./shared/buildContext"; import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate"; +import { join as pathJoin, dirname as pathDirname } from "path"; +import { WELL_KNOWN_DIRECTORY_BASE_NAME } from "./shared/constants"; +import { readThisNpmPackageVersion } from "./tools/readThisNpmPackageVersion"; +import * as fs from "fs"; +import { rmSync } from "./tools/fs.rmSync"; +import type { BuildContext } from "./shared/buildContext"; +import { transformCodebase } from "./tools/transformCodebase"; +import { getThisCodebaseRootDirPath } from "./tools/getThisCodebaseRootDirPath"; export async function command(params: { buildContext: BuildContext }) { const { buildContext } = params; - maybeDelegateCommandToCustomHandler({ + const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({ commandName: "copy-keycloak-resources-to-public", buildContext }); - copyKeycloakResourcesToPublic({ - buildContext + if (hasBeenHandled) { + return; + } + + const destDirPath = pathJoin( + buildContext.publicDirPath, + WELL_KNOWN_DIRECTORY_BASE_NAME.KEYCLOAKIFY_DEV_RESOURCES + ); + + const keycloakifyBuildinfoFilePath = pathJoin(destDirPath, "keycloakify.buildinfo"); + + const keycloakifyBuildinfoRaw = JSON.stringify( + { + keycloakifyVersion: readThisNpmPackageVersion() + }, + null, + 2 + ); + + skip_if_already_done: { + if (!fs.existsSync(keycloakifyBuildinfoFilePath)) { + break skip_if_already_done; + } + + const keycloakifyBuildinfoRaw_previousRun = fs + .readFileSync(keycloakifyBuildinfoFilePath) + .toString("utf8"); + + if (keycloakifyBuildinfoRaw_previousRun !== keycloakifyBuildinfoRaw) { + break skip_if_already_done; + } + + return; + } + + rmSync(destDirPath, { force: true, recursive: true }); + + // NOTE: To remove in a while, remove the legacy keycloak-resources directory + rmSync(pathJoin(pathDirname(destDirPath), "keycloak-resources"), { + force: true, + recursive: true }); + rmSync(pathJoin(pathDirname(destDirPath), ".keycloakify"), { + force: true, + recursive: true + }); + + fs.mkdirSync(destDirPath, { recursive: true }); + + fs.writeFileSync(pathJoin(destDirPath, ".gitignore"), Buffer.from("*", "utf8")); + + transformCodebase({ + srcDirPath: pathJoin( + getThisCodebaseRootDirPath(), + "res", + "public", + WELL_KNOWN_DIRECTORY_BASE_NAME.KEYCLOAKIFY_DEV_RESOURCES + ), + destDirPath + }); + + fs.writeFileSync( + pathJoin(destDirPath, "README.txt"), + Buffer.from( + // prettier-ignore + [ + "This directory is only used in dev mode by Keycloakify", + "It won't be included in your final build.", + "Do not modify anything in this directory.", + ].join("\n") + ) + ); + + fs.writeFileSync( + keycloakifyBuildinfoFilePath, + Buffer.from(keycloakifyBuildinfoRaw, "utf8") + ); } diff --git a/src/bin/eject-page.ts b/src/bin/eject-page.ts index ee4a3581..e982c2f7 100644 --- a/src/bin/eject-page.ts +++ b/src/bin/eject-page.ts @@ -27,11 +27,15 @@ import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_dele export async function command(params: { buildContext: BuildContext }) { const { buildContext } = params; - maybeDelegateCommandToCustomHandler({ + const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({ commandName: "eject-page", buildContext }); + if (hasBeenHandled) { + return; + } + console.log(chalk.cyan("Theme type:")); const themeType = await (async () => { diff --git a/src/bin/initialize-account-theme/initialize-account-theme.ts b/src/bin/initialize-account-theme/initialize-account-theme.ts index e7ae9223..dd7c61c8 100644 --- a/src/bin/initialize-account-theme/initialize-account-theme.ts +++ b/src/bin/initialize-account-theme/initialize-account-theme.ts @@ -11,11 +11,15 @@ import { maybeDelegateCommandToCustomHandler } from "../shared/customHandler_del export async function command(params: { buildContext: BuildContext }) { const { buildContext } = params; - maybeDelegateCommandToCustomHandler({ + const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({ commandName: "initialize-account-theme", buildContext }); + if (hasBeenHandled) { + return; + } + const accountThemeSrcDirPath = pathJoin(buildContext.themeSrcDirPath, "account"); if ( diff --git a/src/bin/initialize-email-theme.ts b/src/bin/initialize-email-theme.ts index d5ce0fd8..7a81205b 100644 --- a/src/bin/initialize-email-theme.ts +++ b/src/bin/initialize-email-theme.ts @@ -9,11 +9,15 @@ import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_dele export async function command(params: { buildContext: BuildContext }) { const { buildContext } = params; - maybeDelegateCommandToCustomHandler({ + const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({ commandName: "initialize-email-theme", buildContext }); + if (hasBeenHandled) { + return; + } + const emailThemeSrcDirPath = pathJoin(buildContext.themeSrcDirPath, "email"); if ( diff --git a/src/bin/shared/copyKeycloakResourcesToPublic.ts b/src/bin/shared/copyKeycloakResourcesToPublic.ts deleted file mode 100644 index db78d313..00000000 --- a/src/bin/shared/copyKeycloakResourcesToPublic.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { join as pathJoin, dirname as pathDirname } from "path"; -import { WELL_KNOWN_DIRECTORY_BASE_NAME } from "../shared/constants"; -import { readThisNpmPackageVersion } from "../tools/readThisNpmPackageVersion"; -import { assert } from "tsafe/assert"; -import * as fs from "fs"; -import { rmSync } from "../tools/fs.rmSync"; -import type { BuildContext } from "./buildContext"; -import { transformCodebase } from "../tools/transformCodebase"; -import { getThisCodebaseRootDirPath } from "../tools/getThisCodebaseRootDirPath"; - -export type BuildContextLike = { - publicDirPath: string; -}; - -assert(); - -export function copyKeycloakResourcesToPublic(params: { - buildContext: BuildContextLike; -}) { - const { buildContext } = params; - - const destDirPath = pathJoin( - buildContext.publicDirPath, - WELL_KNOWN_DIRECTORY_BASE_NAME.KEYCLOAKIFY_DEV_RESOURCES - ); - - const keycloakifyBuildinfoFilePath = pathJoin(destDirPath, "keycloakify.buildinfo"); - - const keycloakifyBuildinfoRaw = JSON.stringify( - { - keycloakifyVersion: readThisNpmPackageVersion() - }, - null, - 2 - ); - - skip_if_already_done: { - if (!fs.existsSync(keycloakifyBuildinfoFilePath)) { - break skip_if_already_done; - } - - const keycloakifyBuildinfoRaw_previousRun = fs - .readFileSync(keycloakifyBuildinfoFilePath) - .toString("utf8"); - - if (keycloakifyBuildinfoRaw_previousRun !== keycloakifyBuildinfoRaw) { - break skip_if_already_done; - } - - return; - } - - rmSync(destDirPath, { force: true, recursive: true }); - - // NOTE: To remove in a while, remove the legacy keycloak-resources directory - rmSync(pathJoin(pathDirname(destDirPath), "keycloak-resources"), { - force: true, - recursive: true - }); - rmSync(pathJoin(pathDirname(destDirPath), ".keycloakify"), { - force: true, - recursive: true - }); - - fs.mkdirSync(destDirPath, { recursive: true }); - - fs.writeFileSync(pathJoin(destDirPath, ".gitignore"), Buffer.from("*", "utf8")); - - transformCodebase({ - srcDirPath: pathJoin( - getThisCodebaseRootDirPath(), - "res", - "public", - WELL_KNOWN_DIRECTORY_BASE_NAME.KEYCLOAKIFY_DEV_RESOURCES - ), - destDirPath - }); - - fs.writeFileSync( - pathJoin(destDirPath, "README.txt"), - Buffer.from( - // prettier-ignore - [ - "This directory is only used in dev mode by Keycloakify", - "It won't be included in your final build.", - "Do not modify anything in this directory.", - ].join("\n") - ) - ); - - fs.writeFileSync( - keycloakifyBuildinfoFilePath, - Buffer.from(keycloakifyBuildinfoRaw, "utf8") - ); -} diff --git a/src/bin/shared/customHandler_delegate.ts b/src/bin/shared/customHandler_delegate.ts index a41cbc7b..691989df 100644 --- a/src/bin/shared/customHandler_delegate.ts +++ b/src/bin/shared/customHandler_delegate.ts @@ -16,11 +16,11 @@ assert>(); export function maybeDelegateCommandToCustomHandler(params: { commandName: CommandName; buildContext: BuildContext; -}) { +}): { hasBeenHandled: boolean } { const { commandName, buildContext } = params; if (!fs.readdirSync(pathDirname(process.argv[1])).includes(BIN_NAME)) { - return; + return { hasBeenHandled: false }; } try { @@ -36,11 +36,11 @@ export function maybeDelegateCommandToCustomHandler(params: { const status = error.status; if (status === NOT_IMPLEMENTED_EXIT_CODE) { - return; + return { hasBeenHandled: false }; } process.exit(status); } - process.exit(0); + return { hasBeenHandled: true }; } diff --git a/src/bin/update-kc-gen.ts b/src/bin/update-kc-gen.ts index f126bcb1..e1366fd4 100644 --- a/src/bin/update-kc-gen.ts +++ b/src/bin/update-kc-gen.ts @@ -7,11 +7,15 @@ import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_dele export async function command(params: { buildContext: BuildContext }) { const { buildContext } = params; - maybeDelegateCommandToCustomHandler({ + const { hasBeenHandled } = maybeDelegateCommandToCustomHandler({ commandName: "update-kc-gen", buildContext }); + if (hasBeenHandled) { + return; + } + const filePath = pathJoin(buildContext.themeSrcDirPath, `kc.gen.tsx`); const currentContent = (await existsAsync(filePath)) diff --git a/src/vite-plugin/vite-plugin.ts b/src/vite-plugin/vite-plugin.ts index e6dfb054..d7ec06d5 100644 --- a/src/vite-plugin/vite-plugin.ts +++ b/src/vite-plugin/vite-plugin.ts @@ -6,7 +6,7 @@ import { } from "../bin/shared/constants"; import { id } from "tsafe/id"; import { rm } from "../bin/tools/fs.rm"; -import { copyKeycloakResourcesToPublic } from "../bin/shared/copyKeycloakResourcesToPublic"; +import { command as copyKeycloakResourcesToPublicCommand } from "../bin/copy-keycloak-resources-to-public"; import { assert } from "tsafe/assert"; import { getBuildContext, @@ -125,7 +125,7 @@ export function keycloakify(params: keycloakify.Params) { projectDirPath }); - copyKeycloakResourcesToPublic({ buildContext }); + await copyKeycloakResourcesToPublicCommand({ buildContext }); await updateKcGenCommand({ buildContext }); }, From 88de58cc2231a9269bc8efbf9ba7ef56b881f396 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sun, 6 Oct 2024 22:56:42 +0200 Subject: [PATCH 54/77] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0ac79b18..f2e015ac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.3.1", + "version": "11.3.2", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From 4cdf26b6f91e64f0ae9fafc8e68d175c1a8152ef Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Mon, 7 Oct 2024 20:56:03 +0200 Subject: [PATCH 55/77] Announcement about Keycloak 26 --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 2c133223..c6edf2d0 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,11 @@ Keycloakify is fully compatible with Keycloak from version 11 to 26...[and beyond](https://github.com/keycloakify/keycloakify/discussions/346#discussioncomment-5889791) +> πŸ“£ **Keycloakify 26 Released** +> Themes built with Keycloakify versions **prior** to Keycloak 26 are **incompatible** with Keycloak 26. +> To ensure compatibility, simply upgrade to the latest Keycloakify version for your major release (v10 or v11) and rebuild your theme. +> No breaking changes have been introduced, but the target version ranges have been updated. For more details, see [this guide](https://docs.keycloakify.dev/targeting-specific-keycloak-versions). + ## Sponsors Friends for the project, we trust and recommend their services. From 9e19aafcd028f7e0cd09d7abe1f31c8470dd5066 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Mon, 7 Oct 2024 21:02:51 +0200 Subject: [PATCH 56/77] Fix initialize-email-theme --- src/bin/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/main.ts b/src/bin/main.ts index 89b03d7c..a6abe1f5 100644 --- a/src/bin/main.ts +++ b/src/bin/main.ts @@ -171,7 +171,7 @@ program program .command({ - name: "initialize-login-theme", + name: "initialize-email-theme", description: "Initialize an email theme." }) .task({ From eb23b40e5c1e061a6553585cce62c39e3ecb00ff Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Mon, 7 Oct 2024 21:03:04 +0200 Subject: [PATCH 57/77] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f2e015ac..cf03259f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.3.2", + "version": "11.3.3", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From 61a86e8e821b359d3fd0ddf79708ece5a8363172 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 21:47:10 +0000 Subject: [PATCH 58/77] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c6edf2d0..11eff6fe 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Omid
Omid

⚠️ πŸ’» Katharina Eiserfey
Katharina Eiserfey

πŸ’» ⚠️ πŸ“– Luca Peruzzo
Luca Peruzzo

πŸ’» ⚠️ + Nima Shokouhfar
Nima Shokouhfar

πŸ’» ⚠️ From 3e38beb19008821b7910366a0834a6d2114f11e7 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 21:47:11 +0000 Subject: [PATCH 59/77] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index fdede216..f7d39edd 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -290,6 +290,16 @@ "code", "test" ] + }, + { + "login": "nima70", + "name": "Nima Shokouhfar", + "avatar_url": "https://avatars.githubusercontent.com/u/5094767?v=4", + "profile": "https://github.com/nima70", + "contributions": [ + "code", + "test" + ] } ], "contributorsPerLine": 7, From ecfdff5454d8cb997a306fe69277582d1f09309d Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Mon, 7 Oct 2024 23:53:05 +0200 Subject: [PATCH 60/77] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cf03259f..9be229b0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.3.3", + "version": "11.3.4", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From 94e278629709d917258af1dc10d9f0bd696953cf Mon Sep 17 00:00:00 2001 From: Liam Lowsley-Williams Date: Tue, 8 Oct 2024 16:50:11 -0500 Subject: [PATCH 61/77] fix: added parameter type for story context on register page --- stories/login/pages/Register.stories.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stories/login/pages/Register.stories.tsx b/stories/login/pages/Register.stories.tsx index 8ab6c16d..3250f927 100644 --- a/stories/login/pages/Register.stories.tsx +++ b/stories/login/pages/Register.stories.tsx @@ -240,8 +240,8 @@ export const WithFieldErrors: Story = { } }, messagesPerField: { - existsError: fieldName => ["username", "email"].includes(fieldName), - get: fieldName => { + existsError: (fieldName: string) => ["username", "email"].includes(fieldName), + get: (fieldName: string) => { if (fieldName === "username") return "Username is required."; if (fieldName === "email") return "Invalid email format."; } From fbf6a329df7a549902209672be71c74cb334df30 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Fri, 11 Oct 2024 23:55:04 +0200 Subject: [PATCH 62/77] typesafety fix --- .../updateAccountThemeImplementationInConfig.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bin/initialize-account-theme/updateAccountThemeImplementationInConfig.ts b/src/bin/initialize-account-theme/updateAccountThemeImplementationInConfig.ts index 1c60ef03..65b3ba39 100644 --- a/src/bin/initialize-account-theme/updateAccountThemeImplementationInConfig.ts +++ b/src/bin/initialize-account-theme/updateAccountThemeImplementationInConfig.ts @@ -5,6 +5,7 @@ import * as fs from "fs"; import chalk from "chalk"; import { z } from "zod"; import { id } from "tsafe/id"; +import { is } from "tsafe/is"; export type BuildContextLike = { bundler: BuildContext["bundler"]; @@ -83,6 +84,8 @@ export function updateAccountThemeImplementationInConfig(params: { zParsedPackageJson.parse(parsedPackageJson); + assert(is(parsedPackageJson)); + return parsedPackageJson; })(); From 59c4675e8a2c19365c26e4953a0304d1ad413587 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sat, 12 Oct 2024 17:30:30 +0200 Subject: [PATCH 63/77] Add dir=rtl attribut to html when using a RTL language --- src/login/KcContext/KcContext.ts | 1 + src/login/i18n/noJsx/getI18n.tsx | 44 ++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/login/KcContext/KcContext.ts b/src/login/KcContext/KcContext.ts index 14d1169f..161e45de 100644 --- a/src/login/KcContext/KcContext.ts +++ b/src/login/KcContext/KcContext.ts @@ -94,6 +94,7 @@ export declare namespace KcContext { languageTag: string; }[]; currentLanguageTag: string; + rtl?: boolean; }; auth?: { showUsername?: boolean; diff --git a/src/login/i18n/noJsx/getI18n.tsx b/src/login/i18n/noJsx/getI18n.tsx index e560e2e3..f619a303 100644 --- a/src/login/i18n/noJsx/getI18n.tsx +++ b/src/login/i18n/noJsx/getI18n.tsx @@ -19,6 +19,7 @@ export type KcContextLike = { locale?: { currentLanguageTag: string; supported: { languageTag: string; url: string; label: string }[]; + rtl?: boolean; }; "x-keycloakify": { messages: Record; @@ -95,6 +96,49 @@ export function createGetI18n< const html = document.querySelector("html"); assert(html !== null); html.lang = currentLanguageTag; + + const isRtl = (() => { + const { rtl } = kcContext.locale ?? {}; + + if (rtl !== undefined) { + return rtl; + } + + return [ + /* spell-checker: disable */ + // Common RTL languages + "ar", // Arabic + "fa", // Persian (Farsi) + "he", // Hebrew + "ur", // Urdu + "ps", // Pashto + "syr", // Syriac + "dv", // Divehi (Maldivian) + "ku", // Kurdish (Sorani) + "ug", // Uighur + "az", // Azerbaijani (Arabic script) + "sd", // Sindhi + + // Less common RTL languages + "yi", // Yiddish + "ha", // Hausa (when written in Arabic script) + "ks", // Kashmiri (written in the Perso-Arabic script) + "bal", // Balochi (when written in Arabic script) + "khw", // Khowar (Chitrali) + "brh", // Brahui (when written in Arabic script) + "tmh", // Tamashek (some dialects use Arabic script) + "bgn", // Western Balochi + "arc", // Aramaic + "sam", // Samaritan Aramaic + "prd", // Parsi-Dari (a dialect of Persian) + "huz", // Hazaragi (a dialect of Persian) + "gbz", // Zaza (written in Arabic script in some areas) + "urj" // Urdu in Romanized script (not always RTL, but to account for edge cases) + /* spell-checker: enable */ + ].includes(currentLanguageTag); + })(); + + html.dir = isRtl ? "rtl" : "ltr"; } const getLanguageLabel = (languageTag: LanguageTag) => { From b86039536e2752251ba1bd44757ff56829b54ea6 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sat, 12 Oct 2024 17:33:44 +0200 Subject: [PATCH 64/77] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9be229b0..cfa9d0cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.3.4", + "version": "11.3.5", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From f5a9a281244b267b1dc616ee5cfb859f983f6fa2 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sun, 13 Oct 2024 00:55:06 +0200 Subject: [PATCH 65/77] Resolve package.json path relative to the package.json --- src/bin/shared/buildContext.ts | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/bin/shared/buildContext.ts b/src/bin/shared/buildContext.ts index 26c746b8..89afa6f9 100644 --- a/src/bin/shared/buildContext.ts +++ b/src/bin/shared/buildContext.ts @@ -508,6 +508,15 @@ export function getBuildContext(params: { return themeNames; })(); + const relativePathsCwd = (() => { + switch (bundler) { + case "vite": + return projectDirPath; + case "webpack": + return pathDirname(packageJsonFilePath); + } + })(); + const projectBuildDirPath = (() => { webpack: { if (bundler !== "webpack") { @@ -519,7 +528,7 @@ export function getBuildContext(params: { if (parsedPackageJson.keycloakify.projectBuildDirPath !== undefined) { return getAbsoluteAndInOsFormatPath({ pathIsh: parsedPackageJson.keycloakify.projectBuildDirPath, - cwd: projectDirPath + cwd: relativePathsCwd }); } @@ -563,7 +572,7 @@ export function getBuildContext(params: { if (buildOptions.keycloakifyBuildDirPath !== undefined) { return getAbsoluteAndInOsFormatPath({ pathIsh: buildOptions.keycloakifyBuildDirPath, - cwd: projectDirPath + cwd: relativePathsCwd }); } @@ -592,7 +601,7 @@ export function getBuildContext(params: { if (parsedPackageJson.keycloakify.publicDirPath !== undefined) { return getAbsoluteAndInOsFormatPath({ pathIsh: parsedPackageJson.keycloakify.publicDirPath, - cwd: projectDirPath + cwd: relativePathsCwd }); } @@ -664,7 +673,7 @@ export function getBuildContext(params: { pathIsh: parsedPackageJson.keycloakify .staticDirPathInProjectBuildDirPath, - cwd: projectBuildDirPath + cwd: relativePathsCwd }); } @@ -992,7 +1001,7 @@ export function getBuildContext(params: { type: "path", path: getAbsoluteAndInOsFormatPath({ pathIsh: urlOrPath, - cwd: projectDirPath + cwd: relativePathsCwd }) }; } @@ -1002,7 +1011,7 @@ export function getBuildContext(params: { ? undefined : getAbsoluteAndInOsFormatPath({ pathIsh: buildOptions.startKeycloakOptions.realmJsonFilePath, - cwd: projectDirPath + cwd: relativePathsCwd }), port: buildOptions.startKeycloakOptions?.port } From d56c53644687668bed774e36caca3e3ef2afcb16 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sun, 13 Oct 2024 00:55:23 +0200 Subject: [PATCH 66/77] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cfa9d0cd..e6259f12 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.3.5", + "version": "11.3.6", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From c932c7d8f66bdb08fa42bbcbd46ff5857176b648 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Wed, 16 Oct 2024 03:37:00 +0200 Subject: [PATCH 67/77] Resize zone2 logo --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 11eff6fe..2b09cd04 100644 --- a/README.md +++ b/README.md @@ -50,19 +50,19 @@ Keycloakify is fully compatible with Keycloak from version 11 to 26...[and beyon ## Sponsors -Friends for the project, we trust and recommend their services. +Project backers, we trust and recommend their services.
-![Logo Dark](https://github.com/user-attachments/assets/088f6631-b7ef-42ad-812b-df4870dc16ae#gh-dark-mode-only) +![Logo Dark](https://github.com/user-attachments/assets/dd3925fb-a58a-4e91-b360-69c2fa1f1087)
-![Logo Light](https://github.com/user-attachments/assets/53fb16f8-02ef-4523-9c36-b42d6e59837e#gh-light-mode-only) +![Logo Light](https://github.com/user-attachments/assets/6c00c201-eed7-485a-a887-70891559d69b#gh-light-mode-only)
From 06fe26fbe71b2ae4f3d9e1f3c409da287df7b65c Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Wed, 16 Oct 2024 04:10:17 +0200 Subject: [PATCH 68/77] Add phaseTwo as sponsor --- README.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2b09cd04..6f3eb47a 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,28 @@ Project backers, we trust and recommend their services.
+![Logo Dark](https://github.com/user-attachments/assets/d8f6b6f5-3de4-4adc-ba15-cb4074e8309b) + +
+ +
+ +![Logo Light](https://github.com/user-attachments/assets/20736d6f-f22d-4a9d-9dfe-93be209a8191#gh-light-mode-only) + +
+ +
+ +

+ Keycloak on Steroids as a Service - Keycloak community contributors of popular extensions providing free and dedicated Keycloak hosting and enterprise Keycloak support to businesses of all sizes. +

+ +
+
+
+ +
+ ![Logo Dark](https://github.com/user-attachments/assets/dd3925fb-a58a-4e91-b360-69c2fa1f1087)
@@ -87,7 +109,7 @@ Project backers, we trust and recommend their services.

-Managed Keycloak Provider - With Cloud-IAM powering your Keycloak clusters, you can sleep easy knowing you've got the software and the experts you need for operational excellence. +Managed Keycloak Provider - With Cloud-IAM powering your Keycloak clusters, you can sleep easy knowing you've got the software and the experts you need for operational excellence. Cloud IAM is a french company.
Use code keycloakify5 at checkout for a 5% discount.

From 7c7e5544e4afa358911b2ee958f74d53844d9744 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Wed, 16 Oct 2024 05:13:52 +0200 Subject: [PATCH 69/77] Fix light mode rendering --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6f3eb47a..af64954c 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Project backers, we trust and recommend their services.
-![Logo Dark](https://github.com/user-attachments/assets/d8f6b6f5-3de4-4adc-ba15-cb4074e8309b) +![Logo Dark](https://github.com/user-attachments/assets/d8f6b6f5-3de4-4adc-ba15-cb4074e8309b#gh-dark-mode-only)
@@ -78,7 +78,7 @@ Project backers, we trust and recommend their services.
-![Logo Dark](https://github.com/user-attachments/assets/dd3925fb-a58a-4e91-b360-69c2fa1f1087) +![Logo Dark](https://github.com/user-attachments/assets/dd3925fb-a58a-4e91-b360-69c2fa1f1087#gh-dark-mode-only)
From ee1b6868f80b2c685e4c6798f67164649bcc42a9 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Thu, 17 Oct 2024 19:54:14 +0200 Subject: [PATCH 70/77] Fix Phase two links --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index af64954c..81228618 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ Project backers, we trust and recommend their services.

- Keycloak on Steroids as a Service - Keycloak community contributors of popular extensions providing free and dedicated Keycloak hosting and enterprise Keycloak support to businesses of all sizes. + Keycloak as a Service - Keycloak community contributors of popular extensions providing free and dedicated Keycloak hosting and enterprise Keycloak support to businesses of all sizes.


From 31d7a938f2d0abd2cd4bc1aaeb5702b14fc81943 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Thu, 17 Oct 2024 23:23:26 +0200 Subject: [PATCH 71/77] #696 --- src/bin/initialize-email-theme.ts | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/bin/initialize-email-theme.ts b/src/bin/initialize-email-theme.ts index 7a81205b..62a5566a 100644 --- a/src/bin/initialize-email-theme.ts +++ b/src/bin/initialize-email-theme.ts @@ -5,6 +5,9 @@ import type { BuildContext } from "./shared/buildContext"; import * as fs from "fs"; import { downloadAndExtractArchive } from "./tools/downloadAndExtractArchive"; import { maybeDelegateCommandToCustomHandler } from "./shared/customHandler_delegate"; +import fetch from "make-fetch-happen"; +import { SemVer } from "./tools/SemVer"; +import { assert } from "tsafe/assert"; export async function command(params: { buildContext: BuildContext }) { const { buildContext } = params; @@ -36,7 +39,7 @@ export async function command(params: { buildContext: BuildContext }) { console.log("Initialize with the base email theme from which version of Keycloak?"); - const { keycloakVersion } = await promptKeycloakVersion({ + let { keycloakVersion } = await promptKeycloakVersion({ // NOTE: This is arbitrary startingFromMajor: 17, excludeMajorVersions: [], @@ -44,8 +47,32 @@ export async function command(params: { buildContext: BuildContext }) { buildContext }); + const getUrl = (keycloakVersion: string) => { + return `https://repo1.maven.org/maven2/org/keycloak/keycloak-themes/${keycloakVersion}/keycloak-themes-${keycloakVersion}.jar`; + }; + + keycloakVersion = await (async () => { + const keycloakVersionParsed = SemVer.parse(keycloakVersion); + + while (true) { + const url = getUrl(SemVer.stringify(keycloakVersionParsed)); + + const response = await fetch(url, buildContext.fetchOptions); + + if (response.ok) { + break; + } + + assert(keycloakVersionParsed.patch !== 0); + + keycloakVersionParsed.patch--; + } + + return SemVer.stringify(keycloakVersionParsed); + })(); + const { extractedDirPath } = await downloadAndExtractArchive({ - url: `https://repo1.maven.org/maven2/org/keycloak/keycloak-themes/${keycloakVersion}/keycloak-themes-${keycloakVersion}.jar`, + url: getUrl(keycloakVersion), cacheDirPath: buildContext.cacheDirPath, fetchOptions: buildContext.fetchOptions, uniqueIdOfOnArchiveFile: "extractOnlyEmailTheme", From 80fd4095c4037ab04de20f130ae83ece9da1a19d Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Thu, 17 Oct 2024 23:23:52 +0200 Subject: [PATCH 72/77] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e6259f12..ccb76d98 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.3.6", + "version": "11.3.7", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git", From c63648a1b007eaa2a0cfc2ae27ca364f77545535 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sat, 19 Oct 2024 02:19:41 +0200 Subject: [PATCH 73/77] #693 #692 --- ...skeysConditionalAuthenticate.useScript.tsx | 9 +++++- ...LoginRecoveryAuthnCodeConfig.useScript.tsx | 9 +++++- .../pages/WebauthnAuthenticate.useScript.tsx | 9 +++++- .../pages/WebauthnRegister.useScript.tsx | 9 +++++- src/tools/waitForElementMountedOnDom.ts | 30 +++++++++++++++++++ 5 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 src/tools/waitForElementMountedOnDom.ts diff --git a/src/login/pages/LoginPasskeysConditionalAuthenticate.useScript.tsx b/src/login/pages/LoginPasskeysConditionalAuthenticate.useScript.tsx index 9cd1c867..4807c6c1 100644 --- a/src/login/pages/LoginPasskeysConditionalAuthenticate.useScript.tsx +++ b/src/login/pages/LoginPasskeysConditionalAuthenticate.useScript.tsx @@ -2,6 +2,7 @@ import { useEffect } from "react"; import { useInsertScriptTags } from "keycloakify/tools/useInsertScriptTags"; import { assert } from "keycloakify/tools/assert"; import { KcContext } from "keycloakify/login/KcContext/KcContext"; +import { waitForElementMountedOnDom } from "keycloakify/tools/waitForElementMountedOnDom"; type KcContextLike = { url: { @@ -67,6 +68,12 @@ export function useScript(params: { authButtonId: string; kcContext: KcContextLi return; } - insertScriptTags(); + (async () => { + await waitForElementMountedOnDom({ + elementId: authButtonId + }); + + insertScriptTags(); + })(); }, [isFetchingTranslations]); } diff --git a/src/login/pages/LoginRecoveryAuthnCodeConfig.useScript.tsx b/src/login/pages/LoginRecoveryAuthnCodeConfig.useScript.tsx index 8a5f9586..ef882462 100644 --- a/src/login/pages/LoginRecoveryAuthnCodeConfig.useScript.tsx +++ b/src/login/pages/LoginRecoveryAuthnCodeConfig.useScript.tsx @@ -1,5 +1,6 @@ import { useEffect } from "react"; import { useInsertScriptTags } from "keycloakify/tools/useInsertScriptTags"; +import { waitForElementMountedOnDom } from "keycloakify/tools/waitForElementMountedOnDom"; type I18nLike = { msgStr: (key: "recovery-codes-download-file-header" | "recovery-codes-download-file-description" | "recovery-codes-download-file-date") => string; @@ -137,6 +138,12 @@ export function useScript(params: { olRecoveryCodesListId: string; i18n: I18nLik return; } - insertScriptTags(); + (async () => { + await waitForElementMountedOnDom({ + elementId: olRecoveryCodesListId + }); + + insertScriptTags(); + })(); }, [isFetchingTranslations]); } diff --git a/src/login/pages/WebauthnAuthenticate.useScript.tsx b/src/login/pages/WebauthnAuthenticate.useScript.tsx index bc8fde5a..0ffde595 100644 --- a/src/login/pages/WebauthnAuthenticate.useScript.tsx +++ b/src/login/pages/WebauthnAuthenticate.useScript.tsx @@ -2,6 +2,7 @@ import { useEffect } from "react"; import { useInsertScriptTags } from "keycloakify/tools/useInsertScriptTags"; import { assert } from "keycloakify/tools/assert"; import { KcContext } from "keycloakify/login/KcContext/KcContext"; +import { waitForElementMountedOnDom } from "keycloakify/tools/waitForElementMountedOnDom"; type KcContextLike = { url: { @@ -59,6 +60,12 @@ export function useScript(params: { authButtonId: string; kcContext: KcContextLi return; } - insertScriptTags(); + (async () => { + await waitForElementMountedOnDom({ + elementId: authButtonId + }); + + insertScriptTags(); + })(); }, [isFetchingTranslations]); } diff --git a/src/login/pages/WebauthnRegister.useScript.tsx b/src/login/pages/WebauthnRegister.useScript.tsx index c1d8cd66..98f3dfa7 100644 --- a/src/login/pages/WebauthnRegister.useScript.tsx +++ b/src/login/pages/WebauthnRegister.useScript.tsx @@ -2,6 +2,7 @@ import { useEffect } from "react"; import { useInsertScriptTags } from "keycloakify/tools/useInsertScriptTags"; import { assert } from "keycloakify/tools/assert"; import { KcContext } from "keycloakify/login/KcContext/KcContext"; +import { waitForElementMountedOnDom } from "keycloakify/tools/waitForElementMountedOnDom"; type KcContextLike = { url: { @@ -88,6 +89,12 @@ export function useScript(params: { authButtonId: string; kcContext: KcContextLi return; } - insertScriptTags(); + (async () => { + await waitForElementMountedOnDom({ + elementId: authButtonId + }); + + insertScriptTags(); + })(); }, [isFetchingTranslations]); } diff --git a/src/tools/waitForElementMountedOnDom.ts b/src/tools/waitForElementMountedOnDom.ts new file mode 100644 index 00000000..08934f74 --- /dev/null +++ b/src/tools/waitForElementMountedOnDom.ts @@ -0,0 +1,30 @@ +export async function waitForElementMountedOnDom(params: { + elementId: string; +}): Promise { + const { elementId } = params; + + const getElement = () => document.getElementById(elementId); + + const element = getElement(); + + if (element === null) { + let prElementPresentInTheDom_resolve: () => void; + const prElementPresentInTheDom = new Promise( + resolve => (prElementPresentInTheDom_resolve = resolve) + ); + + // Observe the dom for the element to be added + const observer = new MutationObserver(() => { + const element = getElement(); + if (element === null) { + return; + } + observer.disconnect(); + prElementPresentInTheDom_resolve(); + }); + + observer.observe(document.body, { childList: true, subtree: true }); + + await prElementPresentInTheDom; + } +} From 5ffc42c9db4caa110fc3a3ea174c428cc1285835 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 19 Oct 2024 00:24:36 +0000 Subject: [PATCH 74/77] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 81228618..9894e35a 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Katharina Eiserfey
Katharina Eiserfey

πŸ’» ⚠️ πŸ“– Luca Peruzzo
Luca Peruzzo

πŸ’» ⚠️ Nima Shokouhfar
Nima Shokouhfar

πŸ’» ⚠️ + Marvin A. Ruder
Marvin A. Ruder

πŸ› From dfe1e7ddd1938f9dc0f6274dc7c94a7d4157b085 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 19 Oct 2024 00:24:37 +0000 Subject: [PATCH 75/77] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index f7d39edd..64b2924f 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -300,6 +300,15 @@ "code", "test" ] + }, + { + "login": "marvinruder", + "name": "Marvin A. Ruder", + "avatar_url": "https://avatars.githubusercontent.com/u/18495294?v=4", + "profile": "https://mruder.dev", + "contributions": [ + "bug" + ] } ], "contributorsPerLine": 7, From e57232edde2b5cfa19892006d4ded4ef2ac16ab3 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sat, 19 Oct 2024 02:28:11 +0200 Subject: [PATCH 76/77] #694 Probably some shell handle double quote differently --- src/bin/start-keycloak/start-keycloak.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bin/start-keycloak/start-keycloak.ts b/src/bin/start-keycloak/start-keycloak.ts index e243fe2b..4e74bed6 100644 --- a/src/bin/start-keycloak/start-keycloak.ts +++ b/src/bin/start-keycloak/start-keycloak.ts @@ -396,12 +396,12 @@ export async function command(params: { ...(realmJsonFilePath === undefined ? [] : [ - `-v${SPACE_PLACEHOLDER}".${pathSep}${pathRelative(process.cwd(), realmJsonFilePath)}":/opt/keycloak/data/import/myrealm-realm.json` + `-v${SPACE_PLACEHOLDER}"${realmJsonFilePath}":/opt/keycloak/data/import/myrealm-realm.json` ]), - `-v${SPACE_PLACEHOLDER}".${pathSep}${pathRelative(process.cwd(), jarFilePath_cacheDir)}":/opt/keycloak/providers/keycloak-theme.jar`, + `-v${SPACE_PLACEHOLDER}"${jarFilePath_cacheDir}":/opt/keycloak/providers/keycloak-theme.jar`, ...extensionJarFilePaths.map( jarFilePath => - `-v${SPACE_PLACEHOLDER}".${pathSep}${pathRelative(process.cwd(), jarFilePath)}":/opt/keycloak/providers/${pathBasename(jarFilePath)}` + `-v${SPACE_PLACEHOLDER}"${jarFilePath}":/opt/keycloak/providers/${pathBasename(jarFilePath)}` ), ...(keycloakMajorVersionNumber <= 20 ? [`-e${SPACE_PLACEHOLDER}JAVA_OPTS=-Dkeycloak.profile=preview`] @@ -424,7 +424,7 @@ export async function command(params: { })) .map( ({ localDirPath, containerDirPath }) => - `-v${SPACE_PLACEHOLDER}".${pathSep}${pathRelative(process.cwd(), localDirPath)}":${containerDirPath}:rw` + `-v${SPACE_PLACEHOLDER}"${localDirPath}":${containerDirPath}:rw` ), ...buildContext.environmentVariables .map(({ name }) => ({ name, envValue: process.env[name] })) From 0d090d50d4cfdcb8906db8236724807ba42b4cb9 Mon Sep 17 00:00:00 2001 From: Joseph Garrone Date: Sat, 19 Oct 2024 02:28:32 +0200 Subject: [PATCH 77/77] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ccb76d98..67c0865f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keycloakify", - "version": "11.3.7", + "version": "11.3.8", "description": "Framework to create custom Keycloak UIs", "repository": { "type": "git",