Packages
OTP widget
@labyrinthe/otp-widget: <labyrinthe-otp> web component and React component for SMS verification, with its server handlers.
Ready-made verification form: number input, code cells, resend with countdown, autofill (Web OTP), light and dark, fr and en. The widget never calls the Labyrinthe API directly: it talks to two routes on your server, which keep the API key.
Version 1.2.0. Source code, examples and changelog: widget-otp folder on GitHub.
Installation#
npm install @labyrinthe/otp-widget @labyrinthe/connectHow it works#
- The browser sends the number to
POST /api/otp/send(your server). - Your server calls
POST /otp/sendwith the Node.js SDK and keepsotp_idin an encrypted cookie. - The browser sends the code to
POST /api/otp/verify; your server verifies it and callsonVerified(open the session…).
Server side#
// lib/otp.ts
import { Labyrinthe } from '@labyrinthe/connect';
import { createOtpHandlers } from '@labyrinthe/otp-widget/server';
export const otp = createOtpHandlers({
client: () => new Labyrinthe(), // LABYRINTHE_API_KEY (lsk_test_…: nothing is sent)
secret: process.env.LABYRINTHE_OTP_SECRET, // 32+ characters, encrypts the cookie
cookie: { path: '/api/otp' },
onVerified: ({ phone, setCookie }) => {
// The number is verified: trust it here, never in the browser
return { redirect: '/dashboard' }; // sent back to the widget
},
});
// Next.js App Router: app/api/otp/send/route.ts and app/api/otp/verify/route.ts
export const runtime = 'nodejs';
export const POST = otp.send; // otp.verify in the other route
// Express: app.post('/api/otp/send', otp.express.send); app.post('/api/otp/verify', otp.express.verify);| Option | Role |
|---|---|
client | Labyrinthe instance or a function returning one. Required. |
secret | 32 characters or more (LABYRINTHE_OTP_SECRET): encrypts the cookie. |
onVerified | Called once the number is verified; its return value is sent to the widget. Required. |
beforeSend, allowedOrigins | Refuse a send (per-IP limit, captcha), restrict origins. |
template, locale, webOtpDomain, stripAccents, codeLength, codeType | Options passed to POST /otp/send. |
exposeTestCode | Returns test_code to the widget (refused when NODE_ENV=production). |
store | Custom session store (memoryStore() provided). |
Browser side#
'use client';
import { LabyrintheOtp } from '@labyrinthe/otp-widget/react';
export default function VerifyPage() {
return (
<LabyrintheOtp
sendUrl="/api/otp/send"
verifyUrl="/api/otp/verify"
locale="fr"
onVerified={({ data }) => { if (data?.redirect) window.location.assign(data.redirect); }}
onError={(error) => console.warn(error.code)}
/>
);
}| Attribute | Default | Role |
|---|---|---|
send-url, verify-url | /api/otp/send, /api/otp/verify | Your server routes. |
locale, country-code, phone | fr, 243, — | Language, country code, pre-filled number. |
code-length, code-type | 6, numeric | Code shape. |
theme, heading | auto, default title | light / dark; "" hides the title. |
no-auto-submit, no-web-otp | — | Disable code auto-submit, Web OTP. |
Events: labyrinthe-otp:sent, labyrinthe-otp:verified, labyrinthe-otp:error (with code, retryAfterSeconds, adminMessage and until during maintenance). Styling: --lab-otp-primary, --lab-otp-radius… CSS variables and ::part(…).
Maintenance: when OTP sending or verification is off (FEATURE_DISABLED, OTP_SERVICE_DISABLED), the server handlers answer 503 service_unavailable with the platform message and the return date; the widget shows them.