@neondatabase/auth 0.3.0-beta → 0.4.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -7
- package/codemods/migrate-auth-ui-imports.mjs +439 -0
- package/dist/{adapter-core-B9uDhoYq.d.mts → adapter-core-BWM7cWOp.d.mts} +311 -176
- package/dist/{adapter-core-D00qcqMo.mjs → adapter-core-Bt4M5I2g.mjs} +21 -11
- package/dist/auth-interface-Clz-oWq1.d.mts +8 -0
- package/dist/better-auth-helpers-Bkezghej.mjs +541 -0
- package/dist/{better-auth-react-adapter-BO4jLN4H.d.mts → better-auth-react-adapter-BDxJ65mF.d.mts} +384 -297
- package/dist/{better-auth-react-adapter-Xdj-69i9.mjs → better-auth-react-adapter-aMv8WeDb.mjs} +1 -1
- package/dist/index.d.mts +5 -4
- package/dist/index.mjs +4 -3
- package/dist/{neon-auth-DBOB8sXF.mjs → neon-auth-CS4FpK2X.mjs} +1 -1
- package/dist/next/index.d.mts +144 -56
- package/dist/next/index.mjs +5 -4
- package/dist/next/server/index.d.mts +25 -3
- package/dist/next/server/index.mjs +60 -30
- package/dist/react/adapters/index.d.mts +3 -3
- package/dist/react/adapters/index.mjs +2 -2
- package/dist/react/index.d.mts +4 -4
- package/dist/react/index.mjs +2 -2
- package/dist/react/ui/index.d.mts +1 -1
- package/dist/{supabase-adapter-CSDRL1ZU.d.mts → supabase-adapter-BGwV0Vu2.d.mts} +381 -294
- package/dist/{supabase-adapter-CIBMebXB.mjs → supabase-adapter-DBt4LJJd.mjs} +3 -514
- package/dist/types/index.d.mts +2 -2
- package/dist/vanilla/adapters/index.d.mts +4 -3
- package/dist/vanilla/adapters/index.mjs +2 -2
- package/dist/vanilla/index.d.mts +4 -3
- package/dist/vanilla/index.mjs +2 -2
- package/llms.txt +2 -2
- package/package.json +6 -2
- package/dist/constants-Cupc_bln.mjs +0 -28
- /package/dist/{index-CPnFzULh.d.mts → index-B0Pd4HOH.d.mts} +0 -0
- /package/dist/{index-UW23fDSn.d.mts → index-CzpoWrv9.d.mts} +0 -0
- /package/dist/{index-B_Q0Tp1D.d.mts → index-DHryUj7e.d.mts} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as NEON_AUTH_POPUP_CALLBACK_PARAM_NAME, f as NEON_AUTH_POPUP_CALLBACK_ROUTE, g as SESSION_CACHE_TTL_MS, h as OAUTH_POPUP_MESSAGE_TYPE, m as NEON_AUTH_SESSION_VERIFIER_PARAM_NAME, p as NEON_AUTH_POPUP_PARAM_NAME, r as normalizeBetterAuthError, u as CLOCK_SKEW_BUFFER_MS } from "./better-auth-helpers-Bkezghej.mjs";
|
|
2
2
|
import { getGlobalBroadcastChannel } from "better-auth/client";
|
|
3
|
-
import { adminClient, emailOTPClient, jwtClient, organizationClient } from "better-auth/client/plugins";
|
|
3
|
+
import { adminClient, emailOTPClient, jwtClient, magicLinkClient, organizationClient } from "better-auth/client/plugins";
|
|
4
4
|
import z from "zod";
|
|
5
5
|
|
|
6
6
|
//#region src/core/in-flight-request-manager.ts
|
|
@@ -490,6 +490,9 @@ const BETTER_AUTH_METHODS_HOOKS = {
|
|
|
490
490
|
},
|
|
491
491
|
getSession: {
|
|
492
492
|
beforeFetch: () => {
|
|
493
|
+
if (isBrowser()) {
|
|
494
|
+
if (new URLSearchParams(globalThis.window.location.search).has(NEON_AUTH_SESSION_VERIFIER_PARAM_NAME)) return null;
|
|
495
|
+
}
|
|
493
496
|
const cachedData = BETTER_AUTH_METHODS_CACHE.getCachedSession();
|
|
494
497
|
if (!cachedData) return null;
|
|
495
498
|
return Response.json(cachedData, { status: 200 });
|
|
@@ -629,7 +632,7 @@ function initBroadcastChannel() {
|
|
|
629
632
|
//#endregion
|
|
630
633
|
//#region package.json
|
|
631
634
|
var name = "@neondatabase/auth";
|
|
632
|
-
var version = "0.
|
|
635
|
+
var version = "0.4.0-beta";
|
|
633
636
|
|
|
634
637
|
//#endregion
|
|
635
638
|
//#region ../internal/dist/index.mjs
|
|
@@ -724,6 +727,7 @@ const supportedBetterAuthClientPlugins = [
|
|
|
724
727
|
adminClient(),
|
|
725
728
|
organizationClient(),
|
|
726
729
|
emailOTPClient(),
|
|
730
|
+
magicLinkClient(),
|
|
727
731
|
anonymousTokenClient()
|
|
728
732
|
];
|
|
729
733
|
var NeonAuthAdapterCore = class {
|
|
@@ -757,10 +761,13 @@ var NeonAuthAdapterCore = class {
|
|
|
757
761
|
});
|
|
758
762
|
if (!response$1.ok) {
|
|
759
763
|
const body = await response$1.clone().json().catch(() => ({}));
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
+
throw normalizeBetterAuthError({
|
|
765
|
+
status: response$1.status,
|
|
766
|
+
statusText: response$1.statusText,
|
|
767
|
+
message: body.message || `HTTP ${response$1.status} ${response$1.statusText}`,
|
|
768
|
+
code: body.code,
|
|
769
|
+
body
|
|
770
|
+
});
|
|
764
771
|
}
|
|
765
772
|
return response$1;
|
|
766
773
|
}
|
|
@@ -776,10 +783,13 @@ var NeonAuthAdapterCore = class {
|
|
|
776
783
|
}));
|
|
777
784
|
if (!response.ok) {
|
|
778
785
|
const errorBody = await response.clone().json().catch(() => ({}));
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
786
|
+
throw normalizeBetterAuthError({
|
|
787
|
+
status: response.status,
|
|
788
|
+
statusText: response.statusText,
|
|
789
|
+
message: errorBody.message || `HTTP ${response.status} ${response.statusText}`,
|
|
790
|
+
code: errorBody.code,
|
|
791
|
+
body: errorBody
|
|
792
|
+
});
|
|
783
793
|
}
|
|
784
794
|
return response.clone();
|
|
785
795
|
},
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AuthApiError, AuthClient, AuthError, isAuthApiError, isAuthError } from "@supabase/auth-js";
|
|
2
|
+
|
|
3
|
+
//#region src/adapters/supabase/auth-interface.d.ts
|
|
4
|
+
type _UpstreamAuthClientInstance = InstanceType<typeof AuthClient>;
|
|
5
|
+
type _AuthClientBase = { [K in keyof _UpstreamAuthClientInstance as _UpstreamAuthClientInstance[K] extends never ? never : K]: _UpstreamAuthClientInstance[K] };
|
|
6
|
+
type SupabaseAuthClientInterface = _AuthClientBase;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { isAuthError as a, isAuthApiError as i, AuthError as n, SupabaseAuthClientInterface as r, AuthApiError as t };
|
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
import { AuthApiError, AuthError, isAuthApiError, isAuthError } from "@supabase/auth-js";
|
|
2
|
+
|
|
3
|
+
//#region src/core/constants.ts
|
|
4
|
+
/**
|
|
5
|
+
* Session caching configuration constants
|
|
6
|
+
*
|
|
7
|
+
* Uses industry-standard 60s cache TTL (common across auth providers).
|
|
8
|
+
*
|
|
9
|
+
* Note: Token refresh detection is now automatic via Better Auth's
|
|
10
|
+
* fetchOptions.onSuccess callback. No polling is needed.
|
|
11
|
+
*/
|
|
12
|
+
/** Session cache TTL in milliseconds (60 seconds) */
|
|
13
|
+
const SESSION_CACHE_TTL_MS = 6e4;
|
|
14
|
+
/** Clock skew buffer for token expiration checks in milliseconds (10 seconds) */
|
|
15
|
+
const CLOCK_SKEW_BUFFER_MS = 1e4;
|
|
16
|
+
/** Default session expiry duration in milliseconds (1 hour) */
|
|
17
|
+
const DEFAULT_SESSION_EXPIRY_MS = 36e5;
|
|
18
|
+
/** Name of the session verifier parameter in the URL, used for the OAUTH flow */
|
|
19
|
+
const NEON_AUTH_SESSION_VERIFIER_PARAM_NAME = "neon_auth_session_verifier";
|
|
20
|
+
/** Name of the popup marker parameter in the URL, used for OAuth popup flow in iframes */
|
|
21
|
+
const NEON_AUTH_POPUP_PARAM_NAME = "neon_popup";
|
|
22
|
+
/** Name of the original callback URL parameter, used in OAuth popup flow */
|
|
23
|
+
const NEON_AUTH_POPUP_CALLBACK_PARAM_NAME = "neon_popup_callback";
|
|
24
|
+
/** The callback route used for OAuth popup completion (must be in middleware SKIP_ROUTES) */
|
|
25
|
+
const NEON_AUTH_POPUP_CALLBACK_ROUTE = "/auth/callback";
|
|
26
|
+
/** Message type for OAuth popup completion postMessage */
|
|
27
|
+
const OAUTH_POPUP_MESSAGE_TYPE = "neon-auth:oauth-complete";
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/utils/date.ts
|
|
31
|
+
function toISOString(date) {
|
|
32
|
+
if (!date) return (/* @__PURE__ */ new Date()).toISOString();
|
|
33
|
+
if (typeof date === "string") return date;
|
|
34
|
+
if (typeof date === "number") return new Date(date).toISOString();
|
|
35
|
+
return date.toISOString();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/adapters/supabase/errors/definitions.ts
|
|
40
|
+
/**
|
|
41
|
+
* Error codes for type-safe error handling
|
|
42
|
+
*/
|
|
43
|
+
const AuthErrorCode = {
|
|
44
|
+
BadJwt: "bad_jwt",
|
|
45
|
+
InvalidCredentials: "invalid_credentials",
|
|
46
|
+
SessionExpired: "session_expired",
|
|
47
|
+
SessionNotFound: "session_not_found",
|
|
48
|
+
InvalidGrant: "invalid_grant",
|
|
49
|
+
UserNotFound: "user_not_found",
|
|
50
|
+
UserAlreadyExists: "user_already_exists",
|
|
51
|
+
EmailExists: "email_exists",
|
|
52
|
+
PhoneExists: "phone_exists",
|
|
53
|
+
EmailNotConfirmed: "email_not_confirmed",
|
|
54
|
+
PhoneNotConfirmed: "phone_not_confirmed",
|
|
55
|
+
ValidationFailed: "validation_failed",
|
|
56
|
+
BadJson: "bad_json",
|
|
57
|
+
WeakPassword: "weak_password",
|
|
58
|
+
EmailAddressInvalid: "email_address_invalid",
|
|
59
|
+
FeatureNotSupported: "feature_not_supported",
|
|
60
|
+
NotImplemented: "not_implemented",
|
|
61
|
+
OAuthProviderNotSupported: "oauth_provider_not_supported",
|
|
62
|
+
PhoneProviderDisabled: "phone_provider_disabled",
|
|
63
|
+
MagicLinkNotSupported: "magic_link_not_supported",
|
|
64
|
+
SsoProviderDisabled: "sso_provider_disabled",
|
|
65
|
+
AnonymousProviderDisabled: "anonymous_provider_disabled",
|
|
66
|
+
Web3ProviderDisabled: "web3_provider_disabled",
|
|
67
|
+
BadOAuthCallback: "bad_oauth_callback",
|
|
68
|
+
OAuthCallbackFailed: "oauth_callback_failed",
|
|
69
|
+
OverRequestRateLimit: "over_request_rate_limit",
|
|
70
|
+
OverEmailSendRateLimit: "over_email_send_rate_limit",
|
|
71
|
+
OverSmsSendRateLimit: "over_sms_send_rate_limit",
|
|
72
|
+
UnexpectedFailure: "unexpected_failure",
|
|
73
|
+
InternalError: "internal_error",
|
|
74
|
+
IdentityNotFound: "identity_not_found",
|
|
75
|
+
UnknownError: "unknown_error"
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Complete error definitions map
|
|
79
|
+
* Maps error codes to HTTP status codes and default messages
|
|
80
|
+
*/
|
|
81
|
+
const ERROR_DEFINITIONS = {
|
|
82
|
+
[AuthErrorCode.BadJwt]: {
|
|
83
|
+
code: AuthErrorCode.BadJwt,
|
|
84
|
+
status: 401,
|
|
85
|
+
message: "Invalid or expired session token",
|
|
86
|
+
description: "The JWT token is malformed, expired, or has an invalid signature"
|
|
87
|
+
},
|
|
88
|
+
[AuthErrorCode.InvalidCredentials]: {
|
|
89
|
+
code: AuthErrorCode.InvalidCredentials,
|
|
90
|
+
status: 401,
|
|
91
|
+
message: "Invalid email or password",
|
|
92
|
+
description: "The provided credentials do not match any user account"
|
|
93
|
+
},
|
|
94
|
+
[AuthErrorCode.SessionExpired]: {
|
|
95
|
+
code: AuthErrorCode.SessionExpired,
|
|
96
|
+
status: 401,
|
|
97
|
+
message: "Session has expired",
|
|
98
|
+
description: "The user session has exceeded its timeout period"
|
|
99
|
+
},
|
|
100
|
+
[AuthErrorCode.SessionNotFound]: {
|
|
101
|
+
code: AuthErrorCode.SessionNotFound,
|
|
102
|
+
status: 401,
|
|
103
|
+
message: "No active session found",
|
|
104
|
+
description: "The user does not have an active session or the session was invalidated"
|
|
105
|
+
},
|
|
106
|
+
[AuthErrorCode.InvalidGrant]: {
|
|
107
|
+
code: AuthErrorCode.InvalidGrant,
|
|
108
|
+
status: 401,
|
|
109
|
+
message: "Invalid authorization grant",
|
|
110
|
+
description: "OAuth/OIDC grant validation failed"
|
|
111
|
+
},
|
|
112
|
+
[AuthErrorCode.UserNotFound]: {
|
|
113
|
+
code: AuthErrorCode.UserNotFound,
|
|
114
|
+
status: 404,
|
|
115
|
+
message: "User not found",
|
|
116
|
+
description: "No user exists with the provided identifier"
|
|
117
|
+
},
|
|
118
|
+
[AuthErrorCode.UserAlreadyExists]: {
|
|
119
|
+
code: AuthErrorCode.UserAlreadyExists,
|
|
120
|
+
status: 409,
|
|
121
|
+
message: "User already exists",
|
|
122
|
+
description: "A user with this email or phone number is already registered"
|
|
123
|
+
},
|
|
124
|
+
[AuthErrorCode.EmailExists]: {
|
|
125
|
+
code: AuthErrorCode.EmailExists,
|
|
126
|
+
status: 409,
|
|
127
|
+
message: "Email address already registered",
|
|
128
|
+
description: "This email address is already associated with an account"
|
|
129
|
+
},
|
|
130
|
+
[AuthErrorCode.PhoneExists]: {
|
|
131
|
+
code: AuthErrorCode.PhoneExists,
|
|
132
|
+
status: 409,
|
|
133
|
+
message: "Phone number already registered",
|
|
134
|
+
description: "This phone number is already associated with an account"
|
|
135
|
+
},
|
|
136
|
+
[AuthErrorCode.EmailNotConfirmed]: {
|
|
137
|
+
code: AuthErrorCode.EmailNotConfirmed,
|
|
138
|
+
status: 422,
|
|
139
|
+
message: "Email verification required",
|
|
140
|
+
description: "The user must verify their email before signing in"
|
|
141
|
+
},
|
|
142
|
+
[AuthErrorCode.PhoneNotConfirmed]: {
|
|
143
|
+
code: AuthErrorCode.PhoneNotConfirmed,
|
|
144
|
+
status: 422,
|
|
145
|
+
message: "Phone verification required",
|
|
146
|
+
description: "The user must verify their phone number before signing in"
|
|
147
|
+
},
|
|
148
|
+
[AuthErrorCode.ValidationFailed]: {
|
|
149
|
+
code: AuthErrorCode.ValidationFailed,
|
|
150
|
+
status: 400,
|
|
151
|
+
message: "Invalid request parameters",
|
|
152
|
+
description: "One or more request parameters are invalid or missing"
|
|
153
|
+
},
|
|
154
|
+
[AuthErrorCode.BadJson]: {
|
|
155
|
+
code: AuthErrorCode.BadJson,
|
|
156
|
+
status: 400,
|
|
157
|
+
message: "Invalid JSON in request body",
|
|
158
|
+
description: "The request body contains malformed JSON"
|
|
159
|
+
},
|
|
160
|
+
[AuthErrorCode.WeakPassword]: {
|
|
161
|
+
code: AuthErrorCode.WeakPassword,
|
|
162
|
+
status: 400,
|
|
163
|
+
message: "Password does not meet security requirements",
|
|
164
|
+
description: "The password is too weak or does not meet complexity requirements"
|
|
165
|
+
},
|
|
166
|
+
[AuthErrorCode.EmailAddressInvalid]: {
|
|
167
|
+
code: AuthErrorCode.EmailAddressInvalid,
|
|
168
|
+
status: 400,
|
|
169
|
+
message: "Invalid email address format",
|
|
170
|
+
description: "The provided email address is not in a valid format"
|
|
171
|
+
},
|
|
172
|
+
[AuthErrorCode.FeatureNotSupported]: {
|
|
173
|
+
code: AuthErrorCode.FeatureNotSupported,
|
|
174
|
+
status: 403,
|
|
175
|
+
message: "Feature not available",
|
|
176
|
+
description: "This feature is not supported in the current configuration"
|
|
177
|
+
},
|
|
178
|
+
[AuthErrorCode.NotImplemented]: {
|
|
179
|
+
code: AuthErrorCode.NotImplemented,
|
|
180
|
+
status: 501,
|
|
181
|
+
message: "Feature not implemented",
|
|
182
|
+
description: "This feature has not been implemented yet"
|
|
183
|
+
},
|
|
184
|
+
[AuthErrorCode.OAuthProviderNotSupported]: {
|
|
185
|
+
code: AuthErrorCode.OAuthProviderNotSupported,
|
|
186
|
+
status: 403,
|
|
187
|
+
message: "OAuth provider not supported",
|
|
188
|
+
description: "The requested OAuth provider is not enabled"
|
|
189
|
+
},
|
|
190
|
+
[AuthErrorCode.PhoneProviderDisabled]: {
|
|
191
|
+
code: AuthErrorCode.PhoneProviderDisabled,
|
|
192
|
+
status: 403,
|
|
193
|
+
message: "Phone authentication not available",
|
|
194
|
+
description: "Phone number authentication is not enabled"
|
|
195
|
+
},
|
|
196
|
+
[AuthErrorCode.MagicLinkNotSupported]: {
|
|
197
|
+
code: AuthErrorCode.MagicLinkNotSupported,
|
|
198
|
+
status: 403,
|
|
199
|
+
message: "Magic link authentication not available",
|
|
200
|
+
description: "Magic link authentication is not supported"
|
|
201
|
+
},
|
|
202
|
+
[AuthErrorCode.SsoProviderDisabled]: {
|
|
203
|
+
code: AuthErrorCode.SsoProviderDisabled,
|
|
204
|
+
status: 403,
|
|
205
|
+
message: "SSO not supported",
|
|
206
|
+
description: "Enterprise SSO authentication is not available"
|
|
207
|
+
},
|
|
208
|
+
[AuthErrorCode.AnonymousProviderDisabled]: {
|
|
209
|
+
code: AuthErrorCode.AnonymousProviderDisabled,
|
|
210
|
+
status: 403,
|
|
211
|
+
message: "Anonymous authentication not available",
|
|
212
|
+
description: "Anonymous sign-in is not enabled"
|
|
213
|
+
},
|
|
214
|
+
[AuthErrorCode.Web3ProviderDisabled]: {
|
|
215
|
+
code: AuthErrorCode.Web3ProviderDisabled,
|
|
216
|
+
status: 403,
|
|
217
|
+
message: "Web3 authentication not supported",
|
|
218
|
+
description: "Web3/blockchain authentication is not available"
|
|
219
|
+
},
|
|
220
|
+
[AuthErrorCode.BadOAuthCallback]: {
|
|
221
|
+
code: AuthErrorCode.BadOAuthCallback,
|
|
222
|
+
status: 400,
|
|
223
|
+
message: "Invalid OAuth callback",
|
|
224
|
+
description: "The OAuth callback request is missing required parameters"
|
|
225
|
+
},
|
|
226
|
+
[AuthErrorCode.OAuthCallbackFailed]: {
|
|
227
|
+
code: AuthErrorCode.OAuthCallbackFailed,
|
|
228
|
+
status: 500,
|
|
229
|
+
message: "OAuth authentication failed",
|
|
230
|
+
description: "The OAuth callback completed but no session was created"
|
|
231
|
+
},
|
|
232
|
+
[AuthErrorCode.OverRequestRateLimit]: {
|
|
233
|
+
code: AuthErrorCode.OverRequestRateLimit,
|
|
234
|
+
status: 429,
|
|
235
|
+
message: "Too many requests",
|
|
236
|
+
description: "Rate limit exceeded. Please try again later"
|
|
237
|
+
},
|
|
238
|
+
[AuthErrorCode.OverEmailSendRateLimit]: {
|
|
239
|
+
code: AuthErrorCode.OverEmailSendRateLimit,
|
|
240
|
+
status: 429,
|
|
241
|
+
message: "Too many email requests",
|
|
242
|
+
description: "Too many emails sent. Please wait before trying again"
|
|
243
|
+
},
|
|
244
|
+
[AuthErrorCode.OverSmsSendRateLimit]: {
|
|
245
|
+
code: AuthErrorCode.OverSmsSendRateLimit,
|
|
246
|
+
status: 429,
|
|
247
|
+
message: "Too many SMS requests",
|
|
248
|
+
description: "Too many SMS messages sent. Please wait before trying again"
|
|
249
|
+
},
|
|
250
|
+
[AuthErrorCode.UnexpectedFailure]: {
|
|
251
|
+
code: AuthErrorCode.UnexpectedFailure,
|
|
252
|
+
status: 500,
|
|
253
|
+
message: "An unexpected error occurred",
|
|
254
|
+
description: "The server encountered an unexpected condition"
|
|
255
|
+
},
|
|
256
|
+
[AuthErrorCode.InternalError]: {
|
|
257
|
+
code: AuthErrorCode.InternalError,
|
|
258
|
+
status: 500,
|
|
259
|
+
message: "Internal server error",
|
|
260
|
+
description: "An internal error occurred while processing the request"
|
|
261
|
+
},
|
|
262
|
+
[AuthErrorCode.IdentityNotFound]: {
|
|
263
|
+
code: AuthErrorCode.IdentityNotFound,
|
|
264
|
+
status: 404,
|
|
265
|
+
message: "Identity not found",
|
|
266
|
+
description: "The requested user identity does not exist"
|
|
267
|
+
},
|
|
268
|
+
[AuthErrorCode.UnknownError]: {
|
|
269
|
+
code: AuthErrorCode.UnknownError,
|
|
270
|
+
status: 500,
|
|
271
|
+
message: "An unknown error occurred",
|
|
272
|
+
description: "The error could not be categorized"
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
/**
|
|
276
|
+
* Helper to get error definition by code
|
|
277
|
+
*/
|
|
278
|
+
function getErrorDefinition(code) {
|
|
279
|
+
return ERROR_DEFINITIONS[code];
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Create an AuthError or AuthApiError with proper status and message
|
|
283
|
+
*
|
|
284
|
+
* @param code - The error code from AuthErrorCode
|
|
285
|
+
* @param customMessage - Optional custom message (defaults to error definition message)
|
|
286
|
+
* @returns AuthError for 5xx errors, AuthApiError for 4xx errors
|
|
287
|
+
*/
|
|
288
|
+
function createAuthError(code, customMessage) {
|
|
289
|
+
const def = getErrorDefinition(code);
|
|
290
|
+
const message = customMessage || def.message;
|
|
291
|
+
const status = def.status;
|
|
292
|
+
if (status !== 500 && status !== 501 && status !== 503) return new AuthApiError(message, status, def.code);
|
|
293
|
+
return new AuthError(message, status, def.code);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
//#endregion
|
|
297
|
+
//#region src/adapters/supabase/errors/mappings.ts
|
|
298
|
+
/**
|
|
299
|
+
* Maps Better Auth error codes to AuthErrorCode
|
|
300
|
+
* Based on Better Auth SDK error codes
|
|
301
|
+
*
|
|
302
|
+
* @see https://www.better-auth.com/docs/concepts/error-handling
|
|
303
|
+
*/
|
|
304
|
+
const BETTER_AUTH_ERROR_MAP = {
|
|
305
|
+
"INVALID_EMAIL_OR_PASSWORD": AuthErrorCode.InvalidCredentials,
|
|
306
|
+
"INVALID_PASSWORD": AuthErrorCode.InvalidCredentials,
|
|
307
|
+
"INVALID_EMAIL": AuthErrorCode.EmailAddressInvalid,
|
|
308
|
+
"USER_NOT_FOUND": AuthErrorCode.UserNotFound,
|
|
309
|
+
"INVALID_TOKEN": AuthErrorCode.BadJwt,
|
|
310
|
+
"SESSION_EXPIRED": AuthErrorCode.SessionExpired,
|
|
311
|
+
"FAILED_TO_GET_SESSION": AuthErrorCode.SessionNotFound,
|
|
312
|
+
"USER_ALREADY_EXISTS": AuthErrorCode.UserAlreadyExists,
|
|
313
|
+
"EMAIL_NOT_VERIFIED": AuthErrorCode.EmailNotConfirmed,
|
|
314
|
+
"USER_EMAIL_NOT_FOUND": AuthErrorCode.UserNotFound,
|
|
315
|
+
"PASSWORD_TOO_SHORT": AuthErrorCode.WeakPassword,
|
|
316
|
+
"PASSWORD_TOO_LONG": AuthErrorCode.WeakPassword,
|
|
317
|
+
"USER_ALREADY_HAS_PASSWORD": AuthErrorCode.ValidationFailed,
|
|
318
|
+
"CREDENTIAL_ACCOUNT_NOT_FOUND": AuthErrorCode.IdentityNotFound,
|
|
319
|
+
"FAILED_TO_UNLINK_LAST_ACCOUNT": AuthErrorCode.ValidationFailed,
|
|
320
|
+
"ACCOUNT_NOT_FOUND": AuthErrorCode.IdentityNotFound,
|
|
321
|
+
"SOCIAL_ACCOUNT_ALREADY_LINKED": AuthErrorCode.ValidationFailed,
|
|
322
|
+
"PROVIDER_NOT_FOUND": AuthErrorCode.OAuthProviderNotSupported,
|
|
323
|
+
"ID_TOKEN_NOT_SUPPORTED": AuthErrorCode.FeatureNotSupported,
|
|
324
|
+
"FAILED_TO_CREATE_USER": AuthErrorCode.InternalError,
|
|
325
|
+
"FAILED_TO_CREATE_SESSION": AuthErrorCode.InternalError,
|
|
326
|
+
"FAILED_TO_UPDATE_USER": AuthErrorCode.InternalError,
|
|
327
|
+
"EMAIL_CAN_NOT_BE_UPDATED": AuthErrorCode.FeatureNotSupported
|
|
328
|
+
};
|
|
329
|
+
/**
|
|
330
|
+
* Maps HTTP status codes from Better Auth to AuthErrorCode
|
|
331
|
+
*/
|
|
332
|
+
const STATUS_CODE_ERROR_MAP = {
|
|
333
|
+
400: AuthErrorCode.ValidationFailed,
|
|
334
|
+
401: AuthErrorCode.BadJwt,
|
|
335
|
+
403: AuthErrorCode.FeatureNotSupported,
|
|
336
|
+
404: AuthErrorCode.UserNotFound,
|
|
337
|
+
409: AuthErrorCode.UserAlreadyExists,
|
|
338
|
+
422: AuthErrorCode.ValidationFailed,
|
|
339
|
+
429: AuthErrorCode.OverRequestRateLimit,
|
|
340
|
+
500: AuthErrorCode.UnexpectedFailure,
|
|
341
|
+
501: AuthErrorCode.NotImplemented,
|
|
342
|
+
503: AuthErrorCode.FeatureNotSupported
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
//#endregion
|
|
346
|
+
//#region src/core/better-auth-helpers.ts
|
|
347
|
+
/**
|
|
348
|
+
* Normalize Better Auth errors to standard AuthError format
|
|
349
|
+
*
|
|
350
|
+
* Handles three error formats:
|
|
351
|
+
* 1. BetterFetchError: { status, statusText, message?, code? }
|
|
352
|
+
* 2. BetterAuthErrorResponse: { status, statusText, message?, code? }
|
|
353
|
+
* 3. Standard Error: { message, name, stack }
|
|
354
|
+
*
|
|
355
|
+
* Maps Better Auth errors to appropriate AuthError/AuthApiError with:
|
|
356
|
+
* - Correct HTTP status codes
|
|
357
|
+
* - Standard error codes (snake_case)
|
|
358
|
+
* - User-friendly, security-conscious messages
|
|
359
|
+
*/
|
|
360
|
+
function normalizeBetterAuthError(error) {
|
|
361
|
+
if (error !== null && error !== void 0 && typeof error === "object" && "status" in error && "statusText" in error) {
|
|
362
|
+
const betterError = error;
|
|
363
|
+
const status = betterError.status;
|
|
364
|
+
if ("code" in betterError && betterError.code && typeof betterError.code === "string") {
|
|
365
|
+
const mappedCode = BETTER_AUTH_ERROR_MAP[betterError.code];
|
|
366
|
+
if (mappedCode) {
|
|
367
|
+
const def$2 = getErrorDefinition(mappedCode);
|
|
368
|
+
return createNormalizedError(def$2.message, def$2.status, def$2.code, status);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
const def$1 = getErrorDefinition(mapStatusCodeToErrorCode(status, betterError.message || betterError.statusText));
|
|
372
|
+
return createNormalizedError(betterError.message || def$1.message, status, def$1.code, status);
|
|
373
|
+
}
|
|
374
|
+
if (error instanceof Error) {
|
|
375
|
+
const def$1 = getErrorDefinition(mapMessageToErrorCode(error.message));
|
|
376
|
+
return createNormalizedError(error.message || def$1.message, def$1.status, def$1.code, def$1.status);
|
|
377
|
+
}
|
|
378
|
+
const def = getErrorDefinition(AuthErrorCode.UnknownError);
|
|
379
|
+
return new AuthError(def.message, def.status, def.code);
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Map HTTP status code to AuthErrorCode
|
|
383
|
+
* Uses message content for disambiguation when status code is ambiguous
|
|
384
|
+
*/
|
|
385
|
+
function mapStatusCodeToErrorCode(status, message) {
|
|
386
|
+
const lowerMessage = message?.toLowerCase() || "";
|
|
387
|
+
switch (status) {
|
|
388
|
+
case 401:
|
|
389
|
+
if (lowerMessage.includes("token") || lowerMessage.includes("jwt")) return AuthErrorCode.BadJwt;
|
|
390
|
+
if (lowerMessage.includes("session")) return AuthErrorCode.SessionNotFound;
|
|
391
|
+
if (lowerMessage.includes("expired")) return AuthErrorCode.SessionExpired;
|
|
392
|
+
return AuthErrorCode.InvalidCredentials;
|
|
393
|
+
case 404:
|
|
394
|
+
if (lowerMessage.includes("identity") || lowerMessage.includes("account")) return AuthErrorCode.IdentityNotFound;
|
|
395
|
+
if (lowerMessage.includes("session")) return AuthErrorCode.SessionNotFound;
|
|
396
|
+
return AuthErrorCode.UserNotFound;
|
|
397
|
+
case 409:
|
|
398
|
+
if (lowerMessage.includes("email")) return AuthErrorCode.EmailExists;
|
|
399
|
+
if (lowerMessage.includes("phone")) return AuthErrorCode.PhoneExists;
|
|
400
|
+
return AuthErrorCode.UserAlreadyExists;
|
|
401
|
+
case 422:
|
|
402
|
+
if (lowerMessage.includes("email") && lowerMessage.includes("confirm")) return AuthErrorCode.EmailNotConfirmed;
|
|
403
|
+
if (lowerMessage.includes("phone") && lowerMessage.includes("confirm")) return AuthErrorCode.PhoneNotConfirmed;
|
|
404
|
+
return AuthErrorCode.ValidationFailed;
|
|
405
|
+
case 429:
|
|
406
|
+
if (lowerMessage.includes("email")) return AuthErrorCode.OverEmailSendRateLimit;
|
|
407
|
+
if (lowerMessage.includes("sms") || lowerMessage.includes("phone")) return AuthErrorCode.OverSmsSendRateLimit;
|
|
408
|
+
return AuthErrorCode.OverRequestRateLimit;
|
|
409
|
+
case 400:
|
|
410
|
+
if (lowerMessage.includes("password") && lowerMessage.includes("weak")) return AuthErrorCode.WeakPassword;
|
|
411
|
+
if (lowerMessage.includes("email") && lowerMessage.includes("invalid")) return AuthErrorCode.EmailAddressInvalid;
|
|
412
|
+
if (lowerMessage.includes("json")) return AuthErrorCode.BadJson;
|
|
413
|
+
if (lowerMessage.includes("oauth") || lowerMessage.includes("callback")) return AuthErrorCode.BadOAuthCallback;
|
|
414
|
+
return AuthErrorCode.ValidationFailed;
|
|
415
|
+
case 403:
|
|
416
|
+
if (lowerMessage.includes("provider") || lowerMessage.includes("oauth")) return AuthErrorCode.OAuthProviderNotSupported;
|
|
417
|
+
if (lowerMessage.includes("phone")) return AuthErrorCode.PhoneProviderDisabled;
|
|
418
|
+
if (lowerMessage.includes("sso")) return AuthErrorCode.SsoProviderDisabled;
|
|
419
|
+
return AuthErrorCode.FeatureNotSupported;
|
|
420
|
+
case 501: return AuthErrorCode.NotImplemented;
|
|
421
|
+
case 503: return AuthErrorCode.FeatureNotSupported;
|
|
422
|
+
default:
|
|
423
|
+
if (lowerMessage.includes("oauth")) return AuthErrorCode.OAuthCallbackFailed;
|
|
424
|
+
return AuthErrorCode.UnexpectedFailure;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Map error message content to AuthErrorCode
|
|
429
|
+
* Used as fallback when status code is not available
|
|
430
|
+
*/
|
|
431
|
+
function mapMessageToErrorCode(message) {
|
|
432
|
+
const lower = message.toLowerCase();
|
|
433
|
+
if (lower.includes("invalid login") || lower.includes("incorrect") || lower.includes("wrong password")) return AuthErrorCode.InvalidCredentials;
|
|
434
|
+
if (lower.includes("token") && (lower.includes("invalid") || lower.includes("expired"))) return AuthErrorCode.BadJwt;
|
|
435
|
+
if (lower.includes("session") && lower.includes("expired")) return AuthErrorCode.SessionExpired;
|
|
436
|
+
if (lower.includes("session") && lower.includes("not found")) return AuthErrorCode.SessionNotFound;
|
|
437
|
+
if (lower.includes("already exists") || lower.includes("already registered")) return AuthErrorCode.UserAlreadyExists;
|
|
438
|
+
if (lower.includes("not found") && lower.includes("user")) return AuthErrorCode.UserNotFound;
|
|
439
|
+
if (lower.includes("not found") && lower.includes("identity")) return AuthErrorCode.IdentityNotFound;
|
|
440
|
+
if (lower.includes("email") && lower.includes("not confirmed")) return AuthErrorCode.EmailNotConfirmed;
|
|
441
|
+
if (lower.includes("phone") && lower.includes("not confirmed")) return AuthErrorCode.PhoneNotConfirmed;
|
|
442
|
+
if (lower.includes("weak password") || lower.includes("password") && lower.includes("requirements")) return AuthErrorCode.WeakPassword;
|
|
443
|
+
if (lower.includes("email") && lower.includes("invalid")) return AuthErrorCode.EmailAddressInvalid;
|
|
444
|
+
if (lower.includes("rate limit") || lower.includes("too many requests")) return AuthErrorCode.OverRequestRateLimit;
|
|
445
|
+
if (lower.includes("oauth") && lower.includes("failed")) return AuthErrorCode.OAuthCallbackFailed;
|
|
446
|
+
if (lower.includes("provider") && lower.includes("not supported")) return AuthErrorCode.OAuthProviderNotSupported;
|
|
447
|
+
return AuthErrorCode.UnexpectedFailure;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Create normalized error with correct type (AuthError vs AuthApiError)
|
|
451
|
+
* Uses AuthApiError for non-500 status codes (API/client errors)
|
|
452
|
+
* Uses AuthError for 500 status codes (server errors)
|
|
453
|
+
*/
|
|
454
|
+
function createNormalizedError(message, targetStatus, code, _originalStatus) {
|
|
455
|
+
const status = targetStatus;
|
|
456
|
+
if (status !== 500 && status !== 501 && status !== 503) return new AuthApiError(message, status, code);
|
|
457
|
+
return new AuthError(message, status, code);
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Map Better Auth session to Session format
|
|
461
|
+
*/
|
|
462
|
+
function mapBetterAuthSession(betterAuthSession, betterAuthUser) {
|
|
463
|
+
if (!betterAuthSession || !betterAuthUser) return null;
|
|
464
|
+
let expiresAt;
|
|
465
|
+
if (typeof betterAuthSession.expiresAt === "string") expiresAt = Math.floor(new Date(betterAuthSession.expiresAt).getTime() / 1e3);
|
|
466
|
+
else if (typeof betterAuthSession.expiresAt === "object" && betterAuthSession.expiresAt instanceof Date) expiresAt = Math.floor(betterAuthSession.expiresAt.getTime() / 1e3);
|
|
467
|
+
else expiresAt = Math.floor(Date.now() / 1e3) + Math.floor(DEFAULT_SESSION_EXPIRY_MS / 1e3);
|
|
468
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
469
|
+
const expiresIn = Math.max(0, expiresAt - now);
|
|
470
|
+
return {
|
|
471
|
+
access_token: betterAuthSession.token,
|
|
472
|
+
refresh_token: "",
|
|
473
|
+
expires_at: expiresAt,
|
|
474
|
+
expires_in: expiresIn,
|
|
475
|
+
token_type: "bearer",
|
|
476
|
+
user: mapBetterAuthUser(betterAuthUser)
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Map Better Auth user to User format
|
|
481
|
+
*/
|
|
482
|
+
function mapBetterAuthUser(betterAuthUser) {
|
|
483
|
+
const createdAt = toISOString(betterAuthUser.createdAt);
|
|
484
|
+
const updatedAt = toISOString(betterAuthUser.updatedAt);
|
|
485
|
+
const userMetadata = {};
|
|
486
|
+
if (betterAuthUser.name) userMetadata.displayName = betterAuthUser.name;
|
|
487
|
+
if (betterAuthUser.image) userMetadata.profileImageUrl = betterAuthUser.image;
|
|
488
|
+
const userRecord = betterAuthUser;
|
|
489
|
+
for (const key of Object.keys(userRecord)) if (![
|
|
490
|
+
"id",
|
|
491
|
+
"email",
|
|
492
|
+
"emailVerified",
|
|
493
|
+
"name",
|
|
494
|
+
"image",
|
|
495
|
+
"createdAt",
|
|
496
|
+
"updatedAt"
|
|
497
|
+
].includes(key)) userMetadata[key] = userRecord[key];
|
|
498
|
+
return {
|
|
499
|
+
id: betterAuthUser.id,
|
|
500
|
+
email: betterAuthUser.email || "",
|
|
501
|
+
email_confirmed_at: betterAuthUser.emailVerified ? createdAt : void 0,
|
|
502
|
+
phone: void 0,
|
|
503
|
+
confirmed_at: betterAuthUser.emailVerified ? createdAt : void 0,
|
|
504
|
+
last_sign_in_at: updatedAt,
|
|
505
|
+
app_metadata: {},
|
|
506
|
+
user_metadata: userMetadata,
|
|
507
|
+
identities: [],
|
|
508
|
+
created_at: createdAt,
|
|
509
|
+
updated_at: updatedAt,
|
|
510
|
+
aud: "authenticated",
|
|
511
|
+
role: "authenticated"
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
function mapBetterAuthIdentity(betterAuthUserIdentityAccount, accountInfoData) {
|
|
515
|
+
return {
|
|
516
|
+
id: betterAuthUserIdentityAccount.id,
|
|
517
|
+
user_id: betterAuthUserIdentityAccount.id,
|
|
518
|
+
identity_id: betterAuthUserIdentityAccount.accountId,
|
|
519
|
+
provider: betterAuthUserIdentityAccount.providerId,
|
|
520
|
+
created_at: toISOString(betterAuthUserIdentityAccount.createdAt),
|
|
521
|
+
updated_at: toISOString(betterAuthUserIdentityAccount.updatedAt),
|
|
522
|
+
last_sign_in_at: toISOString(betterAuthUserIdentityAccount.updatedAt),
|
|
523
|
+
identity_data: accountInfoData ? {
|
|
524
|
+
provider: betterAuthUserIdentityAccount.providerId,
|
|
525
|
+
provider_id: betterAuthUserIdentityAccount.accountId,
|
|
526
|
+
scopes: betterAuthUserIdentityAccount.scopes,
|
|
527
|
+
email: accountInfoData.data.email,
|
|
528
|
+
name: accountInfoData.data.user.name,
|
|
529
|
+
picture: accountInfoData.data.user.picture,
|
|
530
|
+
email_verified: accountInfoData.data.user.email_verified,
|
|
531
|
+
...accountInfoData.data
|
|
532
|
+
} : {
|
|
533
|
+
provider: betterAuthUserIdentityAccount.providerId,
|
|
534
|
+
provider_id: betterAuthUserIdentityAccount.accountId,
|
|
535
|
+
scopes: betterAuthUserIdentityAccount.scopes
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
//#endregion
|
|
541
|
+
export { createAuthError as a, isAuthApiError as c, NEON_AUTH_POPUP_CALLBACK_PARAM_NAME as d, NEON_AUTH_POPUP_CALLBACK_ROUTE as f, SESSION_CACHE_TTL_MS as g, OAUTH_POPUP_MESSAGE_TYPE as h, AuthErrorCode as i, isAuthError as l, NEON_AUTH_SESSION_VERIFIER_PARAM_NAME as m, mapBetterAuthSession as n, AuthApiError as o, NEON_AUTH_POPUP_PARAM_NAME as p, normalizeBetterAuthError as r, AuthError as s, mapBetterAuthIdentity as t, CLOCK_SKEW_BUFFER_MS as u };
|