@neondatabase/auth 0.1.0-beta.12 → 0.1.0-beta.13
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 +20 -1
- package/dist/{adapter-core-Bw9mn_AS.d.mts → adapter-core-D1HVvYeG.d.mts} +162 -130
- package/dist/{adapter-core-DODfSw9P.mjs → adapter-core-Ed-EN_tr.mjs} +160 -35
- package/dist/better-auth-react-adapter-C4kQ31os.d.mts +2194 -0
- package/dist/{better-auth-react-adapter-7JyvHiYG.mjs → better-auth-react-adapter-COSlFmvp.mjs} +1 -6
- package/dist/{chunk-5DLVHPZS-Bxj7snpZ-DoVNlsyk.mjs → chunk-5DLVHPZS-Bxj7snpZ-EhdAQJMu.mjs} +2 -2
- package/dist/index.d.mts +9 -3
- package/dist/index.mjs +1 -1
- package/dist/{neon-auth-Bf2NFptR.mjs → neon-auth-BHWfv-bR.mjs} +4 -3
- package/dist/next/index.d.mts +45 -30
- package/dist/next/index.mjs +2 -2
- package/dist/react/adapters/index.d.mts +2 -2
- package/dist/react/adapters/index.mjs +1 -1
- package/dist/react/index.d.mts +2 -2
- package/dist/react/index.mjs +3 -3
- package/dist/react/ui/index.mjs +2 -2
- package/dist/react/ui/server.mjs +1 -1
- package/dist/supabase-adapter-BFja3Oys.d.mts +2280 -0
- package/dist/{supabase-adapter-DdhH8btX.mjs → supabase-adapter-NVDAeWHu.mjs} +1 -11
- package/dist/{ui-aMoA-9nq.mjs → ui-C1IRQzLY.mjs} +49 -49
- package/dist/vanilla/adapters/index.d.mts +2 -2
- package/dist/vanilla/adapters/index.mjs +1 -1
- package/dist/vanilla/index.d.mts +2 -2
- package/dist/vanilla/index.mjs +1 -1
- package/package.json +4 -4
- package/dist/better-auth-react-adapter-JoscqoDc.d.mts +0 -722
- package/dist/supabase-adapter-Clxlqg1x.d.mts +0 -127
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getGlobalBroadcastChannel } from "better-auth/client";
|
|
2
2
|
import { adminClient, anonymousClient, emailOTPClient, jwtClient, organizationClient } from "better-auth/client/plugins";
|
|
3
|
+
import z from "zod";
|
|
3
4
|
|
|
4
5
|
//#region src/core/in-flight-request-manager.ts
|
|
5
6
|
/**
|
|
@@ -136,16 +137,68 @@ function getJwtExpiration(jwt) {
|
|
|
136
137
|
}
|
|
137
138
|
}
|
|
138
139
|
|
|
140
|
+
//#endregion
|
|
141
|
+
//#region src/core/token-cache.ts
|
|
142
|
+
var TokenCache = class {
|
|
143
|
+
cache = null;
|
|
144
|
+
/**
|
|
145
|
+
* Get cached data if not expired.
|
|
146
|
+
* Returns null if cache is empty or expired.
|
|
147
|
+
*/
|
|
148
|
+
get() {
|
|
149
|
+
if (!this.cache) return null;
|
|
150
|
+
if (Date.now() > this.cache.expiresAt) {
|
|
151
|
+
this.cache = null;
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
return this.cache.data;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Set cached data with TTL.
|
|
158
|
+
* If jwt is provided, TTL is calculated from its expiration.
|
|
159
|
+
* Otherwise, uses default SESSION_CACHE_TTL_MS.
|
|
160
|
+
*/
|
|
161
|
+
set(data, jwt) {
|
|
162
|
+
const ttl = this.calculateTTL(jwt);
|
|
163
|
+
this.cache = {
|
|
164
|
+
data,
|
|
165
|
+
expiresAt: Date.now() + ttl
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Clear the cache.
|
|
170
|
+
*/
|
|
171
|
+
clear() {
|
|
172
|
+
this.cache = null;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Check if cache has valid (non-expired) data.
|
|
176
|
+
*/
|
|
177
|
+
has() {
|
|
178
|
+
return this.get() !== null;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Calculate cache TTL from JWT expiration.
|
|
182
|
+
* Falls back to default TTL if JWT is invalid or missing.
|
|
183
|
+
*/
|
|
184
|
+
calculateTTL(jwt) {
|
|
185
|
+
if (!jwt) return SESSION_CACHE_TTL_MS;
|
|
186
|
+
const exp = getJwtExpiration(jwt);
|
|
187
|
+
if (!exp) return SESSION_CACHE_TTL_MS;
|
|
188
|
+
const now = Date.now();
|
|
189
|
+
const ttl = exp * 1e3 - now - CLOCK_SKEW_BUFFER_MS;
|
|
190
|
+
return Math.max(ttl, 1e3);
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
|
|
139
194
|
//#endregion
|
|
140
195
|
//#region src/core/session-cache-manager.ts
|
|
141
196
|
/**
|
|
142
197
|
* Manages in-memory session cache with TTL expiration.
|
|
143
198
|
*
|
|
144
|
-
*
|
|
145
|
-
* - Stores sessions in Better Auth native format
|
|
146
|
-
* - Automatic expiration based on JWT token expiration
|
|
199
|
+
* Built on TokenCache, adding session-specific features:
|
|
147
200
|
* - Invalidation flag for sign-out scenarios
|
|
148
|
-
* -
|
|
201
|
+
* - Token refresh detection via lastSessionData comparison
|
|
149
202
|
*
|
|
150
203
|
* Example:
|
|
151
204
|
* ```typescript
|
|
@@ -155,48 +208,40 @@ function getJwtExpiration(jwt) {
|
|
|
155
208
|
* ```
|
|
156
209
|
*/
|
|
157
210
|
var SessionCacheManager = class {
|
|
158
|
-
cache =
|
|
211
|
+
cache = new TokenCache();
|
|
159
212
|
lastSessionData = null;
|
|
213
|
+
invalidated = false;
|
|
160
214
|
/**
|
|
161
215
|
* Get cached session if valid and not expired.
|
|
162
216
|
* Returns null if cache is invalid, expired, or doesn't exist.
|
|
163
217
|
*/
|
|
164
218
|
getCachedSession() {
|
|
165
|
-
if (
|
|
166
|
-
|
|
167
|
-
this.clearSessionCache();
|
|
168
|
-
return null;
|
|
169
|
-
}
|
|
170
|
-
return this.cache.data;
|
|
219
|
+
if (this.invalidated) return null;
|
|
220
|
+
return this.cache.get();
|
|
171
221
|
}
|
|
172
222
|
/**
|
|
173
|
-
* Set cached session with
|
|
174
|
-
* If TTL not provided, calculates from JWT expiration.
|
|
223
|
+
* Set cached session with JWT-based TTL.
|
|
175
224
|
* Skips caching if cache was invalidated (sign-out scenario).
|
|
176
225
|
*/
|
|
177
|
-
setCachedSession(data
|
|
178
|
-
if (this.
|
|
179
|
-
this.lastSessionData = this.cache
|
|
180
|
-
|
|
181
|
-
this.cache = {
|
|
182
|
-
data,
|
|
183
|
-
expiresAt: Date.now() + calculatedTtl,
|
|
184
|
-
invalidated: false
|
|
185
|
-
};
|
|
226
|
+
setCachedSession(data) {
|
|
227
|
+
if (this.invalidated) return;
|
|
228
|
+
this.lastSessionData = this.cache.get();
|
|
229
|
+
this.cache.set(data, data.session.token);
|
|
186
230
|
}
|
|
187
231
|
/**
|
|
188
232
|
* Invalidate cache (marks as invalid but doesn't clear).
|
|
189
233
|
* Useful for sign-out scenarios where in-flight requests should not cache.
|
|
190
234
|
*/
|
|
191
235
|
invalidateSessionCache() {
|
|
192
|
-
|
|
236
|
+
this.invalidated = true;
|
|
193
237
|
}
|
|
194
238
|
/**
|
|
195
239
|
* Clear cache completely.
|
|
196
240
|
*/
|
|
197
241
|
clearSessionCache() {
|
|
198
|
-
this.cache
|
|
242
|
+
this.cache.clear();
|
|
199
243
|
this.lastSessionData = null;
|
|
244
|
+
this.invalidated = false;
|
|
200
245
|
}
|
|
201
246
|
/**
|
|
202
247
|
* Check if token was refreshed by comparing tokens with previous session.
|
|
@@ -206,17 +251,45 @@ var SessionCacheManager = class {
|
|
|
206
251
|
if (!this.lastSessionData?.session?.token || !data?.session?.token) return false;
|
|
207
252
|
return this.lastSessionData.session.token !== data.session.token;
|
|
208
253
|
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
//#endregion
|
|
257
|
+
//#region src/core/anonymous-token-cache-manager.ts
|
|
258
|
+
/**
|
|
259
|
+
* Manages in-memory anonymous token cache with TTL expiration.
|
|
260
|
+
*
|
|
261
|
+
* Stores the full anonymous token response (token + expires_at).
|
|
262
|
+
* Unlike SessionCacheManager, doesn't need:
|
|
263
|
+
* - Invalidation flag (no sign-out scenario for anonymous tokens)
|
|
264
|
+
* - Refresh detection (anonymous tokens are stateless)
|
|
265
|
+
*/
|
|
266
|
+
var AnonymousTokenCacheManager = class {
|
|
267
|
+
cache = new TokenCache();
|
|
209
268
|
/**
|
|
210
|
-
*
|
|
211
|
-
*
|
|
269
|
+
* Get cached anonymous token response if not expired.
|
|
270
|
+
* Returns null if cache is empty or expired.
|
|
212
271
|
*/
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
272
|
+
getCachedResponse() {
|
|
273
|
+
return this.cache.get();
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Set cached anonymous token response with JWT-based TTL.
|
|
277
|
+
* TTL is automatically calculated from the JWT expiration.
|
|
278
|
+
*/
|
|
279
|
+
setCachedResponse(data) {
|
|
280
|
+
this.cache.set(data, data.token);
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Clear the cache.
|
|
284
|
+
*/
|
|
285
|
+
clearCache() {
|
|
286
|
+
this.cache.clear();
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Check if cache has a valid (non-expired) response.
|
|
290
|
+
*/
|
|
291
|
+
hasCachedResponse() {
|
|
292
|
+
return this.cache.has();
|
|
220
293
|
}
|
|
221
294
|
};
|
|
222
295
|
|
|
@@ -230,11 +303,34 @@ const isBrowser = () => {
|
|
|
230
303
|
return globalThis.window !== void 0 && typeof document !== "undefined";
|
|
231
304
|
};
|
|
232
305
|
|
|
306
|
+
//#endregion
|
|
307
|
+
//#region src/plugins/anonymous-token.ts
|
|
308
|
+
const ANONYMOUS_TOKEN_ENDPOINT = "/token/anonymous";
|
|
309
|
+
const anonymousTokenResponseSchema = z.object({
|
|
310
|
+
token: z.string(),
|
|
311
|
+
expires_at: z.number()
|
|
312
|
+
});
|
|
313
|
+
const anonymousTokenClient = () => {
|
|
314
|
+
return {
|
|
315
|
+
id: "anonymous-token",
|
|
316
|
+
pathMethods: { [ANONYMOUS_TOKEN_ENDPOINT]: "GET" },
|
|
317
|
+
getActions: ($fetch) => {
|
|
318
|
+
return { getAnonymousToken: async (fetchOptions) => {
|
|
319
|
+
return await $fetch(ANONYMOUS_TOKEN_ENDPOINT, {
|
|
320
|
+
method: "GET",
|
|
321
|
+
...fetchOptions
|
|
322
|
+
});
|
|
323
|
+
} };
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
};
|
|
327
|
+
|
|
233
328
|
//#endregion
|
|
234
329
|
//#region src/core/better-auth-methods.ts
|
|
235
330
|
const CURRENT_TAB_CLIENT_ID = crypto.randomUUID();
|
|
236
331
|
const BETTER_AUTH_METHODS_IN_FLIGHT_REQUESTS = new InFlightRequestManager();
|
|
237
332
|
const BETTER_AUTH_METHODS_CACHE = new SessionCacheManager();
|
|
333
|
+
const BETTER_AUTH_ANONYMOUS_TOKEN_CACHE = new AnonymousTokenCacheManager();
|
|
238
334
|
const BETTER_AUTH_ENDPOINTS = {
|
|
239
335
|
signUp: "/sign-up",
|
|
240
336
|
signIn: "/sign-in",
|
|
@@ -242,7 +338,8 @@ const BETTER_AUTH_ENDPOINTS = {
|
|
|
242
338
|
updateUser: "/update-user",
|
|
243
339
|
getSession: "/get-session",
|
|
244
340
|
token: "/token",
|
|
245
|
-
anonymousSignIn: "/sign-in/anonymous"
|
|
341
|
+
anonymousSignIn: "/sign-in/anonymous",
|
|
342
|
+
anonymousToken: "/token/anonymous"
|
|
246
343
|
};
|
|
247
344
|
const BETTER_AUTH_METHODS_HOOKS = {
|
|
248
345
|
signUp: {
|
|
@@ -338,6 +435,18 @@ const BETTER_AUTH_METHODS_HOOKS = {
|
|
|
338
435
|
}
|
|
339
436
|
}
|
|
340
437
|
}
|
|
438
|
+
},
|
|
439
|
+
anonymousToken: {
|
|
440
|
+
beforeRequest: () => {
|
|
441
|
+
const cachedResponse = BETTER_AUTH_ANONYMOUS_TOKEN_CACHE.getCachedResponse();
|
|
442
|
+
if (!cachedResponse) return null;
|
|
443
|
+
return Response.json(cachedResponse, { status: 200 });
|
|
444
|
+
},
|
|
445
|
+
onRequest: () => {},
|
|
446
|
+
onSuccess: (responseData) => {
|
|
447
|
+
const parsed = anonymousTokenResponseSchema.safeParse(responseData);
|
|
448
|
+
if (parsed.success) BETTER_AUTH_ANONYMOUS_TOKEN_CACHE.setCachedResponse(parsed.data);
|
|
449
|
+
}
|
|
341
450
|
}
|
|
342
451
|
};
|
|
343
452
|
/**
|
|
@@ -396,6 +505,7 @@ function isSessionResponseData(responseData) {
|
|
|
396
505
|
}
|
|
397
506
|
function deriveBetterAuthMethodFromUrl(url) {
|
|
398
507
|
if (url.includes("/sign-in/anonymous")) return "anonymousSignIn";
|
|
508
|
+
if (url.includes(BETTER_AUTH_ENDPOINTS.anonymousToken)) return "anonymousToken";
|
|
399
509
|
if (url.includes(BETTER_AUTH_ENDPOINTS.signIn)) return "signIn";
|
|
400
510
|
if (url.includes(BETTER_AUTH_ENDPOINTS.signUp)) return "signUp";
|
|
401
511
|
if (url.includes(BETTER_AUTH_ENDPOINTS.signOut)) return "signOut";
|
|
@@ -418,7 +528,8 @@ const supportedBetterAuthClientPlugins = [
|
|
|
418
528
|
adminClient(),
|
|
419
529
|
organizationClient(),
|
|
420
530
|
emailOTPClient(),
|
|
421
|
-
anonymousClient()
|
|
531
|
+
anonymousClient(),
|
|
532
|
+
anonymousTokenClient()
|
|
422
533
|
];
|
|
423
534
|
var NeonAuthAdapterCore = class {
|
|
424
535
|
betterAuthOptions;
|
|
@@ -488,6 +599,20 @@ var NeonAuthAdapterCore = class {
|
|
|
488
599
|
};
|
|
489
600
|
initBroadcastChannel();
|
|
490
601
|
}
|
|
602
|
+
/**
|
|
603
|
+
* Get JWT token for authenticated or anonymous access.
|
|
604
|
+
* Single source of truth for token retrieval logic.
|
|
605
|
+
*
|
|
606
|
+
* @param allowAnonymous - When true, fetches anonymous token if no session exists
|
|
607
|
+
* @returns JWT token string or null if unavailable
|
|
608
|
+
*/
|
|
609
|
+
async getJWTToken(allowAnonymous) {
|
|
610
|
+
const client = this.getBetterAuthInstance();
|
|
611
|
+
const session = await client.getSession();
|
|
612
|
+
if (session.data?.session?.token) return session.data.session.token;
|
|
613
|
+
if (allowAnonymous) return (await client.getAnonymousToken()).data?.token ?? null;
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
491
616
|
};
|
|
492
617
|
|
|
493
618
|
//#endregion
|