@neondatabase/auth 0.1.0-beta.14 → 0.1.0-beta.16

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.
Files changed (43) hide show
  1. package/README.md +1 -0
  2. package/dist/{adapter-core-BPv4mLT0.mjs → adapter-core-BQ6ga1zK.mjs} +119 -20
  3. package/dist/{adapter-core-9F3bfyWA.d.mts → adapter-core-DYWYECKK.d.mts} +16 -8
  4. package/dist/{better-auth-react-adapter-BkeuhSFt.mjs → better-auth-react-adapter-BLKXYcWM.mjs} +1 -1
  5. package/dist/{supabase-adapter-_2wgvfZ3.d.mts → better-auth-react-adapter-tKs79AwE.d.mts} +204 -290
  6. package/dist/constants-2bpp2_-f.mjs +30 -0
  7. package/dist/index-B6dAIdkW.d.mts +100 -0
  8. package/dist/index.d.mts +5 -104
  9. package/dist/index.mjs +2 -1
  10. package/dist/middleware-DXwLKcbA.mjs +305 -0
  11. package/dist/neon-auth-B_otuPjh.d.mts +105 -0
  12. package/dist/{neon-auth-BhLZg9v8.mjs → neon-auth-ClDZNB9a.mjs} +1 -1
  13. package/dist/next/index.d.mts +8 -104
  14. package/dist/next/index.mjs +4 -310
  15. package/dist/next/server/index.d.mts +340 -0
  16. package/dist/next/server/index.mjs +432 -0
  17. package/dist/react/adapters/index.d.mts +4 -3
  18. package/dist/react/adapters/index.mjs +2 -1
  19. package/dist/react/index.d.mts +5 -4
  20. package/dist/react/index.mjs +4 -4
  21. package/dist/react/ui/index.d.mts +1 -1
  22. package/dist/react/ui/index.mjs +2 -3
  23. package/dist/react/ui/server.mjs +2 -2
  24. package/dist/{supabase-adapter-rl2coLdb.mjs → supabase-adapter-Bl576usk.mjs} +2 -1
  25. package/dist/{better-auth-react-adapter-BYvAsZdj.d.mts → supabase-adapter-DtT0d6It.d.mts} +147 -61
  26. package/dist/types/index.d.mts +3 -7
  27. package/dist/ui/css.css +1 -1
  28. package/dist/ui/theme.css +1 -1
  29. package/dist/ui-DLtIc4wi.mjs +4 -0
  30. package/dist/vanilla/adapters/index.d.mts +4 -3
  31. package/dist/vanilla/adapters/index.mjs +2 -1
  32. package/dist/vanilla/index.d.mts +4 -3
  33. package/dist/vanilla/index.mjs +2 -1
  34. package/llms.txt +157 -0
  35. package/package.json +7 -2
  36. package/dist/chunk-5DLVHPZS-Bxj7snpZ-EhdAQJMu.mjs +0 -533
  37. package/dist/ui-C1IRQzLY.mjs +0 -9449
  38. /package/dist/{adapters-D0mxG3F-.mjs → adapters-CUvhsAvY.mjs} +0 -0
  39. /package/dist/{adapters-Df6Dd3KK.mjs → adapters-CivF9wql.mjs} +0 -0
  40. /package/dist/{better-auth-types-CE4hLv9E.d.mts → better-auth-types-Kq3kGuiz.d.mts} +0 -0
  41. /package/dist/{index-ClXLQ1fw.d.mts → index-D8dPsry7.d.mts} +0 -0
  42. /package/dist/{index-BXlAjlSt.d.mts → index-D_HDtZfY.d.mts} +0 -0
  43. /package/dist/{index-DCQ5Y2ED.d.mts → index-OEBbnNdr.d.mts} +0 -0
