@fanvue/builder-sdk 0.3.0
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 +427 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +2 -0
- package/dist/core-CvVOMyqr.js +5826 -0
- package/dist/core-CvVOMyqr.js.map +1 -0
- package/dist/index-CweVyIKX.d.ts +48 -0
- package/dist/index-CweVyIKX.d.ts.map +1 -0
- package/dist/index-pS9wR5yg.d.ts +2776 -0
- package/dist/index-pS9wR5yg.d.ts.map +1 -0
- package/dist/nextjs/embedded-app/index.d.ts +105 -0
- package/dist/nextjs/embedded-app/index.d.ts.map +1 -0
- package/dist/nextjs/embedded-app/index.js +162 -0
- package/dist/nextjs/embedded-app/index.js.map +1 -0
- package/dist/nextjs/off-platform/index.d.ts +74 -0
- package/dist/nextjs/off-platform/index.d.ts.map +1 -0
- package/dist/nextjs/off-platform/index.js +257 -0
- package/dist/nextjs/off-platform/index.js.map +1 -0
- package/dist/nextjs-CESI_EiU.js +80 -0
- package/dist/nextjs-CESI_EiU.js.map +1 -0
- package/dist/react/index.d.ts +96 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +192 -0
- package/dist/react/index.js.map +1 -0
- package/package.json +94 -0
package/README.md
ADDED
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
# @fanvue/builder-sdk
|
|
2
|
+
|
|
3
|
+
Authentication for Fanvue apps. Every Fanvue app is either **embedded** or
|
|
4
|
+
**off-platform** ([which should I build?](https://api.fanvue.com/docs/app-store/app-types)) —
|
|
5
|
+
this SDK covers both:
|
|
6
|
+
|
|
7
|
+
| Building an... | Import | What you get | Docs |
|
|
8
|
+
|---|---|---|---|
|
|
9
|
+
| **Embedded App** (runs inside Fanvue, in an iframe) | `@fanvue/builder-sdk/nextjs/embedded-app` + `@fanvue/builder-sdk/react` | Session-token exchange handler, `useEmbeddedAuth` hook, Bearer sessions with auto refresh | [Overview](https://api.fanvue.com/docs/app-store/embedded-apps/overview) · [Integration guide](https://api.fanvue.com/docs/app-store/embedded-apps/integration-guide) |
|
|
10
|
+
| **Off-Platform App** ("Login with Fanvue" on your own domain) | `@fanvue/builder-sdk/nextjs/off-platform` | Full-page redirect flow, httpOnly cookie sessions, auto token refresh | [Auth overview](https://api.fanvue.com/docs/authentication/overview) · [Implementation guide](https://api.fanvue.com/docs/authentication/implementation-guide) |
|
|
11
|
+
|
|
12
|
+
Building something else (Node, Deno, a custom server)? The core `@fanvue/builder-sdk`
|
|
13
|
+
entrypoint exposes the low-level OAuth primitives both flows are built on.
|
|
14
|
+
|
|
15
|
+
- Drop-in route handlers for Next.js -- a couple of files and you're done
|
|
16
|
+
- Handles token exchange, refresh, and session management so you don't have to
|
|
17
|
+
- Fully typed with TypeScript
|
|
18
|
+
|
|
19
|
+
> [!IMPORTANT]
|
|
20
|
+
> **Renamed from `@fanvue/auth`.** This package was previously published as
|
|
21
|
+
> [`@fanvue/auth`](https://www.npmjs.com/package/@fanvue/auth) (≤ 0.2.3). The
|
|
22
|
+
> API is unchanged — to migrate, swap the dependency and update import paths:
|
|
23
|
+
>
|
|
24
|
+
> ```diff
|
|
25
|
+
> - "@fanvue/auth": "^0.2.3"
|
|
26
|
+
> + "@fanvue/builder-sdk": "^0.3.0"
|
|
27
|
+
> ```
|
|
28
|
+
>
|
|
29
|
+
> ```diff
|
|
30
|
+
> - import { useEmbeddedAuth } from "@fanvue/auth/react";
|
|
31
|
+
> + import { useEmbeddedAuth } from "@fanvue/builder-sdk/react";
|
|
32
|
+
> ```
|
|
33
|
+
>
|
|
34
|
+
> `@fanvue/auth` is deprecated on npm and will receive no further releases.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install @fanvue/builder-sdk
|
|
40
|
+
# or
|
|
41
|
+
pnpm add @fanvue/builder-sdk
|
|
42
|
+
# or
|
|
43
|
+
yarn add @fanvue/builder-sdk
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Peer dependencies (`next`, `react`) are optional -- only install what your project uses.
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
### Embedded App (your app inside Fanvue)
|
|
51
|
+
|
|
52
|
+
When Fanvue opens your app in an iframe, it appends a short-lived session token
|
|
53
|
+
to your embed URL (`?token=...`). Exchange it server-side for real OAuth tokens
|
|
54
|
+
-- the SDK handles the whole delegated flow (PKCE, state, code exchange).
|
|
55
|
+
|
|
56
|
+
**1. Add the session-exchange route**
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
// app/api/fanvue/session/route.ts
|
|
60
|
+
import { createConfig, createSessionExchangeHandler } from "@fanvue/builder-sdk/nextjs/embedded-app";
|
|
61
|
+
|
|
62
|
+
export const { POST } = createSessionExchangeHandler(createConfig());
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**2. Authenticate in your embedded page**
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
// app/embedded/page.tsx
|
|
69
|
+
"use client";
|
|
70
|
+
import { AuthProvider, useAuth, useEmbeddedAuth } from "@fanvue/builder-sdk/react";
|
|
71
|
+
|
|
72
|
+
function Embedded() {
|
|
73
|
+
const { status, error, theme } = useEmbeddedAuth();
|
|
74
|
+
const { authFetch } = useAuth();
|
|
75
|
+
|
|
76
|
+
// `theme` is the creator's active colour scheme ("light" | "dark"), read
|
|
77
|
+
// from the iframe URL -- use it to match Fanvue. It's `null` when the app is
|
|
78
|
+
// opened outside Fanvue, so fall back to a sensible default.
|
|
79
|
+
return (
|
|
80
|
+
<div data-theme={theme ?? "light"}>
|
|
81
|
+
{status === "exchanging" && <p>Connecting to Fanvue…</p>}
|
|
82
|
+
{status === "error" && <p>Auth failed: {error}</p>}
|
|
83
|
+
<button onClick={() => authFetch("/api/me")}>Load my profile</button>
|
|
84
|
+
</div>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export default function Page() {
|
|
89
|
+
return (
|
|
90
|
+
<AuthProvider>
|
|
91
|
+
<Embedded />
|
|
92
|
+
</AuthProvider>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**3. Call the Fanvue API from your own routes**
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
// app/api/me/route.ts
|
|
101
|
+
import { NextResponse } from "next/server";
|
|
102
|
+
import { HEADER_UPDATED_SESSION } from "@fanvue/builder-sdk";
|
|
103
|
+
import { createConfig, getAuthenticatedClient } from "@fanvue/builder-sdk/nextjs/embedded-app";
|
|
104
|
+
|
|
105
|
+
const config = createConfig();
|
|
106
|
+
|
|
107
|
+
export async function GET() {
|
|
108
|
+
const auth = await getAuthenticatedClient({ sessionSecret: config.sessionSecret, config });
|
|
109
|
+
if (!auth) return NextResponse.json({ error: "unauthorized" }, { status: 401 });
|
|
110
|
+
|
|
111
|
+
const user = await auth.client.getCurrentUser();
|
|
112
|
+
const res = NextResponse.json(user.isOk() ? user.value : { error: "api_failed" });
|
|
113
|
+
// When the access token was refreshed, hand the new session JWT back to the
|
|
114
|
+
// client -- `authFetch` stores it automatically.
|
|
115
|
+
if (auth.refreshedJwt) res.headers.set(HEADER_UPDATED_SESSION, auth.refreshedJwt);
|
|
116
|
+
return res;
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Hosting requirements for embedded apps:**
|
|
121
|
+
|
|
122
|
+
- Serve over HTTPS with a browser-trusted certificate.
|
|
123
|
+
- Send `Content-Security-Policy: frame-ancestors https://www.fanvue.com` so
|
|
124
|
+
Fanvue can frame your page.
|
|
125
|
+
- The session token lives ~60 seconds -- exchange it promptly (the
|
|
126
|
+
`useEmbeddedAuth` hook does this on mount).
|
|
127
|
+
- To act on the creator's behalf **after** the iframe closes, persist the
|
|
128
|
+
refresh token from the `onTokens` hook:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
export const { POST } = createSessionExchangeHandler(createConfig(), {
|
|
132
|
+
onTokens: async ({ tokens, user }) => {
|
|
133
|
+
await db.saveRefreshToken(user.uuid, tokens.refresh_token);
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
See the [embedded apps integration guide](https://api.fanvue.com/docs/app-store/embedded-apps/integration-guide)
|
|
139
|
+
for the full walkthrough, including app registration in the Builder.
|
|
140
|
+
|
|
141
|
+
### Off-Platform App ("Login with Fanvue")
|
|
142
|
+
|
|
143
|
+
The fastest path: create a config file and three route handlers.
|
|
144
|
+
|
|
145
|
+
**1. Configure**
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
// lib/auth.ts
|
|
149
|
+
import { createConfig } from "@fanvue/builder-sdk/nextjs/off-platform";
|
|
150
|
+
|
|
151
|
+
export const authConfig = {
|
|
152
|
+
...createConfig(), // reads OAUTH_* and SESSION_* env vars
|
|
153
|
+
afterLoginPath: "/dashboard",
|
|
154
|
+
afterLogoutPath: "/",
|
|
155
|
+
};
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**2. Add route handlers**
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
// app/api/oauth/login/route.ts
|
|
162
|
+
import { createLoginHandler } from "@fanvue/builder-sdk/nextjs/off-platform";
|
|
163
|
+
import { authConfig } from "@/lib/auth";
|
|
164
|
+
|
|
165
|
+
export const { GET } = createLoginHandler(authConfig);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
// app/api/oauth/callback/route.ts
|
|
170
|
+
import { createCallbackHandler } from "@fanvue/builder-sdk/nextjs/off-platform";
|
|
171
|
+
import { authConfig } from "@/lib/auth";
|
|
172
|
+
|
|
173
|
+
export const { GET, POST } = createCallbackHandler(authConfig);
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
// app/api/oauth/logout/route.ts
|
|
178
|
+
import { createLogoutHandler } from "@fanvue/builder-sdk/nextjs/off-platform";
|
|
179
|
+
import { authConfig } from "@/lib/auth";
|
|
180
|
+
|
|
181
|
+
export const { POST } = createLogoutHandler(authConfig);
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**3. Use the session**
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
import { getSession, getAuthenticatedClient } from "@fanvue/builder-sdk/nextjs/off-platform";
|
|
188
|
+
import { authConfig } from "@/lib/auth";
|
|
189
|
+
|
|
190
|
+
// Read the session in any server component or route handler
|
|
191
|
+
const session = await getSession(authConfig.sessionSecret, authConfig.sessionCookieName);
|
|
192
|
+
|
|
193
|
+
// Or get an API client that automatically refreshes expired tokens
|
|
194
|
+
const client = await getAuthenticatedClient({
|
|
195
|
+
sessionSecret: authConfig.sessionSecret,
|
|
196
|
+
sessionCookieName: authConfig.sessionCookieName,
|
|
197
|
+
config: authConfig,
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
if (client) {
|
|
201
|
+
const user = await client.getCurrentUser();
|
|
202
|
+
if (user.isOk()) console.log(user.value);
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
See the [authentication docs](https://api.fanvue.com/docs/authentication/overview)
|
|
207
|
+
for scopes, rate limits, and the underlying OAuth flow.
|
|
208
|
+
|
|
209
|
+
### Core (advanced)
|
|
210
|
+
|
|
211
|
+
Use the core entrypoint when you need full control -- CLI tools, custom servers, or non-Next.js frameworks.
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
import {
|
|
215
|
+
createAuthorizationUrl,
|
|
216
|
+
exchangeCodeForToken,
|
|
217
|
+
refreshAccessToken,
|
|
218
|
+
createSessionJwt,
|
|
219
|
+
verifySessionJwt,
|
|
220
|
+
createFanvueClient,
|
|
221
|
+
} from "@fanvue/builder-sdk";
|
|
222
|
+
|
|
223
|
+
const config = {
|
|
224
|
+
clientId: "your-client-id",
|
|
225
|
+
clientSecret: "your-client-secret",
|
|
226
|
+
redirectUri: "http://localhost:3000/callback",
|
|
227
|
+
issuerUrl: null,
|
|
228
|
+
apiBaseUrl: null,
|
|
229
|
+
scopes: null,
|
|
230
|
+
responseMode: null,
|
|
231
|
+
prompt: null,
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
// 1. Build the authorization URL (PKCE is handled automatically)
|
|
235
|
+
const { url, codeVerifier, state } = await createAuthorizationUrl(config);
|
|
236
|
+
// Redirect the user to `url`...
|
|
237
|
+
|
|
238
|
+
// 2. Exchange the authorization code for tokens
|
|
239
|
+
const tokenResult = await exchangeCodeForToken(config, {
|
|
240
|
+
code: "code-from-callback",
|
|
241
|
+
codeVerifier,
|
|
242
|
+
redirectUri: null,
|
|
243
|
+
});
|
|
244
|
+
if (tokenResult.isErr()) throw new Error(tokenResult.error.message);
|
|
245
|
+
const tokens = tokenResult.value;
|
|
246
|
+
|
|
247
|
+
// 3. Make authenticated API calls
|
|
248
|
+
const client = createFanvueClient(tokens.access_token, null);
|
|
249
|
+
const userResult = await client.getCurrentUser();
|
|
250
|
+
if (userResult.isOk()) console.log(userResult.value);
|
|
251
|
+
|
|
252
|
+
// 4. Refresh an expired access token
|
|
253
|
+
const refreshResult = await refreshAccessToken(config, tokens.refresh_token);
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
For the embedded flow, the equivalent core primitive is `exchangeSessionToken(config, sessionToken)`,
|
|
257
|
+
which runs PKCE generation, the delegated authorize-on-behalf request, and the
|
|
258
|
+
code exchange in one call.
|
|
259
|
+
|
|
260
|
+
<details>
|
|
261
|
+
<summary>Session JWT helpers (for managing your own sessions)</summary>
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
import { createSessionJwt, verifySessionJwt } from "@fanvue/builder-sdk";
|
|
265
|
+
|
|
266
|
+
// Create a signed session JWT (HS256, default 30-day expiry)
|
|
267
|
+
const jwt = await createSessionJwt("your-session-secret", {
|
|
268
|
+
accessToken: tokens.access_token,
|
|
269
|
+
refreshToken: tokens.refresh_token,
|
|
270
|
+
expiresAt: Date.now() + tokens.expires_in * 1000,
|
|
271
|
+
tokenType: tokens.token_type,
|
|
272
|
+
scope: tokens.scope,
|
|
273
|
+
idToken: tokens.id_token,
|
|
274
|
+
userUuid: "...",
|
|
275
|
+
handle: "...",
|
|
276
|
+
displayName: "...",
|
|
277
|
+
isCreator: false,
|
|
278
|
+
avatarUrl: null,
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// Verify a session JWT
|
|
282
|
+
const session = await verifySessionJwt("your-session-secret", jwt);
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
</details>
|
|
286
|
+
|
|
287
|
+
## Environment Variables
|
|
288
|
+
|
|
289
|
+
Add these to your `.env.local` (Next.js) or equivalent:
|
|
290
|
+
|
|
291
|
+
```env
|
|
292
|
+
# Required
|
|
293
|
+
OAUTH_CLIENT_ID=your-client-id
|
|
294
|
+
OAUTH_CLIENT_SECRET=your-client-secret
|
|
295
|
+
OAUTH_REDIRECT_URI=http://localhost:3000/api/oauth/callback
|
|
296
|
+
SESSION_SECRET=at-least-32-characters-long-random-string
|
|
297
|
+
|
|
298
|
+
# Optional (shown with defaults)
|
|
299
|
+
OAUTH_ISSUER_BASE_URL=https://auth.fanvue.com
|
|
300
|
+
API_BASE_URL=https://api.fanvue.com
|
|
301
|
+
FANVUE_PLATFORM_URL=https://www.fanvue.com # embedded apps only
|
|
302
|
+
OAUTH_SCOPES="openid offline_access offline"
|
|
303
|
+
SESSION_COOKIE_NAME=fanvue_session
|
|
304
|
+
# OAUTH_RESPONSE_MODE= # query, form_post, etc.
|
|
305
|
+
# OAUTH_PROMPT= # login, consent, etc.
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
For the Fanvue dev environment use `https://auth.dev.fanvue.com`,
|
|
309
|
+
`https://api.dev.fanvue.com`, and `https://dev.fanvue.com` respectively.
|
|
310
|
+
|
|
311
|
+
The `createConfig()` helper reads these automatically. You can also override any value programmatically:
|
|
312
|
+
|
|
313
|
+
```ts
|
|
314
|
+
const config = createConfig({
|
|
315
|
+
clientId: "explicit-id", // overrides OAUTH_CLIENT_ID
|
|
316
|
+
sessionCookieName: "my_session", // overrides SESSION_COOKIE_NAME
|
|
317
|
+
});
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Error Handling
|
|
321
|
+
|
|
322
|
+
All async operations return `Result<T, E>` types from [`neverthrow`](https://github.com/supermacro/neverthrow) instead of throwing exceptions. This gives you type-safe, explicit error handling:
|
|
323
|
+
|
|
324
|
+
```ts
|
|
325
|
+
const result = await client.getCurrentUser();
|
|
326
|
+
|
|
327
|
+
if (result.isOk()) {
|
|
328
|
+
console.log(result.value); // FanvueUser
|
|
329
|
+
} else {
|
|
330
|
+
console.error(result.error); // ApiError
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Or use neverthrow's functional API
|
|
334
|
+
result
|
|
335
|
+
.map((user) => console.log(user.displayName))
|
|
336
|
+
.mapErr((err) => console.error(err.message));
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
Error types: `OAuthError` (token exchange/refresh), `EmbeddedAuthError` (delegated authorize-on-behalf), `ApiError` (API requests), `SessionVerifyError` (JWT verification).
|
|
340
|
+
|
|
341
|
+
## API Reference
|
|
342
|
+
|
|
343
|
+
### Core (`@fanvue/builder-sdk`)
|
|
344
|
+
|
|
345
|
+
| Export | Description |
|
|
346
|
+
|---|---|
|
|
347
|
+
| `createAuthorizationUrl(config, opts?)` | Build a PKCE authorization URL. Returns `{ url, codeVerifier, state }`. |
|
|
348
|
+
| `exchangeCodeForToken(config, opts)` | Exchange an authorization code for tokens. Returns `Result<TokenResponse, OAuthError>`. |
|
|
349
|
+
| `refreshAccessToken(config, refreshToken)` | Refresh an expired access token. Returns `Result<TokenResponse, OAuthError>`. |
|
|
350
|
+
| `exchangeSessionToken(config, sessionToken)` | Exchange an embedded session token for tokens (full delegated flow). Returns `Result<TokenResponse, EmbeddedAuthError \| OAuthError>`. |
|
|
351
|
+
| `requestAuthorizationCodeOnBehalf(config, sessionToken, opts)` | Low-level: request a delegated authorization code from the platform. |
|
|
352
|
+
| `getSessionTokenFromUrl(url)` | Extract the `?token=` session token from a URL. |
|
|
353
|
+
| `getThemeFromUrl(url)` | Extract the `?theme=` colour scheme from a URL. Returns `'light' \| 'dark' \| null`. |
|
|
354
|
+
| `createSessionJwt(secret, payload, expiresIn?)` | Create a signed HS256 session JWT. Default expiry: 30 days. |
|
|
355
|
+
| `verifySessionJwt(secret, token)` | Verify and decode a session JWT. Returns `Result<SessionPayload, SessionVerifyError>`. |
|
|
356
|
+
| `createFanvueClient(accessToken, apiBaseUrl?)` | Create an authenticated API client. |
|
|
357
|
+
| `API_VERSION` | The API version header value (currently `2025-06-26`). |
|
|
358
|
+
| `HEADER_UPDATED_SESSION` | Response header (`X-Updated-Session`) carrying a refreshed session JWT. |
|
|
359
|
+
|
|
360
|
+
#### Types
|
|
361
|
+
|
|
362
|
+
| Type | Description |
|
|
363
|
+
|---|---|
|
|
364
|
+
| `OAuthConfig` | Client ID, secret, redirect URI, and optional overrides |
|
|
365
|
+
| `EmbeddedAuthConfig` | `OAuthConfig` plus the Fanvue platform base URL |
|
|
366
|
+
| `FanvueTheme` | The creator's active colour scheme: `'light' \| 'dark'` |
|
|
367
|
+
| `TokenResponse` | Access token, refresh token, expiry, scopes |
|
|
368
|
+
| `SessionPayload` | JWT claims: tokens, user info, expiry timestamp |
|
|
369
|
+
| `FanvueUser` | User profile (uuid, email, handle, displayName, isCreator, avatarUrl, etc.) |
|
|
370
|
+
| `OAuthError` | Error from token exchange or refresh |
|
|
371
|
+
| `EmbeddedAuthError` | Error from the delegated authorize-on-behalf flow |
|
|
372
|
+
| `ApiError` | Error from API requests |
|
|
373
|
+
| `SessionVerifyError` | Error from session JWT verification |
|
|
374
|
+
|
|
375
|
+
### Next.js Embedded App (`@fanvue/builder-sdk/nextjs/embedded-app`)
|
|
376
|
+
|
|
377
|
+
| Export | Description |
|
|
378
|
+
|---|---|
|
|
379
|
+
| `createConfig(opts?)` | Build an `EmbeddedAppConfig` from env vars and/or explicit options (adds `platformUrl`) |
|
|
380
|
+
| `createSessionExchangeHandler(config, hooks?)` | `{ POST }` -- exchanges the embedded session token for tokens, responds with a session JWT |
|
|
381
|
+
| `getSession(secret)` | Read and verify the session from the `Authorization: Bearer` header |
|
|
382
|
+
| `getAuthenticatedClient(opts)` | Get a `FanvueClient` from the Bearer session, with automatic token refresh |
|
|
383
|
+
| `getSessionTokenFromUrl(url)` | Extract the `?token=` session token from a URL |
|
|
384
|
+
|
|
385
|
+
### Next.js Off-Platform App (`@fanvue/builder-sdk/nextjs/off-platform`)
|
|
386
|
+
|
|
387
|
+
| Export | Description |
|
|
388
|
+
|---|---|
|
|
389
|
+
| `createConfig(opts?)` | Build a `ResolvedConfig` from env vars and/or explicit options |
|
|
390
|
+
| `createLoginHandler(opts)` | `{ GET }` -- redirects to the OAuth provider |
|
|
391
|
+
| `createCallbackHandler(opts)` | `{ GET, POST }` -- completes the code exchange, sets the session cookie |
|
|
392
|
+
| `createLogoutHandler(opts)` | `{ POST }` -- clears the session cookie |
|
|
393
|
+
| `getSession(secret, cookieName?)` | Read and verify the session from cookies |
|
|
394
|
+
| `getAuthenticatedClient(opts)` | Get a `FanvueClient` with automatic token refresh |
|
|
395
|
+
|
|
396
|
+
### React (`@fanvue/builder-sdk/react`)
|
|
397
|
+
|
|
398
|
+
| Export | Description |
|
|
399
|
+
|---|---|
|
|
400
|
+
| `AuthProvider` | Context provider. Manages JWT storage in `sessionStorage`. |
|
|
401
|
+
| `useAuth()` | Returns `{ jwt, isAuthenticated, setJwt, clearJwt, authFetch }` |
|
|
402
|
+
| `useEmbeddedAuth(opts?)` | Returns `{ status, error, theme }`. Exchanges the embedded session token on mount; `theme` is the creator's colour scheme (`'light' \| 'dark' \| null`). |
|
|
403
|
+
|
|
404
|
+
## Examples
|
|
405
|
+
|
|
406
|
+
Complete working examples:
|
|
407
|
+
|
|
408
|
+
| Example | App type | Auth flow |
|
|
409
|
+
|---|---|---|
|
|
410
|
+
| [`examples/nextjs-embedded-app`](examples/nextjs-embedded-app) | Embedded | Session-token exchange (delegated authorize-on-behalf) |
|
|
411
|
+
| [`examples/nextjs-off-platform-app`](examples/nextjs-off-platform-app) | Off-platform | "Login with Fanvue" full-page redirect |
|
|
412
|
+
|
|
413
|
+
## Contributing
|
|
414
|
+
|
|
415
|
+
```bash
|
|
416
|
+
pnpm install # Install dependencies
|
|
417
|
+
pnpm build # Build the library
|
|
418
|
+
pnpm dev # Watch mode
|
|
419
|
+
pnpm test # Run tests
|
|
420
|
+
pnpm typecheck # Type-check
|
|
421
|
+
pnpm lint # Lint
|
|
422
|
+
pnpm check # Run all checks (format + lint + typecheck + test)
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
## License
|
|
426
|
+
|
|
427
|
+
MIT
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { A as DEFAULT_ISSUER_URL, C as OAuthConfig, D as TokenResponse, E as SessionVerifyError, F as HEADER_UPDATED_SESSION, M as DEFAULT_SCOPES, N as assertFanvueDomain, O as API_VERSION, P as BEARER_PREFIX, S as JsonParseError, T as SessionPayload, _ as refreshAccessToken, a as SessionPayloadSchema, b as EmbeddedAuthError, c as FanvueTheme, d as getThemeFromUrl, f as requestAuthorizationCodeOnBehalf, g as exchangeCodeForToken, h as createAuthorizationUrl, i as FanvueUserSchema, j as DEFAULT_PLATFORM_URL, k as DEFAULT_API_BASE_URL, l as exchangeSessionToken, m as verifySessionJwt, n as createFanvueClient, o as TokenResponseSchema, p as createSessionJwt, r as AuthorizeOnBehalfResponseSchema, s as safeJsonParse, t as FanvueClient, u as getSessionTokenFromUrl, v as ApiError, w as OAuthError, x as FanvueUser, y as EmbeddedAuthConfig } from "../index-pS9wR5yg.js";
|
|
2
|
+
export { API_VERSION, ApiError, AuthorizeOnBehalfResponseSchema, BEARER_PREFIX, DEFAULT_API_BASE_URL, DEFAULT_ISSUER_URL, DEFAULT_PLATFORM_URL, DEFAULT_SCOPES, EmbeddedAuthConfig, EmbeddedAuthError, FanvueClient, FanvueTheme, FanvueUser, FanvueUserSchema, HEADER_UPDATED_SESSION, JsonParseError, OAuthConfig, OAuthError, SessionPayload, SessionPayloadSchema, SessionVerifyError, TokenResponse, TokenResponseSchema, assertFanvueDomain, createAuthorizationUrl, createFanvueClient, createSessionJwt, exchangeCodeForToken, exchangeSessionToken, getSessionTokenFromUrl, getThemeFromUrl, refreshAccessToken, requestAuthorizationCodeOnBehalf, safeJsonParse, verifySessionJwt };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { C as assertFanvueDomain, S as DEFAULT_SCOPES, T as HEADER_UPDATED_SESSION, _ as safeJsonParse, a as requestAuthorizationCodeOnBehalf, b as DEFAULT_ISSUER_URL, c as createAuthorizationUrl, d as AuthorizeOnBehalfResponseSchema, f as FanvueUserSchema, i as getThemeFromUrl, l as exchangeCodeForToken, m as TokenResponseSchema, n as exchangeSessionToken, o as createSessionJwt, p as SessionPayloadSchema, r as getSessionTokenFromUrl, s as verifySessionJwt, t as createFanvueClient, u as refreshAccessToken, v as API_VERSION, w as BEARER_PREFIX, x as DEFAULT_PLATFORM_URL, y as DEFAULT_API_BASE_URL } from "../core-CvVOMyqr.js";
|
|
2
|
+
export { API_VERSION, AuthorizeOnBehalfResponseSchema, BEARER_PREFIX, DEFAULT_API_BASE_URL, DEFAULT_ISSUER_URL, DEFAULT_PLATFORM_URL, DEFAULT_SCOPES, FanvueUserSchema, HEADER_UPDATED_SESSION, SessionPayloadSchema, TokenResponseSchema, assertFanvueDomain, createAuthorizationUrl, createFanvueClient, createSessionJwt, exchangeCodeForToken, exchangeSessionToken, getSessionTokenFromUrl, getThemeFromUrl, refreshAccessToken, requestAuthorizationCodeOnBehalf, safeJsonParse, verifySessionJwt };
|