@biab-dev/sdk 0.2.1 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +418 -8
- package/dist/auth-handler.d.ts +91 -0
- package/dist/auth-handler.d.ts.map +1 -0
- package/dist/auth-handler.js +230 -0
- package/dist/auth-handler.js.map +1 -0
- package/dist/client.d.ts +101 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +261 -1
- package/dist/client.js.map +1 -1
- package/dist/contracts.d.ts +645 -42
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +161 -8
- package/dist/contracts.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/proxy.d.ts +58 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +91 -0
- package/dist/proxy.js.map +1 -0
- package/dist/react.d.ts +190 -8
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +285 -23
- package/dist/react.js.map +1 -1
- package/package.json +73 -66
package/README.md
CHANGED
|
@@ -45,29 +45,439 @@ The scaffold currently provides:
|
|
|
45
45
|
|
|
46
46
|
## Example
|
|
47
47
|
|
|
48
|
+
The SDK has two surfaces: a low-level client over the package API, and
|
|
49
|
+
site-scoped helpers under `client.site(siteId)`.
|
|
50
|
+
|
|
48
51
|
```ts
|
|
49
52
|
import { createBiabDevClient } from "@biab-dev/sdk";
|
|
50
53
|
|
|
51
54
|
const client = createBiabDevClient({
|
|
52
|
-
baseUrl:
|
|
55
|
+
baseUrl: process.env.BIAB_PACKAGE_API_BASE_URL!, // ends in /api/package/v1
|
|
53
56
|
apiKey: process.env.BIAB_API_KEY!,
|
|
54
57
|
});
|
|
55
58
|
|
|
56
|
-
|
|
59
|
+
// Identity probe — always the first call in a fresh integration.
|
|
60
|
+
const auth = await client.introspect();
|
|
61
|
+
|
|
62
|
+
const site = client.site(process.env.BIAB_SITE_ID!);
|
|
63
|
+
|
|
64
|
+
// Marketing-page JSON edited from Site Builder → Marketing pages
|
|
65
|
+
const { page } = await site.marketingPages.get("home");
|
|
57
66
|
|
|
58
|
-
|
|
67
|
+
// Custom collections
|
|
59
68
|
await site.collections.list();
|
|
60
|
-
await site.
|
|
61
|
-
|
|
62
|
-
filters: [{ fieldName: "title", operator: "contains", value: "chair" }],
|
|
69
|
+
await site.rows.query("page-content", {
|
|
70
|
+
filters: [{ fieldName: "pageKey", operator: "equals", value: "home" }],
|
|
63
71
|
limit: 10,
|
|
64
72
|
});
|
|
65
73
|
```
|
|
66
74
|
|
|
75
|
+
### End-to-end consumer pattern
|
|
76
|
+
|
|
77
|
+
`custom-demo/src/lib/biab/get-home-content.ts` in this monorepo is the
|
|
78
|
+
canonical consumer pattern: it normalizes the base URL (https-only, ends in
|
|
79
|
+
`/api/package/v1`), reads the marketing-page JSON via the SDK, and falls back
|
|
80
|
+
to a local `content.md` document if the env is not configured. Copy that file
|
|
81
|
+
when you build a new frontend that consumes BIAB content.
|
|
82
|
+
|
|
83
|
+
## Tenant auth (sign-in / sign-up / sign-out)
|
|
84
|
+
|
|
85
|
+
The SDK includes per-tenant authentication built on WorkOS Organizations. A
|
|
86
|
+
visitor who clicks "Sign in" on your site is signed in to **the org bound to
|
|
87
|
+
your API key** — not to BIAB itself. Sign-up, sign-in, password reset, MFA,
|
|
88
|
+
social login, and invitations are all handled by WorkOS's hosted page.
|
|
89
|
+
|
|
90
|
+
### How it works
|
|
91
|
+
|
|
92
|
+
1. Visitor clicks `<SignIn />` (a styled link).
|
|
93
|
+
2. Browser hits your installed `/api/biab-auth/sign-in` route.
|
|
94
|
+
3. SDK redirects to WorkOS's hosted page, scoped to your org.
|
|
95
|
+
4. After auth, WorkOS redirects to your installed `/api/biab-auth/callback`.
|
|
96
|
+
5. SDK exchanges the code, sets an httpOnly session cookie on **your domain**,
|
|
97
|
+
redirects the user to `returnTo`.
|
|
98
|
+
6. Subsequent requests can read the session via `useUser()` (client) or
|
|
99
|
+
`getTenantSession()` (server / SSR).
|
|
100
|
+
|
|
101
|
+
Cookie lives on your tenant's domain, not BIAB's — so your SSR can read it,
|
|
102
|
+
and there's no XSS-readable token floating around.
|
|
103
|
+
|
|
104
|
+
### One-time WorkOS configuration
|
|
105
|
+
|
|
106
|
+
Before the SDK auth flow works for your site, your BIAB platform admin (or
|
|
107
|
+
you, via the WorkOS dashboard if you have access) must register your
|
|
108
|
+
callback URL as an allowed **Redirect URI** for the WorkOS Organization
|
|
109
|
+
bound to your API key:
|
|
110
|
+
|
|
111
|
+
1. Open the [WorkOS dashboard](https://dashboard.workos.com).
|
|
112
|
+
2. Go to **Configuration → Redirects**.
|
|
113
|
+
3. Click **Add Redirect URI** and enter your callback URL exactly:
|
|
114
|
+
- Production: `https://your-site.com/api/biab-auth/callback`
|
|
115
|
+
- Local dev: `http://localhost:3000/api/biab-auth/callback`
|
|
116
|
+
4. Save. WorkOS will reject the auth flow with `redirect_uri_mismatch` if
|
|
117
|
+
this URL isn't registered byte-for-byte.
|
|
118
|
+
|
|
119
|
+
If you self-host BIAB or are using a sandbox, the same applies to the WorkOS
|
|
120
|
+
project that issued your platform's `WORKOS_CLIENT_ID`.
|
|
121
|
+
|
|
122
|
+
### Install the auth handler
|
|
123
|
+
|
|
124
|
+
`createAuthHandler` returns Fetch-standard handlers — they accept a `Request`
|
|
125
|
+
and return a `Response`. Mount the same factory in any framework that speaks
|
|
126
|
+
Web standards. The React components below are framework-agnostic; only the
|
|
127
|
+
handler install line differs.
|
|
128
|
+
|
|
129
|
+
In every example, mount the handler at a path that **matches the
|
|
130
|
+
`callbackUrl` you registered with WorkOS** — a mismatch produces
|
|
131
|
+
`redirect_uri_mismatch`.
|
|
132
|
+
|
|
133
|
+
#### Next.js (App Router)
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
// app/api/biab-auth/[...biab]/route.ts
|
|
137
|
+
import { createAuthHandler } from "@biab-dev/sdk";
|
|
138
|
+
|
|
139
|
+
const handler = createAuthHandler({
|
|
140
|
+
baseUrl: process.env.BIAB_PACKAGE_API_BASE_URL!,
|
|
141
|
+
apiKey: process.env.BIAB_API_KEY!,
|
|
142
|
+
callbackUrl: `${process.env.NEXT_PUBLIC_SITE_URL}/api/biab-auth/callback`,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
export const GET = handler.GET;
|
|
146
|
+
export const POST = handler.POST;
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
#### Next.js (Pages Router)
|
|
150
|
+
|
|
151
|
+
Pages Router uses Node `req`/`res`, not Fetch. Wrap the handler:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
// pages/api/biab-auth/[...biab].ts
|
|
155
|
+
import { createAuthHandler } from "@biab-dev/sdk";
|
|
156
|
+
import type { NextApiRequest, NextApiResponse } from "next";
|
|
157
|
+
|
|
158
|
+
const handler = createAuthHandler({
|
|
159
|
+
baseUrl: process.env.BIAB_PACKAGE_API_BASE_URL!,
|
|
160
|
+
apiKey: process.env.BIAB_API_KEY!,
|
|
161
|
+
callbackUrl: `${process.env.NEXT_PUBLIC_SITE_URL}/api/biab-auth/callback`,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
export default async function (req: NextApiRequest, res: NextApiResponse) {
|
|
165
|
+
const url = new URL(req.url!, `https://${req.headers.host}`);
|
|
166
|
+
const request = new Request(url, {
|
|
167
|
+
method: req.method,
|
|
168
|
+
headers: new Headers(req.headers as Record<string, string>),
|
|
169
|
+
body: req.method === "GET" || req.method === "HEAD"
|
|
170
|
+
? undefined
|
|
171
|
+
: JSON.stringify(req.body),
|
|
172
|
+
});
|
|
173
|
+
const response = await (req.method === "POST" ? handler.POST : handler.GET)(
|
|
174
|
+
request,
|
|
175
|
+
);
|
|
176
|
+
response.headers.forEach((v, k) => res.setHeader(k, v));
|
|
177
|
+
res.status(response.status).send(await response.text());
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
#### Remix
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
// app/routes/api.biab-auth.$.tsx
|
|
185
|
+
import { createAuthHandler } from "@biab-dev/sdk";
|
|
186
|
+
import type { LoaderFunctionArgs, ActionFunctionArgs } from "@remix-run/node";
|
|
187
|
+
|
|
188
|
+
const handler = createAuthHandler({
|
|
189
|
+
baseUrl: process.env.BIAB_PACKAGE_API_BASE_URL!,
|
|
190
|
+
apiKey: process.env.BIAB_API_KEY!,
|
|
191
|
+
callbackUrl: `${process.env.SITE_URL}/api/biab-auth/callback`,
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
export const loader = ({ request }: LoaderFunctionArgs) => handler.GET(request);
|
|
195
|
+
export const action = ({ request }: ActionFunctionArgs) => handler.POST(request);
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
#### SvelteKit
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
// src/routes/api/biab-auth/[...biab]/+server.ts
|
|
202
|
+
import { createAuthHandler } from "@biab-dev/sdk";
|
|
203
|
+
import { env } from "$env/dynamic/private";
|
|
204
|
+
import { env as pubEnv } from "$env/dynamic/public";
|
|
205
|
+
|
|
206
|
+
const handler = createAuthHandler({
|
|
207
|
+
baseUrl: env.BIAB_PACKAGE_API_BASE_URL,
|
|
208
|
+
apiKey: env.BIAB_API_KEY,
|
|
209
|
+
callbackUrl: `${pubEnv.PUBLIC_SITE_URL}/api/biab-auth/callback`,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
export const GET = ({ request }) => handler.GET(request);
|
|
213
|
+
export const POST = ({ request }) => handler.POST(request);
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
#### Astro
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
// src/pages/api/biab-auth/[...biab].ts
|
|
220
|
+
import { createAuthHandler } from "@biab-dev/sdk";
|
|
221
|
+
import type { APIRoute } from "astro";
|
|
222
|
+
|
|
223
|
+
const handler = createAuthHandler({
|
|
224
|
+
baseUrl: import.meta.env.BIAB_PACKAGE_API_BASE_URL,
|
|
225
|
+
apiKey: import.meta.env.BIAB_API_KEY,
|
|
226
|
+
callbackUrl: `${import.meta.env.PUBLIC_SITE_URL}/api/biab-auth/callback`,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
export const GET: APIRoute = ({ request }) => handler.GET(request);
|
|
230
|
+
export const POST: APIRoute = ({ request }) => handler.POST(request);
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
> Astro requires `output: "server"` (or `"hybrid"`) and an SSR-capable adapter
|
|
234
|
+
> for cookies and `Response.redirect` to work. Static-only Astro can't host
|
|
235
|
+
> the auth handler.
|
|
236
|
+
|
|
237
|
+
#### Hono / Cloudflare Workers / Bun / Deno
|
|
238
|
+
|
|
239
|
+
These runtimes are already Fetch-native — pass the raw `Request` through:
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
// Hono
|
|
243
|
+
import { Hono } from "hono";
|
|
244
|
+
import { createAuthHandler } from "@biab-dev/sdk";
|
|
245
|
+
|
|
246
|
+
const app = new Hono();
|
|
247
|
+
const handler = createAuthHandler({
|
|
248
|
+
baseUrl: process.env.BIAB_PACKAGE_API_BASE_URL!,
|
|
249
|
+
apiKey: process.env.BIAB_API_KEY!,
|
|
250
|
+
callbackUrl: `${process.env.SITE_URL}/api/biab-auth/callback`,
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
app.all("/api/biab-auth/*", (c) =>
|
|
254
|
+
c.req.method === "POST" ? handler.POST(c.req.raw) : handler.GET(c.req.raw),
|
|
255
|
+
);
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
// Cloudflare Worker (or Bun.serve / Deno.serve)
|
|
260
|
+
import { createAuthHandler } from "@biab-dev/sdk";
|
|
261
|
+
|
|
262
|
+
const handler = createAuthHandler({
|
|
263
|
+
baseUrl: env.BIAB_PACKAGE_API_BASE_URL,
|
|
264
|
+
apiKey: env.BIAB_API_KEY,
|
|
265
|
+
callbackUrl: `${env.SITE_URL}/api/biab-auth/callback`,
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
export default {
|
|
269
|
+
async fetch(request: Request) {
|
|
270
|
+
const { pathname } = new URL(request.url);
|
|
271
|
+
if (pathname.startsWith("/api/biab-auth")) {
|
|
272
|
+
return request.method === "POST"
|
|
273
|
+
? handler.POST(request)
|
|
274
|
+
: handler.GET(request);
|
|
275
|
+
}
|
|
276
|
+
return new Response("Not found", { status: 404 });
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
#### Express / Fastify (Node)
|
|
282
|
+
|
|
283
|
+
Use a Web-standards adapter such as `@whatwg-node/server` or
|
|
284
|
+
`fastify-web-standards` to bridge Node `req`/`res` ↔ Fetch `Request`/`Response`,
|
|
285
|
+
then call `handler.GET(request)` / `handler.POST(request)`. The adapter does
|
|
286
|
+
the conversion the Pages Router example does inline.
|
|
287
|
+
|
|
288
|
+
#### Nuxt 3 (Nitro)
|
|
289
|
+
|
|
290
|
+
Nitro server routes get a Fetch-style `event.node.req`. Use `toWebRequest`
|
|
291
|
+
from `h3` to bridge:
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
// server/api/biab-auth/[...biab].ts
|
|
295
|
+
import { createAuthHandler } from "@biab-dev/sdk";
|
|
296
|
+
import { toWebRequest } from "h3";
|
|
297
|
+
|
|
298
|
+
const handler = createAuthHandler({
|
|
299
|
+
baseUrl: process.env.BIAB_PACKAGE_API_BASE_URL!,
|
|
300
|
+
apiKey: process.env.BIAB_API_KEY!,
|
|
301
|
+
callbackUrl: `${process.env.NUXT_PUBLIC_SITE_URL}/api/biab-auth/callback`,
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
export default defineEventHandler(async (event) => {
|
|
305
|
+
const request = toWebRequest(event);
|
|
306
|
+
return event.node.req.method === "POST"
|
|
307
|
+
? handler.POST(request)
|
|
308
|
+
: handler.GET(request);
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
#### TanStack Start
|
|
313
|
+
|
|
314
|
+
TanStack Start API routes return Web-standards Responses directly.
|
|
315
|
+
|
|
316
|
+
```ts
|
|
317
|
+
// app/routes/api/biab-auth.$.ts
|
|
318
|
+
import { createAPIFileRoute } from "@tanstack/start/api";
|
|
319
|
+
import { createAuthHandler } from "@biab-dev/sdk";
|
|
320
|
+
|
|
321
|
+
const handler = createAuthHandler({
|
|
322
|
+
baseUrl: process.env.BIAB_PACKAGE_API_BASE_URL!,
|
|
323
|
+
apiKey: process.env.BIAB_API_KEY!,
|
|
324
|
+
callbackUrl: `${process.env.SITE_URL}/api/biab-auth/callback`,
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
export const Route = createAPIFileRoute("/api/biab-auth/$")({
|
|
328
|
+
GET: ({ request }) => handler.GET(request),
|
|
329
|
+
POST: ({ request }) => handler.POST(request),
|
|
330
|
+
});
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
#### SolidStart
|
|
334
|
+
|
|
335
|
+
```ts
|
|
336
|
+
// src/routes/api/biab-auth/[...biab].ts
|
|
337
|
+
import { createAuthHandler } from "@biab-dev/sdk";
|
|
338
|
+
import type { APIEvent } from "@solidjs/start/server";
|
|
339
|
+
|
|
340
|
+
const handler = createAuthHandler({
|
|
341
|
+
baseUrl: process.env.BIAB_PACKAGE_API_BASE_URL!,
|
|
342
|
+
apiKey: process.env.BIAB_API_KEY!,
|
|
343
|
+
callbackUrl: `${process.env.SITE_URL}/api/biab-auth/callback`,
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
export const GET = ({ request }: APIEvent) => handler.GET(request);
|
|
347
|
+
export const POST = ({ request }: APIEvent) => handler.POST(request);
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
#### Vite SPA, plain Svelte, plain Vue, plain Solid
|
|
351
|
+
|
|
352
|
+
Pure client-side apps **don't have a server**, so they can't mount
|
|
353
|
+
`createAuthHandler` directly — there's no place to store a secret API key
|
|
354
|
+
or set an httpOnly cookie. You need an adjacent server runtime:
|
|
355
|
+
|
|
356
|
+
- Cloudflare Workers / Deno Deploy / Bun.serve / Express → use the example
|
|
357
|
+
above for that runtime, then point your Vite SPA at its hostname.
|
|
358
|
+
- Or upgrade to the framework's full-stack variant (SvelteKit instead of
|
|
359
|
+
Svelte, Nuxt instead of Vue, SolidStart instead of Solid).
|
|
360
|
+
|
|
361
|
+
There's no client-only mode for the auth handler by design — the API key
|
|
362
|
+
must stay on a server.
|
|
363
|
+
|
|
364
|
+
### Non-React UI components
|
|
365
|
+
|
|
366
|
+
The `<SignIn />` / `<SignUp />` / `<SignOut />` / `useUser()` exports under
|
|
367
|
+
`@biab-dev/sdk/react` are React-specific. Other frameworks can build the
|
|
368
|
+
exact same UI with two trivial primitives once the auth handler is mounted:
|
|
369
|
+
|
|
370
|
+
1. **Buttons are links.** Sign-in/up/out are plain anchors:
|
|
371
|
+
|
|
372
|
+
```html
|
|
373
|
+
<a href="/api/biab-auth/sign-in">Sign in</a>
|
|
374
|
+
<a href="/api/biab-auth/sign-up">Create account</a>
|
|
375
|
+
<a href="/api/biab-auth/sign-out">Sign out</a>
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Add `?returnTo=/dashboard` or `?loginHint=user@example.com` query params
|
|
379
|
+
as needed.
|
|
380
|
+
|
|
381
|
+
2. **Reading the session.** `GET /api/biab-auth/me` returns the current user
|
|
382
|
+
or `null`. Same shape as `useUser()`'s React hook:
|
|
383
|
+
|
|
384
|
+
```js
|
|
385
|
+
const session = await fetch("/api/biab-auth/me", { credentials: "include" })
|
|
386
|
+
.then((r) => r.json());
|
|
387
|
+
// → { user: {...}, organizationId, role } | null
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Wire that into your framework's reactivity primitive of choice (Svelte
|
|
391
|
+
stores, Vue `ref`, Solid signals, vanilla `useState`).
|
|
392
|
+
|
|
393
|
+
### Drop the components into your pages
|
|
394
|
+
|
|
395
|
+
These are plain React components — they work in any framework that can render
|
|
396
|
+
React (Next.js, Remix, SvelteKit-with-React, Astro, Vite SPAs, etc.). Only
|
|
397
|
+
the handler install above differs per framework.
|
|
398
|
+
|
|
399
|
+
```tsx
|
|
400
|
+
// app/page.tsx
|
|
401
|
+
import { SignIn, SignOut, SignUp, useUser } from "@biab-dev/sdk/react";
|
|
402
|
+
|
|
403
|
+
export default function Home() {
|
|
404
|
+
const me = useUser();
|
|
405
|
+
|
|
406
|
+
if (me.status === "loading") return null;
|
|
407
|
+
|
|
408
|
+
if (me.status === "signed-in") {
|
|
409
|
+
return (
|
|
410
|
+
<div>
|
|
411
|
+
<p>Hi, {me.user.user.firstName ?? me.user.user.email}.</p>
|
|
412
|
+
<SignOut className="btn">Sign out</SignOut>
|
|
413
|
+
</div>
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
return (
|
|
418
|
+
<div>
|
|
419
|
+
<SignIn className="btn">Sign in</SignIn>
|
|
420
|
+
<SignUp className="btn btn-secondary">Create account</SignUp>
|
|
421
|
+
</div>
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### Reading the session in SSR
|
|
427
|
+
|
|
428
|
+
```ts
|
|
429
|
+
// app/dashboard/page.tsx
|
|
430
|
+
import { cookies } from "next/headers";
|
|
431
|
+
import { DEFAULT_AUTH_COOKIE_NAME, getTenantSession } from "@biab-dev/sdk";
|
|
432
|
+
|
|
433
|
+
export default async function Dashboard() {
|
|
434
|
+
const cookieValue = (await cookies()).get(DEFAULT_AUTH_COOKIE_NAME)?.value;
|
|
435
|
+
const session = await getTenantSession({
|
|
436
|
+
cookieValue,
|
|
437
|
+
baseUrl: process.env.BIAB_PACKAGE_API_BASE_URL!,
|
|
438
|
+
apiKey: process.env.BIAB_API_KEY!,
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
if (!session) redirect("/");
|
|
442
|
+
return <p>Welcome, {session.user.email}.</p>;
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### Invitations (existing-vs-new users)
|
|
447
|
+
|
|
448
|
+
Invitations sent from the WorkOS dashboard work for both cases automatically:
|
|
449
|
+
|
|
450
|
+
- **Existing user** → invitation link signs them in and adds them to the org.
|
|
451
|
+
- **New user** → invitation link prompts them to set a password, creates the
|
|
452
|
+
user, and adds them to the org.
|
|
453
|
+
|
|
454
|
+
In both cases the BIAB host syncs the membership into its database on the
|
|
455
|
+
first authed callback. You don't need to write any invitation acceptance
|
|
456
|
+
code — link them at the WorkOS-generated URL and the rest is handled.
|
|
457
|
+
|
|
458
|
+
## Configuration
|
|
459
|
+
|
|
460
|
+
| Env var | Where it comes from |
|
|
461
|
+
| --- | --- |
|
|
462
|
+
| `BIAB_API_KEY` | One-time reveal in **Site Builder → Developer → Package API & Unkey keys** in the host app. |
|
|
463
|
+
| `BIAB_SITE_ID` | The "Site ID" shown directly above the API key list in the same panel. |
|
|
464
|
+
| `BIAB_PACKAGE_API_BASE_URL` | The "Package API base URL" shown above — **always `https://` for production**, and the URL **must** end in `/api/package/v1`. |
|
|
465
|
+
|
|
466
|
+
> **Two production traps to avoid:**
|
|
467
|
+
> 1. `http://` against a production host. The host responds `308 → https://`,
|
|
468
|
+
> and `fetch` strips the `Authorization` header on cross-scheme redirects
|
|
469
|
+
> per the WHATWG fetch spec — the redirected request lands unauthenticated.
|
|
470
|
+
> 2. A base URL that does **not** include `/api/package/v1`. SDK versions
|
|
471
|
+
> ≤ 0.2.0 silently drop the base URL's pathname when constructing site-scoped
|
|
472
|
+
> requests; 0.2.1 fixes this. Either upgrade or include the path component
|
|
473
|
+
> in the env var.
|
|
474
|
+
|
|
67
475
|
## Notes
|
|
68
476
|
|
|
69
|
-
-
|
|
70
|
-
|
|
477
|
+
- Collection contracts intentionally mirror the current BIAB `site_data_*`
|
|
478
|
+
model instead of inventing a second schema system.
|
|
479
|
+
- The host owns API key verification, scope enforcement, RBAC, persistence,
|
|
480
|
+
and audit. The SDK is a transport + typed contract layer.
|
|
71
481
|
|
|
72
482
|
## Publishing to npm
|
|
73
483
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK auth handler — the file a tenant developer mounts on their own site
|
|
3
|
+
* to handle the WorkOS callback. Cookie lives on the tenant's domain so the
|
|
4
|
+
* tenant's SSR can read it via standard cookie APIs.
|
|
5
|
+
*
|
|
6
|
+
* Usage (Next.js App Router):
|
|
7
|
+
*
|
|
8
|
+
* // app/api/biab-auth/[...biab]/route.ts
|
|
9
|
+
* import { createAuthHandler } from "@biab-dev/sdk";
|
|
10
|
+
*
|
|
11
|
+
* const handler = createAuthHandler({
|
|
12
|
+
* baseUrl: process.env.BIAB_PACKAGE_API_BASE_URL!,
|
|
13
|
+
* apiKey: process.env.BIAB_API_KEY!,
|
|
14
|
+
* callbackUrl: "https://my-site.com/api/biab-auth/callback",
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* export const GET = handler.GET;
|
|
18
|
+
* export const POST = handler.POST;
|
|
19
|
+
*
|
|
20
|
+
* Then drop <SignIn />, <SignUp />, <SignOut /> into your pages — they call
|
|
21
|
+
* `/api/biab-auth/sign-in?intent=sign-in` etc., which 302s through the WorkOS
|
|
22
|
+
* hosted page and back to `callbackUrl`. The handler exchanges the code,
|
|
23
|
+
* sets the session cookie, and redirects the user to `returnTo`.
|
|
24
|
+
*/
|
|
25
|
+
import { type BiabDevClientOptions } from "./client";
|
|
26
|
+
export declare const DEFAULT_AUTH_COOKIE_NAME = "biab_session";
|
|
27
|
+
export declare const DEFAULT_AUTH_BASE_PATH = "/api/biab-auth";
|
|
28
|
+
export type CreateAuthHandlerOptions = {
|
|
29
|
+
/** BIAB platform base URL — e.g. https://app.biab.com/api/package/v1 */
|
|
30
|
+
baseUrl: string;
|
|
31
|
+
/** Org-bound package API key (server-side secret). */
|
|
32
|
+
apiKey: string;
|
|
33
|
+
/**
|
|
34
|
+
* Public, fully-qualified URL of THIS handler's `/callback` route on the
|
|
35
|
+
* tenant's domain. Must be registered as a redirect URI in WorkOS.
|
|
36
|
+
*/
|
|
37
|
+
callbackUrl: string;
|
|
38
|
+
/** Where to send the user after successful sign-in if no `returnTo` was passed. */
|
|
39
|
+
defaultReturnTo?: string;
|
|
40
|
+
/** Where to send the user after sign-out. Defaults to "/". */
|
|
41
|
+
signOutReturnTo?: string;
|
|
42
|
+
/** Cookie name. Defaults to "biab_session". */
|
|
43
|
+
cookieName?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Cookie domain. Leave undefined to scope to current host. Set if you want
|
|
46
|
+
* to share the session across subdomains (e.g. ".my-site.com").
|
|
47
|
+
*/
|
|
48
|
+
cookieDomain?: string;
|
|
49
|
+
/**
|
|
50
|
+
* The base path this handler is mounted at — used to derive sub-routes.
|
|
51
|
+
* Defaults to "/api/biab-auth". Must match the route file location.
|
|
52
|
+
*/
|
|
53
|
+
basePath?: string;
|
|
54
|
+
/** Optional fetch override (testing, edge runtimes). */
|
|
55
|
+
fetch?: BiabDevClientOptions["fetch"];
|
|
56
|
+
/**
|
|
57
|
+
* Origin sent on every server-to-server request to the platform. Defaults
|
|
58
|
+
* to the origin of `callbackUrl`. Override only when your dev/prod hosts
|
|
59
|
+
* differ from the callback (rare).
|
|
60
|
+
*/
|
|
61
|
+
siteOrigin?: string;
|
|
62
|
+
};
|
|
63
|
+
type SimpleHandler = (request: Request) => Promise<Response>;
|
|
64
|
+
export type AuthHandlerRoutes = {
|
|
65
|
+
GET: SimpleHandler;
|
|
66
|
+
POST: SimpleHandler;
|
|
67
|
+
};
|
|
68
|
+
export declare function createAuthHandler(options: CreateAuthHandlerOptions): AuthHandlerRoutes;
|
|
69
|
+
/**
|
|
70
|
+
* Server-side helper for tenant SSR: read the current session from the cookie.
|
|
71
|
+
* Returns null when the user isn't signed in.
|
|
72
|
+
*/
|
|
73
|
+
export declare function getTenantSession(input: {
|
|
74
|
+
cookieValue: string | null | undefined;
|
|
75
|
+
baseUrl: string;
|
|
76
|
+
apiKey: string;
|
|
77
|
+
/** Required when the API key has Allowed Host set; see {@link BiabDevClientOptions.siteOrigin}. */
|
|
78
|
+
siteOrigin?: string;
|
|
79
|
+
fetch?: BiabDevClientOptions["fetch"];
|
|
80
|
+
}): Promise<{
|
|
81
|
+
role: string | null;
|
|
82
|
+
user: {
|
|
83
|
+
id: string;
|
|
84
|
+
email: string | null;
|
|
85
|
+
firstName: string | null;
|
|
86
|
+
lastName: string | null;
|
|
87
|
+
};
|
|
88
|
+
organizationId: string;
|
|
89
|
+
} | null>;
|
|
90
|
+
export {};
|
|
91
|
+
//# sourceMappingURL=auth-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-handler.d.ts","sourceRoot":"","sources":["../src/auth-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAEN,KAAK,oBAAoB,EACzB,MAAM,UAAU,CAAC;AAGlB,eAAO,MAAM,wBAAwB,iBAAiB,CAAC;AACvD,eAAO,MAAM,sBAAsB,mBAAmB,CAAC;AAEvD,MAAM,MAAM,wBAAwB,GAAG;IACtC,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,mFAAmF;IACnF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,8DAA8D;IAC9D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+CAA+C;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,KAAK,CAAC,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;IACtC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,KAAK,aAAa,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAE7D,MAAM,MAAM,iBAAiB,GAAG;IAC/B,GAAG,EAAE,aAAa,CAAC;IACnB,IAAI,EAAE,aAAa,CAAC;CACpB,CAAC;AAkGF,wBAAgB,iBAAiB,CAChC,OAAO,EAAE,wBAAwB,GAC/B,iBAAiB,CA0GnB;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,KAAK,EAAE;IAC7C,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,mGAAmG;IACnG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;CACtC;;;;;;;;;UASA"}
|