import type { ValueOf } from "../../tools/ValueOf"; import type { ClassKey } from "../../login/lib/kcClsx"; export type ExtendKcContext; }, KcContextExtensionPerPage extends Record>> = ValueOf<{ [PageId in keyof KcContextExtensionPerPage | KcContext["pageId"]]: Extract extends never ? KcContext.Common & KcContextExtension & { pageId: PageId; } & KcContextExtensionPerPage[PageId] : Extract & KcContextExtension & KcContextExtensionPerPage[PageId]; }>; /** Take theses type definition with a grain of salt. * Some values might be undefined on some pages. * (ex: url.loginAction is undefined on error.ftl) */ export type KcContext = KcContext.Login | KcContext.Register | KcContext.Info | KcContext.Error | KcContext.LoginResetPassword | KcContext.LoginVerifyEmail | KcContext.Terms | KcContext.LoginOauth2DeviceVerifyUserCode | KcContext.LoginOauthGrant | KcContext.LoginOtp | KcContext.LoginUsername | KcContext.WebauthnAuthenticate | KcContext.WebauthnRegister | KcContext.LoginPassword | KcContext.LoginUpdatePassword | KcContext.LinkIdpAction | KcContext.LoginUpdateProfile | KcContext.LoginIdpLinkConfirm | KcContext.LoginIdpLinkEmail | KcContext.LoginPageExpired | KcContext.LoginConfigTotp | KcContext.LogoutConfirm | KcContext.IdpReviewUserProfile | KcContext.UpdateEmail | KcContext.SelectAuthenticator | KcContext.SamlPostForm | KcContext.DeleteCredential | KcContext.Code | KcContext.DeleteAccountConfirm | KcContext.FrontchannelLogout | KcContext.LoginRecoveryAuthnCodeConfig | KcContext.LoginRecoveryAuthnCodeInput | KcContext.LoginResetOtp | KcContext.LoginX509Info | KcContext.WebauthnError | KcContext.LoginPasskeysConditionalAuthenticate | KcContext.LoginIdpLinkConfirmOverride | KcContext.SelectOrganization; export declare namespace KcContext { type Common = { themeVersion: string; keycloakifyVersion: string; themeType: "login"; themeName: string; url: { loginAction: string; resourcesPath: string; resourcesCommonPath: string; loginRestartFlowUrl: string; loginUrl: string; ssoLoginInOtherTabsUrl: string; }; realm: { name: string; displayName: string; displayNameHtml: string; internationalizationEnabled: boolean; registrationEmailAsUsername: boolean; }; /** Undefined if !realm.internationalizationEnabled */ locale?: { supported: { url: string; label: string; languageTag: string; }[]; currentLanguageTag: string; rtl?: boolean; }; auth?: { showUsername?: boolean; showResetCredentials?: boolean; showTryAnotherWayLink?: boolean; attemptedUsername?: string; }; scripts?: string[]; message?: { type: "success" | "warning" | "error" | "info"; summary: string; }; client: { clientId: string; name?: string; description?: string; attributes: Record; }; isAppInitiatedAction?: boolean; messagesPerField: { /** * Return text if message for given field exists. Useful eg. to add css styles for fields with message. * * @param fieldName to check for * @param text to return * @return text if message exists for given field, else undefined */ printIfExists: (fieldName: string, text: T) => T | undefined; /** * Check if exists error message for given fields * * @param fields * @return boolean */ existsError: (fieldName: string, ...otherFiledNames: string[]) => boolean; /** * Get message for given field. * * @param fieldName * @return message text or empty string */ get: (fieldName: string) => string; /** * Check if message for given field exists * * @param field * @return boolean */ exists: (fieldName: string) => boolean; getFirstError: (...fieldNames: string[]) => string; }; authenticationSession?: { authSessionIdHash: string; }; properties: {}; "x-keycloakify": { messages: Record; }; }; type SamlPostForm = Common & { pageId: "saml-post-form.ftl"; samlPost: { url: string; SAMLRequest?: string; SAMLResponse?: string; relayState?: string; }; }; type Login = Common & { pageId: "login.ftl"; url: { loginResetCredentialsUrl: string; registrationUrl: string; }; realm: { loginWithEmailAllowed: boolean; rememberMe: boolean; password: boolean; resetPasswordAllowed: boolean; registrationAllowed: boolean; }; auth: { selectedCredential?: string; }; registrationDisabled: boolean; login: { username?: string; rememberMe?: string; password?: string; }; usernameHidden?: boolean; social?: { displayInfo: boolean; providers?: { loginUrl: string; alias: string; providerId: string; displayName: string; iconClasses?: string; }[]; }; enableWebAuthnConditionalUI?: boolean; authenticators?: { authenticators: WebauthnAuthenticate.WebauthnAuthenticator[]; }; challenge: string; userVerification: WebauthnAuthenticate["userVerification"]; rpId: string; createTimeout: number | string; isUserIdentified: "true" | "false"; shouldDisplayAuthenticators?: boolean; }; type Register = Common & { pageId: "register.ftl"; profile: UserProfile; passwordPolicies?: PasswordPolicies; url: { registrationAction: string; }; passwordRequired: boolean; recaptchaRequired?: boolean; recaptchaVisible?: boolean; recaptchaSiteKey?: string; recaptchaAction?: string; termsAcceptanceRequired?: boolean; messageHeader?: string; }; type Info = Common & { pageId: "info.ftl"; messageHeader?: string; requiredActions?: string[]; skipLink: boolean; pageRedirectUri?: string; actionUri?: string; client: { baseUrl?: string; }; message: NonNullable; }; type Error = Common & { pageId: "error.ftl"; client?: { baseUrl?: string; }; message: NonNullable; skipLink?: boolean; }; type LoginResetPassword = Common & { pageId: "login-reset-password.ftl"; realm: { loginWithEmailAllowed: boolean; duplicateEmailsAllowed: boolean; }; url: { loginResetCredentialsUrl: string; }; auth: { attemptedUsername?: string; }; }; type LoginVerifyEmail = Common & { pageId: "login-verify-email.ftl"; user?: { email: string; }; }; type Terms = Common & { pageId: "terms.ftl"; user?: { id: string; username: string; attributes: Record; email: string; emailVerified: boolean; firstName?: string; lastName?: string; markedForEviction?: boolean; }; __localizationRealmOverridesTermsText?: string; }; type LoginOauth2DeviceVerifyUserCode = Common & { pageId: "login-oauth2-device-verify-user-code.ftl"; url: { oauth2DeviceVerificationAction: string; }; }; type LoginOauthGrant = Common & { pageId: "login-oauth-grant.ftl"; oauth: { code: string; client: string; clientScopesRequested: { consentScreenText: string; dynamicScopeParameter?: string; }[]; }; url: { oauthAction: string; }; }; type LoginOtp = Common & { pageId: "login-otp.ftl"; otpLogin: { userOtpCredentials: { id: string; userLabel: string; }[]; selectedCredentialId?: string; }; }; type LoginUsername = Common & { pageId: "login-username.ftl"; url: { loginResetCredentialsUrl: string; registrationUrl: string; }; realm: { loginWithEmailAllowed: boolean; rememberMe: boolean; password: boolean; resetPasswordAllowed: boolean; registrationAllowed: boolean; }; registrationDisabled: boolean; login: { username?: string; rememberMe?: string; }; usernameHidden?: boolean; social?: Login["social"]; enableWebAuthnConditionalUI?: boolean; authenticators?: { authenticators: WebauthnAuthenticate.WebauthnAuthenticator[]; }; isUserIdentified: "true" | "false"; challenge: string; userVerification: UserVerificationRequirement | "not specified"; rpId: string; createTimeout: string | number; }; type LoginPassword = Common & { pageId: "login-password.ftl"; url: { loginResetCredentialsUrl: string; registrationUrl: string; }; realm: { resetPasswordAllowed: boolean; }; auth?: { showUsername?: boolean; showResetCredentials?: boolean; showTryAnotherWayLink?: boolean; attemptedUsername?: string; }; enableWebAuthnConditionalUI?: boolean; authenticators?: { authenticators: WebauthnAuthenticate.WebauthnAuthenticator[]; }; challenge: string; userVerification: WebauthnAuthenticate["userVerification"]; rpId: string; createTimeout: number | string; isUserIdentified: "true" | "false"; shouldDisplayAuthenticators?: boolean; }; type WebauthnAuthenticate = Common & { pageId: "webauthn-authenticate.ftl"; authenticators: { authenticators: WebauthnAuthenticate.WebauthnAuthenticator[]; }; challenge: string; userVerification: UserVerificationRequirement | "not specified"; rpId: string; createTimeout: string | number; isUserIdentified: "true" | "false"; shouldDisplayAuthenticators: boolean; realm: { password: boolean; registrationAllowed: boolean; }; registrationDisabled?: boolean; url: { registrationUrl?: string; }; }; namespace WebauthnAuthenticate { type WebauthnAuthenticator = { credentialId: string; transports: { iconClass: string; displayNameProperties?: string[]; }; label: string; createdAt: string; }; } type WebauthnRegister = Common & { pageId: "webauthn-register.ftl"; challenge: string; userid: string; username: string; signatureAlgorithms: string[]; rpEntityName: string; rpId: string; attestationConveyancePreference: string; authenticatorAttachment: string; requireResidentKey: string; userVerificationRequirement: string; createTimeout: number | string; excludeCredentialIds: string; isSetRetry?: boolean; isAppInitiatedAction?: boolean; }; type LoginUpdatePassword = Common & { pageId: "login-update-password.ftl"; }; type LinkIdpAction = Common & { pageId: "link-idp-action.ftl"; idpDisplayName: string; }; type LoginIdpLinkConfirm = Common & { pageId: "login-idp-link-confirm.ftl"; idpAlias: string; }; type LoginIdpLinkEmail = Common & { pageId: "login-idp-link-email.ftl"; brokerContext: { username: string; }; idpAlias: string; }; type LoginPageExpired = Common & { pageId: "login-page-expired.ftl"; }; type LoginConfigTotp = Common & { pageId: "login-config-totp.ftl"; mode?: "qr" | "manual" | undefined | null; totp: { totpSecretEncoded: string; qrUrl: string; policy: { algorithm: "HmacSHA1" | "HmacSHA256" | "HmacSHA512"; digits: number; lookAheadWindow: number; getAlgorithmKey: () => string; } & ({ type: "totp"; period: number; } | { type: "hotp"; initialCounter: number; }); supportedApplications: string[]; totpSecretQrCode: string; manualUrl: string; totpSecret: string; otpCredentials: { id: string; userLabel: string; }[]; }; }; type LogoutConfirm = Common & { pageId: "logout-confirm.ftl"; url: { logoutConfirmAction: string; }; client: { baseUrl?: string; }; logoutConfirm: { code: string; skipLink?: boolean; }; }; type LoginUpdateProfile = Common & { pageId: "login-update-profile.ftl"; profile: UserProfile; passwordPolicies?: PasswordPolicies; }; type IdpReviewUserProfile = Common & { pageId: "idp-review-user-profile.ftl"; profile: UserProfile; passwordPolicies?: PasswordPolicies; }; type UpdateEmail = Common & { pageId: "update-email.ftl"; profile: UserProfile; passwordPolicies?: PasswordPolicies; }; type SelectAuthenticator = Common & { pageId: "select-authenticator.ftl"; auth: { authenticationSelections: SelectAuthenticator.AuthenticationSelection[]; }; }; namespace SelectAuthenticator { type AuthenticationSelection = { authExecId: string; displayName: string; helpText: string; iconCssClass?: ClassKey; }; } type DeleteCredential = Common & { pageId: "delete-credential.ftl"; credentialLabel: string; }; type Code = Common & { pageId: "code.ftl"; code: { success: boolean; code?: string; error?: string; }; }; type DeleteAccountConfirm = Common & { pageId: "delete-account-confirm.ftl"; triggered_from_aia: boolean; }; type FrontchannelLogout = Common & { pageId: "frontchannel-logout.ftl"; logout: { clients: { name: string; frontChannelLogoutUrl: string; }[]; logoutRedirectUri?: string; }; }; type LoginRecoveryAuthnCodeConfig = Common & { pageId: "login-recovery-authn-code-config.ftl"; recoveryAuthnCodesConfigBean: { generatedRecoveryAuthnCodesList: string[]; generatedRecoveryAuthnCodesAsString: string; generatedAt: number; }; }; type LoginRecoveryAuthnCodeInput = Common & { pageId: "login-recovery-authn-code-input.ftl"; recoveryAuthnCodesInputBean: { codeNumber: number; }; }; type LoginResetOtp = Common & { pageId: "login-reset-otp.ftl"; configuredOtpCredentials: { userOtpCredentials: { id: string; userLabel: string; }[]; selectedCredentialId: string; }; }; type LoginX509Info = Common & { pageId: "login-x509-info.ftl"; x509: { formData: { subjectDN?: string; isUserEnabled?: boolean; username?: string; }; }; }; type WebauthnError = Common & { pageId: "webauthn-error.ftl"; isAppInitiatedAction?: boolean; }; type LoginPasskeysConditionalAuthenticate = Common & { pageId: "login-passkeys-conditional-authenticate.ftl"; realm: { registrationAllowed: boolean; password: boolean; }; url: { registrationUrl: string; }; registrationDisabled?: boolean; isUserIdentified: boolean | "true" | "false"; challenge: string; userVerification: string; rpId: string; createTimeout: number | string; authenticators?: { authenticators: WebauthnAuthenticate.WebauthnAuthenticator[]; }; shouldDisplayAuthenticators?: boolean; usernameHidden?: boolean; login: { username?: string; }; }; type LoginIdpLinkConfirmOverride = Common & { pageId: "login-idp-link-confirm-override.ftl"; url: { loginRestartFlowUrl: string; }; idpDisplayName: string; }; type SelectOrganization = Common & { pageId: "select-organization.ftl"; user: { organizations: { alias: string; name?: string; }[]; }; }; } export type UserProfile = { attributesByName: Record; html5DataAnnotations?: Record; }; export type Attribute = { name: string; displayName?: string; required: boolean; value?: string; values?: string[]; group?: { annotations: Record; html5DataAnnotations: Record; displayHeader?: string; name: string; displayDescription?: string; }; html5DataAnnotations?: { kcNumberFormat?: string; kcNumberUnFormat?: string; }; readOnly: boolean; validators: Validators; annotations: { inputType?: string; inputTypeSize?: `${number}` | number; inputOptionsFromValidation?: string; inputOptionLabels?: Record; inputOptionLabelsI18nPrefix?: string; inputTypeCols?: `${number}` | number; inputTypeRows?: `${number}` | number; inputTypeMaxlength?: `${number}` | number; inputHelperTextBefore?: string; inputHelperTextAfter?: string; inputTypePlaceholder?: string; inputTypePattern?: string; inputTypeMinlength?: `${number}` | number; inputTypeMax?: string; inputTypeMin?: string; inputTypeStep?: string; }; multivalued?: boolean; autocomplete?: "on" | "off" | "name" | "honorific-prefix" | "given-name" | "additional-name" | "family-name" | "honorific-suffix" | "nickname" | "email" | "username" | "new-password" | "current-password" | "one-time-code" | "organization-title" | "organization" | "street-address" | "address-line1" | "address-line2" | "address-line3" | "address-level4" | "address-level3" | "address-level2" | "address-level1" | "country" | "country-name" | "postal-code" | "cc-name" | "cc-given-name" | "cc-additional-name" | "cc-family-name" | "cc-number" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-csc" | "cc-type" | "transaction-currency" | "transaction-amount" | "language" | "bday" | "bday-day" | "bday-month" | "bday-year" | "sex" | "tel" | "tel-country-code" | "tel-national" | "tel-area-code" | "tel-local" | "tel-extension" | "impp" | "url" | "photo"; }; export type Validators = { length?: Validators.DoIgnoreEmpty & Validators.Range; integer?: Validators.DoIgnoreEmpty & Validators.Range; email?: Validators.DoIgnoreEmpty; pattern?: Validators.DoIgnoreEmpty & Validators.ErrorMessage & { pattern: string; }; options?: Validators.Options; multivalued?: Validators.DoIgnoreEmpty & Validators.Range; }; export declare namespace Validators { type DoIgnoreEmpty = { "ignore.empty.value"?: boolean; }; type ErrorMessage = { "error-message"?: string; }; type Range = { min?: `${number}` | number; max?: `${number}` | number; }; type Options = { options: string[]; }; } /** * Theses values are added by: https://github.com/jcputney/keycloak-theme-additional-info-extension * A Keycloak Java extension used as dependency in Keycloakify. */ export type PasswordPolicies = { /** The minimum length of the password */ length?: number; /** The maximum length of the password */ maxLength?: number; /** The minimum number of digits required in the password */ digits?: number; /** The minimum number of lowercase characters required in the password */ lowerCase?: number; /** The minimum number of uppercase characters required in the password */ upperCase?: number; /** The minimum number of special characters required in the password */ specialChars?: number; /** Whether the password can be the username */ notUsername?: boolean; /** Whether the password can be the email address */ notEmail?: boolean; };