@neondatabase/auth 0.1.0-beta.2 → 0.1.0-beta.21

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 (45) hide show
  1. package/README.md +112 -18
  2. package/dist/{adapter-core-BDOw-gBC.mjs → adapter-core-PD5NQpLE.mjs} +392 -69
  3. package/dist/{adapter-core-C12KoaiU.d.mts → adapter-core-y53SWo8w.d.mts} +181 -627
  4. package/dist/{better-auth-react-adapter-FnBHa2nr.mjs → better-auth-react-adapter-B0XIXPUH.mjs} +10 -8
  5. package/dist/better-auth-react-adapter-B7zoQmoL.d.mts +2170 -0
  6. package/dist/chunk-VCZJYX65-CLnrj1o7-D6ZQkcc_.mjs +543 -0
  7. package/dist/constants-2bpp2_-f.mjs +30 -0
  8. package/dist/index.d.mts +4 -98
  9. package/dist/index.mjs +2 -1
  10. package/dist/{neon-auth-C9XTFffv.mjs → neon-auth-DUbqaO2v.mjs} +7 -4
  11. package/dist/neon-auth-oDgy6lQm.d.mts +107 -0
  12. package/dist/next/index.d.mts +76 -303
  13. package/dist/next/index.mjs +6 -174
  14. package/dist/next/server/index.d.mts +433 -0
  15. package/dist/next/server/index.mjs +731 -0
  16. package/dist/react/adapters/index.d.mts +4 -4
  17. package/dist/react/adapters/index.mjs +2 -1
  18. package/dist/react/index.d.mts +5 -5
  19. package/dist/react/index.mjs +5 -92
  20. package/dist/react/ui/index.d.mts +1 -1
  21. package/dist/react/ui/index.mjs +3 -91
  22. package/dist/react/ui/server.mjs +1 -1
  23. package/dist/{supabase-adapter-ggmqWgPe.mjs → supabase-adapter-Bdw6aPGx.mjs} +72 -167
  24. package/dist/supabase-adapter-Dm56RKRF.d.mts +2258 -0
  25. package/dist/types/index.d.mts +3 -0
  26. package/dist/types/index.mjs +3 -0
  27. package/dist/ui/.safelist.html +3 -0
  28. package/dist/ui/css.css +2 -2
  29. package/dist/ui/tailwind.css +4 -3
  30. package/dist/ui/theme-inline.css +44 -0
  31. package/dist/ui/theme.css +221 -118
  32. package/dist/ui-CrxGg6vQ.mjs +12113 -0
  33. package/dist/vanilla/adapters/index.d.mts +3 -3
  34. package/dist/vanilla/adapters/index.mjs +2 -1
  35. package/dist/vanilla/index.d.mts +3 -3
  36. package/dist/vanilla/index.mjs +2 -1
  37. package/llms.txt +172 -0
  38. package/package.json +22 -11
  39. package/dist/better-auth-react-adapter-BXL48HIU.d.mts +0 -722
  40. package/dist/supabase-adapter-crabDnl2.d.mts +0 -128
  41. package/dist/ui-CNFBSekF.mjs +0 -401
  42. /package/dist/{adapters-Dkx0zoMR.mjs → adapters-B7YKkjaL.mjs} +0 -0
  43. /package/dist/{index-C-svZlpj.d.mts → index-CPnFzULh.d.mts} +0 -0
  44. /package/dist/{index-DuDD6cIY.d.mts → index-CzsGMS7C.d.mts} +0 -0
  45. /package/dist/{index-UW23fDSn.d.mts → index-OEBbnNdr.d.mts} +0 -0
