Rename kcContext -> KcContext and improve consistency
This commit is contained in:
778
src/login/KcContext/KcContext.ts
Normal file
778
src/login/KcContext/KcContext.ts
Normal file
@ -0,0 +1,778 @@
|
||||
import type {
|
||||
ThemeType,
|
||||
LoginThemePageId,
|
||||
nameOfTheLocalizationRealmOverridesUserProfileProperty
|
||||
} from "keycloakify/bin/shared/constants";
|
||||
import type { ExtractAfterStartingWith } from "keycloakify/tools/ExtractAfterStartingWith";
|
||||
import type { ValueOf } from "keycloakify/tools/ValueOf";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { Equals } from "tsafe";
|
||||
import type { MessageKey } from "../i18n/i18n";
|
||||
|
||||
export type ExtendKcContext<
|
||||
KcContextExtraProperties extends { properties?: Record<string, string | undefined> },
|
||||
KcContextExtraPropertiesPerPage extends Record<string, Record<string, unknown>>
|
||||
> = ValueOf<{
|
||||
[PageId in keyof KcContextExtraPropertiesPerPage | KcContext["pageId"]]: Extract<
|
||||
KcContext,
|
||||
{ pageId: PageId }
|
||||
> extends never
|
||||
? KcContext.Common &
|
||||
KcContextExtraProperties & {
|
||||
pageId: PageId;
|
||||
} & KcContextExtraPropertiesPerPage[PageId]
|
||||
: Extract<KcContext, { pageId: PageId }> &
|
||||
KcContextExtraProperties &
|
||||
KcContextExtraPropertiesPerPage[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.LoginDeviceVerifyUserCode
|
||||
| KcContext.LoginOauthGrant
|
||||
| KcContext.LoginOtp
|
||||
| KcContext.LoginUsername
|
||||
| KcContext.WebauthnAuthenticate
|
||||
| KcContext.WebauthnRegister
|
||||
| KcContext.LoginPassword
|
||||
| KcContext.LoginUpdatePassword
|
||||
| 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;
|
||||
|
||||
assert<KcContext["themeType"] extends ThemeType ? true : false>();
|
||||
|
||||
export declare namespace KcContext {
|
||||
export 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;
|
||||
};
|
||||
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<string, string>;
|
||||
};
|
||||
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: <T extends string>(
|
||||
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?: {
|
||||
authSessionId: string;
|
||||
tabId: string;
|
||||
ssoLoginInOtherTabsUrl: string;
|
||||
};
|
||||
properties: {};
|
||||
__localizationRealmOverridesUserProfile?: Record<string, string>;
|
||||
};
|
||||
|
||||
export type SamlPostForm = Common & {
|
||||
pageId: "saml-post-form.ftl";
|
||||
samlPost: {
|
||||
url: string;
|
||||
SAMLRequest?: string;
|
||||
SAMLResponse?: string;
|
||||
relayState?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export 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; // "on" | undefined
|
||||
password?: string;
|
||||
};
|
||||
usernameHidden?: boolean;
|
||||
social: {
|
||||
displayInfo: boolean;
|
||||
providers?: {
|
||||
loginUrl: string;
|
||||
alias: string;
|
||||
providerId: string;
|
||||
displayName: string;
|
||||
iconClasses?: string;
|
||||
}[];
|
||||
};
|
||||
};
|
||||
|
||||
export type Register = Common & {
|
||||
pageId: "register.ftl";
|
||||
profile: UserProfile;
|
||||
url: {
|
||||
registrationAction: string;
|
||||
};
|
||||
passwordRequired: boolean;
|
||||
recaptchaRequired: boolean;
|
||||
recaptchaSiteKey?: string;
|
||||
/**
|
||||
* Theses values are added by: https://github.com/jcputney/keycloak-theme-additional-info-extension
|
||||
* A Keycloak Java extension used as dependency in Keycloakify.
|
||||
*/
|
||||
passwordPolicies?: PasswordPolicies;
|
||||
termsAcceptanceRequired?: boolean;
|
||||
};
|
||||
|
||||
export type Info = Common & {
|
||||
pageId: "info.ftl";
|
||||
messageHeader?: string;
|
||||
requiredActions?: ExtractAfterStartingWith<"requiredAction.", MessageKey>[];
|
||||
skipLink: boolean;
|
||||
pageRedirectUri?: string;
|
||||
actionUri?: string;
|
||||
client: {
|
||||
baseUrl?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type Error = Common & {
|
||||
pageId: "error.ftl";
|
||||
client?: {
|
||||
baseUrl?: string;
|
||||
};
|
||||
message: NonNullable<Common["message"]>;
|
||||
skipLink?: boolean;
|
||||
};
|
||||
|
||||
export type LoginResetPassword = Common & {
|
||||
pageId: "login-reset-password.ftl";
|
||||
realm: {
|
||||
loginWithEmailAllowed: boolean;
|
||||
duplicateEmailsAllowed: boolean;
|
||||
};
|
||||
url: {
|
||||
loginResetCredentialsUrl: string;
|
||||
};
|
||||
auth: {
|
||||
attemptedUsername?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type LoginVerifyEmail = Common & {
|
||||
pageId: "login-verify-email.ftl";
|
||||
//NOTE: Optional because maybe it wasn't defined in older keycloak versions.
|
||||
user?: {
|
||||
email: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type Terms = Common & {
|
||||
pageId: "terms.ftl";
|
||||
//NOTE: Optional because maybe it wasn't defined in older keycloak versions.
|
||||
user?: {
|
||||
id: string;
|
||||
username: string;
|
||||
attributes: Record<string, string[]>;
|
||||
email: string;
|
||||
emailVerified: boolean;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
markedForEviction?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export type LoginDeviceVerifyUserCode = Common & {
|
||||
pageId: "login-oauth2-device-verify-user-code.ftl";
|
||||
url: {
|
||||
oauth2DeviceVerificationAction: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type LoginOauthGrant = Common & {
|
||||
pageId: "login-oauth-grant.ftl";
|
||||
oauth: {
|
||||
code: string;
|
||||
client: string;
|
||||
clientScopesRequested: {
|
||||
consentScreenText: string;
|
||||
dynamicScopeParameter?: string;
|
||||
}[];
|
||||
};
|
||||
url: {
|
||||
oauthAction: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type LoginOtp = Common & {
|
||||
pageId: "login-otp.ftl";
|
||||
otpLogin: {
|
||||
userOtpCredentials: {
|
||||
id: string;
|
||||
userLabel: string;
|
||||
}[];
|
||||
selectedCredentialId?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export 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"];
|
||||
};
|
||||
|
||||
export type LoginPassword = Common & {
|
||||
pageId: "login-password.ftl";
|
||||
url: {
|
||||
loginResetCredentialsUrl: string;
|
||||
registrationUrl: string;
|
||||
};
|
||||
realm: {
|
||||
resetPasswordAllowed: boolean;
|
||||
};
|
||||
auth?: {
|
||||
showUsername?: boolean;
|
||||
showResetCredentials?: boolean;
|
||||
showTryAnotherWayLink?: boolean;
|
||||
attemptedUsername?: string;
|
||||
};
|
||||
social: {
|
||||
displayInfo: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export type WebauthnAuthenticate = Common & {
|
||||
pageId: "webauthn-authenticate.ftl";
|
||||
authenticators: {
|
||||
authenticators: WebauthnAuthenticate.WebauthnAuthenticator[];
|
||||
};
|
||||
challenge: string;
|
||||
// I hate this:
|
||||
userVerification: UserVerificationRequirement | "not specified";
|
||||
rpId: string;
|
||||
createTimeout: string;
|
||||
isUserIdentified: "true" | "false";
|
||||
shouldDisplayAuthenticators: boolean;
|
||||
social: {
|
||||
displayInfo: boolean;
|
||||
};
|
||||
login: {};
|
||||
realm: {
|
||||
password: boolean;
|
||||
registrationAllowed: boolean;
|
||||
};
|
||||
registrationDisabled?: boolean;
|
||||
url: {
|
||||
registrationUrl?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export namespace WebauthnAuthenticate {
|
||||
export type WebauthnAuthenticator = {
|
||||
credentialId: string;
|
||||
transports: {
|
||||
iconClass: string;
|
||||
displayNameProperties?: MessageKey[];
|
||||
};
|
||||
label: string;
|
||||
createdAt: string;
|
||||
};
|
||||
}
|
||||
|
||||
export 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;
|
||||
excludeCredentialIds: string;
|
||||
isSetRetry?: boolean;
|
||||
isAppInitiatedAction?: boolean;
|
||||
};
|
||||
|
||||
export type LoginUpdatePassword = Common & {
|
||||
pageId: "login-update-password.ftl";
|
||||
};
|
||||
|
||||
export type LoginIdpLinkConfirm = Common & {
|
||||
pageId: "login-idp-link-confirm.ftl";
|
||||
idpAlias: string;
|
||||
};
|
||||
|
||||
export type LoginIdpLinkEmail = Common & {
|
||||
pageId: "login-idp-link-email.ftl";
|
||||
brokerContext: {
|
||||
username: string;
|
||||
};
|
||||
idpAlias: string;
|
||||
};
|
||||
|
||||
export type LoginPageExpired = Common & {
|
||||
pageId: "login-page-expired.ftl";
|
||||
};
|
||||
|
||||
export 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 }[];
|
||||
};
|
||||
};
|
||||
|
||||
export type LogoutConfirm = Common & {
|
||||
pageId: "logout-confirm.ftl";
|
||||
url: {
|
||||
logoutConfirmAction: string;
|
||||
};
|
||||
client: {
|
||||
baseUrl?: string;
|
||||
};
|
||||
logoutConfirm: {
|
||||
code: string;
|
||||
skipLink?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export type LoginUpdateProfile = Common & {
|
||||
pageId: "login-update-profile.ftl";
|
||||
profile: UserProfile;
|
||||
};
|
||||
|
||||
export type IdpReviewUserProfile = Common & {
|
||||
pageId: "idp-review-user-profile.ftl";
|
||||
profile: UserProfile;
|
||||
};
|
||||
|
||||
export type UpdateEmail = Common & {
|
||||
pageId: "update-email.ftl";
|
||||
profile: UserProfile;
|
||||
};
|
||||
|
||||
export type SelectAuthenticator = Common & {
|
||||
pageId: "select-authenticator.ftl";
|
||||
auth: {
|
||||
authenticationSelections: SelectAuthenticator.AuthenticationSelection[];
|
||||
};
|
||||
};
|
||||
|
||||
export namespace SelectAuthenticator {
|
||||
export type AuthenticationSelection = {
|
||||
authExecId: string;
|
||||
displayName:
|
||||
| "otp-display-name"
|
||||
| "password-display-name"
|
||||
| "auth-username-form-display-name"
|
||||
| "auth-username-password-form-display-name"
|
||||
| "webauthn-display-name"
|
||||
| "webauthn-passwordless-display-name";
|
||||
helpText:
|
||||
| "otp-help-text"
|
||||
| "password-help-text"
|
||||
| "auth-username-form-help-text"
|
||||
| "auth-username-password-form-help-text"
|
||||
| "webauthn-help-text"
|
||||
| "webauthn-passwordless-help-text";
|
||||
iconCssClass?:
|
||||
| "kcAuthenticatorDefaultClass"
|
||||
| "kcAuthenticatorPasswordClass"
|
||||
| "kcAuthenticatorOTPClass"
|
||||
| "kcAuthenticatorWebAuthnClass"
|
||||
| "kcAuthenticatorWebAuthnPasswordlessClass";
|
||||
};
|
||||
}
|
||||
|
||||
export type DeleteCredential = Common & {
|
||||
pageId: "delete-credential.ftl";
|
||||
credentialLabel: string;
|
||||
};
|
||||
|
||||
export type Code = Common & {
|
||||
pageId: "code.ftl";
|
||||
code: {
|
||||
success: boolean;
|
||||
code?: string;
|
||||
error?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type DeleteAccountConfirm = Common & {
|
||||
pageId: "delete-account-confirm.ftl";
|
||||
triggered_from_aia: boolean;
|
||||
};
|
||||
|
||||
export type FrontchannelLogout = Common & {
|
||||
pageId: "frontchannel-logout.ftl";
|
||||
logout: {
|
||||
clients: {
|
||||
name: string;
|
||||
frontChannelLogoutUrl: string;
|
||||
}[];
|
||||
logoutRedirectUri?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type LoginRecoveryAuthnCodeConfig = Common & {
|
||||
pageId: "login-recovery-authn-code-config.ftl";
|
||||
recoveryAuthnCodesConfigBean: {
|
||||
generatedRecoveryAuthnCodesList: string[];
|
||||
generatedRecoveryAuthnCodesAsString: string;
|
||||
generatedAt: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type LoginRecoveryAuthnCodeInput = Common & {
|
||||
pageId: "login-recovery-authn-code-input.ftl";
|
||||
recoveryAuthnCodesInputBean: {
|
||||
codeNumber: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type LoginResetOtp = Common & {
|
||||
pageId: "login-reset-otp.ftl";
|
||||
configuredOtpCredentials: {
|
||||
userOtpCredentials: {
|
||||
id: string;
|
||||
userLabel: string;
|
||||
}[];
|
||||
selectedCredentialId: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type LoginX509Info = Common & {
|
||||
pageId: "login-x509-info.ftl";
|
||||
x509: {
|
||||
formData: {
|
||||
subjectDN?: string;
|
||||
isUserEnabled?: boolean;
|
||||
username?: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export type WebauthnError = Common & {
|
||||
pageId: "webauthn-error.ftl";
|
||||
isAppInitiatedAction?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export type UserProfile = {
|
||||
attributesByName: Record<string, Attribute>;
|
||||
html5DataAnnotations?: Record<string, string>;
|
||||
};
|
||||
|
||||
export type Attribute = {
|
||||
name: string;
|
||||
displayName?: string;
|
||||
required: boolean;
|
||||
value?: string;
|
||||
values?: string[];
|
||||
group?: {
|
||||
html5DataAnnotations: Record<string, string>;
|
||||
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<string, string | undefined>;
|
||||
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;
|
||||
// NOTE: Following are the validators for which we don't implement client side validation yet
|
||||
// or for which the validation can't be performed on the client side.
|
||||
/*
|
||||
double?: Validators.DoIgnoreEmpty & Validators.Range;
|
||||
"up-immutable-attribute"?: {};
|
||||
"up-attribute-required-by-metadata-value"?: {};
|
||||
"up-username-has-value"?: {};
|
||||
"up-duplicate-username"?: {};
|
||||
"up-username-mutation"?: {};
|
||||
"up-email-exists-as-username"?: {};
|
||||
"up-blank-attribute-value"?: Validators.ErrorMessage & { "fail-on-null": boolean; };
|
||||
"up-duplicate-email"?: {};
|
||||
"local-date"?: Validators.DoIgnoreEmpty;
|
||||
"person-name-prohibited-characters"?: Validators.DoIgnoreEmpty & Validators.ErrorMessage;
|
||||
uri?: Validators.DoIgnoreEmpty;
|
||||
"username-prohibited-characters"?: Validators.DoIgnoreEmpty & Validators.ErrorMessage;
|
||||
*/
|
||||
};
|
||||
|
||||
export declare namespace Validators {
|
||||
export type DoIgnoreEmpty = {
|
||||
"ignore.empty.value"?: boolean;
|
||||
};
|
||||
|
||||
export type ErrorMessage = {
|
||||
"error-message"?: string;
|
||||
};
|
||||
|
||||
export type Range = {
|
||||
min?: `${number}` | number;
|
||||
max?: `${number}` | number;
|
||||
};
|
||||
export type Options = {
|
||||
options: string[];
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
type Got = KcContext["pageId"];
|
||||
type Expected = LoginThemePageId;
|
||||
|
||||
type OnlyInGot = Exclude<Got, Expected>;
|
||||
type OnlyInExpected = Exclude<Expected, Got>;
|
||||
|
||||
assert<Equals<OnlyInGot, never>>();
|
||||
assert<Equals<OnlyInExpected, never>>();
|
||||
}
|
||||
|
||||
export type PasswordPolicies = {
|
||||
/** The minimum length of the password */
|
||||
length?: 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;
|
||||
};
|
||||
|
||||
assert<
|
||||
KcContext.Common extends Partial<
|
||||
Record<typeof nameOfTheLocalizationRealmOverridesUserProfileProperty, unknown>
|
||||
>
|
||||
? true
|
||||
: false
|
||||
>();
|
80
src/login/KcContext/getKcContextMock.ts
Normal file
80
src/login/KcContext/getKcContextMock.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import type { ExtendKcContext, KcContext as KcContextBase } from "./KcContext";
|
||||
import type { LoginThemePageId } from "keycloakify/bin/shared/constants";
|
||||
import type { DeepPartial } from "keycloakify/tools/DeepPartial";
|
||||
import { deepAssign } from "keycloakify/tools/deepAssign";
|
||||
import { structuredCloneButFunctions } from "keycloakify/tools/structuredCloneButFunctions";
|
||||
import { kcContextMocks, kcContextCommonMock } from "./kcContextMocks";
|
||||
import { exclude } from "tsafe/exclude";
|
||||
|
||||
export function createGetKcContextMock<
|
||||
KcContextExtraProperties extends { properties?: Record<string, string | undefined> },
|
||||
KcContextExtraPropertiesPerPage extends Record<
|
||||
`${string}.ftl`,
|
||||
Record<string, unknown>
|
||||
>
|
||||
>(params: {
|
||||
kcContextExtraProperties: KcContextExtraProperties;
|
||||
kcContextExtraPropertiesPerPage: KcContextExtraPropertiesPerPage;
|
||||
overrides?: DeepPartial<KcContextExtraProperties & KcContextBase.Common>;
|
||||
overridesPerPage?: {
|
||||
[PageId in
|
||||
| LoginThemePageId
|
||||
| keyof KcContextExtraPropertiesPerPage]?: DeepPartial<
|
||||
Extract<
|
||||
ExtendKcContext<
|
||||
KcContextExtraProperties,
|
||||
KcContextExtraPropertiesPerPage
|
||||
>,
|
||||
{ pageId: PageId }
|
||||
>
|
||||
>;
|
||||
};
|
||||
}) {
|
||||
const {
|
||||
kcContextExtraProperties,
|
||||
kcContextExtraPropertiesPerPage,
|
||||
overrides: overrides_global,
|
||||
overridesPerPage: overridesPerPage_global
|
||||
} = params;
|
||||
|
||||
type KcContext = ExtendKcContext<
|
||||
KcContextExtraProperties,
|
||||
KcContextExtraPropertiesPerPage
|
||||
>;
|
||||
|
||||
function getKcContextMock<
|
||||
PageId extends LoginThemePageId | keyof KcContextExtraPropertiesPerPage
|
||||
>(params: {
|
||||
pageId: PageId;
|
||||
overrides?: DeepPartial<Extract<KcContext, { pageId: PageId }>>;
|
||||
}): Extract<KcContext, { pageId: PageId }> {
|
||||
const { pageId, overrides } = params;
|
||||
|
||||
const kcContextMock = structuredCloneButFunctions(
|
||||
kcContextMocks.find(kcContextMock => kcContextMock.pageId === pageId) ?? {
|
||||
...kcContextCommonMock,
|
||||
pageId
|
||||
}
|
||||
);
|
||||
|
||||
[
|
||||
kcContextExtraProperties,
|
||||
kcContextExtraPropertiesPerPage[pageId],
|
||||
overrides_global,
|
||||
overridesPerPage_global?.[pageId],
|
||||
overrides
|
||||
]
|
||||
.filter(exclude(undefined))
|
||||
.forEach(overrides =>
|
||||
deepAssign({
|
||||
target: kcContextMock,
|
||||
source: overrides
|
||||
})
|
||||
);
|
||||
|
||||
// @ts-expect-error
|
||||
return kcContextMock;
|
||||
}
|
||||
|
||||
return { getKcContextMock };
|
||||
}
|
8
src/login/KcContext/index.ts
Normal file
8
src/login/KcContext/index.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export type {
|
||||
ExtendKcContext,
|
||||
KcContext,
|
||||
Attribute,
|
||||
PasswordPolicies,
|
||||
Validators
|
||||
} from "./KcContext";
|
||||
export { createGetKcContextMock } from "./getKcContextMock";
|
572
src/login/KcContext/kcContextMocks.ts
Normal file
572
src/login/KcContext/kcContextMocks.ts
Normal file
@ -0,0 +1,572 @@
|
||||
import "keycloakify/tools/Object.fromEntries";
|
||||
import type { KcContext, Attribute } from "./KcContext";
|
||||
import {
|
||||
resources_common,
|
||||
keycloak_resources,
|
||||
type LoginThemePageId
|
||||
} from "keycloakify/bin/shared/constants";
|
||||
import { id } from "tsafe/id";
|
||||
import { assert, type Equals } from "tsafe/assert";
|
||||
import { BASE_URL } from "keycloakify/lib/BASE_URL";
|
||||
|
||||
const attributesByName = Object.fromEntries(
|
||||
id<Attribute[]>([
|
||||
{
|
||||
validators: {
|
||||
length: {
|
||||
"ignore.empty.value": true,
|
||||
min: "3",
|
||||
max: "255"
|
||||
}
|
||||
},
|
||||
displayName: "${username}",
|
||||
annotations: {},
|
||||
required: true,
|
||||
autocomplete: "username",
|
||||
readOnly: false,
|
||||
name: "username"
|
||||
},
|
||||
{
|
||||
validators: {
|
||||
length: {
|
||||
max: "255",
|
||||
"ignore.empty.value": true
|
||||
},
|
||||
email: {
|
||||
"ignore.empty.value": true
|
||||
},
|
||||
pattern: {
|
||||
"ignore.empty.value": true,
|
||||
pattern: "gmail\\.com$"
|
||||
}
|
||||
},
|
||||
displayName: "${email}",
|
||||
annotations: {},
|
||||
required: true,
|
||||
autocomplete: "email",
|
||||
readOnly: false,
|
||||
name: "email"
|
||||
},
|
||||
{
|
||||
validators: {
|
||||
length: {
|
||||
max: "255",
|
||||
"ignore.empty.value": true
|
||||
}
|
||||
},
|
||||
displayName: "${firstName}",
|
||||
annotations: {},
|
||||
required: true,
|
||||
readOnly: false,
|
||||
name: "firstName"
|
||||
},
|
||||
{
|
||||
validators: {
|
||||
length: {
|
||||
max: "255",
|
||||
"ignore.empty.value": true
|
||||
}
|
||||
},
|
||||
displayName: "${lastName}",
|
||||
annotations: {},
|
||||
required: true,
|
||||
readOnly: false,
|
||||
name: "lastName"
|
||||
}
|
||||
]).map(attribute => [attribute.name, attribute])
|
||||
);
|
||||
|
||||
const resourcesPath = `${BASE_URL}${keycloak_resources}/login/resources`;
|
||||
|
||||
export const kcContextCommonMock: KcContext.Common = {
|
||||
themeVersion: "0.0.0",
|
||||
keycloakifyVersion: "0.0.0",
|
||||
themeType: "login",
|
||||
themeName: "my-theme-name",
|
||||
url: {
|
||||
loginAction: "#",
|
||||
resourcesPath,
|
||||
resourcesCommonPath: `${resourcesPath}/${resources_common}`,
|
||||
loginRestartFlowUrl:
|
||||
"/auth/realms/myrealm/login-actions/restart?client_id=account&tab_id=HoAx28ja4xg",
|
||||
loginUrl:
|
||||
"/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg",
|
||||
ssoLoginInOtherTabsUrl:
|
||||
"/auth/realms/myrealm/login-actions/switch?client_id=account&tab_id=HoAx28ja4xg"
|
||||
},
|
||||
realm: {
|
||||
name: "myrealm",
|
||||
displayName: "myrealm",
|
||||
displayNameHtml: "myrealm",
|
||||
internationalizationEnabled: true,
|
||||
registrationEmailAsUsername: false
|
||||
},
|
||||
messagesPerField: {
|
||||
printIfExists: () => {
|
||||
return undefined;
|
||||
},
|
||||
existsError: () => false,
|
||||
get: fieldName => `Fake error for ${fieldName}`,
|
||||
exists: () => false,
|
||||
getFirstError: fieldName => `Fake error for ${fieldName}`
|
||||
},
|
||||
locale: {
|
||||
supported: [
|
||||
/* spell-checker: disable */
|
||||
["de", "Deutsch"],
|
||||
["no", "Norsk"],
|
||||
["ru", "Русский"],
|
||||
["sv", "Svenska"],
|
||||
["pt-BR", "Português (Brasil)"],
|
||||
["lt", "Lietuvių"],
|
||||
["en", "English"],
|
||||
["it", "Italiano"],
|
||||
["fr", "Français"],
|
||||
["zh-CN", "中文简体"],
|
||||
["es", "Español"],
|
||||
["cs", "Čeština"],
|
||||
["ja", "日本語"],
|
||||
["sk", "Slovenčina"],
|
||||
["pl", "Polski"],
|
||||
["ca", "Català"],
|
||||
["nl", "Nederlands"],
|
||||
["tr", "Türkçe"]
|
||||
/* spell-checker: enable */
|
||||
].map(
|
||||
([languageTag, label]) =>
|
||||
({
|
||||
languageTag,
|
||||
label,
|
||||
url: "https://gist.github.com/garronej/52baaca1bb925f2296ab32741e062b8e"
|
||||
}) as const
|
||||
),
|
||||
|
||||
currentLanguageTag: "en"
|
||||
},
|
||||
auth: {
|
||||
showUsername: false,
|
||||
showResetCredentials: false,
|
||||
showTryAnotherWayLink: false
|
||||
},
|
||||
client: {
|
||||
clientId: "myApp",
|
||||
attributes: {}
|
||||
},
|
||||
scripts: [],
|
||||
isAppInitiatedAction: false,
|
||||
properties: {},
|
||||
__localizationRealmOverridesUserProfile: {}
|
||||
};
|
||||
|
||||
const loginUrl = {
|
||||
...kcContextCommonMock.url,
|
||||
loginResetCredentialsUrl:
|
||||
"/auth/realms/myrealm/login-actions/reset-credentials?client_id=account&tab_id=HoAx28ja4xg",
|
||||
registrationUrl:
|
||||
"/auth/realms/myrealm/login-actions/registration?client_id=account&tab_id=HoAx28ja4xg",
|
||||
oauth2DeviceVerificationAction: "/auth/realms/myrealm/device",
|
||||
oauthAction:
|
||||
"/auth/realms/myrealm/login-actions/consent?client_id=account&tab_id=HoAx28ja4xg"
|
||||
};
|
||||
|
||||
export const kcContextMocks = [
|
||||
id<KcContext.Login>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "login.ftl",
|
||||
url: loginUrl,
|
||||
realm: {
|
||||
...kcContextCommonMock.realm,
|
||||
loginWithEmailAllowed: true,
|
||||
rememberMe: true,
|
||||
password: true,
|
||||
resetPasswordAllowed: true,
|
||||
registrationAllowed: true
|
||||
},
|
||||
auth: kcContextCommonMock.auth!,
|
||||
social: {
|
||||
displayInfo: true
|
||||
},
|
||||
usernameHidden: false,
|
||||
login: {},
|
||||
registrationDisabled: false
|
||||
}),
|
||||
id<KcContext.Register>({
|
||||
...kcContextCommonMock,
|
||||
url: {
|
||||
...loginUrl,
|
||||
registrationAction:
|
||||
"http://localhost:8080/auth/realms/myrealm/login-actions/registration?session_code=gwZdUeO7pbYpFTRxiIxRg_QtzMbtFTKrNu6XW_f8asM&execution=12146ce0-b139-4bbd-b25b-0eccfee6577e&client_id=account&tab_id=uS8lYfebLa0"
|
||||
},
|
||||
isAppInitiatedAction: false,
|
||||
passwordRequired: true,
|
||||
recaptchaRequired: false,
|
||||
pageId: "register.ftl",
|
||||
profile: {
|
||||
attributesByName
|
||||
},
|
||||
scripts: [
|
||||
//"https://www.google.com/recaptcha/api.js"
|
||||
]
|
||||
}),
|
||||
id<KcContext.Info>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "info.ftl",
|
||||
messageHeader: "<Message header>",
|
||||
requiredActions: undefined,
|
||||
skipLink: false,
|
||||
actionUri: "#",
|
||||
client: {
|
||||
clientId: "myApp",
|
||||
baseUrl: "#",
|
||||
attributes: {}
|
||||
}
|
||||
}),
|
||||
id<KcContext.Error>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "error.ftl",
|
||||
client: {
|
||||
clientId: "myApp",
|
||||
baseUrl: "#",
|
||||
attributes: {}
|
||||
},
|
||||
message: {
|
||||
type: "error",
|
||||
summary: "This is the error message"
|
||||
}
|
||||
}),
|
||||
id<KcContext.LoginResetPassword>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "login-reset-password.ftl",
|
||||
realm: {
|
||||
...kcContextCommonMock.realm,
|
||||
loginWithEmailAllowed: false,
|
||||
duplicateEmailsAllowed: false
|
||||
},
|
||||
url: loginUrl,
|
||||
auth: {}
|
||||
}),
|
||||
id<KcContext.LoginVerifyEmail>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "login-verify-email.ftl",
|
||||
user: {
|
||||
email: "john.doe@gmail.com"
|
||||
}
|
||||
}),
|
||||
id<KcContext.Terms>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "terms.ftl"
|
||||
}),
|
||||
id<KcContext.LoginDeviceVerifyUserCode>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "login-oauth2-device-verify-user-code.ftl",
|
||||
url: loginUrl
|
||||
}),
|
||||
id<KcContext.LoginOauthGrant>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "login-oauth-grant.ftl",
|
||||
oauth: {
|
||||
code: "5-1N4CIzfi1aprIQjmylI-9e3spLCWW9i5d-GDcs-Sw",
|
||||
clientScopesRequested: [
|
||||
{ consentScreenText: "${profileScopeConsentText}" },
|
||||
{ consentScreenText: "${rolesScopeConsentText}" },
|
||||
{ consentScreenText: "${emailScopeConsentText}" }
|
||||
],
|
||||
client: "account"
|
||||
},
|
||||
url: loginUrl
|
||||
}),
|
||||
id<KcContext.LoginOtp>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "login-otp.ftl",
|
||||
otpLogin: {
|
||||
userOtpCredentials: [
|
||||
{
|
||||
id: "id1",
|
||||
userLabel: "label1"
|
||||
},
|
||||
{
|
||||
id: "id2",
|
||||
userLabel: "label2"
|
||||
}
|
||||
]
|
||||
}
|
||||
}),
|
||||
id<KcContext.LoginUsername>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "login-username.ftl",
|
||||
url: loginUrl,
|
||||
realm: {
|
||||
...kcContextCommonMock.realm,
|
||||
loginWithEmailAllowed: true,
|
||||
rememberMe: true,
|
||||
password: true,
|
||||
resetPasswordAllowed: true,
|
||||
registrationAllowed: true
|
||||
},
|
||||
social: {
|
||||
displayInfo: true
|
||||
},
|
||||
usernameHidden: false,
|
||||
login: {},
|
||||
registrationDisabled: false
|
||||
}),
|
||||
id<KcContext.LoginPassword>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "login-password.ftl",
|
||||
url: loginUrl,
|
||||
realm: {
|
||||
...kcContextCommonMock.realm,
|
||||
resetPasswordAllowed: true
|
||||
},
|
||||
social: {
|
||||
displayInfo: false
|
||||
}
|
||||
}),
|
||||
id<KcContext.WebauthnAuthenticate>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "webauthn-authenticate.ftl",
|
||||
url: loginUrl,
|
||||
authenticators: {
|
||||
authenticators: []
|
||||
},
|
||||
realm: {
|
||||
...kcContextCommonMock.realm,
|
||||
password: true,
|
||||
registrationAllowed: true
|
||||
},
|
||||
challenge: "",
|
||||
userVerification: "not specified",
|
||||
rpId: "",
|
||||
createTimeout: "0",
|
||||
isUserIdentified: "false",
|
||||
shouldDisplayAuthenticators: false,
|
||||
social: {
|
||||
displayInfo: false
|
||||
},
|
||||
login: {}
|
||||
}),
|
||||
id<KcContext.LoginUpdatePassword>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "login-update-password.ftl"
|
||||
}),
|
||||
id<KcContext.LoginUpdateProfile>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "login-update-profile.ftl",
|
||||
profile: {
|
||||
attributesByName
|
||||
}
|
||||
}),
|
||||
id<KcContext.LoginIdpLinkConfirm>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "login-idp-link-confirm.ftl",
|
||||
idpAlias: "FranceConnect"
|
||||
}),
|
||||
id<KcContext.LoginIdpLinkEmail>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "login-idp-link-email.ftl",
|
||||
idpAlias: "FranceConnect",
|
||||
brokerContext: {
|
||||
username: "anUsername"
|
||||
}
|
||||
}),
|
||||
id<KcContext.LoginConfigTotp>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "login-config-totp.ftl",
|
||||
totp: {
|
||||
totpSecretEncoded: "KVVF G2BY N4YX S6LB IUYT K2LH IFYE 4SBV",
|
||||
qrUrl: "#",
|
||||
totpSecretQrCode:
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAPYAAAD2AQAAAADNaUdlAAACM0lEQVR4Xu3OIZJgOQwDUDFd2UxiurLAVnnbHw4YGDKtSiWOn4Gxf81//7r/+q8b4HfLGBZDK9d85NmNR+sB42sXvOYrN5P1DcgYYFTGfOlbzE8gzwy3euweGizw7cfdl34/GRhlkxjKNV+5AebPXPORX1JuB9x8ZfbyyD2y1krWAKsbMq1HnqQDaLfa77p4+MqvzEGSqvSAD/2IHW2yHaigR9tX3m8dDIYGcNf3f+gDpVBZbZU77zyJ6Rlcy+qoTMG887KAPD9hsh6a1Sv3gJUHGHUAxSMzj7zqDDe7Phmt2eG+8UsMxjRGm816MAO+8VMl1R1jGHOrZB/5Zo/WXAPgxixm9Mo96vDGrM1eOto8c4Ax4wF437mifOXlpiPzCnN7Y9l95NnEMxgMY9AAGA8fucH14Y1aVb6N/cqrmyh0BVht7k1e+bU8LK0Cg5vmVq9c5vHIjOfqxDIfeTraNVTwewa4wVe+SW5N+uP1qACeudUZbqGOfA6VZV750Noq2Xx3kpveV44ZelSV1V7KFHzkWyVrrlUwG0Pl9pWnoy3vsQoME6vKI69i5osVgwWzHT7zjmJtMcNUSVn1oYMd7ZodbgowZl45VG0uVuLPUr1yc79uaQBag/mqR34xhlWyHm1prplHboCWdZ4TeZjsK8+dI+jbz1C5hl65mcpgB5dhcj8+dGO+0Ko68+lD37JDD83dpDLzzK+TrQyaVwGj6pUboGV+7+AyN8An/pf84/7rv/4/1l4OCc/1BYMAAAAASUVORK5CYII=",
|
||||
manualUrl: "#",
|
||||
totpSecret: "G4nsI8lQagRMUchH8jEG",
|
||||
otpCredentials: [],
|
||||
supportedApplications: ["FreeOTP", "Google Authenticator"],
|
||||
policy: {
|
||||
algorithm: "HmacSHA1",
|
||||
digits: 6,
|
||||
lookAheadWindow: 1,
|
||||
type: "totp",
|
||||
period: 30,
|
||||
getAlgorithmKey: () => "SHA1"
|
||||
}
|
||||
}
|
||||
}),
|
||||
id<KcContext.LogoutConfirm>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "logout-confirm.ftl",
|
||||
url: {
|
||||
...kcContextCommonMock.url,
|
||||
logoutConfirmAction: "Continuer?"
|
||||
},
|
||||
client: {
|
||||
clientId: "myApp",
|
||||
baseUrl: "#",
|
||||
attributes: {}
|
||||
},
|
||||
logoutConfirm: { code: "123", skipLink: false }
|
||||
}),
|
||||
id<KcContext.IdpReviewUserProfile>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "idp-review-user-profile.ftl",
|
||||
profile: {
|
||||
attributesByName
|
||||
}
|
||||
}),
|
||||
id<KcContext.UpdateEmail>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "update-email.ftl",
|
||||
profile: {
|
||||
attributesByName: {
|
||||
email: attributesByName["email"]
|
||||
}
|
||||
}
|
||||
}),
|
||||
id<KcContext.SelectAuthenticator>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "select-authenticator.ftl",
|
||||
auth: {
|
||||
authenticationSelections: [
|
||||
{
|
||||
authExecId: "f607f83c-537e-42b7-99d7-c52d459afe84",
|
||||
displayName: "otp-display-name",
|
||||
helpText: "otp-help-text",
|
||||
iconCssClass: "kcAuthenticatorOTPClass"
|
||||
},
|
||||
{
|
||||
authExecId: "5ed881b1-84cd-4e9b-b4d9-f329ea61a58c",
|
||||
displayName: "webauthn-display-name",
|
||||
helpText: "webauthn-help-text",
|
||||
iconCssClass: "kcAuthenticatorWebAuthnClass"
|
||||
}
|
||||
]
|
||||
}
|
||||
}),
|
||||
id<KcContext.SamlPostForm>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "saml-post-form.ftl",
|
||||
samlPost: {
|
||||
url: ""
|
||||
}
|
||||
}),
|
||||
id<KcContext.LoginPageExpired>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "login-page-expired.ftl"
|
||||
}),
|
||||
|
||||
id<KcContext.FrontchannelLogout>({
|
||||
...kcContextCommonMock,
|
||||
pageId: "frontchannel-logout.ftl",
|
||||
logout: {
|
||||
clients: [
|
||||
{
|
||||
name: "myApp",
|
||||
frontChannelLogoutUrl: "#"
|
||||
},
|
||||
{
|
||||
name: "myApp2",
|
||||
frontChannelLogoutUrl: "#"
|
||||
}
|
||||
]
|
||||
}
|
||||
}),
|
||||
id<KcContext.WebauthnRegister>({
|
||||
pageId: "webauthn-register.ftl",
|
||||
...kcContextCommonMock,
|
||||
challenge: "random-challenge-string",
|
||||
userid: "user123",
|
||||
username: "johndoe",
|
||||
signatureAlgorithms: ["ES256", "RS256"],
|
||||
rpEntityName: "Example Corp",
|
||||
rpId: "example.com",
|
||||
attestationConveyancePreference: "direct",
|
||||
authenticatorAttachment: "platform",
|
||||
requireResidentKey: "required",
|
||||
userVerificationRequirement: "preferred",
|
||||
createTimeout: 60000,
|
||||
excludeCredentialIds: "credId123,credId456",
|
||||
isSetRetry: false,
|
||||
isAppInitiatedAction: true
|
||||
}),
|
||||
id<KcContext.DeleteCredential>({
|
||||
pageId: "delete-credential.ftl",
|
||||
...kcContextCommonMock,
|
||||
credentialLabel: "myCredential"
|
||||
}),
|
||||
id<KcContext.Code>({
|
||||
pageId: "code.ftl",
|
||||
...kcContextCommonMock,
|
||||
code: {
|
||||
success: true,
|
||||
code: "123456"
|
||||
}
|
||||
}),
|
||||
id<KcContext.DeleteAccountConfirm>({
|
||||
pageId: "delete-account-confirm.ftl",
|
||||
...kcContextCommonMock,
|
||||
triggered_from_aia: true
|
||||
}),
|
||||
id<KcContext.LoginRecoveryAuthnCodeConfig>({
|
||||
pageId: "login-recovery-authn-code-config.ftl",
|
||||
...kcContextCommonMock,
|
||||
recoveryAuthnCodesConfigBean: {
|
||||
generatedRecoveryAuthnCodesList: ["code123", "code456", "code789"],
|
||||
generatedRecoveryAuthnCodesAsString: "code123, code456, code789",
|
||||
generatedAt: new Date().getTime()
|
||||
}
|
||||
}),
|
||||
id<KcContext.LoginRecoveryAuthnCodeInput>({
|
||||
pageId: "login-recovery-authn-code-input.ftl",
|
||||
...kcContextCommonMock,
|
||||
recoveryAuthnCodesInputBean: {
|
||||
codeNumber: 1234
|
||||
}
|
||||
}),
|
||||
id<KcContext.LoginResetOtp>({
|
||||
pageId: "login-reset-otp.ftl",
|
||||
...kcContextCommonMock,
|
||||
configuredOtpCredentials: {
|
||||
userOtpCredentials: [
|
||||
{
|
||||
id: "otpId1",
|
||||
userLabel: "OTP Device 1"
|
||||
},
|
||||
{
|
||||
id: "otpId2",
|
||||
userLabel: "OTP Device 2"
|
||||
},
|
||||
{
|
||||
id: "otpId3",
|
||||
userLabel: "Backup OTP"
|
||||
}
|
||||
],
|
||||
selectedCredentialId: "otpId2"
|
||||
}
|
||||
}),
|
||||
id<KcContext.LoginX509Info>({
|
||||
pageId: "login-x509-info.ftl",
|
||||
...kcContextCommonMock,
|
||||
x509: {
|
||||
formData: {
|
||||
subjectDN: "CN=John Doe, O=Example Corp, C=US",
|
||||
isUserEnabled: true,
|
||||
username: "johndoe"
|
||||
}
|
||||
}
|
||||
}),
|
||||
id<KcContext.WebauthnError>({
|
||||
pageId: "webauthn-error.ftl",
|
||||
...kcContextCommonMock,
|
||||
isAppInitiatedAction: true
|
||||
})
|
||||
];
|
||||
|
||||
{
|
||||
type Got = (typeof kcContextMocks)[number]["pageId"];
|
||||
type Expected = LoginThemePageId;
|
||||
|
||||
type OnlyInGot = Exclude<Got, Expected>;
|
||||
type OnlyInExpected = Exclude<Expected, Got>;
|
||||
|
||||
assert<Equals<OnlyInGot, never>>();
|
||||
assert<Equals<OnlyInExpected, never>>();
|
||||
}
|
Reference in New Issue
Block a user