Skip to content

Add input validation support to flow components#518

Open
SajidMannikeri17 wants to merge 1 commit into
asgardeo:mainfrom
Infosys:feat/input-validation
Open

Add input validation support to flow components#518
SajidMannikeri17 wants to merge 1 commit into
asgardeo:mainfrom
Infosys:feat/input-validation

Conversation

@SajidMannikeri17
Copy link
Copy Markdown

@SajidMannikeri17 SajidMannikeri17 commented May 13, 2026

Purpose

SDK support for declarative input validation rules on flow prompts, paired
with the backend feature in asgardeo/thunder#2410.

Today, a flow's prompt inputs can carry validation rules in the flow definition
and the server returns data.fieldErrors when those rules fail. This PR makes
that feature usable from @asgardeo/react consumers — flow authors put rules on
input components, the SDK runs them client-side for instant feedback, and the SDK
surfaces server-side validation errors into the same render state used for
required-field errors.

Approach

Three layers, mirroring the design in the discussion:

  1. @asgardeo/javascript — new framework-agnostic primitives:

    • ValidationRule, ValidationRuleType, FieldError types.
    • evaluateValidationRule and buildValidatorFromRules utilities.
    • EmbeddedFlowComponent.validation and EmbeddedFlowResponseData.fieldErrors extensions on the v2 flow models.
  2. @asgardeo/react — wires the primitives into the auth components:

    • BaseSignIn, BaseSignUp, BaseAcceptInvite, BaseInviteUser, BaseRecovery — client-side rule evaluation via useForm, server-side fieldErrors projection via useEffect.
    • SignInRenderProps.fieldErrors — exposes server-side validation errors to render-prop consumers (custom UI flows).
  3. @asgardeo/i18n — default validation message keys (validation.pattern.invalid, validation.minLength.invalid, validation.maxLength.invalid) added to all locale bundles.

Backward compatibility

All new fields are optional. Existing consumers without validation rules behave
exactly as before. No package version bumps required at the consumer level; a
consumer pinned to the previous SDK version can upgrade without code changes.

Tests

  • 18 new unit tests for the validation utilities (13 for evaluateValidationRule,
    5 for buildValidatorFromRules).
  • All 482 existing tests across the 4 modified packages still pass.
  • Typecheck clean across all 4 packages.

Related Issues

Related PRs

  • N/A

Checklist

  • Followed the CONTRIBUTING guidelines.
  • Manual test round performed and verified.
  • Documentation provided. (Add links if there are any)
  • Unit tests provided. (Add links if there are any)

Security checks

Summary by CodeRabbit

  • New Features

    • Declarative input validation (pattern, min/max length) with client-side evaluation and utilities.
    • Server-side, per-field error handling surfaced across sign-in, sign-up, invite, recovery and related flows.
    • Field validation errors exposed to render-prop consumers for custom UIs.
    • Added validation messages in nine locales.
  • Tests

    • Unit tests added for validation evaluation and validator composition.
  • Documentation

    • Added changeset documenting the new validation support.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f82162cb-c706-43e9-8678-1ec8b0ba5fcd

📥 Commits

Reviewing files that changed from the base of the PR and between 207a59f and a78d19b.

📒 Files selected for processing (23)
  • .changeset/input-validation-flow-inputs.md
  • packages/i18n/src/models/i18n.ts
  • packages/i18n/src/translations/en-US.ts
  • packages/i18n/src/translations/fr-FR.ts
  • packages/i18n/src/translations/hi-IN.ts
  • packages/i18n/src/translations/ja-JP.ts
  • packages/i18n/src/translations/pt-BR.ts
  • packages/i18n/src/translations/pt-PT.ts
  • packages/i18n/src/translations/si-LK.ts
  • packages/i18n/src/translations/ta-IN.ts
  • packages/i18n/src/translations/te-IN.ts
  • packages/javascript/src/index.ts
  • packages/javascript/src/models/v2/embedded-flow-v2.ts
  • packages/javascript/src/utils/__tests__/buildValidatorFromRules.test.ts
  • packages/javascript/src/utils/__tests__/evaluateValidationRule.test.ts
  • packages/javascript/src/utils/v2/buildValidatorFromRules.ts
  • packages/javascript/src/utils/v2/evaluateValidationRule.ts
  • packages/react/src/components/presentation/auth/AcceptInvite/v2/BaseAcceptInvite.tsx
  • packages/react/src/components/presentation/auth/InviteUser/v2/BaseInviteUser.tsx
  • packages/react/src/components/presentation/auth/Recovery/v2/BaseRecovery.tsx
  • packages/react/src/components/presentation/auth/SignIn/v2/BaseSignIn.tsx
  • packages/react/src/components/presentation/auth/SignIn/v2/SignIn.tsx
  • packages/react/src/components/presentation/auth/SignUp/v2/BaseSignUp.tsx
