{"id":50820531,"url":"https://github.com/kobie3717/wa-flashlogin","last_synced_at":"2026-06-13T13:07:21.264Z","repository":{"id":361152531,"uuid":"1253204503","full_name":"kobie3717/wa-flashlogin","owner":"kobie3717","description":"WhatsApp-native passwordless auth. Zero PIN typing. Reverse-OTP via Baileys.","archived":false,"fork":false,"pushed_at":"2026-05-29T11:46:48.000Z","size":113,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-29T12:25:54.132Z","etag":null,"topics":["authentication","baileys","nodejs","otp","passwordless","reverse-otp","sdk","typescript","wasp","whatsapp"],"latest_commit_sha":null,"homepage":"https://github.com/kobie3717/wa-flashlogin","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kobie3717.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-29T08:38:22.000Z","updated_at":"2026-05-29T11:47:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/kobie3717/wa-flashlogin","commit_stats":null,"previous_names":["kobie3717/wa-flashlogin"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/kobie3717/wa-flashlogin","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kobie3717%2Fwa-flashlogin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kobie3717%2Fwa-flashlogin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kobie3717%2Fwa-flashlogin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kobie3717%2Fwa-flashlogin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kobie3717","download_url":"https://codeload.github.com/kobie3717/wa-flashlogin/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kobie3717%2Fwa-flashlogin/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34285367,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-13T02:00:06.617Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentication","baileys","nodejs","otp","passwordless","reverse-otp","sdk","typescript","wasp","whatsapp"],"created_at":"2026-06-13T13:07:20.366Z","updated_at":"2026-06-13T13:07:21.259Z","avatar_url":"https://github.com/kobie3717.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# wa-flashlogin\n\nWhatsApp-native passwordless authentication. Zero PIN typing.\n\n[![Live Demo](https://img.shields.io/badge/demo-live-brightgreen)](https://flashlogin.whatshubb.co.za)\n\n## Try it Live\n\n**[https://flashlogin.whatshubb.co.za](https://flashlogin.whatshubb.co.za)**\n\nInteractive demo showing the full UX flow. Uses a mock WhatsApp adapter (no real messages sent) — click the orange \"Simulate Verified\" button to see what happens when a user taps Send in WhatsApp.\n\nFor production deployment, replace `MockAdapter` with `BaileysAdapter` to send real WhatsApp messages.\n\n## Why\n\nTraditional OTP flows force users to:\n1. Receive a code via SMS/email\n2. Memorize or copy 6 digits\n3. Switch apps\n4. Type the code\n\n**wa-flashlogin** reverses this. Users tap \"Send\" on a prefilled WhatsApp message. No typing. No app switching. No memorization.\n\nSimilar to [Stytch's WhatsApp OTP](https://stytch.com/products/whatsapp) or [OTPless](https://otpless.com/), but open-source and self-hosted.\n\n## Quickstart\n\n```bash\npnpm add wa-flashlogin-server express\n```\n\n```typescript\nimport express from 'express';\nimport { createFlashLogin, BaileysAdapter } from 'wa-flashlogin-server';\n\nconst flash = createFlashLogin({\n  adapter: new BaileysAdapter({\n    sock: yourBaileysSock,\n    botJid: '27825651069@s.whatsapp.net'\n  }),\n  secret: process.env.FLASHLOGIN_SECRET,\n  sessionTtlMs: 5 * 60 * 1000,\n  tokenLength: 8,\n  messageTemplate: 'Login {token}',\n});\n\nconst app = express();\napp.use('/auth', flash.router());\n\nflash.on('verified', ({ sessionId, phone }) =\u003e {\n  // User verified - create JWT, set session, etc.\n});\n\napp.listen(3000);\n```\n\n## Architecture\n\n```\n┌─────────────┐         ┌──────────────┐         ┌─────────────┐\n│   Browser   │         │  Your Server │         │  WhatsApp   │\n└──────┬──────┘         └──────┬───────┘         └──────┬──────┘\n       │                       │                        │\n       │  POST /auth/init      │                        │\n       │  {phone: \"+27...\"}    │                        │\n       ├──────────────────────\u003e│                        │\n       │                       │                        │\n       │  {sessionId, deeplink}│                        │\n       │\u003c──────────────────────┤                        │\n       │                       │                        │\n       │  GET /auth/stream/id  │                        │\n       │  (SSE connection)     │                        │\n       ├──────────────────────\u003e│                        │\n       │  event: pending       │                        │\n       │\u003c──────────────────────┤                        │\n       │                       │                        │\n       │  [User taps deeplink] │                        │\n       │──────────────────────────────────────────────\u003e │\n       │                       │                        │\n       │                       │  \"Login ABCD1234\"      │\n       │                       │\u003c───────────────────────┤\n       │                       │                        │\n       │  event: verified      │                        │\n       │  {phone: \"+27...\"}    │                        │\n       │\u003c──────────────────────┤                        │\n       │  [connection closed]  │                        │\n       │                       │                        │\n```\n\n## API Reference\n\n### Server SDK\n\n#### `createFlashLogin(options)`\n\n```typescript\ninterface FlashLoginOptions {\n  adapter: WAAdapter;              // Baileys adapter or custom\n  secret: string;                  // HMAC signing key\n  sessionTtlMs?: number;           // Session expiry (default: 5min)\n  tokenLength?: number;            // Token chars (default: 8)\n  messageTemplate?: string;        // Message text (default: \"Login {token}\")\n  rateLimit?: {\n    windowMs?: number;             // Rate limit window (default: 30s)\n    maxRequests?: number;          // Max requests per window (default: 1)\n  };\n}\n```\n\n#### Events\n\n```typescript\nflash.on('verified', ({ sessionId, phone }) =\u003e void);\nflash.on('expired', ({ sessionId, phone }) =\u003e void);\n```\n\n### REST Endpoints\n\nMount `flash.router()` on Express:\n\n```typescript\napp.use('/auth', flash.router());\n```\n\n#### `POST /auth/init`\n\nInitialize auth session.\n\nRequest:\n```json\n{\n  \"phone\": \"+27825651069\"\n}\n```\n\nResponse:\n```json\n{\n  \"sessionId\": \"uuid\",\n  \"deeplink\": \"https://wa.me/1234567890?text=Login%20ABCD1234\",\n  \"expiresAt\": 1672531200000\n}\n```\n\n#### `GET /auth/stream/:sessionId`\n\nSSE stream for real-time status updates.\n\nEvents:\n- `pending` - Session waiting for verification\n- `verified` - User sent message, phone verified\n- `expired` - Session expired\n\n#### `GET /auth/status/:sessionId`\n\nPolling fallback.\n\nResponse:\n```json\n{\n  \"status\": \"verified\",\n  \"phone\": \"+27825651069\"\n}\n```\n\n## Adapters\n\n### BaileysAdapter\n\nFor direct Baileys integration (self-hosted WhatsApp session):\n\n```typescript\nimport { BaileysAdapter } from 'wa-flashlogin-server';\n\nconst adapter = new BaileysAdapter({\n  sock: baileysSock,           // Existing Baileys socket\n  botJid: '27825651069@s.whatsapp.net'\n});\n```\n\n### HttpWebhookAdapter\n\nFor webhook-based message routing (e.g., from WhatsHub, Twilio, MessageBird):\n\n```typescript\nimport { HttpWebhookAdapter } from 'wa-flashlogin-server';\n\nconst adapter = new HttpWebhookAdapter({\n  secret: process.env.FLASH_WEBHOOK_SECRET,  // For HMAC verification\n  botJid: '27825651069@s.whatsapp.net'       // Your bot number\n});\n\n// Mount webhook receiver\napp.use('/flash/webhook', adapter.router());\n\n// Configure your webhook provider to POST to /flash/webhook:\n// - Authorization: Bearer \u003csecret\u003e OR\n// - X-WhatsHub-Signature: sha256=\u003chmac\u003e\n// - Body: { from: \"27825651069@s.whatsapp.net\", text: \"Login ABCD1234\" }\n```\n\nThe adapter normalizes incoming webhook payloads to the standard `{ from, text }` shape:\n- Strips `@s.whatsapp.net` suffix from JIDs\n- Accepts both `from` and `fromJid` fields\n- Accepts both `text` and `body` fields\n- Verifies authenticity via HMAC signature OR Bearer token\n\n### MetaCloudAdapter\n\nFor Meta's official WhatsApp Business Cloud API (no Baileys, lower ban risk):\n\n```typescript\nimport { MetaCloudAdapter } from 'wa-flashlogin-server';\n\nconst adapter = new MetaCloudAdapter({\n  verifyToken: process.env.META_VERIFY_TOKEN,    // for hub.verify_token check\n  appSecret: process.env.META_APP_SECRET,        // for X-Hub-Signature-256 HMAC\n  botJid: '27825651069',                         // phone-number-id or display number\n});\n\n// Mount webhook receiver (handles both GET verification and POST messages)\napp.use('/meta-webhook', adapter.router());\n```\n\n**CRITICAL:** Meta Cloud API requires raw request body for signature verification. The adapter handles this internally - do NOT use `express.json()` middleware before mounting the router.\n\n### Custom Adapter\n\nImplement `WAAdapter` interface:\n\n```typescript\ninterface WAAdapter {\n  onMessage(handler: (msg: { from: string; text: string }) =\u003e void): () =\u003e void;\n}\n```\n\n## Security\n\n- Tokens are cryptographically random (40+ bits entropy)\n- Phone number verification prevents token hijacking\n- Rate limiting prevents brute force (1 init per phone per 30s)\n- Sessions auto-expire (default 5min)\n- HMAC token signing (if needed for extended flows)\n\n## Built on\n\n- [Baileys](https://github.com/WhiskeySockets/Baileys) - WhatsApp Web API\n- [baileys-antiban](https://github.com/kobie3717/baileys-antiban) - Anti-ban middleware (optional)\n- [wasp-protocol](https://github.com/kobie3717/wasp-protocol) - WA session management (optional)\n\n## Frontend SDKs\n\n### React\n\n```bash\npnpm add wa-flashlogin-react\n```\n\n```tsx\nimport { FlashLoginButton } from 'wa-flashlogin-react';\n\nfunction LoginPage() {\n  return (\n    \u003cFlashLoginButton\n      apiBase=\"/auth\"\n      phone=\"+27825651069\"\n      onVerified={(payload) =\u003e router.push('/dashboard')}\n      onError={(err) =\u003e toast.error(err.message)}\n      label=\"Login with WhatsApp\"\n      qrFallback\n    /\u003e\n  );\n}\n```\n\nOr use the hook:\n\n```tsx\nimport { useFlashLogin } from 'wa-flashlogin-react';\n\nconst { init, status, deeplink, error } = useFlashLogin({\n  apiBase: '/auth',\n  phone: '+27825651069',\n});\n```\n\nSee [packages/react/README.md](./packages/react/README.md) for full API reference.\n\n### Vanilla JavaScript\n\n```bash\npnpm add wa-flashlogin-vanilla\n```\n\n```js\nimport { createFlashLogin } from 'wa-flashlogin-vanilla';\n\nconst flash = createFlashLogin({\n  apiBase: '/auth',\n  phone: '+27825651069',\n});\n\nflash.on('verified', ({ phone }) =\u003e {\n  location.href = '/dashboard';\n});\n\nflash.mountButton(document.querySelector('#login-btn'), {\n  label: 'Login with WhatsApp',\n  qrFallback: true,\n});\n```\n\nOr use from CDN:\n\n```html\n\u003cscript src=\"https://unpkg.com/wa-flashlogin-vanilla\"\u003e\u003c/script\u003e\n\u003cscript\u003e\n  const flash = WAFlashLogin.createFlashLogin({ ... });\n\u003c/script\u003e\n```\n\nSee [packages/vanilla/README.md](./packages/vanilla/README.md) for full API reference.\n\n## Examples\n\n- [basic-express](./examples/basic-express) - Server with mock adapter\n- [react-vite](./examples/react-vite) - React 18 + Vite app\n- [vanilla-html](./examples/vanilla-html) - Plain HTML + JavaScript\n\nSee [examples/README.md](./examples/README.md) for setup instructions.\n\n## Full Flow Example\n\nServer (Express):\n\n```typescript\nimport express from 'express';\nimport { createFlashLogin, BaileysAdapter } from 'wa-flashlogin-server';\n\nconst flash = createFlashLogin({\n  adapter: new BaileysAdapter({ sock, botJid }),\n  secret: process.env.FLASHLOGIN_SECRET,\n});\n\nconst app = express();\napp.use('/auth', flash.router());\n\nflash.on('verified', ({ sessionId, phone }) =\u003e {\n  // Create JWT, set session, etc.\n});\n\napp.listen(3000);\n```\n\nClient (React):\n\n```tsx\nimport { FlashLoginButton } from 'wa-flashlogin-react';\n\nfunction LoginPage() {\n  const handleVerified = async (payload: { phone: string }) =\u003e {\n    const response = await fetch('/api/session', { method: 'POST' });\n    const { token } = await response.json();\n    localStorage.setItem('token', token);\n    router.push('/dashboard');\n  };\n\n  return (\n    \u003cFlashLoginButton\n      apiBase=\"/auth\"\n      phone={phoneInput}\n      onVerified={handleVerified}\n      qrFallback\n    /\u003e\n  );\n}\n```\n\n## License\n\nMIT - Copyright (c) 2026 Kobus Wentzel\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkobie3717%2Fwa-flashlogin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkobie3717%2Fwa-flashlogin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkobie3717%2Fwa-flashlogin/lists"}