package/README.md CHANGED
@@ -242,6 +242,7 @@ For Next.js projects, this package provides built-in integration via `@neondatab
242
242
  - Importing styles (with or without Tailwind CSS)
243
243
  - Accessing session data with `neonAuth()` in server components
244
244
  - Using `authClient.useSession()` hook in client components
245
+ - Server-side auth operations with `createAuthServer()` from `@neondatabase/auth/next/server`
245
246
 
246
247
  ## CSS for UI Components
247
248
 
@@ -1,3 +1,4 @@
1
+ import { a as NEON_AUTH_POPUP_CALLBACK_ROUTE, c as OAUTH_POPUP_MESSAGE_TYPE, i as NEON_AUTH_POPUP_CALLBACK_PARAM_NAME, l as SESSION_CACHE_TTL_MS, o as NEON_AUTH_POPUP_PARAM_NAME, s as NEON_AUTH_SESSION_VERIFIER_PARAM_NAME, t as CLOCK_SKEW_BUFFER_MS } from "./constants-2bpp2_-f.mjs";
1
2
  import { getGlobalBroadcastChannel } from "better-auth/client";
2
3
  import { adminClient, emailOTPClient, jwtClient, organizationClient } from "better-auth/client/plugins";
3
4
  import z from "zod";
@@ -100,25 +101,6 @@ var InFlightRequestManager = class {
100
101
  }
101
102
  };
102
103
 
103
- //#endregion
104
- //#region src/core/constants.ts
105
- /**
106
- * Session caching configuration constants
107
- *
108
- * Uses industry-standard 60s cache TTL (common across auth providers).
109
- *
110
- * Note: Token refresh detection is now automatic via Better Auth's
111
- * fetchOptions.onSuccess callback. No polling is needed.
112
- */
113
- /** Session cache TTL in milliseconds (60 seconds) */
114
- const SESSION_CACHE_TTL_MS = 6e4;
115
- /** Clock skew buffer for token expiration checks in milliseconds (10 seconds) */
116
- const CLOCK_SKEW_BUFFER_MS = 1e4;
117
- /** Default session expiry duration in milliseconds (1 hour) */
118
- const DEFAULT_SESSION_EXPIRY_MS = 36e5;
119
- /** Name of the session verifier parameter in the URL, used for the OAUTH flow */
120
- const NEON_AUTH_SESSION_VERIFIER_PARAM_NAME = "neon_auth_session_verifier";
121
-
122
104
  //#endregion
123
105
  //#region src/utils/jwt.ts
124
106
  /**
@@ -293,6 +275,59 @@ var AnonymousTokenCacheManager = class {
293
275
  }
294
276
  };
295
277
 
278
+ //#endregion
279
+ //#region src/core/oauth-popup.ts
280
+ /**
281
+ * Opens an OAuth popup window and waits for completion.
282
+ *
283
+ * This is used when the app is running inside an iframe, where OAuth
284
+ * redirect flows don't work due to X-Frame-Options/CSP restrictions.
285
+ * The popup completes the OAuth flow and sends a postMessage back
286
+ * with the session verifier needed to fetch the session.
287
+ *
288
+ * @param url - The OAuth authorization URL to open in the popup
289
+ * @returns Promise that resolves with the session verifier when OAuth completes
290
+ * @throws Error if popup is blocked, closed by user, or times out
291
+ */
292
+ async function openOAuthPopup(url) {
293
+ const timeout = 12e4;
294
+ const pollInterval = 500;
295
+ return new Promise((resolve, reject) => {
296
+ const popup = globalThis.open(url, "neon_oauth_popup", "width=500,height=700,popup=yes");
297
+ if (!popup || popup.closed) {
298
+ reject(/* @__PURE__ */ new Error("Popup blocked. Please allow popups for this site."));
299
+ return;
300
+ }
301
+ const timeoutId = setTimeout(() => {
302
+ cleanup();
303
+ try {
304
+ popup.close();
305
+ } catch {}
306
+ reject(/* @__PURE__ */ new Error("OAuth popup timed out. Please try again."));
307
+ }, timeout);
308
+ const pollId = setInterval(() => {
309
+ try {
310
+ if (popup.closed) {
311
+ cleanup();
312
+ reject(/* @__PURE__ */ new Error("OAuth popup was closed. Please try again."));
313
+ }
314
+ } catch {}
315
+ }, pollInterval);
316
+ function cleanup() {
317
+ clearTimeout(timeoutId);
318
+ clearInterval(pollId);
319
+ globalThis.removeEventListener("message", handleMessage);
320
+ }
321
+ function handleMessage(event) {
322
+ if (event.origin !== globalThis.location.origin) return;
323
+ if (event.data?.type !== OAUTH_POPUP_MESSAGE_TYPE) return;
324
+ cleanup();
325
+ resolve({ verifier: event.data.verifier || null });
326
+ }
327
+ globalThis.addEventListener("message", handleMessage);
328
+ });
329
+ }
330
+
296
331
  //#endregion
297
332
  //#region src/utils/browser.ts
298
333
  /**
@@ -302,6 +337,19 @@ var AnonymousTokenCacheManager = class {
302
337
  const isBrowser = () => {
303
338
  return globalThis.window !== void 0 && typeof document !== "undefined";
304
339
  };
340
+ /**
341
+ * Checks if the code is running inside an iframe
342
+ * Used to detect embedded contexts where OAuth redirect won't work
343
+ * @returns true if in iframe, false otherwise
344
+ */
345
+ const isIframe = () => {
346
+ if (!isBrowser()) return false;
347
+ try {
348
+ return globalThis.self !== globalThis.top;
349
+ } catch {
350
+ return true;
351
+ }
352
+ };
305
353
 
306
354
  //#endregion
307
355
  //#region src/plugins/anonymous-token.ts
@@ -341,6 +389,39 @@ const BETTER_AUTH_ENDPOINTS = {
341
389
  anonymousSignIn: "/sign-in/anonymous",
342
390
  anonymousToken: "/token/anonymous"
343
391
  };
392
+ /**
393
+ * Handles social sign-in via popup when running inside an iframe.
394
+ * This is necessary because OAuth redirects don't work in iframes due to
395
+ * X-Frame-Options/CSP restrictions from OAuth providers.
396
+ *
397
+ * Flow:
398
+ * 1. Request OAuth URL from server (with disableRedirect)
399
+ * 2. Open popup window with the OAuth URL
400
+ * 3. Wait for popup to complete and send back the session verifier
401
+ * 4. Navigate to callbackURL with verifier - normal page load handles session
402
+ */
403
+ async function handleSocialSignInViaPopup(input, init) {
404
+ const body = JSON.parse(init?.body || "{}");
405
+ const originalCallbackURL = body.callbackURL || "/";
406
+ const popupCallbackUrl = new URL(NEON_AUTH_POPUP_CALLBACK_ROUTE, globalThis.location.origin);
407
+ popupCallbackUrl.searchParams.set(NEON_AUTH_POPUP_PARAM_NAME, "1");
408
+ popupCallbackUrl.searchParams.set(NEON_AUTH_POPUP_CALLBACK_PARAM_NAME, originalCallbackURL);
409
+ body.callbackURL = popupCallbackUrl.toString();
410
+ body.disableRedirect = true;
411
+ const response = await fetch(input, {
412
+ ...init,
413
+ body: JSON.stringify(body)
414
+ });
415
+ const data = await response.json();
416
+ const oauthUrl = data.url;
417
+ if (!oauthUrl) throw new Error("Failed to get OAuth URL");
418
+ const popupResult = await openOAuthPopup(oauthUrl);
419
+ if (!popupResult.verifier) throw new Error("OAuth completed but no session verifier received");
420
+ const navigationUrl = new URL(originalCallbackURL, globalThis.location.origin);
421
+ navigationUrl.searchParams.set(NEON_AUTH_SESSION_VERIFIER_PARAM_NAME, popupResult.verifier);
422
+ globalThis.location.href = navigationUrl.toString();
423
+ return Response.json(data, { status: response.status });
424
+ }
344
425
  const BETTER_AUTH_METHODS_HOOKS = {
345
426
  signUp: {
346
427
  onRequest: () => {},
@@ -359,6 +440,10 @@ const BETTER_AUTH_METHODS_HOOKS = {
359
440
  }
360
441
  },
361
442
  signIn: {
443
+ beforeRequest: (input, init) => {
444
+ if (!(typeof input === "string" ? input : input.toString()).includes("/sign-in/social") || !isIframe()) return null;
445
+ return handleSocialSignInViaPopup(input, init);
446
+ },
362
447
  onRequest: () => {},
363
448
  onSuccess: (responseData) => {
364
449
  if (isSessionResponseData(responseData)) {
@@ -513,6 +598,20 @@ function deriveBetterAuthMethodFromUrl(url) {
513
598
  if (url.includes(BETTER_AUTH_ENDPOINTS.getSession) || url.includes(BETTER_AUTH_ENDPOINTS.token)) return "getSession";
514
599
  }
515
600
  function initBroadcastChannel() {
601
+ if (isBrowser() && globalThis.opener && globalThis.opener !== globalThis) {
602
+ const params = new URLSearchParams(globalThis.location.search);
603
+ if (params.has(NEON_AUTH_POPUP_PARAM_NAME)) {
604
+ const verifier = params.get(NEON_AUTH_SESSION_VERIFIER_PARAM_NAME);
605
+ const originalCallback = params.get(NEON_AUTH_POPUP_CALLBACK_PARAM_NAME);
606
+ globalThis.opener.postMessage({
607
+ type: OAUTH_POPUP_MESSAGE_TYPE,
608
+ verifier,
609
+ originalCallback
610
+ }, "*");
611
+ globalThis.close();
612
+ return;
613
+ }
614
+ }
516
615
  getGlobalBroadcastChannel().subscribe((message) => {
517
616
  if (message.clientId === CURRENT_TAB_CLIENT_ID) return;
518
617
  const trigger = message.data?.trigger;
@@ -615,4 +714,4 @@ var NeonAuthAdapterCore = class {
615
714
  };
616
715
 
617
716
  //#endregion
618
- export { DEFAULT_SESSION_EXPIRY_MS as a, CURRENT_TAB_CLIENT_ID as i, BETTER_AUTH_METHODS_CACHE as n, BETTER_AUTH_METHODS_HOOKS as r, NeonAuthAdapterCore as t };
717
+ export { CURRENT_TAB_CLIENT_ID as i, BETTER_AUTH_METHODS_CACHE as n, BETTER_AUTH_METHODS_HOOKS as r, NeonAuthAdapterCore as t };
@@ -6,10 +6,22 @@ import * as zod0 from "zod";
6
6
  import * as jose2 from "jose";
7
7
  import * as better_auth454 from "better-auth";
8
8
  import * as _better_fetch_fetch266 from "@better-fetch/fetch";
9
+ import { BetterFetchError as BetterFetchError$1 } from "@better-fetch/fetch";
9
10
  import * as nanostores7 from "nanostores";
10
11
  import * as better_call0 from "better-call";
11
12
  import * as better_auth_plugins_email_otp0 from "better-auth/plugins/email-otp";
13
+ import { EmailOTPOptions } from "better-auth/plugins/email-otp";
14
+ import { Invitation, InvitationInput, InvitationStatus, Member, MemberInput, Organization, OrganizationInput, OrganizationRole, Team, TeamInput, TeamMember, TeamMemberInput } from "better-auth/plugins/organization";
15
+ import { JWKOptions, JWSAlgorithms, Jwk, JwtOptions } from "better-auth/plugins/jwt";
16
+ import { AdminOptions, InferAdminRolesFromOption, SessionWithImpersonatedBy, UserWithRole } from "better-auth/plugins/admin";
12
17
 
18
+ //#region src/types/index.d.ts
19
+ type BetterAuthInstance = ReturnType<typeof createAuthClient$1<{
20
+ plugins: SupportedBetterAuthClientPlugins;
21
+ }> | typeof createAuthClient<{
22
+ plugins: SupportedBetterAuthClientPlugins;
23
+ }>>;
24
+ //#endregion
13
25
  //#region src/core/adapter-core.d.ts
14
26
  interface NeonAuthAdapterCoreAuthOptions extends Omit<BetterAuthClientOptions, 'plugins'> {}
15
27
  declare const supportedBetterAuthClientPlugins: ({
@@ -1243,7 +1255,7 @@ declare const supportedBetterAuthClientPlugins: ({
1243
1255
  permission: {
1244
1256
  readonly organization?: ("delete" | "update")[] | undefined;
1245
1257
  readonly member?: ("delete" | "create" | "update")[] | undefined;
1246
- readonly invitation?: ("create" | "cancel")[] | undefined;
1258
+ readonly invitation?: ("cancel" | "create")[] | undefined;
1247
1259
  readonly team?: ("delete" | "create" | "update")[] | undefined;
1248
1260
  readonly ac?: ("delete" | "create" | "update" | "read")[] | undefined;
1249
1261
  };
@@ -1252,7 +1264,7 @@ declare const supportedBetterAuthClientPlugins: ({
1252
1264
  permissions: {
1253
1265
  readonly organization?: ("delete" | "update")[] | undefined;
1254
1266
  readonly member?: ("delete" | "create" | "update")[] | undefined;
1255
- readonly invitation?: ("create" | "cancel")[] | undefined;
1267
+ readonly invitation?: ("cancel" | "create")[] | undefined;
1256
1268
  readonly team?: ("delete" | "create" | "update")[] | undefined;
1257
1269
  readonly ac?: ("delete" | "create" | "update" | "read")[] | undefined;
1258
1270
  };
@@ -1770,11 +1782,7 @@ declare abstract class NeonAuthAdapterCore {
1770
1782
  * See CLAUDE.md for architecture details and API mappings.
1771
1783
  */
1772
1784
  constructor(betterAuthClientOptions: NeonAuthAdapterCoreAuthOptions);
1773
- abstract getBetterAuthInstance(): ReturnType<typeof createAuthClient$1<{
1774
- plugins: SupportedBetterAuthClientPlugins;
1775
- }> | typeof createAuthClient<{
1776
- plugins: SupportedBetterAuthClientPlugins;
1777
- }>>;
1785
+ abstract getBetterAuthInstance(): BetterAuthInstance;
1778
1786
  /**
1779
1787
  * Get JWT token for authenticated or anonymous access.
1780
1788
  * Single source of truth for token retrieval logic.
@@ -1785,4 +1793,4 @@ declare abstract class NeonAuthAdapterCore {
1785
1793
  getJWTToken(allowAnonymous: boolean): Promise<string | null>;
1786
1794
  }
1787
1795
  //#endregion
1788
- export { NeonAuthAdapterCoreAuthOptions as n, SupportedBetterAuthClientPlugins as r, NeonAuthAdapterCore as t };
1796
+ export { TeamInput as C, UserWithRole as E, Team as S, TeamMemberInput as T, MemberInput as _, BetterAuthInstance as a, OrganizationRole as b, InferAdminRolesFromOption as c, InvitationStatus as d, JWKOptions as f, Member as g, JwtOptions as h, AdminOptions as i, Invitation as l, Jwk as m, NeonAuthAdapterCoreAuthOptions as n, BetterFetchError$1 as o, JWSAlgorithms as p, SupportedBetterAuthClientPlugins as r, EmailOTPOptions as s, NeonAuthAdapterCore as t, InvitationInput as u, Organization as v, TeamMember as w, SessionWithImpersonatedBy as x, OrganizationInput as y };
@@ -1,4 +1,4 @@
1
- import { t as NeonAuthAdapterCore } from "./adapter-core-BPv4mLT0.mjs";
1
+ import { t as NeonAuthAdapterCore } from "./adapter-core-BQ6ga1zK.mjs";
2
2
  import { createAuthClient } from "better-auth/react";
3
3
 
4
4
  //#region src/adapters/better-auth-react/better-auth-react-adapter.ts