@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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["createSharedConfig","z.object","z.string"],"sources":["../../../src/nextjs/bearer-session.ts","../../../src/nextjs/embedded-app/index.ts"],"sourcesContent":["import { headers } from 'next/headers';\n\nimport {\n BEARER_PREFIX,\n verifySessionJwt,\n createSessionJwt,\n createFanvueClient,\n refreshAccessToken,\n type FanvueClient,\n type OAuthConfig,\n type SessionPayload,\n} from '../core/index.js';\n\nimport { LOG_PREFIX } from './constants.js';\n\nconst TOKEN_REFRESH_THRESHOLD_MS = 30_000;\n\n/**\n * Reads and verifies the session JWT from the `Authorization: Bearer` header.\n *\n * Used by embedded apps, which hold the session JWT client-side and send it\n * with each request, instead of using cookies.\n *\n * @param secret - The secret used to verify the JWT.\n * @returns The verified {@link SessionPayload}, or `null` if missing/invalid.\n */\nexport async function getSession(secret: string): Promise<SessionPayload | null> {\n const hdrs = await headers();\n const auth = hdrs.get('authorization');\n if (!auth?.startsWith(BEARER_PREFIX)) return null;\n const token = auth.slice(BEARER_PREFIX.length);\n const result = await verifySessionJwt(secret, token);\n if (result.isErr()) {\n console.warn(`${LOG_PREFIX} session verify failed:`, result.error);\n return null;\n }\n return result.value;\n}\n\n/**\n * Returns an authenticated {@link FanvueClient} for the current\n * `Authorization: Bearer` session.\n *\n * If the access token is close to expiring and a refresh token is available,\n * the token is refreshed transparently. Because the session lives client-side\n * (not in a cookie), the refreshed JWT is returned as `refreshedJwt` — set it\n * on the response as the `X-Updated-Session` header so the client can store\n * it (the React `authFetch` helper picks it up automatically).\n *\n * @param opts - Session secret and OAuth config.\n * @returns The client and (when a refresh happened) the new session JWT, or\n * `null` if no valid session exists or token refresh fails.\n */\nexport async function getAuthenticatedClient(opts: {\n sessionSecret: string;\n config: OAuthConfig;\n}): Promise<{ client: FanvueClient; refreshedJwt: string | null } | null> {\n let session = await getSession(opts.sessionSecret);\n if (!session) return null;\n\n let refreshedJwt: string | null = null;\n if (Date.now() >= session.expiresAt - TOKEN_REFRESH_THRESHOLD_MS && session.refreshToken) {\n const refreshResult = await refreshAccessToken(opts.config, session.refreshToken);\n\n if (refreshResult.isErr()) {\n console.warn(`${LOG_PREFIX} token refresh failed:`, refreshResult.error);\n return null;\n }\n\n const refreshed = refreshResult.value;\n // When the refresh response omits optional fields (parsed as null by the schema),\n // preserve the existing session values. Per RFC 6749 §6, a refresh response MAY\n // include a new refresh_token; when absent, the existing one remains valid.\n session = {\n ...session,\n accessToken: refreshed.access_token,\n refreshToken: refreshed.refresh_token ?? session.refreshToken,\n tokenType: refreshed.token_type ?? session.tokenType,\n scope: refreshed.scope ?? session.scope,\n idToken: refreshed.id_token ?? session.idToken,\n expiresAt: Date.now() + refreshed.expires_in * 1000,\n };\n refreshedJwt = await createSessionJwt(opts.sessionSecret, session);\n }\n\n return {\n client: createFanvueClient(session.accessToken, opts.config.apiBaseUrl),\n refreshedJwt,\n };\n}\n","import { NextResponse } from 'next/server';\nimport { z } from 'zod';\n\nimport {\n exchangeSessionToken,\n createSessionJwt,\n type EmbeddedAuthConfig,\n type FanvueUser,\n type SessionPayload,\n type TokenResponse,\n} from '../../core/index.js';\nimport { LOG_PREFIX } from '../constants.js';\nimport { createConfig as createSharedConfig } from '../index.js';\nimport { fetchUserProfile } from '../utils.js';\n\nimport type { FanvueAuthOptions, ResolvedConfig } from '../index.js';\n\nexport { getSession, getAuthenticatedClient } from '../bearer-session.js';\nexport { getSessionTokenFromUrl } from '../../core/index.js';\nexport { type FanvueAuthOptions } from '../index.js';\n\n/**\n * User-facing configuration options for an embedded Fanvue app.\n *\n * Extends {@link FanvueAuthOptions} with the Fanvue platform base URL. All\n * fields are optional — when omitted, values are read from environment\n * variables (`platformUrl` reads `FANVUE_PLATFORM_URL`).\n */\nexport type EmbeddedAppAuthOptions = FanvueAuthOptions & {\n platformUrl?: string;\n};\n\n/**\n * Fully-resolved configuration for the embedded-app handlers, produced by\n * {@link createConfig}.\n */\nexport type EmbeddedAppConfig = ResolvedConfig & EmbeddedAuthConfig;\n\n/**\n * Resolves a complete {@link EmbeddedAppConfig} from the supplied options,\n * falling back to environment variables for any values not provided.\n *\n * @param opts - Optional overrides for the OAuth, session, and platform configuration.\n * @returns A fully-resolved configuration object.\n * @throws If any required value (`clientId`, `clientSecret`, `redirectUri`,\n * `sessionSecret`) is missing from both `opts` and the environment.\n */\nexport function createConfig(opts?: EmbeddedAppAuthOptions): EmbeddedAppConfig {\n const shared = createSharedConfig(opts);\n const platformUrl = opts?.platformUrl ?? process.env['FANVUE_PLATFORM_URL'];\n return { ...shared, platformUrl: platformUrl ?? null };\n}\n\nconst ExchangeRequestSchema = z.object({\n token: z.string().min(1),\n});\n\n/**\n * Callback invoked after a successful session-token exchange, before the\n * response is sent. Use it to persist the refresh token per creator so your\n * app can act on their behalf later, without the iframe open.\n */\nexport type OnTokensCallback = (input: {\n tokens: TokenResponse;\n user: FanvueUser;\n}) => void | Promise<void>;\n\n/**\n * Creates a Next.js route handler that exchanges an embedded session token\n * for a Fanvue session.\n *\n * The embedded page reads the `?token=` query parameter that Fanvue appends\n * to the iframe URL and POSTs it here as JSON (`{ \"token\": \"...\" }`). The\n * handler runs the delegated authorize-on-behalf flow, fetches the user's\n * profile, and responds with a signed session JWT (`{ \"jwt\": \"...\" }`) that\n * the client sends back as an `Authorization: Bearer` header — the React\n * `useEmbeddedAuth` hook does all of this for you.\n *\n * Error responses mirror the platform's semantics:\n * - `400 { error: \"invalid_request\" }` — malformed body.\n * - `401 { error: \"invalid_session_token\" }` — the session token was\n * rejected (they expire after ~60 seconds; reopening the surface mints a\n * fresh one).\n * - `403 { error: \"consent_required\" }` — the creator has not approved (or\n * has revoked) consent for this app in the Fanvue UI.\n * - `502 { error: \"exchange_failed\" }` — any other failure.\n *\n * @param config - The embedded-app configuration.\n * @param hooks - Optional `onTokens` callback for persisting tokens (e.g.\n * the refresh token, keyed by `user.uuid`, for offline/background work).\n * @returns A route handler object with a `POST` method.\n */\nexport function createSessionExchangeHandler(\n config: EmbeddedAppConfig,\n hooks?: { onTokens?: OnTokensCallback },\n): {\n POST(request: Request): Promise<NextResponse>;\n} {\n return {\n async POST(request: Request): Promise<NextResponse> {\n let body: unknown;\n try {\n body = await request.json();\n } catch {\n return NextResponse.json({ error: 'invalid_request' }, { status: 400 });\n }\n\n const parsed = ExchangeRequestSchema.safeParse(body);\n if (!parsed.success) {\n return NextResponse.json({ error: 'invalid_request' }, { status: 400 });\n }\n\n const tokenResult = await exchangeSessionToken(config, parsed.data.token);\n if (tokenResult.isErr()) {\n const error = tokenResult.error;\n if (error.code === 'SESSION_TOKEN_REJECTED') {\n return NextResponse.json({ error: 'invalid_session_token' }, { status: 401 });\n }\n if (error.code === 'CONSENT_REQUIRED') {\n return NextResponse.json({ error: 'consent_required' }, { status: 403 });\n }\n console.error(`${LOG_PREFIX} session token exchange failed:`, error);\n return NextResponse.json({ error: 'exchange_failed' }, { status: 502 });\n }\n\n const tokens = tokenResult.value;\n const user = await fetchUserProfile(tokens.access_token, config.apiBaseUrl);\n if (!user) {\n return NextResponse.json({ error: 'exchange_failed' }, { status: 502 });\n }\n\n if (hooks?.onTokens) {\n try {\n await hooks.onTokens({ tokens, user });\n } catch (error) {\n console.error(`${LOG_PREFIX} onTokens callback failed:`, error);\n return NextResponse.json({ error: 'exchange_failed' }, { status: 502 });\n }\n }\n\n const sessionPayload: SessionPayload = {\n accessToken: tokens.access_token,\n refreshToken: tokens.refresh_token,\n tokenType: tokens.token_type,\n scope: tokens.scope,\n idToken: tokens.id_token,\n expiresAt: Date.now() + tokens.expires_in * 1000,\n userUuid: user.uuid,\n handle: user.handle,\n displayName: user.displayName,\n isCreator: user.isCreator,\n avatarUrl: user.avatarUrl,\n };\n\n const jwt = await createSessionJwt(config.sessionSecret, sessionPayload);\n return NextResponse.json({ jwt });\n },\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ import { C as OAuthConfig, T as SessionPayload, t as FanvueClient } from "../../index-pS9wR5yg.js";
2
+ import { n as ResolvedConfig, r as createConfig, t as FanvueAuthOptions } from "../../index-CweVyIKX.js";
3
+ import { NextResponse } from "next/server";
4
+
5
+ //#region src/nextjs/off-platform/session.d.ts
6
+ /**
7
+ * Reads and verifies the session JWT from the cookie store.
8
+ *
9
+ * @param secret - The secret used to verify the JWT.
10
+ * @param cookieName - The cookie name to read. Defaults to {@link DEFAULT_SESSION_COOKIE_NAME}.
11
+ * @returns The verified {@link SessionPayload}, or `null` if missing/invalid.
12
+ */
13
+ declare function getSession(secret: string, cookieName?: string | null): Promise<SessionPayload | null>;
14
+ /**
15
+ * Returns an authenticated {@link FanvueClient} for the current session.
16
+ *
17
+ * If the access token is close to expiring and a refresh token is available,
18
+ * the token is refreshed transparently and the session cookie is updated.
19
+ *
20
+ * @param opts - Session secret, cookie name, and OAuth config.
21
+ * @returns A {@link FanvueClient} instance, or `null` if no valid session exists
22
+ * or token refresh fails.
23
+ */
24
+ declare function getAuthenticatedClient(opts: {
25
+ sessionSecret: string;
26
+ sessionCookieName: string | null;
27
+ config: OAuthConfig;
28
+ }): Promise<FanvueClient | null>;
29
+ //#endregion
30
+ //#region src/nextjs/off-platform/index.d.ts
31
+ /**
32
+ * Configuration for the off-platform OAuth handlers.
33
+ *
34
+ * Extends {@link ResolvedConfig} with optional routing overrides.
35
+ */
36
+ type OffPlatformOptions = ResolvedConfig & {
37
+ afterLoginPath: string | null;
38
+ afterLogoutPath: string | null;
39
+ };
40
+ /**
41
+ * Creates a Next.js route handler that initiates the OAuth login flow.
42
+ *
43
+ * @param opts - The off-platform configuration.
44
+ * @returns A route handler object with a `GET` method.
45
+ */
46
+ declare function createLoginHandler(opts: OffPlatformOptions): {
47
+ GET(request: Request): Promise<NextResponse>;
48
+ };
49
+ /**
50
+ * Creates a Next.js route handler that processes the OAuth callback.
51
+ *
52
+ * Supports both `GET` (query-string params) and `POST` (form-encoded body)
53
+ * response modes.
54
+ *
55
+ * @param opts - The off-platform configuration.
56
+ * @returns A route handler object with `GET` and `POST` methods.
57
+ */
58
+ declare function createCallbackHandler(opts: OffPlatformOptions): {
59
+ GET(request: Request): Promise<NextResponse>;
60
+ POST(request: Request): Promise<NextResponse>;
61
+ };
62
+ /**
63
+ * Creates a Next.js route handler that logs the user out by deleting the
64
+ * session cookie and redirecting.
65
+ *
66
+ * @param opts - The off-platform configuration.
67
+ * @returns A route handler object with a `POST` method.
68
+ */
69
+ declare function createLogoutHandler(opts: OffPlatformOptions): {
70
+ POST(request: Request): NextResponse;
71
+ };
72
+ //#endregion
73
+ export { type FanvueAuthOptions, OffPlatformOptions, type ResolvedConfig, createCallbackHandler, createConfig, createLoginHandler, createLogoutHandler, getAuthenticatedClient, getSession };
74
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/nextjs/off-platform/session.ts","../../../src/nextjs/off-platform/index.ts"],"mappings":";;;;;;;;;AA2BA;;;iBAAsB,UAAA,CACpB,MAAA,UACA,UAAA,mBACC,OAAA,CAAQ,cAAA;;;;;;;;;;;iBAuDW,sBAAA,CAAuB,IAAA;EAC3C,aAAA;EACA,iBAAA;EACA,MAAA,EAAQ,WAAA;AAAA,IACN,OAAA,CAAQ,YAAA;;;;AA9DZ;;;;KCLY,kBAAA,GAAqB,cAAA;EAC/B,cAAA;EACA,eAAA;AAAA;;;AD6DF;;;;iBCpDgB,kBAAA,CAAmB,IAAA,EAAM,kBAAA;EACvC,GAAA,CAAI,OAAA,EAAS,OAAA,GAAU,OAAA,CAAQ,YAAA;AAAA;;;;;;;;;;iBAcjB,qBAAA,CAAsB,IAAA,EAAM,kBAAA;EAC1C,GAAA,CAAI,OAAA,EAAS,OAAA,GAAU,OAAA,CAAQ,YAAA;EAC/B,IAAA,CAAK,OAAA,EAAS,OAAA,GAAU,OAAA,CAAQ,YAAA;AAAA;AA5BlC;;;;;;;AAAA,iBAoEgB,mBAAA,CAAoB,IAAA,EAAM,kBAAA;EACxC,IAAA,CAAK,OAAA,EAAS,OAAA,GAAU,YAAA;AAAA"}
@@ -0,0 +1,257 @@
1
+ import { c as createAuthorizationUrl, l as exchangeCodeForToken, o as createSessionJwt, s as verifySessionJwt, t as createFanvueClient, u as refreshAccessToken } from "../../core-CvVOMyqr.js";
2
+ import { i as getFormString, n as fetchUserProfile, o as LOG_PREFIX, r as getBaseUrl, s as SESSION_COOKIE_MAX_AGE_SECONDS, t as createConfig } from "../../nextjs-CESI_EiU.js";
3
+ import { NextResponse } from "next/server";
4
+ import { cookies } from "next/headers";
5
+ //#region src/nextjs/callback.ts
6
+ const COOKIE_OAUTH_STATE = "oauth_state";
7
+ const COOKIE_OAUTH_VERIFIER = "oauth_verifier";
8
+ const OAUTH_COOKIE_MAX_AGE_SECONDS = 600;
9
+ const ERROR_STATE_MISMATCH = "oauth_state_mismatch";
10
+ const ERROR_TOKEN_EXCHANGE_FAILED = "oauth_token_exchange_failed";
11
+ const ERROR_USER_PROFILE_FAILED = "oauth_user_profile_failed";
12
+ /**
13
+ * Shared callback handler that validates the OAuth state, exchanges the
14
+ * authorization code for tokens, fetches the user profile, creates a session
15
+ * JWT, and delegates to the strategy for final response.
16
+ */
17
+ async function handleOAuthCallback(request, opts, getParams, strategy) {
18
+ const baseUrl = getBaseUrl(opts, request.url);
19
+ const params = await getParams(request);
20
+ const cookieStore = await cookies();
21
+ const storedState = cookieStore.get(COOKIE_OAUTH_STATE)?.value;
22
+ const verifier = cookieStore.get(COOKIE_OAUTH_VERIFIER)?.value;
23
+ cookieStore.delete(COOKIE_OAUTH_STATE);
24
+ cookieStore.delete(COOKIE_OAUTH_VERIFIER);
25
+ if (params.kind === "error") {
26
+ const search = new URLSearchParams();
27
+ search.set("error", params.error);
28
+ if (params.errorDescription) search.set("error_description", params.errorDescription);
29
+ return NextResponse.redirect(new URL(`${baseUrl}/?${search.toString()}`, request.url));
30
+ }
31
+ if (params.kind === "empty" || !storedState || !verifier || params.state !== storedState) return NextResponse.redirect(new URL(`${baseUrl}/?error=${ERROR_STATE_MISMATCH}`, request.url));
32
+ const { code } = params;
33
+ const tokenResult = await exchangeCodeForToken(opts, {
34
+ code,
35
+ codeVerifier: verifier,
36
+ redirectUri: null
37
+ });
38
+ if (tokenResult.isErr()) {
39
+ console.error(`${LOG_PREFIX} token exchange failed:`, tokenResult.error);
40
+ return NextResponse.redirect(new URL(`${baseUrl}/?error=${ERROR_TOKEN_EXCHANGE_FAILED}`, request.url));
41
+ }
42
+ const token = tokenResult.value;
43
+ const user = await fetchUserProfile(token.access_token, opts.apiBaseUrl);
44
+ if (!user) return NextResponse.redirect(new URL(`${baseUrl}/?error=${ERROR_USER_PROFILE_FAILED}`, request.url));
45
+ const sessionPayload = {
46
+ accessToken: token.access_token,
47
+ refreshToken: token.refresh_token,
48
+ tokenType: token.token_type,
49
+ scope: token.scope,
50
+ idToken: token.id_token,
51
+ expiresAt: Date.now() + token.expires_in * 1e3,
52
+ userUuid: user.uuid,
53
+ handle: user.handle,
54
+ displayName: user.displayName,
55
+ isCreator: user.isCreator,
56
+ avatarUrl: user.avatarUrl
57
+ };
58
+ const jwt = await createSessionJwt(opts.sessionSecret, sessionPayload);
59
+ return strategy.complete(request, jwt, baseUrl);
60
+ }
61
+ /**
62
+ * Creates a Next.js route handler that initiates the OAuth login flow.
63
+ * Used by the off-platform module.
64
+ */
65
+ function createLoginHandler$1(opts) {
66
+ return { async GET(request) {
67
+ const { url, codeVerifier, state } = await createAuthorizationUrl(opts);
68
+ const cookieStore = await cookies();
69
+ const secure = new URL(request.url).protocol === "https:";
70
+ cookieStore.set(COOKIE_OAUTH_STATE, state, {
71
+ httpOnly: true,
72
+ path: "/",
73
+ sameSite: "lax",
74
+ secure,
75
+ maxAge: OAUTH_COOKIE_MAX_AGE_SECONDS
76
+ });
77
+ cookieStore.set(COOKIE_OAUTH_VERIFIER, codeVerifier, {
78
+ httpOnly: true,
79
+ path: "/",
80
+ sameSite: "lax",
81
+ secure,
82
+ maxAge: OAUTH_COOKIE_MAX_AGE_SECONDS
83
+ });
84
+ return NextResponse.redirect(url);
85
+ } };
86
+ }
87
+ /** Extracts callback params from a GET request's query string. */
88
+ function getParamsFromQuery(request) {
89
+ const url = new URL(request.url);
90
+ const error = url.searchParams.get("error");
91
+ if (error) return Promise.resolve({
92
+ kind: "error",
93
+ error,
94
+ errorDescription: url.searchParams.get("error_description")
95
+ });
96
+ const code = url.searchParams.get("code");
97
+ const state = url.searchParams.get("state");
98
+ if (code && state) return Promise.resolve({
99
+ kind: "success",
100
+ code,
101
+ state
102
+ });
103
+ return Promise.resolve({ kind: "empty" });
104
+ }
105
+ /** Extracts callback params from a POST request's form body. */
106
+ async function getParamsFromForm(request, getFormString) {
107
+ const form = await request.formData();
108
+ const error = getFormString(form, "error");
109
+ if (error) return {
110
+ kind: "error",
111
+ error,
112
+ errorDescription: getFormString(form, "error_description")
113
+ };
114
+ const code = getFormString(form, "code");
115
+ const state = getFormString(form, "state");
116
+ if (code && state) return {
117
+ kind: "success",
118
+ code,
119
+ state
120
+ };
121
+ return { kind: "empty" };
122
+ }
123
+ //#endregion
124
+ //#region src/nextjs/off-platform/session.ts
125
+ const TOKEN_REFRESH_THRESHOLD_MS = 3e4;
126
+ /**
127
+ * Reads and verifies the session JWT from the cookie store.
128
+ *
129
+ * @param secret - The secret used to verify the JWT.
130
+ * @param cookieName - The cookie name to read. Defaults to {@link DEFAULT_SESSION_COOKIE_NAME}.
131
+ * @returns The verified {@link SessionPayload}, or `null` if missing/invalid.
132
+ */
133
+ async function getSession(secret, cookieName = null) {
134
+ const token = (await cookies()).get(cookieName ?? "fanvue_session")?.value;
135
+ if (!token) return null;
136
+ const result = await verifySessionJwt(secret, token);
137
+ if (result.isErr()) {
138
+ console.warn(`${LOG_PREFIX} session verify failed:`, result.error);
139
+ return null;
140
+ }
141
+ return result.value;
142
+ }
143
+ /**
144
+ * Creates or updates the session cookie with a fresh JWT.
145
+ *
146
+ * @param secret - The secret used to sign the JWT.
147
+ * @param cookieName - The cookie name to set.
148
+ * @param payload - The session data to encode into the JWT.
149
+ */
150
+ async function setSession(secret, cookieName, payload) {
151
+ const jwt = await createSessionJwt(secret, payload);
152
+ (await cookies()).set(cookieName, jwt, {
153
+ httpOnly: true,
154
+ secure: process.env["NODE_ENV"] === "production",
155
+ sameSite: "lax",
156
+ path: "/",
157
+ maxAge: SESSION_COOKIE_MAX_AGE_SECONDS
158
+ });
159
+ }
160
+ /**
161
+ * Returns an authenticated {@link FanvueClient} for the current session.
162
+ *
163
+ * If the access token is close to expiring and a refresh token is available,
164
+ * the token is refreshed transparently and the session cookie is updated.
165
+ *
166
+ * @param opts - Session secret, cookie name, and OAuth config.
167
+ * @returns A {@link FanvueClient} instance, or `null` if no valid session exists
168
+ * or token refresh fails.
169
+ */
170
+ async function getAuthenticatedClient(opts) {
171
+ const cookieName = opts.sessionCookieName ?? "fanvue_session";
172
+ let session = await getSession(opts.sessionSecret, cookieName);
173
+ if (!session) return null;
174
+ if (Date.now() >= session.expiresAt - TOKEN_REFRESH_THRESHOLD_MS && session.refreshToken) {
175
+ const refreshResult = await refreshAccessToken(opts.config, session.refreshToken);
176
+ if (refreshResult.isErr()) {
177
+ console.warn(`${LOG_PREFIX} token refresh failed:`, refreshResult.error);
178
+ return null;
179
+ }
180
+ const refreshed = refreshResult.value;
181
+ session = {
182
+ ...session,
183
+ accessToken: refreshed.access_token,
184
+ refreshToken: refreshed.refresh_token ?? session.refreshToken,
185
+ tokenType: refreshed.token_type ?? session.tokenType,
186
+ scope: refreshed.scope ?? session.scope,
187
+ idToken: refreshed.id_token ?? session.idToken,
188
+ expiresAt: Date.now() + refreshed.expires_in * 1e3
189
+ };
190
+ try {
191
+ await setSession(opts.sessionSecret, cookieName, session);
192
+ } catch (error) {
193
+ console.warn(`${LOG_PREFIX} failed to persist refreshed session:`, error);
194
+ }
195
+ }
196
+ return createFanvueClient(session.accessToken, opts.config.apiBaseUrl);
197
+ }
198
+ //#endregion
199
+ //#region src/nextjs/off-platform/index.ts
200
+ /**
201
+ * Creates a Next.js route handler that initiates the OAuth login flow.
202
+ *
203
+ * @param opts - The off-platform configuration.
204
+ * @returns A route handler object with a `GET` method.
205
+ */
206
+ function createLoginHandler(opts) {
207
+ return createLoginHandler$1(opts);
208
+ }
209
+ /**
210
+ * Creates a Next.js route handler that processes the OAuth callback.
211
+ *
212
+ * Supports both `GET` (query-string params) and `POST` (form-encoded body)
213
+ * response modes.
214
+ *
215
+ * @param opts - The off-platform configuration.
216
+ * @returns A route handler object with `GET` and `POST` methods.
217
+ */
218
+ function createCallbackHandler(opts) {
219
+ const strategy = { complete(request, jwt, baseUrl) {
220
+ const res = NextResponse.redirect(new URL(`${baseUrl}${opts.afterLoginPath ?? "/"}`, request.url));
221
+ res.cookies.set(opts.sessionCookieName, jwt, {
222
+ httpOnly: true,
223
+ secure: new URL(request.url).protocol === "https:",
224
+ sameSite: "lax",
225
+ path: "/",
226
+ maxAge: SESSION_COOKIE_MAX_AGE_SECONDS
227
+ });
228
+ return res;
229
+ } };
230
+ return {
231
+ async GET(request) {
232
+ return handleOAuthCallback(request, opts, getParamsFromQuery, strategy);
233
+ },
234
+ async POST(request) {
235
+ return handleOAuthCallback(request, opts, (req) => getParamsFromForm(req, getFormString), strategy);
236
+ }
237
+ };
238
+ }
239
+ /**
240
+ * Creates a Next.js route handler that logs the user out by deleting the
241
+ * session cookie and redirecting.
242
+ *
243
+ * @param opts - The off-platform configuration.
244
+ * @returns A route handler object with a `POST` method.
245
+ */
246
+ function createLogoutHandler(opts) {
247
+ return { POST(request) {
248
+ const baseUrl = getBaseUrl(opts, request.url);
249
+ const res = NextResponse.redirect(new URL(`${baseUrl}${opts.afterLogoutPath ?? "/"}`, request.url));
250
+ res.cookies.delete(opts.sessionCookieName);
251
+ return res;
252
+ } };
253
+ }
254
+ //#endregion
255
+ export { createCallbackHandler, createConfig, createLoginHandler, createLogoutHandler, getAuthenticatedClient, getSession };
256
+
257
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["createLoginHandler","createSharedLoginHandler"],"sources":["../../../src/nextjs/callback.ts","../../../src/nextjs/off-platform/session.ts","../../../src/nextjs/off-platform/index.ts"],"sourcesContent":["import { cookies } from 'next/headers';\nimport { NextResponse } from 'next/server';\n\nimport {\n createAuthorizationUrl,\n exchangeCodeForToken,\n createSessionJwt,\n type SessionPayload,\n} from '../core/index.js';\n\nimport { LOG_PREFIX, COOKIE_SAME_SITE } from './constants.js';\nimport { fetchUserProfile, getBaseUrl } from './utils.js';\n\nimport type { ResolvedConfig } from './index.js';\n\nconst COOKIE_OAUTH_STATE = 'oauth_state';\nconst COOKIE_OAUTH_VERIFIER = 'oauth_verifier';\nconst OAUTH_COOKIE_MAX_AGE_SECONDS = 600;\nconst ERROR_STATE_MISMATCH = 'oauth_state_mismatch';\nconst ERROR_TOKEN_EXCHANGE_FAILED = 'oauth_token_exchange_failed';\nconst ERROR_USER_PROFILE_FAILED = 'oauth_user_profile_failed';\n\n/** Parameters extracted from the OAuth callback request. */\nexport type CallbackParams =\n | { kind: 'success'; code: string; state: string }\n | { kind: 'error'; error: string; errorDescription: string | null }\n | { kind: 'empty' };\n\n/** Strategy for completing the OAuth callback — how the JWT is delivered to the client. */\nexport interface CallbackStrategy {\n complete(request: Request, jwt: string, baseUrl: string): NextResponse;\n}\n\n/** Options required by the shared callback handler. */\nexport type CallbackHandlerOptions = ResolvedConfig;\n\n/**\n * Shared callback handler that validates the OAuth state, exchanges the\n * authorization code for tokens, fetches the user profile, creates a session\n * JWT, and delegates to the strategy for final response.\n */\nexport async function handleOAuthCallback(\n request: Request,\n opts: CallbackHandlerOptions,\n getParams: (request: Request) => Promise<CallbackParams>,\n strategy: CallbackStrategy,\n): Promise<NextResponse> {\n const baseUrl = getBaseUrl(opts, request.url);\n const params = await getParams(request);\n\n const cookieStore = await cookies();\n const storedState = cookieStore.get(COOKIE_OAUTH_STATE)?.value;\n const verifier = cookieStore.get(COOKIE_OAUTH_VERIFIER)?.value;\n\n cookieStore.delete(COOKIE_OAUTH_STATE);\n cookieStore.delete(COOKIE_OAUTH_VERIFIER);\n\n if (params.kind === 'error') {\n const search = new URLSearchParams();\n search.set('error', params.error);\n if (params.errorDescription) search.set('error_description', params.errorDescription);\n return NextResponse.redirect(new URL(`${baseUrl}/?${search.toString()}`, request.url));\n }\n\n if (params.kind === 'empty' || !storedState || !verifier || params.state !== storedState) {\n return NextResponse.redirect(new URL(`${baseUrl}/?error=${ERROR_STATE_MISMATCH}`, request.url));\n }\n\n const { code } = params;\n\n const tokenResult = await exchangeCodeForToken(opts, {\n code,\n codeVerifier: verifier,\n redirectUri: null,\n });\n\n if (tokenResult.isErr()) {\n console.error(`${LOG_PREFIX} token exchange failed:`, tokenResult.error);\n return NextResponse.redirect(\n new URL(`${baseUrl}/?error=${ERROR_TOKEN_EXCHANGE_FAILED}`, request.url),\n );\n }\n\n const token = tokenResult.value;\n const user = await fetchUserProfile(token.access_token, opts.apiBaseUrl);\n\n if (!user) {\n return NextResponse.redirect(\n new URL(`${baseUrl}/?error=${ERROR_USER_PROFILE_FAILED}`, request.url),\n );\n }\n\n const sessionPayload: SessionPayload = {\n accessToken: token.access_token,\n refreshToken: token.refresh_token,\n tokenType: token.token_type,\n scope: token.scope,\n idToken: token.id_token,\n expiresAt: Date.now() + token.expires_in * 1000,\n userUuid: user.uuid,\n handle: user.handle,\n displayName: user.displayName,\n isCreator: user.isCreator,\n avatarUrl: user.avatarUrl,\n };\n\n const jwt = await createSessionJwt(opts.sessionSecret, sessionPayload);\n\n return strategy.complete(request, jwt, baseUrl);\n}\n\n/**\n * Creates a Next.js route handler that initiates the OAuth login flow.\n * Used by the off-platform module.\n */\nexport function createLoginHandler(opts: CallbackHandlerOptions): {\n GET(request: Request): Promise<NextResponse>;\n} {\n return {\n async GET(request: Request): Promise<NextResponse> {\n const { url, codeVerifier, state } = await createAuthorizationUrl(opts);\n\n const cookieStore = await cookies();\n const reqUrl = new URL(request.url);\n const secure = reqUrl.protocol === 'https:';\n\n cookieStore.set(COOKIE_OAUTH_STATE, state, {\n httpOnly: true,\n path: '/',\n sameSite: COOKIE_SAME_SITE,\n secure,\n maxAge: OAUTH_COOKIE_MAX_AGE_SECONDS,\n });\n cookieStore.set(COOKIE_OAUTH_VERIFIER, codeVerifier, {\n httpOnly: true,\n path: '/',\n sameSite: COOKIE_SAME_SITE,\n secure,\n maxAge: OAUTH_COOKIE_MAX_AGE_SECONDS,\n });\n\n return NextResponse.redirect(url);\n },\n };\n}\n\n/** Extracts callback params from a GET request's query string. */\nexport function getParamsFromQuery(request: Request): Promise<CallbackParams> {\n const url = new URL(request.url);\n const error = url.searchParams.get('error');\n if (error) {\n return Promise.resolve({\n kind: 'error',\n error,\n errorDescription: url.searchParams.get('error_description'),\n });\n }\n const code = url.searchParams.get('code');\n const state = url.searchParams.get('state');\n if (code && state) {\n return Promise.resolve({ kind: 'success', code, state });\n }\n return Promise.resolve({ kind: 'empty' });\n}\n\n/** Extracts callback params from a POST request's form body. */\nexport async function getParamsFromForm(\n request: Request,\n getFormString: (form: FormData, key: string) => string | null,\n): Promise<CallbackParams> {\n const form = await request.formData();\n const error = getFormString(form, 'error');\n if (error) {\n return {\n kind: 'error',\n error,\n errorDescription: getFormString(form, 'error_description'),\n };\n }\n const code = getFormString(form, 'code');\n const state = getFormString(form, 'state');\n if (code && state) {\n return { kind: 'success', code, state };\n }\n return { kind: 'empty' };\n}\n","import { cookies } from 'next/headers';\n\nimport {\n verifySessionJwt,\n createSessionJwt,\n createFanvueClient,\n refreshAccessToken,\n type FanvueClient,\n type OAuthConfig,\n type SessionPayload,\n} from '../../core/index.js';\nimport {\n DEFAULT_SESSION_COOKIE_NAME,\n SESSION_COOKIE_MAX_AGE_SECONDS,\n COOKIE_SAME_SITE,\n LOG_PREFIX,\n} from '../constants.js';\n\nconst TOKEN_REFRESH_THRESHOLD_MS = 30_000;\n\n/**\n * Reads and verifies the session JWT from the cookie store.\n *\n * @param secret - The secret used to verify the JWT.\n * @param cookieName - The cookie name to read. Defaults to {@link DEFAULT_SESSION_COOKIE_NAME}.\n * @returns The verified {@link SessionPayload}, or `null` if missing/invalid.\n */\nexport async function getSession(\n secret: string,\n cookieName: string | null = null,\n): Promise<SessionPayload | null> {\n const cookieStore = await cookies();\n const token = cookieStore.get(cookieName ?? DEFAULT_SESSION_COOKIE_NAME)?.value;\n if (!token) return null;\n const result = await verifySessionJwt(secret, token);\n if (result.isErr()) {\n console.warn(`${LOG_PREFIX} session verify failed:`, result.error);\n return null;\n }\n return result.value;\n}\n\n/**\n * Creates or updates the session cookie with a fresh JWT.\n *\n * @param secret - The secret used to sign the JWT.\n * @param cookieName - The cookie name to set.\n * @param payload - The session data to encode into the JWT.\n */\nexport async function setSession(\n secret: string,\n cookieName: string,\n payload: SessionPayload,\n): Promise<void> {\n const jwt = await createSessionJwt(secret, payload);\n const cookieStore = await cookies();\n cookieStore.set(cookieName, jwt, {\n httpOnly: true,\n secure: process.env['NODE_ENV'] === 'production',\n sameSite: COOKIE_SAME_SITE,\n path: '/',\n maxAge: SESSION_COOKIE_MAX_AGE_SECONDS,\n });\n}\n\n/**\n * Deletes the session cookie.\n *\n * @param cookieName - The cookie name to delete.\n */\nexport async function clearSession(cookieName: string): Promise<void> {\n const cookieStore = await cookies();\n cookieStore.delete(cookieName);\n}\n\n/**\n * Returns an authenticated {@link FanvueClient} for the current session.\n *\n * If the access token is close to expiring and a refresh token is available,\n * the token is refreshed transparently and the session cookie is updated.\n *\n * @param opts - Session secret, cookie name, and OAuth config.\n * @returns A {@link FanvueClient} instance, or `null` if no valid session exists\n * or token refresh fails.\n */\nexport async function getAuthenticatedClient(opts: {\n sessionSecret: string;\n sessionCookieName: string | null;\n config: OAuthConfig;\n}): Promise<FanvueClient | null> {\n const cookieName = opts.sessionCookieName ?? DEFAULT_SESSION_COOKIE_NAME;\n let session = await getSession(opts.sessionSecret, cookieName);\n if (!session) return null;\n\n if (Date.now() >= session.expiresAt - TOKEN_REFRESH_THRESHOLD_MS && session.refreshToken) {\n const refreshResult = await refreshAccessToken(opts.config, session.refreshToken);\n\n if (refreshResult.isErr()) {\n console.warn(`${LOG_PREFIX} token refresh failed:`, refreshResult.error);\n return null;\n }\n\n const refreshed = refreshResult.value;\n // When the refresh response omits optional fields (parsed as null by the schema),\n // preserve the existing session values. Per RFC 6749 §6, a refresh response MAY\n // include a new refresh_token; when absent, the existing one remains valid.\n session = {\n ...session,\n accessToken: refreshed.access_token,\n refreshToken: refreshed.refresh_token ?? session.refreshToken,\n tokenType: refreshed.token_type ?? session.tokenType,\n scope: refreshed.scope ?? session.scope,\n idToken: refreshed.id_token ?? session.idToken,\n expiresAt: Date.now() + refreshed.expires_in * 1000,\n };\n try {\n await setSession(opts.sessionSecret, cookieName, session);\n } catch (error) {\n console.warn(`${LOG_PREFIX} failed to persist refreshed session:`, error);\n }\n }\n\n return createFanvueClient(session.accessToken, opts.config.apiBaseUrl);\n}\n","import { NextResponse } from 'next/server';\n\nimport {\n handleOAuthCallback,\n createLoginHandler as createSharedLoginHandler,\n getParamsFromQuery,\n getParamsFromForm,\n} from '../callback.js';\nimport { COOKIE_SAME_SITE, SESSION_COOKIE_MAX_AGE_SECONDS } from '../constants.js';\nimport { type ResolvedConfig } from '../index.js';\nimport { getBaseUrl, getFormString } from '../utils.js';\n\nimport type { CallbackStrategy } from '../callback.js';\n\nexport { getSession, getAuthenticatedClient } from './session.js';\nexport { createConfig, type FanvueAuthOptions, type ResolvedConfig } from '../index.js';\n\n/**\n * Configuration for the off-platform OAuth handlers.\n *\n * Extends {@link ResolvedConfig} with optional routing overrides.\n */\nexport type OffPlatformOptions = ResolvedConfig & {\n afterLoginPath: string | null;\n afterLogoutPath: string | null;\n};\n\n/**\n * Creates a Next.js route handler that initiates the OAuth login flow.\n *\n * @param opts - The off-platform configuration.\n * @returns A route handler object with a `GET` method.\n */\nexport function createLoginHandler(opts: OffPlatformOptions): {\n GET(request: Request): Promise<NextResponse>;\n} {\n return createSharedLoginHandler(opts);\n}\n\n/**\n * Creates a Next.js route handler that processes the OAuth callback.\n *\n * Supports both `GET` (query-string params) and `POST` (form-encoded body)\n * response modes.\n *\n * @param opts - The off-platform configuration.\n * @returns A route handler object with `GET` and `POST` methods.\n */\nexport function createCallbackHandler(opts: OffPlatformOptions): {\n GET(request: Request): Promise<NextResponse>;\n POST(request: Request): Promise<NextResponse>;\n} {\n const strategy: CallbackStrategy = {\n complete(request: Request, jwt: string, baseUrl: string): NextResponse {\n const res = NextResponse.redirect(\n new URL(`${baseUrl}${opts.afterLoginPath ?? '/'}`, request.url),\n );\n res.cookies.set(opts.sessionCookieName, jwt, {\n httpOnly: true,\n secure: new URL(request.url).protocol === 'https:',\n sameSite: COOKIE_SAME_SITE,\n path: '/',\n maxAge: SESSION_COOKIE_MAX_AGE_SECONDS,\n });\n return res;\n },\n };\n\n return {\n async GET(request: Request): Promise<NextResponse> {\n return handleOAuthCallback(request, opts, getParamsFromQuery, strategy);\n },\n async POST(request: Request): Promise<NextResponse> {\n return handleOAuthCallback(\n request,\n opts,\n (req) => getParamsFromForm(req, getFormString),\n strategy,\n );\n },\n };\n}\n\n/**\n * Creates a Next.js route handler that logs the user out by deleting the\n * session cookie and redirecting.\n *\n * @param opts - The off-platform configuration.\n * @returns A route handler object with a `POST` method.\n */\nexport function createLogoutHandler(opts: OffPlatformOptions): {\n POST(request: Request): NextResponse;\n} {\n return {\n POST(request: Request): NextResponse {\n const baseUrl = getBaseUrl(opts, request.url);\n const res = NextResponse.redirect(\n new URL(`${baseUrl}${opts.afterLogoutPath ?? '/'}`, request.url),\n );\n res.cookies.delete(opts.sessionCookieName);\n return res;\n },\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ import { C as assertFanvueDomain, t as createFanvueClient } from "./core-CvVOMyqr.js";
2
+ //#region src/nextjs/constants.ts
3
+ /** Default name for the session cookie. */
4
+ const DEFAULT_SESSION_COOKIE_NAME = "fanvue_session";
5
+ /** Session cookie max-age in seconds (30 days). */
6
+ const SESSION_COOKIE_MAX_AGE_SECONDS = 3600 * 24 * 30;
7
+ /** Log prefix for all library log messages. */
8
+ const LOG_PREFIX = "[fanvue/auth]";
9
+ //#endregion
10
+ //#region src/nextjs/utils.ts
11
+ /**
12
+ * Fetches the authenticated user's profile from the Fanvue API.
13
+ *
14
+ * @returns The user profile on success, or `null` on failure.
15
+ */
16
+ async function fetchUserProfile(accessToken, apiBaseUrl) {
17
+ const result = await createFanvueClient(accessToken, apiBaseUrl).getCurrentUser();
18
+ if (result.isErr()) {
19
+ console.warn(`${LOG_PREFIX} user profile fetch failed:`, result.error);
20
+ return null;
21
+ }
22
+ return result.value;
23
+ }
24
+ /**
25
+ * Extracts a string value from `FormData`, returning `null` when the value
26
+ * is missing or is not a string (e.g. a `File`).
27
+ */
28
+ function getFormString(form, key) {
29
+ const value = form.get(key);
30
+ return typeof value === "string" ? value : null;
31
+ }
32
+ /**
33
+ * Derives the application base URL from the options or falls back to the
34
+ * request's origin.
35
+ */
36
+ function getBaseUrl(opts, requestUrl) {
37
+ return opts.baseUrl ?? new URL(requestUrl).origin;
38
+ }
39
+ //#endregion
40
+ //#region src/nextjs/index.ts
41
+ /**
42
+ * Resolves a complete {@link ResolvedConfig} from the supplied options,
43
+ * falling back to environment variables for any values not provided.
44
+ *
45
+ * @param opts - Optional overrides for the OAuth and session configuration.
46
+ * @returns A fully-resolved configuration object.
47
+ * @throws If any required value (`clientId`, `clientSecret`, `redirectUri`,
48
+ * `sessionSecret`) is missing from both `opts` and the environment.
49
+ */
50
+ function createConfig(opts) {
51
+ const clientId = opts?.clientId ?? process.env["OAUTH_CLIENT_ID"];
52
+ const clientSecret = opts?.clientSecret ?? process.env["OAUTH_CLIENT_SECRET"];
53
+ const redirectUri = opts?.redirectUri ?? process.env["OAUTH_REDIRECT_URI"];
54
+ const sessionSecret = opts?.sessionSecret ?? process.env["SESSION_SECRET"];
55
+ if (!clientId || !clientSecret || !redirectUri || !sessionSecret) throw new Error("Missing required OAuth configuration. Provide via options or env vars: OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_REDIRECT_URI, SESSION_SECRET");
56
+ const issuerUrl = opts?.issuerUrl ?? process.env["OAUTH_ISSUER_BASE_URL"];
57
+ const apiBaseUrl = opts?.apiBaseUrl ?? process.env["API_BASE_URL"];
58
+ if (apiBaseUrl != null) assertFanvueDomain(apiBaseUrl);
59
+ const scopes = opts?.scopes ?? process.env["OAUTH_SCOPES"];
60
+ const responseMode = opts?.responseMode ?? process.env["OAUTH_RESPONSE_MODE"];
61
+ const prompt = opts?.prompt ?? process.env["OAUTH_PROMPT"];
62
+ const baseUrl = opts?.baseUrl ?? process.env["BASE_URL"];
63
+ return {
64
+ clientId,
65
+ clientSecret,
66
+ redirectUri,
67
+ issuerUrl: issuerUrl ?? null,
68
+ apiBaseUrl: apiBaseUrl ?? null,
69
+ scopes: scopes ?? null,
70
+ responseMode: responseMode ?? null,
71
+ prompt: prompt ?? null,
72
+ baseUrl: baseUrl ?? null,
73
+ sessionSecret,
74
+ sessionCookieName: opts?.sessionCookieName ?? process.env["SESSION_COOKIE_NAME"] ?? "fanvue_session"
75
+ };
76
+ }
77
+ //#endregion
78
+ export { DEFAULT_SESSION_COOKIE_NAME as a, getFormString as i, fetchUserProfile as n, LOG_PREFIX as o, getBaseUrl as r, SESSION_COOKIE_MAX_AGE_SECONDS as s, createConfig as t };
79
+
80
+ //# sourceMappingURL=nextjs-CESI_EiU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nextjs-CESI_EiU.js","names":[],"sources":["../src/nextjs/constants.ts","../src/nextjs/utils.ts","../src/nextjs/index.ts"],"sourcesContent":["/** Default name for the session cookie. */\nexport const DEFAULT_SESSION_COOKIE_NAME = 'fanvue_session' as const;\n\n/** Session cookie max-age in seconds (30 days). */\nexport const SESSION_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 30;\n\n/** SameSite attribute for cookies. */\nexport const COOKIE_SAME_SITE = 'lax' as const;\n\n/** Log prefix for all library log messages. */\nexport const LOG_PREFIX = '[fanvue/auth]' as const;\n","import { createFanvueClient } from '../core/index.js';\n\nimport { LOG_PREFIX } from './constants.js';\n\nimport type { FanvueUser } from '../core/index.js';\n\n/**\n * Fetches the authenticated user's profile from the Fanvue API.\n *\n * @returns The user profile on success, or `null` on failure.\n */\nexport async function fetchUserProfile(\n accessToken: string,\n apiBaseUrl: string | null,\n): Promise<FanvueUser | null> {\n const client = createFanvueClient(accessToken, apiBaseUrl);\n const result = await client.getCurrentUser();\n if (result.isErr()) {\n console.warn(`${LOG_PREFIX} user profile fetch failed:`, result.error);\n return null;\n }\n return result.value;\n}\n\n/**\n * Extracts a string value from `FormData`, returning `null` when the value\n * is missing or is not a string (e.g. a `File`).\n */\nexport function getFormString(form: FormData, key: string): string | null {\n const value = form.get(key);\n return typeof value === 'string' ? value : null;\n}\n\n/**\n * Derives the application base URL from the options or falls back to the\n * request's origin.\n */\nexport function getBaseUrl(opts: { baseUrl: string | null }, requestUrl: string): string {\n return opts.baseUrl ?? new URL(requestUrl).origin;\n}\n","import { assertFanvueDomain } from '../core/index.js';\n\nimport { DEFAULT_SESSION_COOKIE_NAME } from './constants.js';\n\nimport type { OAuthConfig } from '../core/index.js';\n\n/**\n * User-facing configuration options for Fanvue OAuth.\n *\n * All fields are optional — when omitted, values are read from the\n * corresponding environment variables at runtime. This type uses optional\n * properties (`?`) rather than nullable fields because every value has an\n * env-var fallback, so `undefined` genuinely means \"use the default.\"\n */\nexport type FanvueAuthOptions = {\n clientId?: string;\n clientSecret?: string;\n redirectUri?: string;\n issuerUrl?: string;\n apiBaseUrl?: string;\n scopes?: string;\n sessionSecret?: string;\n sessionCookieName?: string;\n responseMode?: string;\n prompt?: string;\n baseUrl?: string;\n};\n\n/**\n * Fully-resolved configuration produced by {@link createConfig}.\n *\n * Extends the core {@link OAuthConfig} with session-specific settings that\n * are guaranteed to be present.\n */\nexport type ResolvedConfig = OAuthConfig & {\n sessionSecret: string;\n sessionCookieName: string;\n baseUrl: string | null;\n};\n\n/**\n * Resolves a complete {@link ResolvedConfig} from the supplied options,\n * falling back to environment variables for any values not provided.\n *\n * @param opts - Optional overrides for the OAuth and session configuration.\n * @returns A fully-resolved configuration object.\n * @throws If any required value (`clientId`, `clientSecret`, `redirectUri`,\n * `sessionSecret`) is missing from both `opts` and the environment.\n */\nexport function createConfig(opts?: FanvueAuthOptions): ResolvedConfig {\n const clientId = opts?.clientId ?? process.env['OAUTH_CLIENT_ID'];\n const clientSecret = opts?.clientSecret ?? process.env['OAUTH_CLIENT_SECRET'];\n const redirectUri = opts?.redirectUri ?? process.env['OAUTH_REDIRECT_URI'];\n const sessionSecret = opts?.sessionSecret ?? process.env['SESSION_SECRET'];\n\n if (!clientId || !clientSecret || !redirectUri || !sessionSecret) {\n throw new Error(\n 'Missing required OAuth configuration. Provide via options or env vars: OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_REDIRECT_URI, SESSION_SECRET',\n );\n }\n\n const issuerUrl = opts?.issuerUrl ?? process.env['OAUTH_ISSUER_BASE_URL'];\n const apiBaseUrl = opts?.apiBaseUrl ?? process.env['API_BASE_URL'];\n if (apiBaseUrl != null) assertFanvueDomain(apiBaseUrl);\n const scopes = opts?.scopes ?? process.env['OAUTH_SCOPES'];\n const responseMode = opts?.responseMode ?? process.env['OAUTH_RESPONSE_MODE'];\n const prompt = opts?.prompt ?? process.env['OAUTH_PROMPT'];\n const baseUrl = opts?.baseUrl ?? process.env['BASE_URL'];\n\n return {\n clientId,\n clientSecret,\n redirectUri,\n issuerUrl: issuerUrl ?? null,\n apiBaseUrl: apiBaseUrl ?? null,\n scopes: scopes ?? null,\n responseMode: responseMode ?? null,\n prompt: prompt ?? null,\n baseUrl: baseUrl ?? null,\n sessionSecret,\n sessionCookieName:\n opts?.sessionCookieName ?? process.env['SESSION_COOKIE_NAME'] ?? DEFAULT_SESSION_COOKIE_NAME,\n };\n}\n"],"mappings":";;;AACA,MAAa,8BAA8B;;AAG3C,MAAa,iCAAiC,OAAU,KAAK;;AAM7D,MAAa,aAAa;;;;;;;;ACC1B,eAAsB,iBACpB,aACA,YAC4B;CAE5B,MAAM,SAAS,MADA,mBAAmB,aAAa,WAAW,CAC9B,gBAAgB;AAC5C,KAAI,OAAO,OAAO,EAAE;AAClB,UAAQ,KAAK,GAAG,WAAW,8BAA8B,OAAO,MAAM;AACtE,SAAO;;AAET,QAAO,OAAO;;;;;;AAOhB,SAAgB,cAAc,MAAgB,KAA4B;CACxE,MAAM,QAAQ,KAAK,IAAI,IAAI;AAC3B,QAAO,OAAO,UAAU,WAAW,QAAQ;;;;;;AAO7C,SAAgB,WAAW,MAAkC,YAA4B;AACvF,QAAO,KAAK,WAAW,IAAI,IAAI,WAAW,CAAC;;;;;;;;;;;;;ACW7C,SAAgB,aAAa,MAA0C;CACrE,MAAM,WAAW,MAAM,YAAY,QAAQ,IAAI;CAC/C,MAAM,eAAe,MAAM,gBAAgB,QAAQ,IAAI;CACvD,MAAM,cAAc,MAAM,eAAe,QAAQ,IAAI;CACrD,MAAM,gBAAgB,MAAM,iBAAiB,QAAQ,IAAI;AAEzD,KAAI,CAAC,YAAY,CAAC,gBAAgB,CAAC,eAAe,CAAC,cACjD,OAAM,IAAI,MACR,kJACD;CAGH,MAAM,YAAY,MAAM,aAAa,QAAQ,IAAI;CACjD,MAAM,aAAa,MAAM,cAAc,QAAQ,IAAI;AACnD,KAAI,cAAc,KAAM,oBAAmB,WAAW;CACtD,MAAM,SAAS,MAAM,UAAU,QAAQ,IAAI;CAC3C,MAAM,eAAe,MAAM,gBAAgB,QAAQ,IAAI;CACvD,MAAM,SAAS,MAAM,UAAU,QAAQ,IAAI;CAC3C,MAAM,UAAU,MAAM,WAAW,QAAQ,IAAI;AAE7C,QAAO;EACL;EACA;EACA;EACA,WAAW,aAAa;EACxB,YAAY,cAAc;EAC1B,QAAQ,UAAU;EAClB,cAAc,gBAAgB;EAC9B,QAAQ,UAAU;EAClB,SAAS,WAAW;EACpB;EACA,mBACE,MAAM,qBAAqB,QAAQ,IAAI,0BAAA;EAC1C"}
@@ -0,0 +1,96 @@
1
+ import { c as FanvueTheme } from "../index-pS9wR5yg.js";
2
+ import { ReactNode } from "react";
3
+
4
+ //#region src/react/auth-context.d.ts
5
+ /**
6
+ * The value exposed by {@link AuthProvider} via React context.
7
+ *
8
+ * Includes the current JWT, authentication status, and helpers for
9
+ * managing credentials and making authenticated requests.
10
+ */
11
+ interface AuthContextValue {
12
+ jwt: string | null;
13
+ isAuthenticated: boolean;
14
+ setJwt: (jwt: string) => void;
15
+ clearJwt: () => void;
16
+ authFetch: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
17
+ }
18
+ /**
19
+ * Provides authentication state to the React component tree.
20
+ *
21
+ * Wrap your application (or the authenticated section) with this provider
22
+ * so that descendant components can access auth helpers via {@link useAuth}.
23
+ */
24
+ declare function AuthProvider(props: {
25
+ children: ReactNode;
26
+ }): ReactNode;
27
+ /**
28
+ * Access the current authentication context.
29
+ *
30
+ * Must be called from a component that is a descendant of {@link AuthProvider}.
31
+ *
32
+ * @throws {Error} If called outside of an `AuthProvider`.
33
+ */
34
+ declare function useAuth(): AuthContextValue;
35
+ //#endregion
36
+ //#region src/react/use-embedded-auth.d.ts
37
+ /**
38
+ * The state of the embedded session exchange.
39
+ *
40
+ * - `idle` — no session token in the URL (the page was not opened from a
41
+ * Fanvue embedded surface) and no existing session.
42
+ * - `exchanging` — the session token is being exchanged with the backend.
43
+ * - `authenticated` — a session JWT is available; `authFetch` will attach it.
44
+ * - `error` — the exchange failed; see `error` for the cause.
45
+ */
46
+ type EmbeddedAuthStatus = 'idle' | 'exchanging' | 'authenticated' | 'error';
47
+ /**
48
+ * Options for {@link useEmbeddedAuth}.
49
+ *
50
+ * @property exchangePath - The path of the session-exchange route created by
51
+ * `createSessionExchangeHandler`. Defaults to `/api/fanvue/session`.
52
+ */
53
+ interface UseEmbeddedAuthOptions {
54
+ exchangePath?: string;
55
+ }
56
+ /**
57
+ * The value returned by {@link useEmbeddedAuth}.
58
+ *
59
+ * @property status - The current state of the embedded session exchange.
60
+ * @property error - The error code when `status` is `error` (e.g.
61
+ * `consent_required`, `invalid_session_token`), otherwise `null`.
62
+ * @property theme - The creator's active colour scheme (`'light'` or `'dark'`)
63
+ * read from the iframe URL, or `null` when absent (e.g. the app was opened
64
+ * outside Fanvue). Use it to theme-match Fanvue.
65
+ */
66
+ interface UseEmbeddedAuthResult {
67
+ status: EmbeddedAuthStatus;
68
+ error: string | null;
69
+ theme: FanvueTheme | null;
70
+ }
71
+ /**
72
+ * Completes embedded authentication when the app is opened inside Fanvue.
73
+ *
74
+ * On mount, reads the short-lived session token that Fanvue appends to the
75
+ * iframe URL (`?token=`), exchanges it with your backend session route
76
+ * (created by `createSessionExchangeHandler`), and stores the returned
77
+ * session JWT in the auth context — after which `authFetch` automatically
78
+ * authenticates your API calls.
79
+ *
80
+ * Must be used inside an `AuthProvider`.
81
+ *
82
+ * @param opts - Optional overrides (e.g. a custom `exchangePath`).
83
+ * @returns The exchange {@link EmbeddedAuthStatus} and error code, if any.
84
+ *
85
+ * @example
86
+ * function EmbeddedHome() {
87
+ * const { status, error, theme } = useEmbeddedAuth();
88
+ * if (status === 'exchanging') return <p>Connecting…</p>;
89
+ * if (status === 'error') return <p>Auth failed: {error}</p>;
90
+ * return <Dashboard theme={theme ?? 'light'} />;
91
+ * }
92
+ */
93
+ declare function useEmbeddedAuth(opts?: UseEmbeddedAuthOptions): UseEmbeddedAuthResult;
94
+ //#endregion
95
+ export { type AuthContextValue, AuthProvider, type EmbeddedAuthStatus, type FanvueTheme, type UseEmbeddedAuthOptions, type UseEmbeddedAuthResult, useAuth, useEmbeddedAuth };
96
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/react/auth-context.tsx","../../src/react/use-embedded-auth.ts"],"mappings":";;;;;;;;;;UAuDiB,gBAAA;EACf,GAAA;EACA,eAAA;EACA,MAAA,GAAS,GAAA;EACT,QAAA;EACA,SAAA,GAAY,KAAA,EAAO,WAAA,GAAc,GAAA,EAAK,IAAA,GAAO,WAAA,KAAgB,OAAA,CAAQ,QAAA;AAAA;;;;;;;iBAWvD,YAAA,CAAa,KAAA;EAAS,QAAA,EAAU,SAAA;AAAA,IAAc,SAAA;;;;;;;;iBA6D9C,OAAA,CAAA,GAAW,gBAAA;;;;;;AA7E3B;;;;;;KCpCY,kBAAA;;;;;;;UAQK,sBAAA;EACf,YAAA;AAAA;;;;;;;;;;AD2CF;UC9BiB,qBAAA;EACf,MAAA,EAAQ,kBAAA;EACR,KAAA;EACA,KAAA,EAAO,WAAA;AAAA;;;;;ADwFT;;;;;;;;ACjHA;;;;;AAQA;;;;;iBA0CgB,eAAA,CAAgB,IAAA,GAAO,sBAAA,GAAyB,qBAAA"}