From 98f51117be6cb39b936667c873801fac3797f8a4 Mon Sep 17 00:00:00 2001 From: Alex Yang Date: Tue, 9 Dec 2025 04:57:06 +0900 Subject: [PATCH] fix(adapter): allow run internal adapter outside context (#6617) --- .../src/db/internal-adapter.test.ts | 181 +++++++----------- packages/better-auth/src/db/with-hooks.ts | 10 +- packages/core/src/types/init-options.ts | 48 ++--- 3 files changed, 101 insertions(+), 138 deletions(-) diff --git a/packages/better-auth/src/db/internal-adapter.test.ts b/packages/better-auth/src/db/internal-adapter.test.ts index 02152147ce..d355a73142 100644 --- a/packages/better-auth/src/db/internal-adapter.test.ts +++ b/packages/better-auth/src/db/internal-adapter.test.ts @@ -1,5 +1,4 @@ import type { GenericEndpointContext } from "@better-auth/core"; -import { runWithEndpointContext } from "@better-auth/core/context"; import { safeJSONParse } from "@better-auth/core/utils"; import Database from "better-sqlite3"; import { Kysely, SqliteDialect } from "kysely"; @@ -122,22 +121,20 @@ describe("internal adapter test", async () => { const internalAdapter = authContext.internalAdapter; it("should create oauth user with custom generate id", async () => { - const user = await runWithEndpointContext(ctx, () => - internalAdapter.createOAuthUser( - { - email: "email@email.com", - name: "name", - emailVerified: false, - }, - { - providerId: "provider", - accountId: "account", - accessTokenExpiresAt: new Date(), - refreshTokenExpiresAt: new Date(), - createdAt: new Date(), - updatedAt: new Date(), - }, - ), + const user = await internalAdapter.createOAuthUser( + { + email: "email@email.com", + name: "name", + emailVerified: false, + }, + { + providerId: "provider", + accountId: "account", + accessTokenExpiresAt: new Date(), + refreshTokenExpiresAt: new Date(), + createdAt: new Date(), + updatedAt: new Date(), + }, ); expect(user).toMatchObject({ user: { @@ -184,26 +181,22 @@ describe("internal adapter test", async () => { }); it("should delete expired verification values on find", async () => { - await runWithEndpointContext(ctx, () => - internalAdapter.createVerificationValue({ - identifier: `test-id-1`, - value: "test-id-1", - expiresAt: new Date(Date.now() - 1000), - }), - ); + await internalAdapter.createVerificationValue({ + identifier: `test-id-1`, + value: "test-id-1", + expiresAt: new Date(Date.now() - 1000), + }); const value = await internalAdapter.findVerificationValue("test-id-1"); expect(value).toMatchObject({ identifier: "test-id-1", }); const value2 = await internalAdapter.findVerificationValue("test-id-1"); expect(value2).toBe(undefined); - await runWithEndpointContext(ctx, () => - internalAdapter.createVerificationValue({ - identifier: `test-id-1`, - value: "test-id-1", - expiresAt: new Date(Date.now() + 1000), - }), - ); + await internalAdapter.createVerificationValue({ + identifier: `test-id-1`, + value: "test-id-1", + expiresAt: new Date(Date.now() + 1000), + }); const value3 = await internalAdapter.findVerificationValue("test-id-1"); expect(value3).toMatchObject({ identifier: "test-id-1", @@ -336,9 +329,7 @@ describe("internal adapter test", async () => { }; // Create a user in the database first - await runWithEndpointContext(ctx, () => - ctx.context.internalAdapter.createUser(testUser), - ); + await ctx.context.internalAdapter.createUser(testUser); // Test case 1: Session with fractional seconds in TTL const expiresAt = new Date(Date.now() + 3599500); // 59 minutes and 59.5 seconds from now @@ -369,11 +360,9 @@ describe("internal adapter test", async () => { ); // Trigger refreshUserSessions by updating the user - await runWithEndpointContext(ctx, () => - ctx.context.internalAdapter.updateUser(testUser.id, { - name: "Updated Name", - }), - ); + await ctx.context.internalAdapter.updateUser(testUser.id, { + name: "Updated Name", + }); // The TTL should be properly rounded down const lastTTL = capturedTTLs[capturedTTLs.length - 1]; @@ -403,11 +392,9 @@ describe("internal adapter test", async () => { JSON.stringify({ session: almostExpiredSession, user: testUser }), ); - await runWithEndpointContext(ctx, () => - ctx.context.internalAdapter.updateUser(testUser.id, { - name: "Updated Again", - }), - ); + await ctx.context.internalAdapter.updateUser(testUser.id, { + name: "Updated Again", + }); // Should be rounded down to 0 expect(capturedTTLs.at(-1)).toBe(0); @@ -435,11 +422,9 @@ describe("internal adapter test", async () => { JSON.stringify({ session: longSession, user: testUser }), ); - await runWithEndpointContext(ctx, () => - ctx.context.internalAdapter.updateUser(testUser.id, { - name: "Final Update", - }), - ); + await ctx.context.internalAdapter.updateUser(testUser.id, { + name: "Final Update", + }); // Should be rounded down to 7199 const finalTTL = capturedTTLs.at(-1); @@ -451,15 +436,11 @@ describe("internal adapter test", async () => { // Create session const now = Date.now(); const expiresAt = new Date(now + 60 * 60 * 24 * 7 * 1000); - const user = await runWithEndpointContext(ctx, () => - internalAdapter.createUser({ - name: "test-user", - email: "test@email.com", - }), - ); - const session = await runWithEndpointContext(ctx, () => - internalAdapter.createSession(user.id), - ); + const user = await internalAdapter.createUser({ + name: "test-user", + email: "test@email.com", + }); + const session = await internalAdapter.createSession(user.id); const storedSessions: { token: string; expiresAt: number }[] = JSON.parse( map.get(`active-sessions-${user.id}`), ); @@ -505,15 +486,13 @@ describe("internal adapter test", async () => { for (let i = -5; i < 5; i++) { const expiresIn = i * 60 * 60 * 24 * 1000; const expiresAt = new Date(now + expiresIn); - await runWithEndpointContext(ctx, () => - internalAdapter.createSession( - userId, - undefined, - { - expiresAt, - }, - true, - ), + await internalAdapter.createSession( + userId, + undefined, + { + expiresAt, + }, + true, ); if (i > 0) { const actualExp = expirationMap.get(`active-sessions-${userId}`); @@ -535,9 +514,7 @@ describe("internal adapter test", async () => { expect(tokenStored).toBeDefined(); // Delete session should clean expiresAt and token - await runWithEndpointContext(ctx, () => - internalAdapter.deleteSession(token!), - ); + await internalAdapter.deleteSession(token!); const afterDeleted: { token: string; expiresAt: number }[] = JSON.parse( map.get(`active-sessions-${userId}`), ); @@ -557,62 +534,48 @@ describe("internal adapter test", async () => { }); it("should delete a single account", async () => { - const user = await runWithEndpointContext(ctx, () => - internalAdapter.createUser({ - name: "Account Delete User", - email: "account.delete@example.com", - }), - ); + const user = await internalAdapter.createUser({ + name: "Account Delete User", + email: "account.delete@example.com", + }); - const account = await runWithEndpointContext(ctx, () => - internalAdapter.createAccount({ - userId: user.id, - providerId: "test-provider", - accountId: "test-account-id-1", - }), - ); + const account = await internalAdapter.createAccount({ + userId: user.id, + providerId: "test-provider", + accountId: "test-account-id-1", + }); let foundAccount = await internalAdapter.findAccount(account.accountId); expect(foundAccount).toBeDefined(); - await runWithEndpointContext(ctx, () => - internalAdapter.deleteAccount(account.id), - ); + await internalAdapter.deleteAccount(account.id); foundAccount = await internalAdapter.findAccount(account.accountId); expect(foundAccount).toBeNull(); }); it("should delete multiple accounts for a user", async () => { - const user = await runWithEndpointContext(ctx, () => - internalAdapter.createUser({ - name: "Accounts Delete User", - email: "accounts.delete@example.com", - }), - ); + const user = await internalAdapter.createUser({ + name: "Accounts Delete User", + email: "accounts.delete@example.com", + }); - await runWithEndpointContext(ctx, () => - internalAdapter.createAccount({ - userId: user.id, - providerId: "test-provider-1", - accountId: "test-account-id-2", - }), - ); + await internalAdapter.createAccount({ + userId: user.id, + providerId: "test-provider-1", + accountId: "test-account-id-2", + }); - await runWithEndpointContext(ctx, () => - internalAdapter.createAccount({ - userId: user.id, - providerId: "test-provider-2", - accountId: "test-account-id-3", - }), - ); + await internalAdapter.createAccount({ + userId: user.id, + providerId: "test-provider-2", + accountId: "test-account-id-3", + }); let accounts = await internalAdapter.findAccounts(user.id); expect(accounts.length).toBe(2); - await runWithEndpointContext(ctx, () => - internalAdapter.deleteAccounts(user.id), - ); + await internalAdapter.deleteAccounts(user.id); accounts = await internalAdapter.findAccounts(user.id); expect(accounts.length).toBe(0); diff --git a/packages/better-auth/src/db/with-hooks.ts b/packages/better-auth/src/db/with-hooks.ts index ac85370412..5559a460f6 100644 --- a/packages/better-auth/src/db/with-hooks.ts +++ b/packages/better-auth/src/db/with-hooks.ts @@ -24,7 +24,7 @@ export function getWithHooks( } | undefined, ) { - const context = await getCurrentAuthContext(); + const context = await getCurrentAuthContext().catch(() => null); let actualData = data; for (const hook of hooks || []) { const toRun = hook[model]?.create?.before; @@ -78,7 +78,7 @@ export function getWithHooks( } | undefined, ) { - const context = await getCurrentAuthContext(); + const context = await getCurrentAuthContext().catch(() => null); let actualData = data; for (const hook of hooks || []) { @@ -133,7 +133,7 @@ export function getWithHooks( } | undefined, ) { - const context = await getCurrentAuthContext(); + const context = await getCurrentAuthContext().catch(() => null); let actualData = data; for (const hook of hooks || []) { @@ -188,7 +188,7 @@ export function getWithHooks( } | undefined, ) { - const context = await getCurrentAuthContext(); + const context = await getCurrentAuthContext().catch(() => null); let entityToDelete: T | null = null; try { @@ -250,7 +250,7 @@ export function getWithHooks( } | undefined, ) { - const context = await getCurrentAuthContext(); + const context = await getCurrentAuthContext().catch(() => null); let entitiesToDelete: T[] = []; try { diff --git a/packages/core/src/types/init-options.ts b/packages/core/src/types/init-options.ts index f0683c0c43..f05e0537f6 100644 --- a/packages/core/src/types/init-options.ts +++ b/packages/core/src/types/init-options.ts @@ -995,7 +995,7 @@ export type BetterAuthOptions = { */ before?: ( user: User & Record, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise< | boolean | void @@ -1008,7 +1008,7 @@ export type BetterAuthOptions = { */ after?: ( user: User & Record, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise; }; update?: { @@ -1019,7 +1019,7 @@ export type BetterAuthOptions = { */ before?: ( user: Partial & Record, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise< | boolean | void @@ -1032,7 +1032,7 @@ export type BetterAuthOptions = { */ after?: ( user: User & Record, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise; }; delete?: { @@ -1042,14 +1042,14 @@ export type BetterAuthOptions = { */ before?: ( user: User & Record, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise; /** * Hook that is called after a user is deleted. */ after?: ( user: User & Record, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise; }; }; @@ -1065,7 +1065,7 @@ export type BetterAuthOptions = { */ before?: ( session: Session & Record, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise< | boolean | void @@ -1078,7 +1078,7 @@ export type BetterAuthOptions = { */ after?: ( session: Session & Record, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise; }; /** @@ -1092,7 +1092,7 @@ export type BetterAuthOptions = { */ before?: ( session: Partial & Record, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise< | boolean | void @@ -1105,7 +1105,7 @@ export type BetterAuthOptions = { */ after?: ( session: Session & Record, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise; }; delete?: { @@ -1115,14 +1115,14 @@ export type BetterAuthOptions = { */ before?: ( session: Session & Record, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise; /** * Hook that is called after a session is deleted. */ after?: ( session: Session & Record, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise; }; }; @@ -1138,7 +1138,7 @@ export type BetterAuthOptions = { */ before?: ( account: Account, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise< | boolean | void @@ -1151,7 +1151,7 @@ export type BetterAuthOptions = { */ after?: ( account: Account, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise; }; /** @@ -1165,7 +1165,7 @@ export type BetterAuthOptions = { */ before?: ( account: Partial & Record, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise< | boolean | void @@ -1178,7 +1178,7 @@ export type BetterAuthOptions = { */ after?: ( account: Account & Record, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise; }; delete?: { @@ -1188,14 +1188,14 @@ export type BetterAuthOptions = { */ before?: ( account: Account & Record, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise; /** * Hook that is called after an account is deleted. */ after?: ( account: Account & Record, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise; }; }; @@ -1211,7 +1211,7 @@ export type BetterAuthOptions = { */ before?: ( verification: Verification & Record, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise< | boolean | void @@ -1224,7 +1224,7 @@ export type BetterAuthOptions = { */ after?: ( verification: Verification & Record, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise; }; update?: { @@ -1235,7 +1235,7 @@ export type BetterAuthOptions = { */ before?: ( verification: Partial & Record, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise< | boolean | void @@ -1248,7 +1248,7 @@ export type BetterAuthOptions = { */ after?: ( verification: Verification & Record, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise; }; delete?: { @@ -1258,14 +1258,14 @@ export type BetterAuthOptions = { */ before?: ( verification: Verification & Record, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise; /** * Hook that is called after a verification is deleted. */ after?: ( verification: Verification & Record, - context?: GenericEndpointContext, + context: GenericEndpointContext | null, ) => Promise; }; };