Skip to main content
SignupForm and ForgotPasswordForm are standalone components that handle their respective multi-step flows internally. Both use ChallengeView for email verification. Both require <AilitaProvider> as an ancestor to access merchant configuration and inject API credentials.

Signup Flow

How It Works

1

User fills the registration form

SignupForm collects firstName, lastName, email, password (optional when loginMode is OTP_EMAIL), and phone (optional). Password requirements: minimum 8 characters, must contain at least one letter and one number. The password field is hidden entirely when the merchant uses OTP-only authentication.
2

Library submits signup request

On submit, the library calls the signup API with the form data plus device fingerprint and behavioral biometrics — both collected automatically in the background before the user clicks submit. The app and mid tenant identifiers are injected from the merchant context automatically. You do not provide them.
3

Email verification

Two outcomes depending on the backend response:1. challengeId returnedSignupForm shows an inline ChallengeView where the user enters a 6-digit OTP sent to their email. After successful verification, onSuccess(userId) fires and the user is considered fully registered.2. No challengeIdSignupForm shows a “Check your email” message with an activation link instruction. The user must click the link in their email before they can log in. onSuccess does not fire in this case.
4

Account activated

After email verification (OTP or link), the account moves to active status and the user can log in via LoginForm.
A signed-up user cannot log in until their email is verified. If your app redirects to the login page immediately after signup, display a message reminding the user to check their email first. Do not call router.push('/dashboard') inside onSuccess without confirming the user’s email is already verified — the standard approach is to navigate to a “check your email” page instead.

Post-Signup State

The table below shows what state the user account is in at each stage of the signup flow.
AfterUser StatusEmail StatusCan Login?
Form submittedACTIVEVALIDATION_SENTNo
Email verified (OTP or link)ACTIVEVALIDATEDYes
The signupMode of the merchant controls who can sign up. When set to INVITATION_ONLY, SignupForm shows a 403 error: “Registration on this platform is by invitation only.” Users without a prior invitation cannot create accounts. The signupMode is configured on the backend — see Provider Setup for details.

SignupForm Props

layout
'full-screen' | 'side-screen'
Controls the visual layout. full-screen renders a centered card on a gray background. side-screen renders the form on the right half with a branded colored panel on the left (uses --color-primary). Defaults to 'full-screen'.
onSuccess
(userId: string) => void
Fires after successful signup and email OTP verification. Receives the new user’s ID. If the backend returns no challengeId (email link verification path), this callback does not fire — the user must navigate to login manually after clicking the link in their email.
onLoginClick
() => void
Optional. Fires when the user clicks “Already have an account? Log in.” Use this to navigate back to your login page.
onChallengeRequired
(challengeId: string) => void
Optional. If provided, SignupForm does not render the inline ChallengeView after signup. Instead it calls this callback with the challengeId and stops — you are responsible for rendering ChallengeView in a custom location.
This follows the same pattern as LoginForm.onChallengeRequired — provide it only if you want to render ChallengeView outside the form (e.g., in a modal or a separate route). For most apps, omitting this prop is the right choice.

Signup Integration Example

'use client'
import { SignupForm } from 'ailita-library'
import { useRouter } from 'next/navigation'

export default function SignupPage() {
  const router = useRouter()

  return (
    <SignupForm
      onSuccess={() => router.push('/dashboard')}
      onLoginClick={() => router.push('/login')}
    />
  )
}

Forgot Password Flow

How It Works

1

User enters email

ForgotPasswordForm collects the user’s email address and calls the forgot-password API. The form is a single email field with a submit button.
2

Email OTP verification

The backend sends a 6-digit code to the provided email address. ForgotPasswordForm transitions to a verification screen and renders an inline ChallengeView for OTP entry.Security behavior: If the email address does not exist in the system, the API returns 404 — but the form still advances to the verification screen and shows: “If the email exists, we sent a code.” This prevents email enumeration attacks. Developers must not override this behavior by adding a custom “email not found” error message.
3

Set new password

After OTP verification, the form transitions to a new password screen. The user enters a new password (minimum 8 characters, must contain at least one letter and one number) and a confirmation field. Mismatched passwords are validated client-side before the API call.
4

Password updated

A success confirmation screen is shown with a “Go to login” button. The onSuccess() callback fires at this point.

allowPasswordRecovery

If the merchant has allowPasswordRecovery set to false, ForgotPasswordForm renders only a message: “Forgot your password? Please contact your administrator.” with a link back to login. The forgot-password API is never called. This setting is read from the discovery response automatically — you do not check it manually. See Provider Setup for details on merchant configuration.

ForgotPasswordForm Props

onSuccess
() => void
Optional. Fires after the password has been successfully reset. Use this to navigate the user to the login page or display a global success notification.
onLoginClick
() => void
Optional. Fires when the user clicks “Back to login” on any step of the flow. Wire this to your login page route.

Forgot Password Integration Example

'use client'
import { ForgotPasswordForm } from 'ailita-library'
import { useRouter } from 'next/navigation'

export default function ForgotPasswordPage() {
  const router = useRouter()

  return (
    <ForgotPasswordForm
      onSuccess={() => router.push('/login')}
      onLoginClick={() => router.push('/login')}
    />
  )
}

Wiring All Auth Pages Together

The recommended routing structure for a complete authentication surface:
/login           → <LoginForm onSignupClick={→/signup} onForgotPasswordClick={→/forgot-password} />
/signup          → <SignupForm onLoginClick={→/login} />
/forgot-password → <ForgotPasswordForm onLoginClick={→/login} />
All three components are self-contained. They manage their own internal state machines and render ChallengeView inline when needed. You only need to wire the navigation callbacks — no state lifting required.

Next Steps

Auth Flows

Deep dive into the login state machine, challenge handling, and TOTP verification.

Hooks: useAuth & useUser

Access auth state, user data, and trigger auth actions programmatically.