Search by

3neti / form-handler-otp

3neti

OTP verification handler for form flow system

Package info

github.com/3neti/form-handler-otp

pkg:composer/3neti/form-handler-otp

Statistics

Installs: 3 988

Dependents: 1

Suggesters: 0

Stars: 0

Open Issues: 0

v1.4.2 2026-11-11 08:10 UTC

README

3neti/form-handler-otp adds mobile one-time-password verification to 3neti/form-flow. It delegates OTP generation, delivery, expiry, attempt tracking, and verification to a configured Txtcmdr service.

Requirements

  • PHP 8.2 or newer
  • Laravel 12 or 13
  • Form Flow 1.8 or newer
  • Inertia Laravel 2 or 3 in applications that render the published Vue page

Installation

composer require 3neti/form-handler-otp
php artisan otp-handler:install --no-interaction

The package service provider registers the otp handler automatically. The install command publishes the package configuration and Vue page.

Configuration

Publish the configuration independently when needed:

php artisan vendor:publish --tag=otp-handler-config

Configure the Txtcmdr endpoint and bearer token in the host environment:

TXTCMDR_API_URL=https://txtcmdr.example
TXTCMDR_API_TOKEN=
TXTCMDR_CONNECT_TIMEOUT=5
TXTCMDR_TIMEOUT=15
TXTCMDR_VERIFY_SSL=true
OTP_HANDLER_DRIVER=txtcmdr

OTP_MAX_RESENDS=3
OTP_RESEND_COOLDOWN=30

Do not commit the API token. The handler sends it only as the bearer token for Txtcmdr requests.

Form Flow contract

Add an otp step after a step that collects mobile:

[
    'handler' => 'otp',
    'config' => [
        'purpose' => 'onboarding.account',
        'max_resends' => 3,
        'resend_cooldown' => 30,
        'digits' => 6,
        'ui_variant' => 'compact',
    ],
]

The handler reads the mobile number from the current Form Flow session. It fails before contacting Txtcmdr if no mobile number has been collected.

Rendering the handler has no delivery side effect. The claimant explicitly selects Send verification code. The handler then:

  1. creates a challenge through POST /api/v1/otp/challenges;
  2. stores only the returned challenge reference in the session;
  3. enables verification and the throttled resend control.

On submission, it verifies through POST /api/v1/otp/challenges/{challenge}/verify. A successful verification clears the challenge and delivery-control session state, then returns:

[
    'mobile' => '+639171234567',
    'verified_at' => '2026-07-31T15:00:00+08:00',
    'reference_id' => 'flow-123',
    'verification_reference' => '01K...',
    'verification_purpose' => 'onboarding.account',
]

The raw code is never returned as Form Flow result data. Hosts must validate the structured proof against the expected purpose and claimant identity before executing a protected workflow.

User interface

The published Vue page supports Form Flow's default, compact, and immersive UI variants. It uses the shared Form Flow screen and action components, accepts numeric input, submits the nested data.otp_code payload, and focuses the code input on entry.

Delivery safeguards

  • Verification identifiers are isolated by Form Flow reference in the session.
  • Opening or refreshing the page cannot send an SMS.
  • Successful codes are one-time because Txtcmdr is the verification authority and local session state is cleared after acceptance.
  • Resend count and cooldown are enforced by the server handler, not only by the browser.
  • Provider rejection reasons are mapped to sanitized validation messages.
  • A missing verification session or mobile number fails closed.
  • Hosts should still rate-limit Form Flow endpoints by authenticated principal, session, mobile reference, and source address.

Testing

composer test
composer pint -- --test
composer audit

The package suite fakes HTTP and verifies the request, session, resend, provider-rejection, expiry, and UI render contracts without sending SMS.

License

MIT

YAML presentation integration

The expanded tokens/screens/actions contract requires the companion Form Flow changes in this development slice; it is not provided by the released v1.10.0 alone. Release the manager before its dependent handler versions. The portable schema, inheritance and limits are documented in docs/PRESENTATION.md in 3neti/form-flow. x-change owns its Pay Code profile and domain driver; this package owns the handler interaction.

In an existing complete YAML driver, merge this step configuration:

steps:
  otp:
    handler: otp
    step_name: otp_step
    title: Verify phone number
    config:
      presentation:
        layout:
          density: compact
        actions:
          placement: viewport_bottom

The driver may also select a registered profile in top-level presentation. Step settings override driver settings, which override the selected profile and manager defaults. The handler page uses the shared presentation and action components; legacy ui_variant remains a fallback without that policy.

The Send Verification Code and Verify OTP states share a step. A step-level primary_label applies to both states, so leave it unset unless a single label is accurate for both. Resend/error styling remains component-owned. Delivery, expiry, retry limits, and verification are handler behavior, not presentation.

After package updates, publish this handler's Vue resources using its installer and rebuild the standalone host. In x-change hosts, use php artisan x-change:publish --scope=build --force --verify --no-interaction and the host build workflow instead. Review intentional host customizations before forced publication; never maintain fixes in generated host copies.

Verify a fresh fixture-based flow and preview after changes. Active flows retain their frozen presentation. Test initial, processing, error, and completed/review states on mobile; do not collect real identity evidence or send real OTPs merely to verify appearance. Unsupported schema settings must fail validation.