diff --git a/.changeset/twenty-panthers-deny.md b/.changeset/twenty-panthers-deny.md new file mode 100644 index 00000000000..bc94809a0e6 --- /dev/null +++ b/.changeset/twenty-panthers-deny.md @@ -0,0 +1,18 @@ +--- +"@wso2is/admin.push-providers.v1": major +"@wso2is/admin.identity-providers.v1": patch +"@wso2is/admin.email-providers.v1": patch +"@wso2is/admin.email-and-sms.v1": patch +"@wso2is/admin.sms-providers.v1": patch +"@wso2is/admin.template-core.v1": patch +"@wso2is/admin.connections.v1": patch +"@wso2is/react-components": patch +"@wso2is/admin.core.v1": patch +"@wso2is/identity-apps-core": patch +"@wso2is/theme": patch +"@wso2is/console": patch +"@wso2is/form": patch +"@wso2is/i18n": patch +--- + +Add UI support for Push notification based authentication diff --git a/apps/console/package.json b/apps/console/package.json index 70b00377f1e..d57897c7600 100644 --- a/apps/console/package.json +++ b/apps/console/package.json @@ -75,6 +75,7 @@ "@wso2is/admin.organization-discovery.v1": "^2.25.110", "@wso2is/admin.organizations.v1": "^2.26.110", "@wso2is/admin.private-key-jwt.v1": "^2.25.110", + "@wso2is/admin.push-providers.v1": "^0.0.0", "@wso2is/admin.remote-repository-configuration.v1": "^2.25.110", "@wso2is/admin.impersonation.v1": "^1.6.110", "@wso2is/admin.policy-administration.v1": "^1.0.26", diff --git a/apps/console/src/configs/routes.tsx b/apps/console/src/configs/routes.tsx index 1d174b9aef7..197cb27a788 100644 --- a/apps/console/src/configs/routes.tsx +++ b/apps/console/src/configs/routes.tsx @@ -72,6 +72,9 @@ import FullScreenLayout from "../layouts/full-screen-layout"; export const getAppViewRoutes = (): RouteInterface[] => { const legacyMode: LegacyModeInterface = window["AppUtils"]?.getConfig()?.ui?.legacyMode; + const isPushProviderFeatureEnabled: boolean = + window["AppUtils"]?.getConfig()?.ui?.features?.pushProviders?.enabled; + const defaultRoutes: RouteInterface[] = [ { category: "extensions:manage.sidePanel.categories.userManagement", @@ -736,9 +739,11 @@ export const getAppViewRoutes = (): RouteInterface[] => { exact: true, icon: { icon: }, id: "notificationChannels", - name: "Email & SMS", + name: isPushProviderFeatureEnabled ? "Notification Channels" : "Email & SMS", order: 16, - path: `${ AppConstants.getDeveloperViewBasePath() }/email-and-sms`, + path: isPushProviderFeatureEnabled + ? AppConstants.getPaths().get("NOTIFICATION_CHANNELS") + : AppConstants.getPaths().get("EMAIL_AND_SMS"), protected: true, showOnSidePanel: true }, @@ -774,6 +779,22 @@ export const getAppViewRoutes = (): RouteInterface[] => { protected: true, showOnSidePanel: false }, + { + category: "extensions:develop.sidePanel.categories.pushProvider", + component: lazy(() => + import("@wso2is/admin.push-providers.v1/pages/push-providers") + ), + exact: true, + icon: { + icon: getSidePanelIcons().push + }, + id: "pushProviders", + name: "Push Notification Provider", + order: 18, + path: AppConstants.getPaths().get("PUSH_PROVIDER"), + protected: true, + showOnSidePanel: false + }, { category: "extensions:manage.sidePanel.categories.AccountManagement", children: [ diff --git a/apps/console/src/extensions/i18n/models/extensions.ts b/apps/console/src/extensions/i18n/models/extensions.ts index fcc902cab54..a8477c9f989 100755 --- a/apps/console/src/extensions/i18n/models/extensions.ts +++ b/apps/console/src/extensions/i18n/models/extensions.ts @@ -1967,6 +1967,29 @@ export interface Extensions { }; }; }; + pushAuth: { + quickStart: { + addLoginModal: { + heading: string; + subHeading: string; + }; + connectApp: { + description: string; + }; + heading: string; + subHeading: string; + steps: { + selectApplication: { + content: string; + heading: string; + }; + selectPushAuth: { + content: string; + heading: string; + }; + }; + } + }, facebook: { quickStart: { addLoginModal: { diff --git a/apps/console/src/extensions/i18n/resources/en-US/extensions.ts b/apps/console/src/extensions/i18n/resources/en-US/extensions.ts index 6ad321aa950..c359ed87f12 100755 --- a/apps/console/src/extensions/i18n/resources/en-US/extensions.ts +++ b/apps/console/src/extensions/i18n/resources/en-US/extensions.ts @@ -1992,7 +1992,7 @@ export const extensions: Extensions = { info: "You can customize the email content using <1>Email Templates.", updateButton: "Update", sendTestMailButton: "Send Test Email", - goBack: "Go back to Email & SMS", + goBack: "Go back to Notification Providers", confirmationModal: { assertionHint: "Please confirm your action.", content: "If you delete this configuration, the emails will be sent from the Asgardeo Email Address. " + @@ -2086,9 +2086,9 @@ export const extensions: Extensions = { } }, notificationChannel: { - heading: "SMS / Email Providers", - title: "SMS / Email Providers", - description: "Configure the SMS and Email providers for your organization." + heading: "Notification Channels", + title: "Notification Channels", + description: "Configure the notifiction channels for your organization." }, identityProviders: { apple: { @@ -2216,6 +2216,31 @@ export const extensions: Extensions = { } } }, + pushAuth: { + quickStart: { + addLoginModal: { + heading: "Add Push Authenticator", + subHeading: "Select an application to set up push authentication." + }, + connectApp: { + description: + "Add <1>Push Notification to <3>Step 2 on the <5>Login Flow" + + " section of your <7>application." + }, + heading: "Push Authenticator Set Up Guide", + subHeading: "Follow the instructions to set up Push Authentication in your login flow.", + steps: { + selectApplication: { + content: "Choose the <1>application for which you want to set up Push Authentication.", + heading: "Select Application" + }, + selectPushAuth: { + content: "Go to <1>Login Flow tab and click on the <3>Username & Password + Push option from the Multi-factor login section to configure a basic Push Authentication flow.", + heading: "Select <1>Push Authenticator option" + } + } + } + }, facebook: { quickStart: { addLoginModal: { diff --git a/apps/console/src/public/deployment.config.json b/apps/console/src/public/deployment.config.json index cb0223fe96e..c17832a84dd 100644 --- a/apps/console/src/public/deployment.config.json +++ b/apps/console/src/public/deployment.config.json @@ -1012,6 +1012,34 @@ "update": [] } }, + "pushProviders": { + "disabledFeatures": [], + "enabled": true, + "features": [ + { + "feature": "pushProviders", + "flag": "" + }, + { + "feature": "pushProviders.templates", + "flag": "" + } + ], + "scopes": { + "create": [ + "internal_notification_senders_create" + ], + "delete": [ + "internal_notification_senders_delete" + ], + "read": [ + "internal_notification_senders_view" + ], + "update": [ + "internal_notification_senders_update" + ] + } + }, "remoteFetchConfig": { "disabledFeatures": [], "enabled": false, diff --git a/apps/console/src/public/resources/notification-providers/assets/images/illustrations/push.svg b/apps/console/src/public/resources/notification-providers/assets/images/illustrations/push.svg new file mode 100644 index 00000000000..b792a720d04 --- /dev/null +++ b/apps/console/src/public/resources/notification-providers/assets/images/illustrations/push.svg @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + + + + + + + + + + diff --git a/features/admin.connections.v1/components/authenticator-grid.tsx b/features/admin.connections.v1/components/authenticator-grid.tsx index 5d268a2e385..07473e6e278 100644 --- a/features/admin.connections.v1/components/authenticator-grid.tsx +++ b/features/admin.connections.v1/components/authenticator-grid.tsx @@ -1,5 +1,5 @@ /** - * Copyright (c) 2023-2024, WSO2 LLC. (https://www.wso2.com). + * Copyright (c) 2023-2025, WSO2 LLC. (https://www.wso2.com). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -50,6 +50,7 @@ import React, { MouseEvent, ReactElement, SyntheticEvent, + useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; @@ -157,7 +158,10 @@ export const AuthenticatorGrid: FunctionComponent([]); const [ showDeleteConfirmationModal, setShowDeleteConfirmationModal ] = useState(false); const [ deletingIDP, setDeletingIDP ] = useState(undefined); const [ isDeletionloading, setIsDeletionLoading ] = useState(false); @@ -179,6 +183,15 @@ export const AuthenticatorGrid: FunctionComponent { + const shownAuthenticatorList: (ConnectionInterface | AuthenticatorInterface)[] = + authenticators.filter((authenticator:ConnectionInterface | AuthenticatorInterface) => { + return !hiddenAuthenticators.includes(authenticator.name); + }); + + setDisplayingAuthenticators(shownAuthenticatorList); + }, [ authenticators ]); + /** * Redirects to the authenticator edit page when the edit button is clicked. * @@ -448,14 +461,14 @@ export const AuthenticatorGrid: FunctionComponent { - authenticators?.map((authenticator: ConnectionInterface + displayingAuthenticators?.map((authenticator: ConnectionInterface | AuthenticatorInterface, index: number) => { const authenticatorConfig: AuthenticatorExtensionsConfigInterface = get( diff --git a/features/admin.connections.v1/components/authenticators/push/assets/build-login-flow.png b/features/admin.connections.v1/components/authenticators/push/assets/build-login-flow.png new file mode 100644 index 00000000000..96d81c56cec Binary files /dev/null and b/features/admin.connections.v1/components/authenticators/push/assets/build-login-flow.png differ diff --git a/features/admin.connections.v1/components/authenticators/push/quick-start.tsx b/features/admin.connections.v1/components/authenticators/push/quick-start.tsx new file mode 100644 index 00000000000..f2a49be2df8 --- /dev/null +++ b/features/admin.connections.v1/components/authenticators/push/quick-start.tsx @@ -0,0 +1,160 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { useRequiredScopes } from "@wso2is/access-control"; +import ApplicationSelectionModal from "@wso2is/admin.applications.v1/components/application-selection-modal"; +import { + VerticalStepper, + VerticalStepperStepInterface +} from "@wso2is/admin.core.v1/components/vertical-stepper/vertical-stepper"; +import { FeatureConfigInterface } from "@wso2is/admin.core.v1/models"; +import { AppState } from "@wso2is/admin.core.v1/store"; +import { IdentifiableComponentInterface } from "@wso2is/core/models"; +import { GenericIcon, Heading, Link, PageHeader, Text } from "@wso2is/react-components"; +import React, { FunctionComponent, ReactElement, useState } from "react"; +import { Trans, useTranslation } from "react-i18next"; +import { useSelector } from "react-redux"; +import { Grid } from "semantic-ui-react"; +import BuildLoginFlowIllustration from "./assets/build-login-flow.png"; + +/** + * Prop types of the component. + */ +type PushAuthQuickStartPropsInterface = IdentifiableComponentInterface; + +/** + * Quick start content for the Push authenticator. + * + * @param props - Props injected into the component. + * + * @returns Push Authenticator Quick start component. + */ +const PushAuthQuickStart: FunctionComponent = ( + props: PushAuthQuickStartPropsInterface +): ReactElement => { + + const { + [ "data-componentid" ]: componentId = "push-authenticator-quick-start" + } = props; + + const { t } = useTranslation(); + + const [ showApplicationModal, setShowApplicationModal ] = useState(false); + + const featureConfig: FeatureConfigInterface = useSelector((state: AppState) => state.config.ui.features); + + const hasApplicationReadPermissions: boolean = useRequiredScopes( + featureConfig?.applications?.scopes?.read + ); + + /** + * Vertical Stepper steps. + * @returns An array of Vertical Stepper steps. + */ + const steps: VerticalStepperStepInterface[] = [ + { + stepContent: ( + + + Choose the { hasApplicationReadPermissions ? ( + setShowApplicationModal(true) }> + application ) : "application" } + for which you want to set up Push Authentication login. + + + ), + stepTitle: t("extensions:develop.identityProviders.pushAuth.quickStart.steps.selectApplication.heading") + }, + { + stepContent: ( + <> + + + Go to Login Flow tab and click on the Username & Password + Push + option from the Multi-factor login section to configure a basic + Push Authentication flow. + + + + + ), + stepTitle: ( + + Select Push Notification option + + ) + } + ]; + + return ( + <> + + + + + + { t("extensions:develop.identityProviders.pushAuth.quickStart.subHeading") } + + + + + + + + + + { + showApplicationModal && ( + setShowApplicationModal(false) } + heading={ + t("extensions:develop.identityProviders.pushAuth.quickStart.addLoginModal.heading") + } + subHeading={ + t("extensions:develop.identityProviders.pushAuth.quickStart.addLoginModal.subHeading") + } + data-componentid={ componentId + "-application-selection-modal" } + /> + ) + } + + ); +}; + + +export default PushAuthQuickStart; diff --git a/features/admin.connections.v1/components/edit/forms/authenticators/index.ts b/features/admin.connections.v1/components/edit/forms/authenticators/index.ts index 7407e96e2ec..1ac1e89489b 100644 --- a/features/admin.connections.v1/components/edit/forms/authenticators/index.ts +++ b/features/admin.connections.v1/components/edit/forms/authenticators/index.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2020, WSO2 LLC. (https://www.wso2.com). All Rights Reserved. + * Copyright (c) 2020-2025, WSO2 LLC. (https://www.wso2.com). * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -24,3 +24,4 @@ export * from "./github-authenticator-form"; export * from "./google-authenticator-form"; export * from "./microsoft-authenticator-form"; export * from "./sms-otp-authenticator-form"; +export * from "./push-authenticator-form"; diff --git a/features/admin.connections.v1/components/edit/forms/authenticators/push-authenticator-form.tsx b/features/admin.connections.v1/components/edit/forms/authenticators/push-authenticator-form.tsx new file mode 100644 index 00000000000..834f04b2321 --- /dev/null +++ b/features/admin.connections.v1/components/edit/forms/authenticators/push-authenticator-form.tsx @@ -0,0 +1,491 @@ +/** + * Copyright (c) 2025, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { TestableComponentInterface } from "@wso2is/core/models"; +import { Field, Form } from "@wso2is/form"; +import { Code } from "@wso2is/react-components"; +import { FormValidation } from "@wso2is/validation"; +import isBoolean from "lodash-es/isBoolean"; +import isEmpty from "lodash-es/isEmpty"; +import React, { FunctionComponent, ReactElement, useEffect, useState } from "react"; +import { Trans, useTranslation } from "react-i18next"; +import { Label } from "semantic-ui-react"; +import { ConnectionUIConstants } from "../../../../constants/connection-ui-constants"; +import { LocalAuthenticatorConstants } from "../../../../constants/local-authenticator-constants"; +import { + CommonAuthenticatorFormFieldInterface, + CommonAuthenticatorFormFieldMetaInterface, + CommonAuthenticatorFormInitialValuesInterface, + CommonAuthenticatorFormMetaInterface, + CommonAuthenticatorFormPropertyInterface +} from "../../../../models/authenticators"; +import { + CommonPluggableComponentMetaPropertyInterface, + CommonPluggableComponentPropertyInterface +} from "../../../../models/connection"; + +interface PushAuthenticatorFormPropsInterface extends TestableComponentInterface { + /** + * Push Authenticator Metadata. + */ + metadata: CommonAuthenticatorFormMetaInterface; + /** + * Push Authenticator configured initial values. + */ + initialValues: CommonAuthenticatorFormInitialValuesInterface; + /** + * Callback for form submit. + * @param values - Resolved Form Values. + */ + onSubmit: (values: CommonAuthenticatorFormInitialValuesInterface) => void; + /** + * Is readonly. + */ + readOnly?: boolean; + /** + * Flag to trigger form submit externally. + */ + triggerSubmit: boolean; + /** + * Flag to enable/disable form submit button. + */ + enableSubmitButton: boolean; + /** + * Flag to show/hide custom properties. + * @remarks Not implemented ATM. Do this when needed. + */ + showCustomProperties: boolean; + /** + * Specifies if the form is submitting. + */ + isSubmitting?: boolean; +} + +/** + * Form initial values interface. + */ +interface PushAuthenticatorFormInitialValuesInterface { + /** + * Enable number challenge. + */ + PUSH_EnableNumberChallenge: boolean; + /** + * Enable progressive enrollment. + */ + PUSH_EnableProgressiveEnrollment: boolean; + /** + * Resend notification after time in seconds. + */ + PUSH_ResendNotificationTime: number; + /** + * Resend notification max attempts. + */ + PUSH_ResendNotificationMaxAttempts: number; +} + +/** + * Form fields interface. + */ +interface PushAuthenticatorFormFieldsInterface { + /** + * Enable number challenge. + */ + PUSH_EnableNumberChallenge: CommonAuthenticatorFormFieldInterface; + /** + * Enable progressive enrollment. + */ + PUSH_EnableProgressiveEnrollment: CommonAuthenticatorFormFieldInterface; + /** + * Resend notification after time in seconds. + */ + PUSH_ResendNotificationTime: CommonAuthenticatorFormFieldInterface; + /** + * Resend notification max attempts. + */ + PUSH_ResendNotificationMaxAttempts: CommonAuthenticatorFormFieldInterface; +} + +/** + * Proptypes for the Push Authenticator Form error messages. + */ +export interface PushAuthenticatorFormErrorValidationsInterface { + /** + * Enable number challenge. + */ + PUSH_EnableNumberChallenge: string; + /** + * Enable progressive enrollment. + */ + PUSH_EnableProgressiveEnrollment: string; + /** + * Resend notification after time in seconds. + */ + PUSH_ResendNotificationTime: string; + /** + * Resend notification max attempts. + */ + PUSH_ResendNotificationMaxAttempts: string; +} + +const FORM_ID: string = "push-authenticator-form"; + +/** + * Push Authenticator Form. + * + * @param props - Props injected to the component. + * @returns Functional component. + */ +export const PushAuthenticatorForm: FunctionComponent = ( + props: PushAuthenticatorFormPropsInterface +): ReactElement => { + + const { + metadata, + initialValues: originalInitialValues, + onSubmit, + readOnly, + isSubmitting, + ["data-testid"]: testId + } = props; + + const { t } = useTranslation(); + + // This can be used when `meta` support is there. + const [ , setFormFields ] = useState(undefined); + const [ initialValues, setInitialValues ] = useState(undefined); + + /** + * Flattens and resolved form initial values and field metadata. + */ + useEffect(() => { + + if (isEmpty(originalInitialValues?.properties)) { + return; + } + + let resolvedFormFields: PushAuthenticatorFormFieldsInterface = null; + let resolvedInitialValues: PushAuthenticatorFormInitialValuesInterface = null; + + originalInitialValues.properties.forEach((value: CommonAuthenticatorFormPropertyInterface) => { + const meta: CommonAuthenticatorFormFieldMetaInterface = metadata?.properties + .find((meta: CommonPluggableComponentMetaPropertyInterface) => meta.key === value.key); + + const moderatedName: string = value.name.replace(/\./g, "_"); + + // Converting resend time from seconds to minutes. + if (moderatedName === LocalAuthenticatorConstants.MODERATED_PUSH_RESEND_NOTIFICATION_TIME_KEY) { + const resendTimeInMinutes: number = Math.round(parseInt(value.value,10) / 60); + + resolvedInitialValues = { + ...resolvedInitialValues, + [moderatedName]: resendTimeInMinutes + }; + resolvedFormFields = { + ...resolvedFormFields, + [moderatedName]: { + meta, + value: resendTimeInMinutes.toString() + } + }; + } else { + resolvedFormFields = { + ...resolvedFormFields, + [moderatedName]: { + meta, + value: (value.value === "true" || value.value === "false") + ? JSON.parse(value.value) + : value.value + } + }; + + resolvedInitialValues = { + ...resolvedInitialValues, + [moderatedName]: (value.value === "true" || value.value === "false") + ? JSON.parse(value.value) + : value.value + }; + } + }); + setFormFields(resolvedFormFields); + setInitialValues(resolvedInitialValues); + }, [ originalInitialValues ]); + + /** + * Prepare form values for submitting. + * + * @param values - Form values. + * @returns Sanitized form values. + */ + const getUpdatedConfigurations = (values: PushAuthenticatorFormInitialValuesInterface) + : CommonAuthenticatorFormInitialValuesInterface => { + + const properties: CommonPluggableComponentPropertyInterface[] = []; + + for (const [ name, value ] of Object.entries(values)) { + if (name != undefined) { + const moderatedName: string = name.replace(/_/g, "."); + + if (name === LocalAuthenticatorConstants.MODERATED_PUSH_RESEND_NOTIFICATION_TIME_KEY) { + const timeInSeconds: number = value * 60; + + properties.push({ + name: moderatedName, + value: timeInSeconds.toString() + }); + + continue; + } + + properties.push({ + name: moderatedName, + value: isBoolean(value) ? value.toString() : value + }); + } + } + + return { + ...originalInitialValues, + properties + }; + }; + + /** + * Validates the Form. + * + * @param values - Form Values. + * @returns Form validation + */ + const validateForm = (values: PushAuthenticatorFormInitialValuesInterface): + PushAuthenticatorFormErrorValidationsInterface => { + + const errors: PushAuthenticatorFormErrorValidationsInterface = { + PUSH_EnableNumberChallenge: undefined, + PUSH_EnableProgressiveEnrollment: undefined, + PUSH_ResendNotificationMaxAttempts: undefined, + PUSH_ResendNotificationTime: undefined + }; + + if (!values.PUSH_ResendNotificationMaxAttempts) { + // Check for required error. + errors.PUSH_ResendNotificationMaxAttempts = t("authenticationProvider:forms" + + ".authenticatorSettings.push.allowedResendAttemptsCount.validations.required"); + } else if (!FormValidation.isInteger(values.PUSH_ResendNotificationMaxAttempts as unknown as number)) { + // Check for invalid input. + errors.PUSH_ResendNotificationMaxAttempts = t("authenticationProvider:forms" + + ".authenticatorSettings.push.allowedResendAttemptsCount.validations.invalid"); + } else if (values.PUSH_ResendNotificationMaxAttempts < ConnectionUIConstants + .PUSH_AUTHENTICATOR_SETTINGS_FORM_FIELD_CONSTRAINTS.ALLOWED_RESEND_ATTEMPT_COUNT_MIN_VALUE + || (values.PUSH_ResendNotificationMaxAttempts > ConnectionUIConstants + .PUSH_AUTHENTICATOR_SETTINGS_FORM_FIELD_CONSTRAINTS.ALLOWED_RESEND_ATTEMPT_COUNT_MAX_VALUE)) { + // Check for invalid range. + errors.PUSH_ResendNotificationMaxAttempts = t("authenticationProvider:forms" + + ".authenticatorSettings.push.allowedResendAttemptsCount.validations.range"); + } + + if (!values.PUSH_ResendNotificationTime) { + // Check for required error. + errors.PUSH_ResendNotificationTime = t("authenticationProvider:forms" + + ".authenticatorSettings.push.resendInterval.validations.required"); + } else if (!FormValidation.isInteger(values.PUSH_ResendNotificationTime as unknown as number)) { + // Check for invalid input. + errors.PUSH_ResendNotificationTime = t("authenticationProvider:forms" + + ".authenticatorSettings.push.resendInterval.validations.invalid"); + } else if (values.PUSH_ResendNotificationTime < ConnectionUIConstants + .PUSH_AUTHENTICATOR_SETTINGS_FORM_FIELD_CONSTRAINTS.RESEND_INTERVAL_MIN_VALUE + || (values.PUSH_ResendNotificationTime > ConnectionUIConstants + .PUSH_AUTHENTICATOR_SETTINGS_FORM_FIELD_CONSTRAINTS.RESEND_INTERVAL_MAX_VALUE)) { + // Check for invalid range. + errors.PUSH_ResendNotificationTime = t("authenticationProvider:forms" + + ".authenticatorSettings.push.resendInterval.validations.range"); + } + + return errors; + }; + + return ( +
) =>{ + onSubmit(getUpdatedConfigurations(values as PushAuthenticatorFormInitialValuesInterface)); + } } + initialValues={ initialValues } + validate={ validateForm } + > + + Please check this checkbox to enable number challenge during authentication. + ) + } + readOnly={ readOnly } + width={ 16 } + data-testid={ `${ testId }-push-enable-number-challenge-checkbox` } + /> + + Please check this checkbox to enable progressive enrollment. + ) + } + readOnly={ readOnly } + width={ 16 } + data-testid={ `${ testId }-push-enable-progressive-enrollment-checkbox` } + /> + + Please pich a value between 1 minute & 10 minutes. + ) + } + required={ true } + readOnly={ readOnly } + min={ + ConnectionUIConstants + .PUSH_AUTHENTICATOR_SETTINGS_FORM_FIELD_CONSTRAINTS.RESEND_INTERVAL_MIN_VALUE + } + maxLength={ + ConnectionUIConstants + .PUSH_AUTHENTICATOR_SETTINGS_FORM_FIELD_CONSTRAINTS.RESEND_INTERVAL_MAX_LENGTH + } + minLength={ + ConnectionUIConstants + .PUSH_AUTHENTICATOR_SETTINGS_FORM_FIELD_CONSTRAINTS.RESEND_INTERVAL_MIN_LENGTH + } + width={ 12 } + data-testid={ `${ testId }-push-resend-interval-input` } + > + + + + + Users will be limited to the specified resend attempt count when trying to resend the + push notification. + ) + } + required={ true } + readOnly={ readOnly } + min={ + ConnectionUIConstants + .PUSH_AUTHENTICATOR_SETTINGS_FORM_FIELD_CONSTRAINTS.ALLOWED_RESEND_ATTEMPT_COUNT_MIN_VALUE + } + maxLength={ + ConnectionUIConstants + .PUSH_AUTHENTICATOR_SETTINGS_FORM_FIELD_CONSTRAINTS.ALLOWED_RESEND_ATTEMPT_COUNT_MAX_LENGTH + } + minLength={ + ConnectionUIConstants + .PUSH_AUTHENTICATOR_SETTINGS_FORM_FIELD_CONSTRAINTS.ALLOWED_RESEND_ATTEMPT_COUNT_MIN_LENGTH + } + width={ 12 } + data-testid={ `${ testId }-push-allowed-resend-attempts-input` } + > + + + +