@@ -0,0 +1,731 @@
1
+ import { r as NEON_AUTH_COOKIE_PREFIX, s as NEON_AUTH_SESSION_VERIFIER_PARAM_NAME } from "../../constants-2bpp2_-f.mjs";
2
+ import { parseCookies, parseSetCookieHeader } from "better-auth/cookies";
3
+ import { cookies, headers } from "next/headers";
4
+ import { NextRequest, NextResponse } from "next/server";
5
+
6
+ //#region src/server/request-context.ts
7
+ /**
8
+ * Header name used to identify server-side proxy requests.
9
+ * The value will be the framework name (e.g., 'nextjs', 'remix').
10
+ */
11
+ const NEON_AUTH_SERVER_PROXY_HEADER = "x-neon-auth-proxy";
12
+
13
+ //#endregion
14
+ //#region src/server/endpoints.ts
15
+ const API_ENDPOINTS = {
16
+ getSession: {
17
+ path: "get-session",
18
+ method: "GET"
19
+ },
20
+ getAccessToken: {
21
+ path: "get-access-token",
22
+ method: "GET"
23
+ },
24
+ listSessions: {
25
+ path: "list-sessions",
26
+ method: "GET"
27
+ },
28
+ revokeSession: {
29
+ path: "revoke-session",
30
+ method: "POST"
31
+ },
32
+ revokeSessions: {
33
+ path: "revoke-sessions",
34
+ method: "POST"
35
+ },
36
+ revokeOtherSessions: {
37
+ path: "revoke-all-sessions",
38
+ method: "POST"
39
+ },
40
+ refreshToken: {
41
+ path: "refresh-token",
42
+ method: "POST"
43
+ },
44
+ signIn: {
45
+ email: {
46
+ path: "sign-in/email",
47
+ method: "POST"
48
+ },
49
+ social: {
50
+ path: "sign-in/social",
51
+ method: "POST"
52
+ },
53
+ emailOtp: {
54
+ path: "sign-in/email-otp",
55
+ method: "POST"
56
+ }
57
+ },
58
+ signUp: { email: {
59
+ path: "sign-up/email",
60
+ method: "POST"
61
+ } },
62
+ signOut: {
63
+ path: "sign-out",
64
+ method: "POST"
65
+ },
66
+ listAccounts: {
67
+ path: "list-accounts",
68
+ method: "GET"
69
+ },
70
+ accountInfo: {
71
+ path: "account-info",
72
+ method: "GET"
73
+ },
74
+ updateUser: {
75
+ path: "update-user",
76
+ method: "POST"
77
+ },
78
+ deleteUser: {
79
+ path: "delete-user",
80
+ method: "POST"
81
+ },
82
+ changePassword: {
83
+ path: "change-password",
84
+ method: "POST"
85
+ },
86
+ sendVerificationEmail: {
87
+ path: "send-verification-email",
88
+ method: "POST"
89
+ },
90
+ verifyEmail: {
91
+ path: "verify-email",
92
+ method: "POST"
93
+ },
94
+ resetPassword: {
95
+ path: "reset-password",
96
+ method: "POST"
97
+ },
98
+ requestPasswordReset: {
99
+ path: "request-password-reset",
100
+ method: "POST"
101
+ },
102
+ token: {
103
+ path: "token",
104
+ method: "GET"
105
+ },
106
+ jwks: {
107
+ path: "jwt",
108
+ method: "GET"
109
+ },
110
+ getAnonymousToken: {
111
+ path: "token/anonymous",
112
+ method: "GET"
113
+ },
114
+ admin: {
115
+ createUser: {
116
+ path: "admin/create-user",
117
+ method: "POST"
118
+ },
119
+ listUsers: {
120
+ path: "admin/list-users",
121
+ method: "GET"
122
+ },
123
+ setRole: {
124
+ path: "admin/set-role",
125
+ method: "POST"
126
+ },
127
+ setUserPassword: {
128
+ path: "admin/set-user-password",
129
+ method: "POST"
130
+ },
131
+ updateUser: {
132
+ path: "admin/update-user",
133
+ method: "POST"
134
+ },
135
+ banUser: {
136
+ path: "admin/ban-user",
137
+ method: "POST"
138
+ },
139
+ unbanUser: {
140
+ path: "admin/unban-user",
141
+ method: "POST"
142
+ },
143
+ listUserSessions: {
144
+ path: "admin/list-user-sessions",
145
+ method: "GET"
146
+ },
147
+ revokeUserSession: {
148
+ path: "admin/revoke-user-session",
149
+ method: "POST"
150
+ },
151
+ revokeUserSessions: {
152
+ path: "admin/revoke-user-sessions",
153
+ method: "POST"
154
+ },
155
+ impersonateUser: {
156
+ path: "admin/impersonate-user",
157
+ method: "POST"
158
+ },
159
+ stopImpersonating: {
160
+ path: "admin/stop-impersonating",
161
+ method: "POST"
162
+ },
163
+ removeUser: {
164
+ path: "admin/remove-user",
165
+ method: "POST"
166
+ },
167
+ hasPermission: {
168
+ path: "admin/has-permission",
169
+ method: "POST"
170
+ }
171
+ },
172
+ organization: {
173
+ create: {
174
+ path: "organization/create",
175
+ method: "POST"
176
+ },
177
+ update: {
178
+ path: "organization/update",
179
+ method: "POST"
180
+ },
181
+ delete: {
182
+ path: "organization/delete",
183
+ method: "POST"
184
+ },
185
+ list: {
186
+ path: "organization/list",
187
+ method: "GET"
188
+ },
189
+ getFullOrganization: {
190
+ path: "organization/get-full-organization",
191
+ method: "GET"
192
+ },
193
+ setActive: {
194
+ path: "organization/set-active",
195
+ method: "POST"
196
+ },
197
+ checkSlug: {
198
+ path: "organization/check-slug",
199
+ method: "GET"
200
+ },
201
+ listMembers: {
202
+ path: "organization/list-members",
203
+ method: "GET"
204
+ },
205
+ removeMember: {
206
+ path: "organization/remove-member",
207
+ method: "POST"
208
+ },
209
+ updateMemberRole: {
210
+ path: "organization/update-member-role",
211
+ method: "POST"
212
+ },
213
+ leave: {
214
+ path: "organization/leave",
215
+ method: "POST"
216
+ },
217
+ getActiveMember: {
218
+ path: "organization/get-active-member",
219
+ method: "GET"
220
+ },
221
+ getActiveMemberRole: {
222
+ path: "organization/get-active-member-role",
223
+ method: "GET"
224
+ },
225
+ inviteMember: {
226
+ path: "organization/invite-member",
227
+ method: "POST"
228
+ },
229
+ acceptInvitation: {
230
+ path: "organization/accept-invitation",
231
+ method: "POST"
232
+ },
233
+ rejectInvitation: {
234
+ path: "organization/reject-invitation",
235
+ method: "POST"
236
+ },
237
+ cancelInvitation: {
238
+ path: "organization/cancel-invitation",
239
+ method: "POST"
240
+ },
241
+ getInvitation: {
242
+ path: "organization/get-invitation",
243
+ method: "GET"
244
+ },
245
+ listInvitations: {
246
+ path: "organization/list-invitations",
247
+ method: "GET"
248
+ },
249
+ listUserInvitations: {
250
+ path: "organization/list-user-invitations",
251
+ method: "GET"
252
+ },
253
+ hasPermission: {
254
+ path: "organization/has-permission",
255
+ method: "POST"
256
+ }
257
+ },
258
+ emailOtp: {
259
+ sendVerificationOtp: {
260
+ path: "email-otp/send-verification-otp",
261
+ method: "POST"
262
+ },
263
+ verifyEmail: {
264
+ path: "email-otp/verify-email",
265
+ method: "POST"
266
+ },
267
+ checkVerificationOtp: {
268
+ path: "email-otp/check-verification-otp",
269
+ method: "POST"
270
+ },
271
+ resetPassword: {
272
+ path: "email-otp/passcode",
273
+ method: "POST"
274
+ }
275
+ }
276
+ };
277
+
278
+ //#endregion
279
+ //#region src/utils/cookies.ts
280
+ /**
281
+ * Extract the Neon Auth cookies from the request headers.
282
+ * Only returns cookies that start with the NEON_AUTH_COOKIE_PREFIX.
283
+ *
284
+ * @param headers - The request headers or cookie header string.
285
+ * @returns The cookie string with all Neon Auth cookies (e.g., "name=value; name2=value2").
286
+ */
287
+ const extractNeonAuthCookies = (headers$1) => {
288
+ const cookieHeader = typeof headers$1 === "string" ? headers$1 : headers$1.get("cookie");
289
+ if (!cookieHeader) return "";
290
+ const parsedCookies = parseCookies(cookieHeader);
291
+ const result = [];
292
+ for (const [name, value] of parsedCookies.entries()) if (name.startsWith(NEON_AUTH_COOKIE_PREFIX)) result.push(`${name}=${value}`);
293
+ return result.join("; ");
294
+ };
295
+ /**
296
+ * Parses the `set-cookie` header from Neon Auth response into a list of cookies.
297
+ *
298
+ * @param setCookieHeader - The `set-cookie` header from Neon Auth response.
299
+ * @returns The list of parsed cookies with their options.
300
+ */
301
+ const parseSetCookies = (setCookieHeader) => {
302
+ const parsedCookies = parseSetCookieHeader(setCookieHeader);
303
+ const cookies$1 = [];
304
+ for (const entry of parsedCookies.entries()) {
305
+ const [name, parsedCookie] = entry;
306
+ cookies$1.push({
307
+ name,
308
+ value: decodeURIComponent(parsedCookie.value),
309
+ path: parsedCookie.path,
310
+ maxAge: parsedCookie["max-age"] ?? parsedCookie.maxAge,
311
+ httpOnly: parsedCookie.httponly ?? true,
312
+ secure: parsedCookie.secure ?? true,
313
+ sameSite: parsedCookie.samesite ?? "lax",
314
+ partitioned: parsedCookie.partitioned
315
+ });
316
+ }
317
+ return cookies$1;
318
+ };
319
+
320
+ //#endregion
321
+ //#region src/server/client-factory.ts
322
+ function createAuthServerInternal(config) {
323
+ const { baseUrl, context: getContext } = config;
324
+ const fetchWithAuth = async (path, method, args) => {
325
+ const ctx = await getContext();
326
+ const cookies$1 = await ctx.getCookies();
327
+ const origin = await ctx.getOrigin();
328
+ const framework = ctx.getFramework();
329
+ const url = new URL(path, baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
330
+ const { query, fetchOptions: _fetchOptions, ...body } = args || {};
331
+ if (query && typeof query === "object") {
332
+ const queryParams = query;
333
+ for (const [key, value] of Object.entries(queryParams)) if (value !== void 0 && value !== null) url.searchParams.set(key, String(value));
334
+ }
335
+ const headers$1 = {
336
+ Cookie: cookies$1,
337
+ Origin: origin,
338
+ [NEON_AUTH_SERVER_PROXY_HEADER]: framework
339
+ };
340
+ let requestBody;
341
+ if (method === "POST") {
342
+ headers$1["Content-Type"] = "application/json";
343
+ requestBody = JSON.stringify(Object.keys(body).length > 0 ? body : {});
344
+ }
345
+ const response = await fetch(url.toString(), {
346
+ method,
347
+ headers: headers$1,
348
+ body: requestBody
349
+ });
350
+ const setCookieHeader = response.headers.get("set-cookie");
351
+ if (setCookieHeader) {
352
+ const parsedCookies = parseSetCookies(setCookieHeader);
353
+ for (const cookie of parsedCookies) await ctx.setCookie(cookie.name, cookie.value, cookie);
354
+ }
355
+ const responseData = await response.json().catch(() => null);
356
+ if (!response.ok) return {
357
+ data: null,
358
+ error: {
359
+ message: responseData?.message || response.statusText,
360
+ status: response.status,
361
+ statusText: response.statusText
362
+ }
363
+ };
364
+ return {
365
+ data: responseData,
366
+ error: null
367
+ };
368
+ };
369
+ return createApiProxy(API_ENDPOINTS, fetchWithAuth);
370
+ }
371
+ function isEndpointConfig(value) {
372
+ return typeof value === "object" && value !== null && "path" in value && "method" in value;
373
+ }
374
+ function createApiProxy(endpoints, fetchFn) {
375
+ return new Proxy({}, { get(_, prop) {
376
+ const endpoint = endpoints[prop];
377
+ if (!endpoint) return;
378
+ if (isEndpointConfig(endpoint)) return (args) => fetchFn(endpoint.path, endpoint.method, args);
379
+ return createApiProxy(endpoint, fetchFn);
380
+ } });
381
+ }
382
+
383
+ //#endregion
384
+ //#region src/next/server/adapter.ts
385
+ /**
386
+ * Creates a Next.js-specific RequestContext that reads cookies and headers
387
+ * from next/headers and handles cookie setting.
388
+ */
389
+ async function createNextRequestContext() {
390
+ const cookieStore = await cookies();
391
+ const headerStore = await headers();
392
+ return {
393
+ getCookies() {
394
+ return extractNeonAuthCookies(headerStore);
395
+ },
396
+ setCookie(name, value, options) {
397
+ cookieStore.set(name, value, options);
398
+ },
399
+ getHeader(name) {
400
+ return headerStore.get(name) ?? null;
401
+ },
402
+ getOrigin() {
403
+ return headerStore.get("origin") || headerStore.get("referer")?.split("/").slice(0, 3).join("/") || "";
404
+ },
405
+ getFramework() {
406
+ return "nextjs";
407
+ }
408
+ };
409
+ }
410
+
411
+ //#endregion
412
+ //#region src/next/constants.ts
413
+ const NEON_AUTH_SESSION_COOKIE_NAME = `${NEON_AUTH_COOKIE_PREFIX}.session_token`;
414
+ const NEON_AUTH_SESSION_CHALLENGE_COOKIE_NAME = `${NEON_AUTH_COOKIE_PREFIX}.session_challange`;
415
+ const NEON_AUTH_HEADER_MIDDLEWARE_NAME = "X-Neon-Auth-Next-Middleware";
416
+
417
+ //#endregion
418
+ //#region src/next/handler/request.ts
419
+ const PROXY_HEADERS = [
420
+ "user-agent",
421
+ "authorization",
422
+ "referer",
423
+ "content-type"
424
+ ];
425
+ const handleAuthRequest = async (baseUrl, request, path) => {
426
+ const headers$1 = prepareRequestHeaders(request);
427
+ const body = await parseRequestBody(request);
428
+ try {
429
+ const upstreamURL = getUpstreamURL(baseUrl, path, { originalUrl: new URL(request.url) });
430
+ return await fetch(upstreamURL.toString(), {
431
+ method: request.method,
432
+ headers: headers$1,
433
+ body
434
+ });
435
+ } catch (error) {
436
+ const message = error instanceof Error ? error.message : "Internal Server Error";
437
+ console.error(`[AuthError] ${message}`, error);
438
+ return new Response(`[AuthError] ${message}`, { status: 500 });
439
+ }
440
+ };
441
+ const getUpstreamURL = (baseUrl, path, { originalUrl }) => {
442
+ const url = new URL(`${baseUrl}/${path}`);
443
+ if (originalUrl) {
444
+ url.search = originalUrl.search;
445
+ return url;
446
+ }
447
+ return url;
448
+ };
449
+ const prepareRequestHeaders = (request) => {
450
+ const headers$1 = new Headers();
451
+ for (const header of PROXY_HEADERS) if (request.headers.get(header)) headers$1.set(header, request.headers.get(header));
452
+ headers$1.set("Origin", getOrigin(request));
453
+ headers$1.set("Cookie", extractNeonAuthCookies(request.headers));
454
+ headers$1.set(NEON_AUTH_HEADER_MIDDLEWARE_NAME, "true");
455
+ return headers$1;
456
+ };
457
+ const getOrigin = (request) => {
458
+ return request.headers.get("origin") || request.headers.get("referer")?.split("/").slice(0, 3).join("/") || new URL(request.url).origin;
459
+ };
460
+ const parseRequestBody = async (request) => {
461
+ if (request.body) return request.text();
462
+ };
463
+
464
+ //#endregion
465
+ //#region src/next/env-variables.ts
466
+ const NEON_AUTH_BASE_URL = process.env.NEON_AUTH_BASE_URL;
467
+
468
+ //#endregion
469
+ //#region src/next/auth/session.ts
470
+ /**
471
+ * A utility function to be used in react server components fetch the session details from the Neon Auth API, if session token is available in cookie.
472
+ *
473
+ * @returns - `{ session: Session, user: User }` | `{ session: null, user: null}`.
474
+ *
475
+ * @example
476
+ * ```ts
477
+ * import { neonAuth } from "@neondatabase/auth/next/server"
478
+ *
479
+ * const { session, user } = await neonAuth()
480
+ * ```
481
+ */
482
+ const neonAuth = async () => {
483
+ return await fetchSession();
484
+ };
485
+ /**
486
+ * A utility function to fetch the session details from the Neon Auth API, if session token is available in cookie.
487
+ *
488
+ * @returns - `{ session: Session, user: User }` | `{ session: null, user: null}`.
489
+ */
490
+ const fetchSession = async () => {
491
+ const baseUrl = NEON_AUTH_BASE_URL;
492
+ const requestHeaders = await headers();
493
+ const upstreamURL = getUpstreamURL(baseUrl, "get-session", { originalUrl: new URL("get-session", baseUrl) });
494
+ const response = await fetch(upstreamURL.toString(), {
495
+ method: "GET",
496
+ headers: { Cookie: extractNeonAuthCookies(requestHeaders) }
497
+ });
498
+ const body = await response.json();
499
+ const cookieHeader = response.headers.get("set-cookie");
500
+ if (cookieHeader) {
501
+ const cookieStore = await cookies();
502
+ parseSetCookies(cookieHeader).map((cookie) => {
503
+ cookieStore.set(cookie.name, cookie.value, cookie);
504
+ });
505
+ }
506
+ if (!response.ok || body === null) return {
507
+ session: null,
508
+ user: null
509
+ };
510
+ return {
511
+ session: body.session,
512
+ user: body.user
513
+ };
514
+ };
515
+
516
+ //#endregion
517
+ //#region src/next/handler/response.ts
518
+ const RESPONSE_HEADERS_ALLOWLIST = [
519
+ "content-type",
520
+ "content-length",
521
+ "content-encoding",
522
+ "transfer-encoding",
523
+ "connection",
524
+ "date",
525
+ "set-cookie",
526
+ "set-auth-jwt",
527
+ "set-auth-token",
528
+ "x-neon-ret-request-id"
529
+ ];
530
+ const handleAuthResponse = async (response) => {
531
+ return new Response(response.body, {
532
+ status: response.status,
533
+ statusText: response.statusText,
534
+ headers: prepareResponseHeaders(response)
535
+ });
536
+ };
537
+ const prepareResponseHeaders = (response) => {
538
+ const headers$1 = new Headers();
539
+ for (const header of RESPONSE_HEADERS_ALLOWLIST) {
540
+ const value = response.headers.get(header);
541
+ if (value) headers$1.set(header, value);
542
+ }
543
+ return headers$1;
544
+ };
545
+
546
+ //#endregion
547
+ //#region src/next/auth/cookies.ts
548
+ /**
549
+ * Extract the Neon Auth cookies from the response headers.
550
+ * @deprecated Use parseSetCookies instead
551
+ */
552
+ const extractResponseCookies = (headers$1) => {
553
+ const cookieHeader = headers$1.get("set-cookie");
554
+ if (!cookieHeader) return [];
555
+ return cookieHeader.split(", ").map((c) => c.trim());
556
+ };
557
+
558
+ //#endregion
559
+ //#region src/next/middleware/oauth.ts
560
+ const needsSessionVerification = (request) => {
561
+ const hasVerifier = request.nextUrl.searchParams.has(NEON_AUTH_SESSION_VERIFIER_PARAM_NAME);
562
+ const hasChallenge = request.cookies.get(NEON_AUTH_SESSION_CHALLENGE_COOKIE_NAME);
563
+ return hasVerifier && hasChallenge;
564
+ };
565
+ const exchangeOAuthToken = async (request, baseUrl) => {
566
+ const url = request.nextUrl;
567
+ const verifier = url.searchParams.get(NEON_AUTH_SESSION_VERIFIER_PARAM_NAME);
568
+ const challenge = request.cookies.get(NEON_AUTH_SESSION_CHALLENGE_COOKIE_NAME);
569
+ if (!verifier || !challenge) return null;
570
+ const response = await handleAuthResponse(await handleAuthRequest(baseUrl, new Request(request.url, {
571
+ method: "GET",
572
+ headers: request.headers
573
+ }), "get-session"));
574
+ if (response.ok) {
575
+ const headers$1 = new Headers();
576
+ const cookies$1 = extractResponseCookies(response.headers);
577
+ for (const cookie of cookies$1) headers$1.append("Set-Cookie", cookie);
578
+ url.searchParams.delete(NEON_AUTH_SESSION_VERIFIER_PARAM_NAME);
579
+ return NextResponse.redirect(url, { headers: headers$1 });
580
+ }
581
+ return null;
582
+ };
583
+
584
+ //#endregion
585
+ //#region src/next/errors.ts
586
+ const ERRORS = { MISSING_AUTH_BASE_URL: "Missing environment variable: NEON_AUTH_BASE_URL. \n You must provide the auth url of your Neon Auth instance in environment variables" };
587
+
588
+ //#endregion
589
+ //#region src/next/middleware/index.ts
590
+ const SKIP_ROUTES = [
591
+ "/api/auth",
592
+ "/auth/callback",
593
+ "/auth/sign-in",
594
+ "/auth/sign-up",
595
+ "/auth/magic-link",
596
+ "/auth/email-otp",
597
+ "/auth/forgot-password"
598
+ ];
599
+ /**
600
+ * A Next.js middleware to protect routes from unauthenticated requests and refresh the session if required.
601
+ *
602
+ * @param loginUrl - The URL to redirect to when the user is not authenticated.
603
+ * @returns A middleware function that can be used in the Next.js app.
604
+ *
605
+ * @example
606
+ * ```ts
607
+ * import { neonAuthMiddleware } from "@neondatabase/auth/next"
608
+ *
609
+ * export default neonAuthMiddleware({
610
+ * loginUrl: '/auth/sign-in',
611
+ * });
612
+ * ```
613
+ */
614
+ function neonAuthMiddleware({ loginUrl = "/auth/sign-in" }) {
615
+ const baseUrl = NEON_AUTH_BASE_URL;
616
+ if (!baseUrl) throw new Error(ERRORS.MISSING_AUTH_BASE_URL);
617
+ return async (request) => {
618
+ const { pathname } = request.nextUrl;
619
+ if (pathname.startsWith(loginUrl)) return NextResponse.next();
620
+ if (needsSessionVerification(request)) {
621
+ const response = await exchangeOAuthToken(request, baseUrl);
622
+ if (response !== null) return response;
623
+ }
624
+ if (SKIP_ROUTES.some((route) => pathname.startsWith(route))) return NextResponse.next();
625
+ if ((await fetchSession()).session === null) return NextResponse.redirect(new URL(loginUrl, request.url));
626
+ const reqHeaders = new Headers(request.headers);
627
+ reqHeaders.set(NEON_AUTH_HEADER_MIDDLEWARE_NAME, "true");
628
+ return NextResponse.next({ request: { headers: reqHeaders } });
629
+ };
630
+ }
631
+
632
+ //#endregion
633
+ //#region src/next/handler/index.ts
634
+ /**
635
+ *
636
+ * An API route handler to handle the auth requests from the client and proxy them to the Neon Auth.
637
+ *
638
+ * @returns A Next.js API handler functions those can be used in a Next.js route.
639
+ *
640
+ * @example
641
+ * Mount the `authApiHandler` to an API route. Create a route file inside `/api/auth/[...all]/route.ts` directory.
642
+ * And add the following code:
643
+ *
644
+ * ```ts
645
+ * // app/api/auth/[...all]/route.ts
646
+ * import { authApiHandler } from '@neondatabase/auth/next';
647
+ *
648
+ * export const { GET, POST } = authApiHandler();
649
+ * ```
650
+ */
651
+ function authApiHandler() {
652
+ const baseURL = process.env.NEON_AUTH_BASE_URL;
653
+ if (!baseURL) throw new Error(ERRORS.MISSING_AUTH_BASE_URL);
654
+ const handler = async (request, { params }) => {
655
+ return await handleAuthResponse(await handleAuthRequest(baseURL, request, (await params).path.join("/")));
656
+ };
657
+ return {
658
+ GET: handler,
659
+ POST: handler,
660
+ PUT: handler,
661
+ DELETE: handler,
662
+ PATCH: handler
663
+ };
664
+ }
665
+
666
+ //#endregion
667
+ //#region src/next/server/index.ts
668
+ /**
669
+ * Creates a server-side auth API client for Next.js.
670
+ *
671
+ * This client exposes the Neon Auth APIs including authentication, user management, organizations, and admin operations.
672
+ *
673
+ * **Where to use:**
674
+ * - React Server Components
675
+ * - Server Actions
676
+ * - Route Handlers
677
+ *
678
+ * **Requirements:**
679
+ * - `NEON_AUTH_BASE_URL` environment variable must be set
680
+ * - Cookies are automatically read/written via `next/headers`
681
+ *
682
+ * @returns Auth server API client for Next.js
683
+ * @throws Error if `NEON_AUTH_BASE_URL` environment variable is not set
684
+ *
685
+ * @example
686
+ * ```typescript
687
+ * // lib/auth/server.ts - Create a singleton instance
688
+ * import { createAuthServer } from '@neondatabase/auth/next/server';
689
+ * export const authServer = createAuthServer();
690
+ * ```
691
+ *
692
+ * @example
693
+ * ```typescript
694
+ * // Server Component - Reading session
695
+ * import { authServer } from '@/lib/auth/server';
696
+ *
697
+ * export default async function Page() {
698
+ * const { data: session } = await authServer.getSession();
699
+ * if (!session?.user) return <div>Not logged in</div>;
700
+ * return <div>Hello {session.user.name}</div>;
701
+ * }
702
+ * ```
703
+ *
704
+ * @example
705
+ * ```typescript
706
+ * // Server Action - Sign in
707
+ * 'use server';
708
+ * import { authServer } from '@/lib/auth/server';
709
+ * import { redirect } from 'next/navigation';
710
+ *
711
+ * export async function signIn(formData: FormData) {
712
+ * const { error } = await authServer.signIn.email({
713
+ * email: formData.get('email') as string,
714
+ * password: formData.get('password') as string,
715
+ * });
716
+ * if (error) return { error: error.message };
717
+ * redirect('/dashboard');
718
+ * }
719
+ * ```
720
+ */
721
+ function createAuthServer() {
722
+ const baseUrl = process.env.NEON_AUTH_BASE_URL;
723
+ if (!baseUrl) throw new Error("NEON_AUTH_BASE_URL environment variable is required for createAuthServer()");
724
+ return createAuthServerInternal({
725
+ baseUrl,
726
+ context: createNextRequestContext
727
+ });
728
+ }
729
+
730
+ //#endregion
731
+ export { authApiHandler, createAuthServer, neonAuth, neonAuthMiddleware };