{"_id":"@disruptica/ladybug-identity","_rev":"2-f84bd873ccab215967e5b55cef6fe086","name":"@disruptica/ladybug-identity","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@disruptica/ladybug-identity","version":"0.1.0","keywords":["ladybug","identity","widget","hmac"],"license":"MIT","_id":"@disruptica/ladybug-identity@0.1.0","maintainers":[{"name":"sebastian-disruptica","email":"sebastian@disruptica.com"}],"dist":{"shasum":"4335d60f09558c57da20673a9d1e11eedc55844d","tarball":"https://registry.npmjs.org/@disruptica/ladybug-identity/-/ladybug-identity-0.1.0.tgz","fileCount":3,"integrity":"sha512-kGTOFDOI5n56TmtPvcnZ0zpr9aGGMiDbjY1n+2JmmFArly9TQqm/vidBg9Gb+T6GlModravfImv13ZRXOI2TmA==","signatures":[{"sig":"MEQCIGKBDvukc1z+GPYIP2wZI5vmj6gR7Nf9VxEafAIU9gUzAiAdXWnQilrKFDNVHQ8A3CZ8ZMYqzo7E69wORSzzTyfX4Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":2165},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"2f9e279848327b24be4ce366788a92c2392f1ee0","scripts":{"build":"bun run build.ts"},"_npmUser":{"name":"sebastian-disruptica","email":"sebastian@disruptica.com"},"_npmVersion":"11.12.1","description":"Server-side helpers for signing Ladybug widget identities","directories":{},"_nodeVersion":"25.9.0","_hasShrinkwrap":false,"devDependencies":{"@types/bun":"latest","typescript":"^5"},"_npmOperationalInternal":{"tmp":"tmp/ladybug-identity_0.1.0_1777485769435_0.9047989468836781","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@disruptica/ladybug-identity","version":"0.1.1","description":"Server-side helpers for signing Ladybug widget identities","type":"module","main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"}},"scripts":{"build":"bun run build.ts"},"devDependencies":{"@types/bun":"latest","typescript":"^5"},"keywords":["ladybug","identity","widget","hmac"],"license":"MIT","gitHead":"2f9e279848327b24be4ce366788a92c2392f1ee0","_id":"@disruptica/ladybug-identity@0.1.1","_nodeVersion":"25.9.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-NdcM5K7OADZ08jd7syDwgeRKowTGPHO1UBBsV9+U5XXl0UpRPL3LgbNRRznD/Sj1puWqfhLD90D9geghKW6qng==","shasum":"70d6db945123c83e13d442a7e08b995bfdb7ca83","tarball":"https://registry.npmjs.org/@disruptica/ladybug-identity/-/ladybug-identity-0.1.1.tgz","fileCount":4,"unpackedSize":6911,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCcC3cwHeRC8W95WsF+yg6kgTcn5nQlB3fDZidCFxe1vAIgfmAApjV3OGvVMq3YdUN6k+8dVO+vvt1UAuHcKcu1d/Y="}]},"_npmUser":{"name":"sebastian-disruptica","email":"sebastian@disruptica.com"},"directories":{},"maintainers":[{"name":"sebastian-disruptica","email":"sebastian@disruptica.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ladybug-identity_0.1.1_1777485881970_0.22157284854135884"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-29T18:02:49.315Z","modified":"2026-04-29T18:04:42.249Z","0.1.0":"2026-04-29T18:02:49.589Z","0.1.1":"2026-04-29T18:04:42.107Z"},"license":"MIT","keywords":["ladybug","identity","widget","hmac"],"description":"Server-side helpers for signing Ladybug widget identities","maintainers":[{"name":"sebastian-disruptica","email":"sebastian@disruptica.com"}],"readme":"# @disruptica/ladybug-identity\n\nServer-side helper for signing [Ladybug](https://github.com/disruptica/ladybug) widget identity tokens. Use this in your backend to identify logged-in users in the embedded widget.\n\nWorks in any Node.js or Bun server — no dependencies beyond the built-in `crypto` module.\n\n## Installation\n\n```bash\nnpm install @disruptica/ladybug-identity\n```\n\n---\n\n## How it works\n\nYour backend signs a short-lived HMAC token containing the user's identity. The widget sends this token to Ladybug, which verifies it and associates the chat session with the real user.\n\n```\nYour server  →  signLadybugIdentity()  →  token  →  widget  →  Ladybug\n```\n\n---\n\n## Usage\n\n### Basic\n\n```ts\nimport { signLadybugIdentity } from '@disruptica/ladybug-identity';\n\nconst token = signLadybugIdentity({\n  secret: process.env.WIDGET_SESSION_SECRET,\n  user: {\n    id: 'user_123',\n    email: 'alice@example.com',\n    name: 'Alice',\n  },\n});\n// Returns a signed token string, valid for 15 minutes by default\n```\n\nPass this token to the widget:\n\n```html\n<script\n  src=\"https://cdn.jsdelivr.net/npm/@disruptica/ladybug-widget/dist/widget.js\"\n  data-embed-key=\"emb_xxx\"\n  data-user-token=\"<token from your backend>\"\n></script>\n```\n\nOr via the programmatic API:\n\n```ts\nnew LadybugWidget({\n  embedKey: 'emb_xxx',\n  userToken: tokenFromYourBackend,\n});\n```\n\n### Expose an endpoint\n\nThe recommended pattern is a short-lived token endpoint your frontend calls on load.\n\n**Next.js (App Router)**\n\n```ts\n// app/api/ladybug-token/route.ts\nimport { signLadybugIdentity } from '@disruptica/ladybug-identity';\nimport { auth } from '@/lib/auth';\n\nexport async function GET() {\n  const session = await auth();\n  if (!session?.user) return new Response('Unauthorized', { status: 401 });\n\n  const token = signLadybugIdentity({\n    secret: process.env.WIDGET_SESSION_SECRET!,\n    user: {\n      id: session.user.id,\n      email: session.user.email,\n      name: session.user.name,\n    },\n    expiresInSeconds: 900, // 15 minutes\n  });\n\n  return Response.json({ token });\n}\n```\n\n**Express / Hono / any framework**\n\n```ts\nimport { signLadybugIdentity } from '@disruptica/ladybug-identity';\n\napp.get('/api/ladybug-token', requireAuth, (req, res) => {\n  const token = signLadybugIdentity({\n    secret: process.env.WIDGET_SESSION_SECRET,\n    user: {\n      id: req.user.id,\n      email: req.user.email,\n      name: req.user.name,\n      role: req.user.role,        // optional\n      metadata: { plan: 'pro' },  // optional arbitrary data\n    },\n  });\n  res.json({ token });\n});\n```\n\n**Rails**\n\n```ruby\n# config/routes.rb\nget '/api/ladybug-token', to: 'ladybug#token'\n\n# app/controllers/ladybug_controller.rb\nclass LadybugController < ApplicationController\n  before_action :authenticate_user!\n\n  def token\n    require 'openssl'\n    require 'base64'\n    require 'json'\n\n    secret = ENV['WIDGET_SESSION_SECRET']\n    now    = Time.now.to_i\n    payload = {\n      sub:   current_user.id.to_s,\n      email: current_user.email,\n      name:  current_user.name,\n      iat:   now,\n      exp:   now + 900\n    }.to_json\n\n    body = Base64.urlsafe_encode64(payload, padding: false)\n    sig  = Base64.urlsafe_encode64(\n      OpenSSL::HMAC.digest('sha256', secret, body),\n      padding: false\n    )\n\n    render json: { token: \"#{body}.#{sig}\" }\n  end\nend\n```\n\n### With `getUserToken` (dynamic refresh)\n\nThe widget calls this function each time it needs to authenticate, so tokens are always fresh:\n\n```ts\nnew LadybugWidget({\n  embedKey: 'emb_xxx',\n  getUserToken: async () => {\n    const res = await fetch('/api/ladybug-token');\n    if (!res.ok) return null;\n    const { token } = await res.json();\n    return token;\n  },\n});\n```\n\n---\n\n## API reference\n\n### `signLadybugIdentity(options)`\n\n| Option | Type | Default | Description |\n|---|---|---|---|\n| `secret` | `string` | required | `WIDGET_SESSION_SECRET` from your Ladybug instance |\n| `user.id` | `string` | required | Unique user identifier |\n| `user.email` | `string` | — | User email |\n| `user.name` | `string` | — | Display name |\n| `user.role` | `string` | — | User role |\n| `user.metadata` | `Record<string, unknown>` | — | Any extra data |\n| `embedKey` | `string` | — | Scope token to a specific embed installation |\n| `expiresInSeconds` | `number` | `900` (15 min) | Token TTL |\n\nReturns a `string` — a base64url-encoded signed token.\n\n### `signLadybugIdentityPayload(payload, secret)`\n\nLow-level function if you need to construct the payload yourself.\n\n```ts\nimport { signLadybugIdentityPayload } from '@disruptica/ladybug-identity';\n\nconst token = signLadybugIdentityPayload(\n  { sub: 'user_123', exp: Math.floor(Date.now() / 1000) + 900 },\n  process.env.WIDGET_SESSION_SECRET,\n);\n```\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md"}