✅ Files skipped from review due to trivial changes (6)
  • packages/i18n/src/translations/hi-IN.ts
  • packages/i18n/src/translations/te-IN.ts
  • .changeset/input-validation-flow-inputs.md
  • packages/i18n/src/translations/si-LK.ts
  • packages/i18n/src/models/i18n.ts
  • packages/i18n/src/translations/pt-PT.ts
🚧 Files skipped from review as they are similar to previous changes (15)
  • packages/i18n/src/translations/ta-IN.ts
  • packages/i18n/src/translations/fr-FR.ts
  • packages/javascript/src/utils/tests/buildValidatorFromRules.test.ts
  • packages/i18n/src/translations/en-US.ts
  • packages/javascript/src/utils/v2/buildValidatorFromRules.ts
  • packages/javascript/src/utils/tests/evaluateValidationRule.test.ts
  • packages/react/src/components/presentation/auth/SignIn/v2/SignIn.tsx
  • packages/javascript/src/models/v2/embedded-flow-v2.ts
  • packages/react/src/components/presentation/auth/AcceptInvite/v2/BaseAcceptInvite.tsx
  • packages/javascript/src/index.ts
  • packages/react/src/components/presentation/auth/SignUp/v2/BaseSignUp.tsx
  • packages/i18n/src/translations/ja-JP.ts
  • packages/react/src/components/presentation/auth/SignIn/v2/BaseSignIn.tsx
  • packages/react/src/components/presentation/auth/Recovery/v2/BaseRecovery.tsx
  • packages/react/src/components/presentation/auth/InviteUser/v2/BaseInviteUser.tsx

📝 Walkthrough

Walkthrough

Adds ValidationRule and FieldError types, client utilities (evaluateValidationRule, buildValidatorFromRules) with i18n fallback keys, updates translation bundles, re-exports new APIs, and wires client/server field-level validation into React flow components to surface per-field errors and touched state.

Changes

Input validation flow

