@neondatabase/auth 0.1.0-beta.1

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,1623 @@
1
+ import { a as DEFAULT_SESSION_EXPIRY_MS, i as CURRENT_TAB_CLIENT_ID, n as BETTER_AUTH_METHODS_CACHE, r as BETTER_AUTH_METHODS_HOOKS, t as NeonAuthAdapterCore } from "./adapter-core-BDOw-gBC.mjs";
2
+ import { createAuthClient, getGlobalBroadcastChannel } from "better-auth/client";
3
+ import { AuthApiError, AuthError, isAuthError } from "@supabase/auth-js";
4
+ import { base64url, decodeJwt, decodeProtectedHeader } from "jose";
5
+
6
+ //#region src/adapters/better-auth-vanilla/better-auth-vanilla-adapter.ts
7
+ /**
8
+ * Internal implementation class - use BetterAuthVanillaAdapter factory function instead
9
+ */
10
+ var BetterAuthVanillaAdapterImpl = class extends NeonAuthAdapterCore {
11
+ _betterAuth;
12
+ constructor(betterAuthClientOptions) {
13
+ super(betterAuthClientOptions);
14
+ this._betterAuth = createAuthClient(this.betterAuthOptions);
15
+ }
16
+ getBetterAuthInstance() {
17
+ return this._betterAuth;
18
+ }
19
+ async getJWTToken() {
20
+ const session = await this._betterAuth.getSession();
21
+ if (session.error) throw session.error;
22
+ return session.data?.session?.token ?? null;
23
+ }
24
+ };
25
+ /**
26
+ * Factory function that returns an adapter builder.
27
+ * The builder is called by createClient/createAuthClient with the URL.
28
+ *
29
+ * @param options - Optional adapter configuration (baseURL is injected separately)
30
+ * @returns A builder function that creates the adapter instance
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * const client = createClient({
35
+ * auth: {
36
+ * url: 'https://auth.example.com',
37
+ * adapter: BetterAuthVanillaAdapter(),
38
+ * },
39
+ * dataApi: { url: 'https://data-api.example.com' },
40
+ * });
41
+ * ```
42
+ */
43
+ function BetterAuthVanillaAdapter(options) {
44
+ return (url) => new BetterAuthVanillaAdapterImpl({
45
+ baseURL: url,
46
+ ...options
47
+ });
48
+ }
49
+
50
+ //#endregion
51
+ //#region src/utils/date.ts
52
+ function toISOString(date) {
53
+ if (!date) return (/* @__PURE__ */ new Date()).toISOString();
54
+ if (typeof date === "string") return date;
55
+ if (typeof date === "number") return new Date(date).toISOString();
56
+ return date.toISOString();
57
+ }
58
+
59
+ //#endregion
60
+ //#region src/adapters/supabase/errors/definitions.ts
61
+ /**
62
+ * Error codes for type-safe error handling
63
+ */
64
+ const AuthErrorCode = {
65
+ BadJwt: "bad_jwt",
66
+ InvalidCredentials: "invalid_credentials",
67
+ SessionExpired: "session_expired",
68
+ SessionNotFound: "session_not_found",
69
+ InvalidGrant: "invalid_grant",
70
+ UserNotFound: "user_not_found",
71
+ UserAlreadyExists: "user_already_exists",
72
+ EmailExists: "email_exists",
73
+ PhoneExists: "phone_exists",
74
+ EmailNotConfirmed: "email_not_confirmed",
75
+ PhoneNotConfirmed: "phone_not_confirmed",
76
+ ValidationFailed: "validation_failed",
77
+ BadJson: "bad_json",
78
+ WeakPassword: "weak_password",
79
+ EmailAddressInvalid: "email_address_invalid",
80
+ FeatureNotSupported: "feature_not_supported",
81
+ NotImplemented: "not_implemented",
82
+ OAuthProviderNotSupported: "oauth_provider_not_supported",
83
+ PhoneProviderDisabled: "phone_provider_disabled",
84
+ SsoProviderDisabled: "sso_provider_disabled",
85
+ AnonymousProviderDisabled: "anonymous_provider_disabled",
86
+ Web3ProviderDisabled: "web3_provider_disabled",
87
+ BadOAuthCallback: "bad_oauth_callback",
88
+ OAuthCallbackFailed: "oauth_callback_failed",
89
+ OverRequestRateLimit: "over_request_rate_limit",
90
+ OverEmailSendRateLimit: "over_email_send_rate_limit",
91
+ OverSmsSendRateLimit: "over_sms_send_rate_limit",
92
+ UnexpectedFailure: "unexpected_failure",
93
+ InternalError: "internal_error",
94
+ IdentityNotFound: "identity_not_found",
95
+ UnknownError: "unknown_error"
96
+ };
97
+ /**
98
+ * Complete error definitions map
99
+ * Maps error codes to HTTP status codes and default messages
100
+ */
101
+ const ERROR_DEFINITIONS = {
102
+ [AuthErrorCode.BadJwt]: {
103
+ code: AuthErrorCode.BadJwt,
104
+ status: 401,
105
+ message: "Invalid or expired session token",
106
+ description: "The JWT token is malformed, expired, or has an invalid signature"
107
+ },
108
+ [AuthErrorCode.InvalidCredentials]: {
109
+ code: AuthErrorCode.InvalidCredentials,
110
+ status: 401,
111
+ message: "Invalid email or password",
112
+ description: "The provided credentials do not match any user account"
113
+ },
114
+ [AuthErrorCode.SessionExpired]: {
115
+ code: AuthErrorCode.SessionExpired,
116
+ status: 401,
117
+ message: "Session has expired",
118
+ description: "The user session has exceeded its timeout period"
119
+ },
120
+ [AuthErrorCode.SessionNotFound]: {
121
+ code: AuthErrorCode.SessionNotFound,
122
+ status: 401,
123
+ message: "No active session found",
124
+ description: "The user does not have an active session or the session was invalidated"
125
+ },
126
+ [AuthErrorCode.InvalidGrant]: {
127
+ code: AuthErrorCode.InvalidGrant,
128
+ status: 401,
129
+ message: "Invalid authorization grant",
130
+ description: "OAuth/OIDC grant validation failed"
131
+ },
132
+ [AuthErrorCode.UserNotFound]: {
133
+ code: AuthErrorCode.UserNotFound,
134
+ status: 404,
135
+ message: "User not found",
136
+ description: "No user exists with the provided identifier"
137
+ },
138
+ [AuthErrorCode.UserAlreadyExists]: {
139
+ code: AuthErrorCode.UserAlreadyExists,
140
+ status: 409,
141
+ message: "User already exists",
142
+ description: "A user with this email or phone number is already registered"
143
+ },
144
+ [AuthErrorCode.EmailExists]: {
145
+ code: AuthErrorCode.EmailExists,
146
+ status: 409,
147
+ message: "Email address already registered",
148
+ description: "This email address is already associated with an account"
149
+ },
150
+ [AuthErrorCode.PhoneExists]: {
151
+ code: AuthErrorCode.PhoneExists,
152
+ status: 409,
153
+ message: "Phone number already registered",
154
+ description: "This phone number is already associated with an account"
155
+ },
156
+ [AuthErrorCode.EmailNotConfirmed]: {
157
+ code: AuthErrorCode.EmailNotConfirmed,
158
+ status: 422,
159
+ message: "Email verification required",
160
+ description: "The user must verify their email before signing in"
161
+ },
162
+ [AuthErrorCode.PhoneNotConfirmed]: {
163
+ code: AuthErrorCode.PhoneNotConfirmed,
164
+ status: 422,
165
+ message: "Phone verification required",
166
+ description: "The user must verify their phone number before signing in"
167
+ },
168
+ [AuthErrorCode.ValidationFailed]: {
169
+ code: AuthErrorCode.ValidationFailed,
170
+ status: 400,
171
+ message: "Invalid request parameters",
172
+ description: "One or more request parameters are invalid or missing"
173
+ },
174
+ [AuthErrorCode.BadJson]: {
175
+ code: AuthErrorCode.BadJson,
176
+ status: 400,
177
+ message: "Invalid JSON in request body",
178
+ description: "The request body contains malformed JSON"
179
+ },
180
+ [AuthErrorCode.WeakPassword]: {
181
+ code: AuthErrorCode.WeakPassword,
182
+ status: 400,
183
+ message: "Password does not meet security requirements",
184
+ description: "The password is too weak or does not meet complexity requirements"
185
+ },
186
+ [AuthErrorCode.EmailAddressInvalid]: {
187
+ code: AuthErrorCode.EmailAddressInvalid,
188
+ status: 400,
189
+ message: "Invalid email address format",
190
+ description: "The provided email address is not in a valid format"
191
+ },
192
+ [AuthErrorCode.FeatureNotSupported]: {
193
+ code: AuthErrorCode.FeatureNotSupported,
194
+ status: 403,
195
+ message: "Feature not available",
196
+ description: "This feature is not supported in the current configuration"
197
+ },
198
+ [AuthErrorCode.NotImplemented]: {
199
+ code: AuthErrorCode.NotImplemented,
200
+ status: 501,
201
+ message: "Feature not implemented",
202
+ description: "This feature has not been implemented yet"
203
+ },
204
+ [AuthErrorCode.OAuthProviderNotSupported]: {
205
+ code: AuthErrorCode.OAuthProviderNotSupported,
206
+ status: 403,
207
+ message: "OAuth provider not supported",
208
+ description: "The requested OAuth provider is not enabled"
209
+ },
210
+ [AuthErrorCode.PhoneProviderDisabled]: {
211
+ code: AuthErrorCode.PhoneProviderDisabled,
212
+ status: 403,
213
+ message: "Phone authentication not available",
214
+ description: "Phone number authentication is not enabled"
215
+ },
216
+ [AuthErrorCode.SsoProviderDisabled]: {
217
+ code: AuthErrorCode.SsoProviderDisabled,
218
+ status: 403,
219
+ message: "SSO not supported",
220
+ description: "Enterprise SSO authentication is not available"
221
+ },
222
+ [AuthErrorCode.AnonymousProviderDisabled]: {
223
+ code: AuthErrorCode.AnonymousProviderDisabled,
224
+ status: 403,
225
+ message: "Anonymous authentication not available",
226
+ description: "Anonymous sign-in is not enabled"
227
+ },
228
+ [AuthErrorCode.Web3ProviderDisabled]: {
229
+ code: AuthErrorCode.Web3ProviderDisabled,
230
+ status: 403,
231
+ message: "Web3 authentication not supported",
232
+ description: "Web3/blockchain authentication is not available"
233
+ },
234
+ [AuthErrorCode.BadOAuthCallback]: {
235
+ code: AuthErrorCode.BadOAuthCallback,
236
+ status: 400,
237
+ message: "Invalid OAuth callback",
238
+ description: "The OAuth callback request is missing required parameters"
239
+ },
240
+ [AuthErrorCode.OAuthCallbackFailed]: {
241
+ code: AuthErrorCode.OAuthCallbackFailed,
242
+ status: 500,
243
+ message: "OAuth authentication failed",
244
+ description: "The OAuth callback completed but no session was created"
245
+ },
246
+ [AuthErrorCode.OverRequestRateLimit]: {
247
+ code: AuthErrorCode.OverRequestRateLimit,
248
+ status: 429,
249
+ message: "Too many requests",
250
+ description: "Rate limit exceeded. Please try again later"
251
+ },
252
+ [AuthErrorCode.OverEmailSendRateLimit]: {
253
+ code: AuthErrorCode.OverEmailSendRateLimit,
254
+ status: 429,
255
+ message: "Too many email requests",
256
+ description: "Too many emails sent. Please wait before trying again"
257
+ },
258
+ [AuthErrorCode.OverSmsSendRateLimit]: {
259
+ code: AuthErrorCode.OverSmsSendRateLimit,
260
+ status: 429,
261
+ message: "Too many SMS requests",
262
+ description: "Too many SMS messages sent. Please wait before trying again"
263
+ },
264
+ [AuthErrorCode.UnexpectedFailure]: {
265
+ code: AuthErrorCode.UnexpectedFailure,
266
+ status: 500,
267
+ message: "An unexpected error occurred",
268
+ description: "The server encountered an unexpected condition"
269
+ },
270
+ [AuthErrorCode.InternalError]: {
271
+ code: AuthErrorCode.InternalError,
272
+ status: 500,
273
+ message: "Internal server error",
274
+ description: "An internal error occurred while processing the request"
275
+ },
276
+ [AuthErrorCode.IdentityNotFound]: {
277
+ code: AuthErrorCode.IdentityNotFound,
278
+ status: 404,
279
+ message: "Identity not found",
280
+ description: "The requested user identity does not exist"
281
+ },
282
+ [AuthErrorCode.UnknownError]: {
283
+ code: AuthErrorCode.UnknownError,
284
+ status: 500,
285
+ message: "An unknown error occurred",
286
+ description: "The error could not be categorized"
287
+ }
288
+ };
289
+ /**
290
+ * Helper to get error definition by code
291
+ */
292
+ function getErrorDefinition(code) {
293
+ return ERROR_DEFINITIONS[code];
294
+ }
295
+ /**
296
+ * Create an AuthError or AuthApiError with proper status and message
297
+ *
298
+ * @param code - The error code from AuthErrorCode
299
+ * @param customMessage - Optional custom message (defaults to error definition message)
300
+ * @returns AuthError for 5xx errors, AuthApiError for 4xx errors
301
+ */
302
+ function createAuthError(code, customMessage) {
303
+ const def = getErrorDefinition(code);
304
+ const message = customMessage || def.message;
305
+ const status = def.status;
306
+ if (status !== 500 && status !== 501 && status !== 503) return new AuthApiError(message, status, def.code);
307
+ return new AuthError(message, status, def.code);
308
+ }
309
+
310
+ //#endregion
311
+ //#region src/adapters/supabase/errors/mappings.ts
312
+ /**
313
+ * Maps Better Auth error codes to AuthErrorCode
314
+ * Based on Better Auth SDK error codes
315
+ *
316
+ * @see https://www.better-auth.com/docs/concepts/error-handling
317
+ */
318
+ const BETTER_AUTH_ERROR_MAP = {
319
+ "INVALID_EMAIL_OR_PASSWORD": AuthErrorCode.InvalidCredentials,
320
+ "INVALID_PASSWORD": AuthErrorCode.InvalidCredentials,
321
+ "INVALID_EMAIL": AuthErrorCode.EmailAddressInvalid,
322
+ "USER_NOT_FOUND": AuthErrorCode.UserNotFound,
323
+ "INVALID_TOKEN": AuthErrorCode.BadJwt,
324
+ "SESSION_EXPIRED": AuthErrorCode.SessionExpired,
325
+ "FAILED_TO_GET_SESSION": AuthErrorCode.SessionNotFound,
326
+ "USER_ALREADY_EXISTS": AuthErrorCode.UserAlreadyExists,
327
+ "EMAIL_NOT_VERIFIED": AuthErrorCode.EmailNotConfirmed,
328
+ "USER_EMAIL_NOT_FOUND": AuthErrorCode.UserNotFound,
329
+ "PASSWORD_TOO_SHORT": AuthErrorCode.WeakPassword,
330
+ "PASSWORD_TOO_LONG": AuthErrorCode.WeakPassword,
331
+ "USER_ALREADY_HAS_PASSWORD": AuthErrorCode.ValidationFailed,
332
+ "CREDENTIAL_ACCOUNT_NOT_FOUND": AuthErrorCode.IdentityNotFound,
333
+ "FAILED_TO_UNLINK_LAST_ACCOUNT": AuthErrorCode.ValidationFailed,
334
+ "ACCOUNT_NOT_FOUND": AuthErrorCode.IdentityNotFound,
335
+ "SOCIAL_ACCOUNT_ALREADY_LINKED": AuthErrorCode.ValidationFailed,
336
+ "PROVIDER_NOT_FOUND": AuthErrorCode.OAuthProviderNotSupported,
337
+ "ID_TOKEN_NOT_SUPPORTED": AuthErrorCode.FeatureNotSupported,
338
+ "FAILED_TO_CREATE_USER": AuthErrorCode.InternalError,
339
+ "FAILED_TO_CREATE_SESSION": AuthErrorCode.InternalError,
340
+ "FAILED_TO_UPDATE_USER": AuthErrorCode.InternalError,
341
+ "EMAIL_CAN_NOT_BE_UPDATED": AuthErrorCode.FeatureNotSupported
342
+ };
343
+ /**
344
+ * Maps HTTP status codes from Better Auth to AuthErrorCode
345
+ */
346
+ const STATUS_CODE_ERROR_MAP = {
347
+ 400: AuthErrorCode.ValidationFailed,
348
+ 401: AuthErrorCode.BadJwt,
349
+ 403: AuthErrorCode.FeatureNotSupported,
350
+ 404: AuthErrorCode.UserNotFound,
351
+ 409: AuthErrorCode.UserAlreadyExists,
352
+ 422: AuthErrorCode.ValidationFailed,
353
+ 429: AuthErrorCode.OverRequestRateLimit,
354
+ 500: AuthErrorCode.UnexpectedFailure,
355
+ 501: AuthErrorCode.NotImplemented,
356
+ 503: AuthErrorCode.FeatureNotSupported
357
+ };
358
+
359
+ //#endregion
360
+ //#region src/core/better-auth-helpers.ts
361
+ /**
362
+ * Normalize Better Auth errors to standard AuthError format
363
+ *
364
+ * Handles three error formats:
365
+ * 1. BetterFetchError: { status, statusText, message?, code? }
366
+ * 2. BetterAuthErrorResponse: { status, statusText, message?, code? }
367
+ * 3. Standard Error: { message, name, stack }
368
+ *
369
+ * Maps Better Auth errors to appropriate AuthError/AuthApiError with:
370
+ * - Correct HTTP status codes
371
+ * - Standard error codes (snake_case)
372
+ * - User-friendly, security-conscious messages
373
+ */
374
+ function normalizeBetterAuthError(error) {
375
+ if (error !== null && error !== void 0 && typeof error === "object" && "status" in error && "statusText" in error) {
376
+ const betterError = error;
377
+ const status = betterError.status;
378
+ if ("code" in betterError && betterError.code && typeof betterError.code === "string") {
379
+ const mappedCode = BETTER_AUTH_ERROR_MAP[betterError.code];
380
+ if (mappedCode) {
381
+ const def$2 = getErrorDefinition(mappedCode);
382
+ return createNormalizedError(def$2.message, def$2.status, def$2.code, status);
383
+ }
384
+ }
385
+ const def$1 = getErrorDefinition(mapStatusCodeToErrorCode(status, betterError.message || betterError.statusText));
386
+ return createNormalizedError(betterError.message || def$1.message, status, def$1.code, status);
387
+ }
388
+ if (error instanceof Error) {
389
+ const def$1 = getErrorDefinition(mapMessageToErrorCode(error.message));
390
+ return createNormalizedError(error.message || def$1.message, def$1.status, def$1.code, def$1.status);
391
+ }
392
+ const def = getErrorDefinition(AuthErrorCode.UnknownError);
393
+ return new AuthError(def.message, def.status, def.code);
394
+ }
395
+ /**
396
+ * Map HTTP status code to AuthErrorCode
397
+ * Uses message content for disambiguation when status code is ambiguous
398
+ */
399
+ function mapStatusCodeToErrorCode(status, message) {
400
+ const lowerMessage = message?.toLowerCase() || "";
401
+ switch (status) {
402
+ case 401:
403
+ if (lowerMessage.includes("token") || lowerMessage.includes("jwt")) return AuthErrorCode.BadJwt;
404
+ if (lowerMessage.includes("session")) return AuthErrorCode.SessionNotFound;
405
+ if (lowerMessage.includes("expired")) return AuthErrorCode.SessionExpired;
406
+ return AuthErrorCode.InvalidCredentials;
407
+ case 404:
408
+ if (lowerMessage.includes("identity") || lowerMessage.includes("account")) return AuthErrorCode.IdentityNotFound;
409
+ if (lowerMessage.includes("session")) return AuthErrorCode.SessionNotFound;
410
+ return AuthErrorCode.UserNotFound;
411
+ case 409:
412
+ if (lowerMessage.includes("email")) return AuthErrorCode.EmailExists;
413
+ if (lowerMessage.includes("phone")) return AuthErrorCode.PhoneExists;
414
+ return AuthErrorCode.UserAlreadyExists;
415
+ case 422:
416
+ if (lowerMessage.includes("email") && lowerMessage.includes("confirm")) return AuthErrorCode.EmailNotConfirmed;
417
+ if (lowerMessage.includes("phone") && lowerMessage.includes("confirm")) return AuthErrorCode.PhoneNotConfirmed;
418
+ return AuthErrorCode.ValidationFailed;
419
+ case 429:
420
+ if (lowerMessage.includes("email")) return AuthErrorCode.OverEmailSendRateLimit;
421
+ if (lowerMessage.includes("sms") || lowerMessage.includes("phone")) return AuthErrorCode.OverSmsSendRateLimit;
422
+ return AuthErrorCode.OverRequestRateLimit;
423
+ case 400:
424
+ if (lowerMessage.includes("password") && lowerMessage.includes("weak")) return AuthErrorCode.WeakPassword;
425
+ if (lowerMessage.includes("email") && lowerMessage.includes("invalid")) return AuthErrorCode.EmailAddressInvalid;
426
+ if (lowerMessage.includes("json")) return AuthErrorCode.BadJson;
427
+ if (lowerMessage.includes("oauth") || lowerMessage.includes("callback")) return AuthErrorCode.BadOAuthCallback;
428
+ return AuthErrorCode.ValidationFailed;
429
+ case 403:
430
+ if (lowerMessage.includes("provider") || lowerMessage.includes("oauth")) return AuthErrorCode.OAuthProviderNotSupported;
431
+ if (lowerMessage.includes("phone")) return AuthErrorCode.PhoneProviderDisabled;
432
+ if (lowerMessage.includes("sso")) return AuthErrorCode.SsoProviderDisabled;
433
+ return AuthErrorCode.FeatureNotSupported;
434
+ case 501: return AuthErrorCode.NotImplemented;
435
+ case 503: return AuthErrorCode.FeatureNotSupported;
436
+ default:
437
+ if (lowerMessage.includes("oauth")) return AuthErrorCode.OAuthCallbackFailed;
438
+ return AuthErrorCode.UnexpectedFailure;
439
+ }
440
+ }
441
+ /**
442
+ * Map error message content to AuthErrorCode
443
+ * Used as fallback when status code is not available
444
+ */
445
+ function mapMessageToErrorCode(message) {
446
+ const lower = message.toLowerCase();
447
+ if (lower.includes("invalid login") || lower.includes("incorrect") || lower.includes("wrong password")) return AuthErrorCode.InvalidCredentials;
448
+ if (lower.includes("token") && (lower.includes("invalid") || lower.includes("expired"))) return AuthErrorCode.BadJwt;
449
+ if (lower.includes("session") && lower.includes("expired")) return AuthErrorCode.SessionExpired;
450
+ if (lower.includes("session") && lower.includes("not found")) return AuthErrorCode.SessionNotFound;
451
+ if (lower.includes("already exists") || lower.includes("already registered")) return AuthErrorCode.UserAlreadyExists;
452
+ if (lower.includes("not found") && lower.includes("user")) return AuthErrorCode.UserNotFound;
453
+ if (lower.includes("not found") && lower.includes("identity")) return AuthErrorCode.IdentityNotFound;
454
+ if (lower.includes("email") && lower.includes("not confirmed")) return AuthErrorCode.EmailNotConfirmed;
455
+ if (lower.includes("phone") && lower.includes("not confirmed")) return AuthErrorCode.PhoneNotConfirmed;
456
+ if (lower.includes("weak password") || lower.includes("password") && lower.includes("requirements")) return AuthErrorCode.WeakPassword;
457
+ if (lower.includes("email") && lower.includes("invalid")) return AuthErrorCode.EmailAddressInvalid;
458
+ if (lower.includes("rate limit") || lower.includes("too many requests")) return AuthErrorCode.OverRequestRateLimit;
459
+ if (lower.includes("oauth") && lower.includes("failed")) return AuthErrorCode.OAuthCallbackFailed;
460
+ if (lower.includes("provider") && lower.includes("not supported")) return AuthErrorCode.OAuthProviderNotSupported;
461
+ return AuthErrorCode.UnexpectedFailure;
462
+ }
463
+ /**
464
+ * Create normalized error with correct type (AuthError vs AuthApiError)
465
+ * Uses AuthApiError for non-500 status codes (API/client errors)
466
+ * Uses AuthError for 500 status codes (server errors)
467
+ */
468
+ function createNormalizedError(message, targetStatus, code, _originalStatus) {
469
+ const status = targetStatus;
470
+ if (status !== 500 && status !== 501 && status !== 503) return new AuthApiError(message, status, code);
471
+ return new AuthError(message, status, code);
472
+ }
473
+ /**
474
+ * Map Better Auth session to Session format
475
+ */
476
+ function mapBetterAuthSession(betterAuthSession, betterAuthUser) {
477
+ if (!betterAuthSession || !betterAuthUser) return null;
478
+ let expiresAt;
479
+ if (typeof betterAuthSession.expiresAt === "string") expiresAt = Math.floor(new Date(betterAuthSession.expiresAt).getTime() / 1e3);
480
+ else if (typeof betterAuthSession.expiresAt === "object" && betterAuthSession.expiresAt instanceof Date) expiresAt = Math.floor(betterAuthSession.expiresAt.getTime() / 1e3);
481
+ else expiresAt = Math.floor(Date.now() / 1e3) + Math.floor(DEFAULT_SESSION_EXPIRY_MS / 1e3);
482
+ const now = Math.floor(Date.now() / 1e3);
483
+ const expiresIn = Math.max(0, expiresAt - now);
484
+ return {
485
+ access_token: betterAuthSession.token,
486
+ refresh_token: "",
487
+ expires_at: expiresAt,
488
+ expires_in: expiresIn,
489
+ token_type: "bearer",
490
+ user: mapBetterAuthUser(betterAuthUser)
491
+ };
492
+ }
493
+ /**
494
+ * Map Better Auth user to User format
495
+ */
496
+ function mapBetterAuthUser(betterAuthUser) {
497
+ const createdAt = toISOString(betterAuthUser.createdAt);
498
+ const updatedAt = toISOString(betterAuthUser.updatedAt);
499
+ const userMetadata = {};
500
+ if (betterAuthUser.name) userMetadata.displayName = betterAuthUser.name;
501
+ if (betterAuthUser.image) userMetadata.profileImageUrl = betterAuthUser.image;
502
+ const userRecord = betterAuthUser;
503
+ for (const key of Object.keys(userRecord)) if (![
504
+ "id",
505
+ "email",
506
+ "emailVerified",
507
+ "name",
508
+ "image",
509
+ "createdAt",
510
+ "updatedAt"
511
+ ].includes(key)) userMetadata[key] = userRecord[key];
512
+ return {
513
+ id: betterAuthUser.id,
514
+ email: betterAuthUser.email || "",
515
+ email_confirmed_at: betterAuthUser.emailVerified ? createdAt : void 0,
516
+ phone: void 0,
517
+ confirmed_at: betterAuthUser.emailVerified ? createdAt : void 0,
518
+ last_sign_in_at: updatedAt,
519
+ app_metadata: {},
520
+ user_metadata: userMetadata,
521
+ identities: [],
522
+ created_at: createdAt,
523
+ updated_at: updatedAt,
524
+ aud: "authenticated",
525
+ role: "authenticated"
526
+ };
527
+ }
528
+ function mapBetterAuthIdentity(betterAuthUserIdentityAccount, accountInfoData) {
529
+ return {
530
+ id: betterAuthUserIdentityAccount.id,
531
+ user_id: betterAuthUserIdentityAccount.id,
532
+ identity_id: betterAuthUserIdentityAccount.accountId,
533
+ provider: betterAuthUserIdentityAccount.providerId,
534
+ created_at: toISOString(betterAuthUserIdentityAccount.createdAt),
535
+ updated_at: toISOString(betterAuthUserIdentityAccount.updatedAt),
536
+ last_sign_in_at: toISOString(betterAuthUserIdentityAccount.updatedAt),
537
+ identity_data: accountInfoData ? {
538
+ provider: betterAuthUserIdentityAccount.providerId,
539
+ provider_id: betterAuthUserIdentityAccount.accountId,
540
+ scopes: betterAuthUserIdentityAccount.scopes,
541
+ email: accountInfoData.data.email,
542
+ name: accountInfoData.data.user.name,
543
+ picture: accountInfoData.data.user.picture,
544
+ email_verified: accountInfoData.data.user.email_verified,
545
+ ...accountInfoData.data
546
+ } : {
547
+ provider: betterAuthUserIdentityAccount.providerId,
548
+ provider_id: betterAuthUserIdentityAccount.accountId,
549
+ scopes: betterAuthUserIdentityAccount.scopes
550
+ }
551
+ };
552
+ }
553
+
554
+ //#endregion
555
+ //#region src/adapters/supabase/supabase-adapter.ts
556
+ /**
557
+ * Duck-type check for Better Auth API errors.
558
+ * Replaces `instanceof APIError` to avoid importing server-side code.
559
+ */
560
+ function isBetterAuthAPIError(error) {
561
+ return error !== null && typeof error === "object" && "status" in error && typeof error.status === "number";
562
+ }
563
+ /**
564
+ * Internal implementation class - use SupabaseAuthAdapter factory function instead
565
+ */
566
+ var SupabaseAuthAdapterImpl = class extends NeonAuthAdapterCore {
567
+ admin = void 0;
568
+ mfa = void 0;
569
+ oauth = void 0;
570
+ _betterAuth;
571
+ _stateChangeEmitters = /* @__PURE__ */ new Map();
572
+ constructor(betterAuthClientOptions) {
573
+ super(betterAuthClientOptions);
574
+ this._betterAuth = createAuthClient(this.betterAuthOptions);
575
+ /**
576
+ * useSession() - Automatic Session Management
577
+ *
578
+ * Enabled by Default:
579
+ * - ✅ Refetch on Window Focus: Automatically refetches session when user returns to the tab
580
+ * - ✅ Cross-Tab Sync: Syncs session state across all browser tabs (sign out in one = sign out in all)
581
+ * - ✅ Online/Offline Detection: Refetches session when network connection is restored
582
+ * - ❌ Interval Polling: Disabled (refetchInterval: 0)
583
+ *
584
+ * Returns:
585
+ * - data: Session object (user + session)
586
+ * - isPending: Loading state
587
+ * - isRefetching: Refetch in progress
588
+ * - error: Error object if any
589
+ * - refetch(): Manual refetch function
590
+ *
591
+ * Customize with:
592
+ * createAuthClient({
593
+ * sessionOptions: {
594
+ * refetchOnWindowFocus: true, // default
595
+ * refetchInterval: 0, // default (seconds, 0 = off)
596
+ * refetchWhenOffline: false // default
597
+ * }
598
+ * })
599
+ */
600
+ this._betterAuth.useSession.subscribe((value) => {
601
+ if (!value.data?.session || !value.data?.user) {
602
+ BETTER_AUTH_METHODS_CACHE.clearSessionCache();
603
+ return;
604
+ }
605
+ });
606
+ getGlobalBroadcastChannel().subscribe((message) => {
607
+ if (message.clientId === CURRENT_TAB_CLIENT_ID) return;
608
+ if (message.data && "sessionData" in message.data) {
609
+ const sessionData = message.data.sessionData;
610
+ const trigger = message.data.trigger;
611
+ if (sessionData) BETTER_AUTH_METHODS_CACHE.setCachedSession(sessionData);
612
+ else BETTER_AUTH_METHODS_CACHE.clearSessionCache();
613
+ const supabaseSession = sessionData ? mapBetterAuthSession(sessionData.session, sessionData.user) : null;
614
+ const promises = [...this._stateChangeEmitters.values()].map((subscription) => {
615
+ try {
616
+ return Promise.resolve(subscription.callback(trigger, supabaseSession));
617
+ } catch {
618
+ return Promise.resolve();
619
+ }
620
+ });
621
+ Promise.allSettled(promises);
622
+ }
623
+ });
624
+ }
625
+ getBetterAuthInstance() {
626
+ return this._betterAuth;
627
+ }
628
+ async getJWTToken() {
629
+ const session = await this.getSession();
630
+ if (session.error) return null;
631
+ return session.data.session?.access_token ?? null;
632
+ }
633
+ initialize = async () => {
634
+ try {
635
+ const session = await this.getSession();
636
+ if (session.error) throw session.error;
637
+ return {
638
+ data: session.data,
639
+ error: null
640
+ };
641
+ } catch (error) {
642
+ if (isAuthError(error)) return {
643
+ data: { session: null },
644
+ error
645
+ };
646
+ if (isBetterAuthAPIError(error)) return {
647
+ data: { session: null },
648
+ error: normalizeBetterAuthError(error)
649
+ };
650
+ throw error;
651
+ }
652
+ };
653
+ async getSession(options) {
654
+ try {
655
+ const currentSession = await this._betterAuth.getSession(options?.forceFetch ? { fetchOptions: { headers: { "X-Force-Fetch": "true" } } } : void 0);
656
+ if (!currentSession.data?.session) return {
657
+ data: { session: null },
658
+ error: null
659
+ };
660
+ return {
661
+ data: { session: mapBetterAuthSession(currentSession.data.session, currentSession.data.user) },
662
+ error: null
663
+ };
664
+ } catch (error) {
665
+ if (isAuthError(error)) return {
666
+ data: { session: null },
667
+ error
668
+ };
669
+ if (isBetterAuthAPIError(error)) return {
670
+ data: { session: null },
671
+ error: normalizeBetterAuthError(error)
672
+ };
673
+ throw error;
674
+ }
675
+ }
676
+ refreshSession = async () => {
677
+ try {
678
+ const sessionResult = await this.getSession();
679
+ if (sessionResult.error) throw sessionResult.error;
680
+ return {
681
+ data: {
682
+ user: sessionResult.data.session?.user ?? null,
683
+ session: sessionResult.data.session
684
+ },
685
+ error: null
686
+ };
687
+ } catch (error) {
688
+ if (isAuthError(error)) return {
689
+ data: {
690
+ user: null,
691
+ session: null
692
+ },
693
+ error
694
+ };
695
+ if (isBetterAuthAPIError(error)) return {
696
+ data: {
697
+ user: null,
698
+ session: null
699
+ },
700
+ error: normalizeBetterAuthError(error)
701
+ };
702
+ throw error;
703
+ }
704
+ };
705
+ setSession = async () => {
706
+ return {
707
+ data: {
708
+ user: null,
709
+ session: null
710
+ },
711
+ error: createAuthError(AuthErrorCode.NotImplemented, "setSession() is not supported by Better Auth. Use signInWithPassword() instead.")
712
+ };
713
+ };
714
+ signUp = async (credentials) => {
715
+ try {
716
+ if ("email" in credentials && credentials.email && credentials.password) {
717
+ const displayName = credentials.options?.data && "displayName" in credentials.options.data && typeof credentials.options.data.displayName === "string" ? credentials.options.data.displayName : "";
718
+ const result = await this._betterAuth.signUp.email({
719
+ email: credentials.email,
720
+ password: credentials.password,
721
+ name: displayName,
722
+ callbackURL: credentials.options?.emailRedirectTo,
723
+ ...credentials.options?.data
724
+ });
725
+ if (result.error) throw normalizeBetterAuthError(result.error);
726
+ const sessionResult = await this.getSession();
727
+ if (!sessionResult.data.session?.user) throw createAuthError(AuthErrorCode.SessionNotFound, "Failed to retrieve user session");
728
+ return {
729
+ data: {
730
+ user: sessionResult.data.session.user,
731
+ session: sessionResult.data.session
732
+ },
733
+ error: null
734
+ };
735
+ } else if ("phone" in credentials && credentials.phone) throw createAuthError(AuthErrorCode.PhoneProviderDisabled, "Phone sign-up not supported");
736
+ else throw createAuthError(AuthErrorCode.ValidationFailed, "Invalid credentials format");
737
+ } catch (error) {
738
+ if (isAuthError(error)) return {
739
+ data: {
740
+ user: null,
741
+ session: null
742
+ },
743
+ error
744
+ };
745
+ if (isBetterAuthAPIError(error)) return {
746
+ data: {
747
+ user: null,
748
+ session: null
749
+ },
750
+ error: normalizeBetterAuthError(error)
751
+ };
752
+ throw error;
753
+ }
754
+ };
755
+ signInAnonymously = async (credentials) => {
756
+ try {
757
+ const result = await this._betterAuth.signIn.anonymous({ query: credentials?.options?.data });
758
+ if (result.error) throw normalizeBetterAuthError(result.error);
759
+ const sessionResult = await this.getSession();
760
+ if (!sessionResult.data.session?.user) throw createAuthError(AuthErrorCode.SessionNotFound, "Failed to retrieve user session");
761
+ return {
762
+ data: {
763
+ user: sessionResult.data.session.user,
764
+ session: sessionResult.data.session
765
+ },
766
+ error: null
767
+ };
768
+ } catch (error) {
769
+ if (isAuthError(error)) return {
770
+ data: {
771
+ user: null,
772
+ session: null
773
+ },
774
+ error
775
+ };
776
+ if (isBetterAuthAPIError(error)) return {
777
+ data: {
778
+ user: null,
779
+ session: null
780
+ },
781
+ error: normalizeBetterAuthError(error)
782
+ };
783
+ throw error;
784
+ }
785
+ };
786
+ signInWithPassword = async (credentials) => {
787
+ try {
788
+ if ("email" in credentials && credentials.email) {
789
+ const result = await this._betterAuth.signIn.email({
790
+ email: credentials.email,
791
+ password: credentials.password
792
+ });
793
+ if (result.error) throw normalizeBetterAuthError(result.error);
794
+ const sessionResult = await this.getSession();
795
+ if (!sessionResult.data.session?.user) throw createAuthError(AuthErrorCode.SessionNotFound, "Failed to retrieve user session");
796
+ return {
797
+ data: {
798
+ user: sessionResult.data.session.user,
799
+ session: sessionResult.data.session
800
+ },
801
+ error: null
802
+ };
803
+ } else if ("phone" in credentials && credentials.phone) throw createAuthError(AuthErrorCode.PhoneProviderDisabled, "Phone sign-in not supported");
804
+ else throw createAuthError(AuthErrorCode.ValidationFailed, "Invalid credentials format");
805
+ } catch (error) {
806
+ if (isAuthError(error)) return {
807
+ data: {
808
+ user: null,
809
+ session: null
810
+ },
811
+ error
812
+ };
813
+ if (isBetterAuthAPIError(error)) return {
814
+ data: {
815
+ user: null,
816
+ session: null
817
+ },
818
+ error: normalizeBetterAuthError(error)
819
+ };
820
+ throw error;
821
+ }
822
+ };
823
+ signInWithOAuth = async (credentials) => {
824
+ try {
825
+ const { provider, options } = credentials;
826
+ await this._betterAuth.signIn.social({
827
+ provider,
828
+ scopes: options?.scopes?.split(" "),
829
+ disableRedirect: options?.skipBrowserRedirect,
830
+ callbackURL: options?.redirectTo || (globalThis.window === void 0 ? "" : globalThis.location.origin)
831
+ });
832
+ return {
833
+ data: {
834
+ provider,
835
+ url: options?.redirectTo || (globalThis.window === void 0 ? "" : globalThis.location.origin)
836
+ },
837
+ error: null
838
+ };
839
+ } catch (error) {
840
+ if (isAuthError(error)) return {
841
+ data: {
842
+ provider: credentials.provider,
843
+ url: null
844
+ },
845
+ error
846
+ };
847
+ if (isBetterAuthAPIError(error)) return {
848
+ data: {
849
+ provider: credentials.provider,
850
+ url: null
851
+ },
852
+ error: normalizeBetterAuthError(error)
853
+ };
854
+ throw error;
855
+ }
856
+ };
857
+ signInWithOtp = async (credentials) => {
858
+ try {
859
+ if ("email" in credentials) {
860
+ await this._betterAuth.emailOtp.sendVerificationOtp({
861
+ email: credentials.email,
862
+ type: "sign-in"
863
+ });
864
+ return {
865
+ data: {
866
+ user: null,
867
+ session: null,
868
+ messageId: void 0
869
+ },
870
+ error: null
871
+ };
872
+ }
873
+ throw createAuthError(AuthErrorCode.NotImplemented, `We haven't implemented this type of otp authentication.`);
874
+ } catch (error) {
875
+ if (isAuthError(error)) return {
876
+ data: {
877
+ user: null,
878
+ session: null,
879
+ messageId: void 0
880
+ },
881
+ error
882
+ };
883
+ if (isBetterAuthAPIError(error)) return {
884
+ data: {
885
+ user: null,
886
+ session: null,
887
+ messageId: void 0
888
+ },
889
+ error: normalizeBetterAuthError(error)
890
+ };
891
+ throw error;
892
+ }
893
+ };
894
+ signInWithIdToken = async (credentials) => {
895
+ try {
896
+ const result = await this._betterAuth.signIn.social({
897
+ provider: credentials.provider,
898
+ idToken: {
899
+ token: credentials.token,
900
+ accessToken: credentials.access_token,
901
+ nonce: credentials.nonce
902
+ }
903
+ });
904
+ if (result.error) throw normalizeBetterAuthError(result.error);
905
+ if (!("user" in result.data) || !result.data.user) throw createAuthError(AuthErrorCode.OAuthCallbackFailed, "Failed to sign in with ID token");
906
+ const session = await this.getSession();
907
+ if (session.error || !session.data.session) throw session.error || createAuthError(AuthErrorCode.SessionNotFound, "Failed to get session");
908
+ return {
909
+ data: {
910
+ user: session.data.session.user,
911
+ session: session.data.session
912
+ },
913
+ error: null
914
+ };
915
+ } catch (error) {
916
+ if (isAuthError(error)) return {
917
+ data: {
918
+ user: null,
919
+ session: null
920
+ },
921
+ error
922
+ };
923
+ if (isBetterAuthAPIError(error)) return {
924
+ data: {
925
+ user: null,
926
+ session: null
927
+ },
928
+ error: normalizeBetterAuthError(error)
929
+ };
930
+ throw error;
931
+ }
932
+ };
933
+ signInWithSSO = async (params) => {
934
+ const attemptedWith = "providerId" in params ? `provider ID: ${params.providerId}` : `domain: ${"domain" in params ? params.domain : "unknown"}`;
935
+ return {
936
+ data: null,
937
+ error: createAuthError(AuthErrorCode.SsoProviderDisabled, `Better Auth does not support enterprise SAML SSO. Attempted with ${attemptedWith}. Use signInWithOAuth() for OAuth providers instead.`)
938
+ };
939
+ };
940
+ signInWithWeb3 = async (credentials) => {
941
+ const attemptedChain = credentials.chain;
942
+ return {
943
+ data: {
944
+ user: null,
945
+ session: null
946
+ },
947
+ error: createAuthError(AuthErrorCode.Web3ProviderDisabled, `Better Auth does not support Web3 authentication. Attempted with chain: ${attemptedChain}. Supported: OAuth, email/password, magic link.`)
948
+ };
949
+ };
950
+ signOut = async () => {
951
+ try {
952
+ const result = await this._betterAuth.signOut();
953
+ if (result.error) throw normalizeBetterAuthError(result.error);
954
+ return { error: null };
955
+ } catch (error) {
956
+ if (isAuthError(error)) return { error };
957
+ if (isBetterAuthAPIError(error)) return { error: normalizeBetterAuthError(error) };
958
+ throw error;
959
+ }
960
+ };
961
+ getUser = async () => {
962
+ try {
963
+ const sessionResult = await this.getSession();
964
+ if (sessionResult.error || !sessionResult.data.session) throw sessionResult.error || createAuthError(AuthErrorCode.SessionNotFound, "No user session found");
965
+ return {
966
+ data: { user: sessionResult.data.session.user },
967
+ error: null
968
+ };
969
+ } catch (error) {
970
+ if (isAuthError(error)) return {
971
+ data: { user: null },
972
+ error
973
+ };
974
+ if (isBetterAuthAPIError(error)) return {
975
+ data: { user: null },
976
+ error: normalizeBetterAuthError(error)
977
+ };
978
+ throw error;
979
+ }
980
+ };
981
+ getClaims = async (jwtArg) => {
982
+ try {
983
+ let jwt = jwtArg;
984
+ if (!jwt) {
985
+ const sessionResult = await this.getSession();
986
+ if (sessionResult.error || !sessionResult.data?.session) throw sessionResult.error || createAuthError(AuthErrorCode.SessionNotFound, "No user session found");
987
+ jwt = sessionResult.data.session.access_token;
988
+ }
989
+ if (!jwt) throw createAuthError(AuthErrorCode.SessionNotFound, "No access token found");
990
+ if (jwt.split(".").length !== 3) throw createAuthError(AuthErrorCode.BadJwt, "Invalid token format");
991
+ return {
992
+ data: {
993
+ header: decodeProtectedHeader(jwt),
994
+ claims: decodeJwt(jwt),
995
+ signature: base64url.decode(jwt.split(".")[2])
996
+ },
997
+ error: null
998
+ };
999
+ } catch (error) {
1000
+ if (isAuthError(error)) return {
1001
+ data: null,
1002
+ error
1003
+ };
1004
+ if (isBetterAuthAPIError(error)) return {
1005
+ data: null,
1006
+ error: normalizeBetterAuthError(error)
1007
+ };
1008
+ throw error;
1009
+ }
1010
+ };
1011
+ updateUser = async (attributes) => {
1012
+ try {
1013
+ if (attributes.password) throw createAuthError(AuthErrorCode.FeatureNotSupported, "The password cannot be updated through the updateUser method, use the changePassword method instead.");
1014
+ if (attributes.email) throw createAuthError(AuthErrorCode.FeatureNotSupported, "The email cannot be updated through the updateUser method, use the changeEmail method instead.");
1015
+ const result = await this._betterAuth.updateUser({ ...attributes.data });
1016
+ if (result.data?.status) throw createAuthError(AuthErrorCode.InternalError, "Failed to update user");
1017
+ if (result?.error) throw normalizeBetterAuthError(result.error);
1018
+ const updatedSessionResult = await this.getSession({ forceFetch: true });
1019
+ if (!updatedSessionResult.data.session) throw createAuthError(AuthErrorCode.SessionNotFound, "Failed to retrieve updated user");
1020
+ return {
1021
+ data: { user: updatedSessionResult.data.session.user },
1022
+ error: null
1023
+ };
1024
+ } catch (error) {
1025
+ if (isAuthError(error)) return {
1026
+ data: { user: null },
1027
+ error
1028
+ };
1029
+ if (isBetterAuthAPIError(error)) return {
1030
+ data: { user: null },
1031
+ error: normalizeBetterAuthError(error)
1032
+ };
1033
+ throw error;
1034
+ }
1035
+ };
1036
+ getUserIdentities = async () => {
1037
+ try {
1038
+ const sessionResult = await this.getSession();
1039
+ if (sessionResult.error || !sessionResult.data.session) throw sessionResult.error || createAuthError(AuthErrorCode.SessionNotFound, "No user session found");
1040
+ const result = await this._betterAuth.listAccounts();
1041
+ if (!result) throw createAuthError(AuthErrorCode.InternalError, "Failed to list accounts");
1042
+ if (result.error) throw normalizeBetterAuthError(result.error);
1043
+ const identitiesPromises = result.data.map(async (account) => {
1044
+ let accountInfo = null;
1045
+ try {
1046
+ accountInfo = (await this._betterAuth.accountInfo({ query: { accountId: account.accountId } })).data;
1047
+ } catch (error) {
1048
+ console.warn(`Failed to get account info for ${account.providerId}:`, error);
1049
+ }
1050
+ return mapBetterAuthIdentity(account, accountInfo ?? null);
1051
+ });
1052
+ return {
1053
+ data: { identities: await Promise.all(identitiesPromises) },
1054
+ error: null
1055
+ };
1056
+ } catch (error) {
1057
+ if (isAuthError(error)) return {
1058
+ data: null,
1059
+ error
1060
+ };
1061
+ if (isBetterAuthAPIError(error)) return {
1062
+ data: null,
1063
+ error: normalizeBetterAuthError(error)
1064
+ };
1065
+ throw error;
1066
+ }
1067
+ };
1068
+ linkIdentity = async (credentials) => {
1069
+ const provider = credentials.provider;
1070
+ try {
1071
+ const sessionResult = await this.getSession();
1072
+ if (sessionResult.error || !sessionResult.data.session) throw sessionResult.error || createAuthError(AuthErrorCode.SessionNotFound, "No user session found");
1073
+ if ("token" in credentials) {
1074
+ const result$1 = await this._betterAuth.linkSocial({
1075
+ provider,
1076
+ idToken: {
1077
+ token: credentials.token,
1078
+ accessToken: credentials.access_token,
1079
+ nonce: credentials.nonce
1080
+ }
1081
+ });
1082
+ if (result$1.error) throw normalizeBetterAuthError(result$1.error);
1083
+ return {
1084
+ data: {
1085
+ user: sessionResult.data.session.user,
1086
+ session: sessionResult.data.session,
1087
+ provider,
1088
+ url: result$1.data?.url
1089
+ },
1090
+ error: null
1091
+ };
1092
+ }
1093
+ const callbackURL = credentials.options?.redirectTo || (globalThis.window === void 0 ? "" : globalThis.location.origin);
1094
+ const scopes = credentials.options?.scopes?.split(" ").filter((s) => s.length > 0);
1095
+ const result = await this._betterAuth.linkSocial({
1096
+ provider,
1097
+ callbackURL,
1098
+ errorCallbackURL: callbackURL ? `${callbackURL}?error=linking-failed` : void 0,
1099
+ scopes
1100
+ });
1101
+ if (result.error) throw normalizeBetterAuthError(result.error);
1102
+ return {
1103
+ data: {
1104
+ provider,
1105
+ url: result.data?.url,
1106
+ user: sessionResult.data.session.user,
1107
+ session: sessionResult.data.session
1108
+ },
1109
+ error: null
1110
+ };
1111
+ } catch (error) {
1112
+ if (isAuthError(error)) return {
1113
+ data: {
1114
+ provider,
1115
+ url: null,
1116
+ user: null,
1117
+ session: null
1118
+ },
1119
+ error
1120
+ };
1121
+ if (isBetterAuthAPIError(error)) return {
1122
+ data: {
1123
+ provider,
1124
+ url: null,
1125
+ user: null,
1126
+ session: null
1127
+ },
1128
+ error: normalizeBetterAuthError(error)
1129
+ };
1130
+ throw error;
1131
+ }
1132
+ };
1133
+ unlinkIdentity = async (identity) => {
1134
+ try {
1135
+ const sessionResult = await this.getSession();
1136
+ if (sessionResult.error || !sessionResult.data.session) throw sessionResult.error || createAuthError(AuthErrorCode.SessionNotFound, "No user session found");
1137
+ const identities = await this.getUserIdentities();
1138
+ if (identities.error || !identities.data) throw identities.error || createAuthError(AuthErrorCode.InternalError, "Failed to fetch identities");
1139
+ const targetIdentity = identities.data.identities.find((i) => i.id === identity.identity_id);
1140
+ if (!targetIdentity) throw createAuthError(AuthErrorCode.IdentityNotFound, "Identity not found");
1141
+ const providerId = targetIdentity.provider;
1142
+ const accountId = targetIdentity.identity_id;
1143
+ const result = await this._betterAuth.unlinkAccount({
1144
+ providerId,
1145
+ accountId
1146
+ });
1147
+ if (result?.error) throw normalizeBetterAuthError(result.error);
1148
+ const updatedSession = await this.getSession({ forceFetch: true });
1149
+ if (updatedSession.data.session) BETTER_AUTH_METHODS_HOOKS["updateUser"].onSuccess(updatedSession.data.session);
1150
+ return {
1151
+ data: {},
1152
+ error: null
1153
+ };
1154
+ } catch (error) {
1155
+ if (isAuthError(error)) return {
1156
+ data: null,
1157
+ error
1158
+ };
1159
+ if (isBetterAuthAPIError(error)) return {
1160
+ data: null,
1161
+ error: normalizeBetterAuthError(error)
1162
+ };
1163
+ throw error;
1164
+ }
1165
+ };
1166
+ verifyOtp = async (params) => {
1167
+ try {
1168
+ if ("email" in params && params.email) return await this.verifyEmailOtp(params);
1169
+ if ("phone" in params && params.phone) return await this.verifyPhoneOtp(params);
1170
+ if ("token_hash" in params && params.token_hash) throw createAuthError(AuthErrorCode.FeatureNotSupported, "Token hash verification not supported");
1171
+ throw createAuthError(AuthErrorCode.ValidationFailed, "Invalid OTP verification parameters");
1172
+ } catch (error) {
1173
+ if (isAuthError(error)) return {
1174
+ data: {
1175
+ user: null,
1176
+ session: null
1177
+ },
1178
+ error
1179
+ };
1180
+ if (isBetterAuthAPIError(error)) return {
1181
+ data: {
1182
+ user: null,
1183
+ session: null
1184
+ },
1185
+ error: normalizeBetterAuthError(error)
1186
+ };
1187
+ throw error;
1188
+ }
1189
+ };
1190
+ resetPasswordForEmail = async (email, options) => {
1191
+ try {
1192
+ const result = await this._betterAuth.requestPasswordReset({
1193
+ email,
1194
+ redirectTo: options?.redirectTo || (globalThis.window === void 0 ? "" : globalThis.location.origin)
1195
+ });
1196
+ if (result?.error) throw normalizeBetterAuthError(result.error);
1197
+ return {
1198
+ data: {},
1199
+ error: null
1200
+ };
1201
+ } catch (error) {
1202
+ if (isAuthError(error)) return {
1203
+ data: null,
1204
+ error
1205
+ };
1206
+ if (isBetterAuthAPIError(error)) return {
1207
+ data: null,
1208
+ error: normalizeBetterAuthError(error)
1209
+ };
1210
+ throw error;
1211
+ }
1212
+ };
1213
+ reauthenticate = async () => {
1214
+ try {
1215
+ const newSession = await this.getSession();
1216
+ if (newSession.error || !newSession.data.session) throw newSession.error || createAuthError(AuthErrorCode.SessionNotFound, "No session found");
1217
+ return {
1218
+ data: {
1219
+ user: newSession.data.session?.user || null,
1220
+ session: newSession.data.session
1221
+ },
1222
+ error: null
1223
+ };
1224
+ } catch (error) {
1225
+ if (isAuthError(error)) return {
1226
+ data: {
1227
+ user: null,
1228
+ session: null
1229
+ },
1230
+ error
1231
+ };
1232
+ if (isBetterAuthAPIError(error)) return {
1233
+ data: {
1234
+ user: null,
1235
+ session: null
1236
+ },
1237
+ error: normalizeBetterAuthError(error)
1238
+ };
1239
+ throw error;
1240
+ }
1241
+ };
1242
+ resend = async (credentials) => {
1243
+ try {
1244
+ if ("email" in credentials) {
1245
+ const { email, type, options } = credentials;
1246
+ if (type === "signup" || type === "email_change") {
1247
+ const result = await this._betterAuth.sendVerificationEmail({
1248
+ email,
1249
+ callbackURL: options?.emailRedirectTo || (globalThis.window === void 0 ? "" : globalThis.location.origin)
1250
+ });
1251
+ if (result?.error) throw normalizeBetterAuthError(result.error);
1252
+ return {
1253
+ data: {
1254
+ user: null,
1255
+ session: null
1256
+ },
1257
+ error: null
1258
+ };
1259
+ }
1260
+ throw createAuthError(AuthErrorCode.ValidationFailed, `Unsupported resend type: ${type}`);
1261
+ }
1262
+ if ("phone" in credentials) {
1263
+ const { phone, type } = credentials;
1264
+ if (type === "sms" || type === "phone_change") {
1265
+ const result = await this._betterAuth.phoneNumber.sendOtp({ phoneNumber: phone });
1266
+ if (result?.error) throw normalizeBetterAuthError(result.error);
1267
+ return {
1268
+ data: {
1269
+ messageId: type === "sms" ? "sms-otp-sent" : "phone-change-otp-sent",
1270
+ user: null,
1271
+ session: null
1272
+ },
1273
+ error: null
1274
+ };
1275
+ }
1276
+ }
1277
+ throw createAuthError(AuthErrorCode.ValidationFailed, "Invalid credentials format");
1278
+ } catch (error) {
1279
+ if (isAuthError(error)) return {
1280
+ data: {
1281
+ user: null,
1282
+ session: null
1283
+ },
1284
+ error
1285
+ };
1286
+ if (isBetterAuthAPIError(error)) return {
1287
+ data: {
1288
+ user: null,
1289
+ session: null
1290
+ },
1291
+ error: normalizeBetterAuthError(error)
1292
+ };
1293
+ throw error;
1294
+ }
1295
+ };
1296
+ exchangeCodeForSession = async (_authCode) => {
1297
+ try {
1298
+ const sessionResult = await this.getSession();
1299
+ if (sessionResult.data.session) return {
1300
+ data: {
1301
+ session: sessionResult.data.session,
1302
+ user: sessionResult.data.session.user
1303
+ },
1304
+ error: null
1305
+ };
1306
+ throw createAuthError(AuthErrorCode.OAuthCallbackFailed, "OAuth callback completed but no session was created. Make sure the OAuth callback has been processed.");
1307
+ } catch (error) {
1308
+ if (isAuthError(error)) return {
1309
+ data: {
1310
+ session: null,
1311
+ user: null
1312
+ },
1313
+ error
1314
+ };
1315
+ if (isBetterAuthAPIError(error)) return {
1316
+ data: {
1317
+ session: null,
1318
+ user: null
1319
+ },
1320
+ error: normalizeBetterAuthError(error)
1321
+ };
1322
+ throw error;
1323
+ }
1324
+ };
1325
+ onAuthStateChange = (callback) => {
1326
+ const id = crypto.randomUUID();
1327
+ const subscription = {
1328
+ id,
1329
+ callback,
1330
+ unsubscribe: () => {
1331
+ this._stateChangeEmitters.delete(id);
1332
+ }
1333
+ };
1334
+ this._stateChangeEmitters.set(id, subscription);
1335
+ this.emitInitialSession(callback);
1336
+ return { data: { subscription: {
1337
+ id,
1338
+ callback,
1339
+ unsubscribe: subscription.unsubscribe
1340
+ } } };
1341
+ };
1342
+ isThrowOnErrorEnabled = () => false;
1343
+ startAutoRefresh = async () => {};
1344
+ stopAutoRefresh = async () => {};
1345
+ async verifyEmailOtp(params) {
1346
+ const { type } = params;
1347
+ if (type === "email") {
1348
+ const result = await this._betterAuth.signIn.emailOtp({
1349
+ email: params.email,
1350
+ otp: params.token
1351
+ });
1352
+ if (result.error) return {
1353
+ data: {
1354
+ user: null,
1355
+ session: null
1356
+ },
1357
+ error: normalizeBetterAuthError(result.error)
1358
+ };
1359
+ const sessionResult = await this.getSession({ forceFetch: true });
1360
+ if (!sessionResult.data.session) return {
1361
+ data: {
1362
+ user: null,
1363
+ session: null
1364
+ },
1365
+ error: createAuthError(AuthErrorCode.SessionNotFound, "Failed to retrieve session after OTP verification. Make sure the magic link callback has been processed.")
1366
+ };
1367
+ return {
1368
+ data: {
1369
+ user: sessionResult.data.session.user,
1370
+ session: sessionResult.data.session
1371
+ },
1372
+ error: null
1373
+ };
1374
+ }
1375
+ if (type === "magiclink") {
1376
+ const result = await this._betterAuth.magicLink.verify({ query: {
1377
+ token: params.token,
1378
+ callbackURL: params.options?.redirectTo || (globalThis.window === void 0 ? "" : globalThis.location.origin)
1379
+ } });
1380
+ if (result?.error) return {
1381
+ data: {
1382
+ user: null,
1383
+ session: null
1384
+ },
1385
+ error: normalizeBetterAuthError(result.error)
1386
+ };
1387
+ const sessionResult = await this.getSession({ forceFetch: true });
1388
+ if (!sessionResult.data?.session) return {
1389
+ data: {
1390
+ user: null,
1391
+ session: null
1392
+ },
1393
+ error: createAuthError(AuthErrorCode.SessionNotFound, "Failed to retrieve session after magic link verification")
1394
+ };
1395
+ return {
1396
+ data: {
1397
+ user: sessionResult.data.session?.user || null,
1398
+ session: sessionResult.data.session
1399
+ },
1400
+ error: null
1401
+ };
1402
+ }
1403
+ if (type === "signup" || type === "invite") {
1404
+ const result = await this._betterAuth.emailOtp.verifyEmail({
1405
+ email: params.email,
1406
+ otp: params.token
1407
+ });
1408
+ if (result?.error) return {
1409
+ data: {
1410
+ user: null,
1411
+ session: null
1412
+ },
1413
+ error: normalizeBetterAuthError(result.error)
1414
+ };
1415
+ const sessionResult = await this.getSession({ forceFetch: true });
1416
+ return {
1417
+ data: {
1418
+ user: sessionResult.data.session?.user ?? null,
1419
+ session: sessionResult.data.session
1420
+ },
1421
+ error: null
1422
+ };
1423
+ }
1424
+ if (type === "recovery") {
1425
+ const checkResult = await this._betterAuth.emailOtp.checkVerificationOtp({
1426
+ email: params.email,
1427
+ otp: params.token,
1428
+ type: "forget-password"
1429
+ });
1430
+ if (checkResult.error || !checkResult.data?.success) return {
1431
+ data: {
1432
+ user: null,
1433
+ session: null
1434
+ },
1435
+ error: normalizeBetterAuthError(checkResult.error)
1436
+ };
1437
+ return {
1438
+ data: {
1439
+ user: null,
1440
+ session: null
1441
+ },
1442
+ error: null
1443
+ };
1444
+ }
1445
+ if (type === "email_change") {
1446
+ const result = await this._betterAuth.verifyEmail({ query: {
1447
+ token: params.token,
1448
+ callbackURL: params.options?.redirectTo
1449
+ } });
1450
+ if (result?.error) return {
1451
+ data: {
1452
+ user: null,
1453
+ session: null
1454
+ },
1455
+ error: normalizeBetterAuthError(result.error)
1456
+ };
1457
+ const sessionResult = await this.getSession({ forceFetch: true });
1458
+ if (sessionResult.error || !sessionResult.data) return {
1459
+ data: {
1460
+ user: null,
1461
+ session: null
1462
+ },
1463
+ error: sessionResult.error || createAuthError(AuthErrorCode.InternalError, "Failed to get session")
1464
+ };
1465
+ if (sessionResult.data.session) BETTER_AUTH_METHODS_HOOKS["updateUser"].onSuccess(sessionResult.data.session);
1466
+ return {
1467
+ data: {
1468
+ user: sessionResult.data?.session?.user || null,
1469
+ session: sessionResult.data?.session || null
1470
+ },
1471
+ error: null
1472
+ };
1473
+ }
1474
+ if (type === "invite") {
1475
+ const result = await this._betterAuth.organization.acceptInvitation({ invitationId: params.token });
1476
+ if (result.error) return {
1477
+ data: {
1478
+ user: null,
1479
+ session: null
1480
+ },
1481
+ error: normalizeBetterAuthError(result.error)
1482
+ };
1483
+ const sessionResult = await this.getSession({ forceFetch: true });
1484
+ if (sessionResult.error || !sessionResult.data) return {
1485
+ data: {
1486
+ user: null,
1487
+ session: null
1488
+ },
1489
+ error: sessionResult.error || createAuthError(AuthErrorCode.InternalError, "Failed to get session")
1490
+ };
1491
+ return {
1492
+ data: {
1493
+ user: sessionResult.data?.session?.user || null,
1494
+ session: sessionResult.data?.session
1495
+ },
1496
+ error: null
1497
+ };
1498
+ }
1499
+ return {
1500
+ data: {
1501
+ user: null,
1502
+ session: null
1503
+ },
1504
+ error: createAuthError(AuthErrorCode.ValidationFailed, `Unsupported email OTP type: ${type}`)
1505
+ };
1506
+ }
1507
+ async verifyPhoneOtp(params) {
1508
+ if (params.type === "sms") {
1509
+ const result = await this._betterAuth.phoneNumber.verify({
1510
+ phoneNumber: params.phone,
1511
+ code: params.token,
1512
+ disableSession: false,
1513
+ updatePhoneNumber: false
1514
+ });
1515
+ if (result.error) return {
1516
+ data: {
1517
+ user: null,
1518
+ session: null
1519
+ },
1520
+ error: normalizeBetterAuthError(result.error)
1521
+ };
1522
+ const sessionResult = await this.getSession({ forceFetch: true });
1523
+ if (sessionResult.error || !sessionResult.data) return {
1524
+ data: {
1525
+ user: null,
1526
+ session: null
1527
+ },
1528
+ error: sessionResult.error || createAuthError(AuthErrorCode.InternalError, "Failed to get session")
1529
+ };
1530
+ return {
1531
+ data: {
1532
+ user: sessionResult.data?.session?.user || null,
1533
+ session: sessionResult.data?.session || null
1534
+ },
1535
+ error: null
1536
+ };
1537
+ }
1538
+ if (params.type === "phone_change") {
1539
+ const currentSession = await this._betterAuth.getSession();
1540
+ if (currentSession.error || !currentSession.data?.session) return {
1541
+ data: {
1542
+ user: null,
1543
+ session: null
1544
+ },
1545
+ error: createAuthError(AuthErrorCode.SessionNotFound, "You must be signed in to change your phone number")
1546
+ };
1547
+ const result = await this._betterAuth.phoneNumber.verify({
1548
+ phoneNumber: params.phone,
1549
+ code: params.token,
1550
+ disableSession: false,
1551
+ updatePhoneNumber: true
1552
+ });
1553
+ if (result.error) return {
1554
+ data: {
1555
+ user: null,
1556
+ session: null
1557
+ },
1558
+ error: normalizeBetterAuthError(result.error)
1559
+ };
1560
+ const sessionResult = await this.getSession({ forceFetch: true });
1561
+ if (sessionResult.error || !sessionResult.data) return {
1562
+ data: {
1563
+ user: null,
1564
+ session: null
1565
+ },
1566
+ error: sessionResult.error || createAuthError(AuthErrorCode.InternalError, "Failed to get updated session")
1567
+ };
1568
+ return {
1569
+ data: {
1570
+ user: sessionResult.data.session?.user || null,
1571
+ session: sessionResult.data.session
1572
+ },
1573
+ error: null
1574
+ };
1575
+ }
1576
+ return {
1577
+ data: {
1578
+ user: null,
1579
+ session: null
1580
+ },
1581
+ error: createAuthError(AuthErrorCode.ValidationFailed, `Unsupported phone OTP type: ${params.type}`)
1582
+ };
1583
+ }
1584
+ async emitInitialSession(callback) {
1585
+ try {
1586
+ const { data, error } = await this.getSession();
1587
+ if (error) {
1588
+ await callback("INITIAL_SESSION", null);
1589
+ return;
1590
+ }
1591
+ await callback("INITIAL_SESSION", data.session);
1592
+ } catch {
1593
+ await callback("INITIAL_SESSION", null);
1594
+ }
1595
+ }
1596
+ };
1597
+ /**
1598
+ * Factory function that returns an adapter builder.
1599
+ * The builder is called by createClient/createAuthClient with the URL.
1600
+ *
1601
+ * @param options - Optional adapter configuration (baseURL is injected separately)
1602
+ * @returns A builder function that creates the adapter instance
1603
+ *
1604
+ * @example
1605
+ * ```typescript
1606
+ * const client = createClient({
1607
+ * auth: {
1608
+ * url: 'https://auth.example.com',
1609
+ * adapter: SupabaseAuthAdapter(),
1610
+ * },
1611
+ * dataApi: { url: 'https://data-api.example.com' },
1612
+ * });
1613
+ * ```
1614
+ */
1615
+ function SupabaseAuthAdapter(options) {
1616
+ return (url) => new SupabaseAuthAdapterImpl({
1617
+ baseURL: url,
1618
+ ...options
1619
+ });
1620
+ }
1621
+
1622
+ //#endregion
1623
+ export { BetterAuthVanillaAdapter as n, SupabaseAuthAdapter as t };