Layer / File(s) Summary
Validation model and data contracts
packages/javascript/src/models/v2/embedded-flow-v2.ts
EmbeddedFlowComponent gains validation?: ValidationRule[]; EmbeddedFlowResponseData gains fieldErrors?: FieldError[]; new exports: ValidationRuleType, ValidationRule, FieldError.
Client-side validation utilities and tests
packages/javascript/src/utils/v2/evaluateValidationRule.ts, packages/javascript/src/utils/v2/buildValidatorFromRules.ts, packages/javascript/src/utils/__tests__/*
Adds evaluateValidationRule and DEFAULT_VALIDATION_MESSAGE_KEYS; adds buildValidatorFromRules returning a form-compatible validator or null; Vitest suites cover rule evaluation, fallback keys, lenient client behavior, and validator composition.
i18n validation messages
packages/i18n/src/models/i18n.ts, packages/i18n/src/translations/*.ts
Adds validation.pattern.invalid, validation.minLength.invalid, and validation.maxLength.invalid to I18nTranslations and to all language bundles updated in this PR.
Public API exports and changeset
packages/javascript/src/index.ts, .changeset/input-validation-flow-inputs.md
Re-exports ValidationRuleV2, ValidationRuleTypeV2, FieldErrorV2, evaluateValidationRule, DEFAULT_VALIDATION_MESSAGE_KEYS, and buildValidatorFromRules; changeset documents the new SDK exports and package version bumps.
React component validation wiring
packages/react/src/components/presentation/auth/SignIn/v2/BaseSignIn.tsx, packages/react/src/components/presentation/auth/SignIn/v2/SignIn.tsx, packages/react/src/components/presentation/auth/SignUp/v2/BaseSignUp.tsx, packages/react/src/components/presentation/auth/AcceptInvite/v2/BaseAcceptInvite.tsx, packages/react/src/components/presentation/auth/InviteUser/v2/BaseInviteUser.tsx, packages/react/src/components/presentation/auth/Recovery/v2/BaseRecovery.tsx
Components create per-component rule validators from component.validation, translate returned rule message keys, set form errors from client and server fieldErrors, mark touched fields, and for SignIn maintain serverFieldErrors state and expose a collapsed fieldErrors map to render-prop consumers.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Possibly related PRs

Suggested labels

Type/Improvement

Suggested reviewers

  • brionmario
  • thiva-k
  • DonOmalVindula

Poem

🐇 I hopped through rules both short and long,
Patterns and lengths in my little song.
Client and server now share a glance,
Errors shown early, given a chance.
Input fields smile — validation’s strong.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding input validation support to flow components.
Description check ✅ Passed The description comprehensively covers all required template sections including Purpose, Related Issues, Related PRs, Checklist items, and Security checks with substantive content.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
packages/react/src/components/presentation/auth/SignIn/v2/BaseSignIn.tsx (1)

64-70: 💤 Low value

JSDoc comment could clarify the server error lifecycle.

The inline documentation states that fieldErrors reflects both client-side and server-side errors, but it doesn't explain when server errors are cleared. Consider adding a note that server errors persist until the next submission or until the field is edited (depending on implementation).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react/src/components/presentation/auth/SignIn/v2/BaseSignIn.tsx`
around lines 64 - 70, Update the JSDoc for the fieldErrors block in BaseSignIn
to explicitly document the server error lifecycle: state that fieldErrors
combines client-side rule evaluation and server-side failures (from
data.fieldErrors), that full FieldError[] is on the raw response and mirrored to
the serverFieldErrors prop, and add a sentence stating when server-side errors
are cleared (e.g., they persist until the next form submission or until the user
edits the associated field depending on the existing implementation of
BaseSignIn's submit/field-change handlers). Reference the fieldErrors identifier
and the serverFieldErrors prop so readers can locate the related state/prop
handling.
packages/react/src/components/presentation/auth/InviteUser/v2/BaseInviteUser.tsx (1)

435-452: 💤 Low value

Complex nested validation logic can be simplified.

Lines 435-452 have nested conditionals: if required & empty → error, else → email validation, then rule validation (if no error yet). The if (value && !errors[comp.ref]) check at line 443 suggests defensive coding, but errors[comp.ref] won't be set yet in this context since we're building the errors object.

Consider restructuring for clarity:

♻️ Optional refactor for clarity
            const value: any = formValues[comp.ref];
            if (comp.required && (!value || value.trim() === '')) {
              errors[comp.ref] = `${comp.label || comp.ref} is required`;
+             return; // Skip further validation for empty required fields
            } else {
              // Email validation
              if (comp.type === 'EMAIL_INPUT' && value && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
                errors[comp.ref] = 'Please enter a valid email address';
+               return; // Skip rule validation if email format is invalid
              }
              // Evaluate declarative validation rules from meta.components[].validation.
-             if (value && !errors[comp.ref]) {
+             if (value) {
                const ruleValidator = buildValidatorFromRules(comp.validation);
                if (ruleValidator) {
                  const message = ruleValidator(value);
                  if (message) {
                    errors[comp.ref] = message;
                  }
                }
              }
            }

This makes the validation precedence explicit: required → email format → rules.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/react/src/components/presentation/auth/InviteUser/v2/BaseInviteUser.tsx`
around lines 435 - 452, The validation block in BaseInviteUser.tsx is overly
nested and uses a redundant guard (checking value && !errors[comp.ref]) which is
unnecessary because errors is being built here; refactor the logic in the loop
that inspects each comp so it enforces precedence explicitly: first check
required and set errors[comp.ref] if missing, then if comp.type ===
'EMAIL_INPUT' validate the email format and set errors[comp.ref] if invalid, and
finally if value exists and no error yet call
buildValidatorFromRules(comp.validation) and apply the returned ruleValidator to
set errors[comp.ref] if it returns a message; keep references to comp.ref,
comp.label, comp.type, buildValidatorFromRules and the errors object to locate
and update the code.
packages/react/src/components/presentation/auth/AcceptInvite/v2/BaseAcceptInvite.tsx (1)

491-502: 💤 Low value

Validation logic flow may allow both required and rule errors to be set.

Lines 491-502 check comp.required first, then evaluate rules. However, if comp.required && !value, the function returns early, but the else if (value) block at line 493 means rules are only evaluated when there's a value. The structure is correct, but the code would be clearer with an explicit early return after setting the required error.

♻️ Optional refactor for clarity
            const value: any = formValues[comp.ref];
            if (comp.required && (!value || value.trim() === '')) {
              errors[comp.ref] = t('validations.required.field.error');
-           } else if (value) {
+             return; // Skip rule evaluation for empty required fields
+           }
+           if (value) {
              // Evaluate declarative validation rules from meta.components[].validation.
              const ruleValidator = buildValidatorFromRules(comp.validation);
              if (ruleValidator) {
                const message = ruleValidator(value);
                if (message) {
                  errors[comp.ref] = t(message);
                }
              }
            }

This makes it explicit that we don't evaluate rules for empty required fields.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/react/src/components/presentation/auth/AcceptInvite/v2/BaseAcceptInvite.tsx`
around lines 491 - 502, The required-field branch may be ambiguous; after
setting errors[comp.ref] when comp.required && (!value || value.trim() === ''),
short-circuit so we don't run buildValidatorFromRules/ ruleValidator for empty
values — add an explicit early exit (e.g., continue the loop or return from the
enclosing function) immediately after setting the required error so rules are
not evaluated for empty required fields and t(...) is only called once.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/react/src/components/presentation/auth/AcceptInvite/v2/BaseAcceptInvite.tsx`:
- Around line 293-308: The useEffect that reads server FieldError entries from
currentFlow (FieldError) currently merges them into state via setFormErrors(prev
=> ({...prev,...errors})) which leaves stale server errors; change setFormErrors
and setTouchedFields to replace the server error state when responseFieldErrors
exist (e.g., call setFormErrors(errors) and setTouchedFields(touched)) so
server-provided errors do not persist across responses; if you need to preserve
client-side validation errors, track them separately or merge only client-side
errors back into state after replacing server errors.

In
`@packages/react/src/components/presentation/auth/InviteUser/v2/BaseInviteUser.tsx`:
- Around line 284-299: The useEffect that processes server FieldError objects
(referencing currentFlow, FieldError, and useEffect) merges the new server
errors into existing form state via setFormErrors((prev) => ({...prev,
...errors})) and similarly for setTouchedFields, which leaves stale errors;
change the update to replace the server-provided error state instead of merging
(e.g., setFormErrors to set only the new errors and setTouchedFields to set only
the new touched map), ensuring you still preserve non-server local form state
elsewhere if needed.

In `@packages/react/src/components/presentation/auth/SignIn/v2/BaseSignIn.tsx`:
- Around line 385-397: The effect that applies serverFieldErrors should also
clear injected errors when serverFieldErrors becomes null/empty: update the
useEffect around serverFieldErrors to, when serverFieldErrors is falsy or length
=== 0, call setFormErrors({}) and clear touched flags for any previously-set
server error fields by calling setFormTouched(key, false) for each key (you can
read previous keys from current formErrors or track them in a ref) so stale
messages/touched state are removed; keep the existing logic for populating
errors when serverFieldErrors exists and adjust the effect dependencies to
include formErrors or the ref used to track previous keys.

---

Nitpick comments:
In
`@packages/react/src/components/presentation/auth/AcceptInvite/v2/BaseAcceptInvite.tsx`:
- Around line 491-502: The required-field branch may be ambiguous; after setting
errors[comp.ref] when comp.required && (!value || value.trim() === ''),
short-circuit so we don't run buildValidatorFromRules/ ruleValidator for empty
values — add an explicit early exit (e.g., continue the loop or return from the
enclosing function) immediately after setting the required error so rules are
not evaluated for empty required fields and t(...) is only called once.

In
`@packages/react/src/components/presentation/auth/InviteUser/v2/BaseInviteUser.tsx`:
- Around line 435-452: The validation block in BaseInviteUser.tsx is overly
nested and uses a redundant guard (checking value && !errors[comp.ref]) which is
unnecessary because errors is being built here; refactor the logic in the loop
that inspects each comp so it enforces precedence explicitly: first check
required and set errors[comp.ref] if missing, then if comp.type ===
'EMAIL_INPUT' validate the email format and set errors[comp.ref] if invalid, and
finally if value exists and no error yet call
buildValidatorFromRules(comp.validation) and apply the returned ruleValidator to
set errors[comp.ref] if it returns a message; keep references to comp.ref,
comp.label, comp.type, buildValidatorFromRules and the errors object to locate
and update the code.

In `@packages/react/src/components/presentation/auth/SignIn/v2/BaseSignIn.tsx`:
- Around line 64-70: Update the JSDoc for the fieldErrors block in BaseSignIn to
explicitly document the server error lifecycle: state that fieldErrors combines
client-side rule evaluation and server-side failures (from data.fieldErrors),
that full FieldError[] is on the raw response and mirrored to the
serverFieldErrors prop, and add a sentence stating when server-side errors are
cleared (e.g., they persist until the next form submission or until the user
edits the associated field depending on the existing implementation of
BaseSignIn's submit/field-change handlers). Reference the fieldErrors identifier
and the serverFieldErrors prop so readers can locate the related state/prop
handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 021eeeb6-b26e-4ba3-9e3a-4e980b34fc2e

📥 Commits

Reviewing files that changed from the base of the PR and between 22ee26d and 207a59f.

📒 Files selected for processing (23)
  • .changeset/input-validation-flow-inputs.md
  • packages/i18n/src/models/i18n.ts
  • packages/i18n/src/translations/en-US.ts
  • packages/i18n/src/translations/fr-FR.ts
  • packages/i18n/src/translations/hi-IN.ts
  • packages/i18n/src/translations/ja-JP.ts
  • packages/i18n/src/translations/pt-BR.ts
  • packages/i18n/src/translations/pt-PT.ts
  • packages/i18n/src/translations/si-LK.ts
  • packages/i18n/src/translations/ta-IN.ts
  • packages/i18n/src/translations/te-IN.ts
  • packages/javascript/src/index.ts
  • packages/javascript/src/models/v2/embedded-flow-v2.ts
  • packages/javascript/src/utils/__tests__/buildValidatorFromRules.test.ts
  • packages/javascript/src/utils/__tests__/evaluateValidationRule.test.ts
  • packages/javascript/src/utils/v2/buildValidatorFromRules.ts
  • packages/javascript/src/utils/v2/evaluateValidationRule.ts
  • packages/react/src/components/presentation/auth/AcceptInvite/v2/BaseAcceptInvite.tsx
  • packages/react/src/components/presentation/auth/InviteUser/v2/BaseInviteUser.tsx
  • packages/react/src/components/presentation/auth/Recovery/v2/BaseRecovery.tsx
  • packages/react/src/components/presentation/auth/SignIn/v2/BaseSignIn.tsx
  • packages/react/src/components/presentation/auth/SignIn/v2/SignIn.tsx
  • packages/react/src/components/presentation/auth/SignUp/v2/BaseSignUp.tsx

Comment thread packages/react/src/components/presentation/auth/SignIn/v2/BaseSignIn.tsx Outdated
@SajidMannikeri17 SajidMannikeri17 force-pushed the feat/input-validation branch from 207a59f to a78d19b Compare May 15, 2026 07:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant