fix(adapter): allow run internal adapter outside context (#6617)

This commit is contained in:
Alex Yang
2025-12-09 04:57:06 +09:00
committed by GitHub
parent 59aca25bec
commit 98f51117be
3 changed files with 101 additions and 138 deletions

View File

@@ -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);

View File

@@ -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 {

View File

@@ -995,7 +995,7 @@ export type BetterAuthOptions = {
*/
before?: (
user: User & Record<string, unknown>,
context?: GenericEndpointContext,
context: GenericEndpointContext | null,
) => Promise<
| boolean
| void
@@ -1008,7 +1008,7 @@ export type BetterAuthOptions = {
*/
after?: (
user: User & Record<string, unknown>,
context?: GenericEndpointContext,
context: GenericEndpointContext | null,
) => Promise<void>;
};
update?: {
@@ -1019,7 +1019,7 @@ export type BetterAuthOptions = {
*/
before?: (
user: Partial<User> & Record<string, unknown>,
context?: GenericEndpointContext,
context: GenericEndpointContext | null,
) => Promise<
| boolean
| void
@@ -1032,7 +1032,7 @@ export type BetterAuthOptions = {
*/
after?: (
user: User & Record<string, unknown>,
context?: GenericEndpointContext,
context: GenericEndpointContext | null,
) => Promise<void>;
};
delete?: {
@@ -1042,14 +1042,14 @@ export type BetterAuthOptions = {
*/
before?: (
user: User & Record<string, unknown>,
context?: GenericEndpointContext,
context: GenericEndpointContext | null,
) => Promise<boolean | void>;
/**
* Hook that is called after a user is deleted.
*/
after?: (
user: User & Record<string, unknown>,
context?: GenericEndpointContext,
context: GenericEndpointContext | null,
) => Promise<void>;
};
};
@@ -1065,7 +1065,7 @@ export type BetterAuthOptions = {
*/
before?: (
session: Session & Record<string, unknown>,
context?: GenericEndpointContext,
context: GenericEndpointContext | null,
) => Promise<
| boolean
| void
@@ -1078,7 +1078,7 @@ export type BetterAuthOptions = {
*/
after?: (
session: Session & Record<string, unknown>,
context?: GenericEndpointContext,
context: GenericEndpointContext | null,
) => Promise<void>;
};
/**
@@ -1092,7 +1092,7 @@ export type BetterAuthOptions = {
*/
before?: (
session: Partial<Session> & Record<string, unknown>,
context?: GenericEndpointContext,
context: GenericEndpointContext | null,
) => Promise<
| boolean
| void
@@ -1105,7 +1105,7 @@ export type BetterAuthOptions = {
*/
after?: (
session: Session & Record<string, unknown>,
context?: GenericEndpointContext,
context: GenericEndpointContext | null,
) => Promise<void>;
};
delete?: {
@@ -1115,14 +1115,14 @@ export type BetterAuthOptions = {
*/
before?: (
session: Session & Record<string, unknown>,
context?: GenericEndpointContext,
context: GenericEndpointContext | null,
) => Promise<boolean | void>;
/**
* Hook that is called after a session is deleted.
*/
after?: (
session: Session & Record<string, unknown>,
context?: GenericEndpointContext,
context: GenericEndpointContext | null,
) => Promise<void>;
};
};
@@ -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<void>;
};
/**
@@ -1165,7 +1165,7 @@ export type BetterAuthOptions = {
*/
before?: (
account: Partial<Account> & Record<string, unknown>,
context?: GenericEndpointContext,
context: GenericEndpointContext | null,
) => Promise<
| boolean
| void
@@ -1178,7 +1178,7 @@ export type BetterAuthOptions = {
*/
after?: (
account: Account & Record<string, unknown>,
context?: GenericEndpointContext,
context: GenericEndpointContext | null,
) => Promise<void>;
};
delete?: {
@@ -1188,14 +1188,14 @@ export type BetterAuthOptions = {
*/
before?: (
account: Account & Record<string, unknown>,
context?: GenericEndpointContext,
context: GenericEndpointContext | null,
) => Promise<boolean | void>;
/**
* Hook that is called after an account is deleted.
*/
after?: (
account: Account & Record<string, unknown>,
context?: GenericEndpointContext,
context: GenericEndpointContext | null,
) => Promise<void>;
};
};
@@ -1211,7 +1211,7 @@ export type BetterAuthOptions = {
*/
before?: (
verification: Verification & Record<string, unknown>,
context?: GenericEndpointContext,
context: GenericEndpointContext | null,
) => Promise<
| boolean
| void
@@ -1224,7 +1224,7 @@ export type BetterAuthOptions = {
*/
after?: (
verification: Verification & Record<string, unknown>,
context?: GenericEndpointContext,
context: GenericEndpointContext | null,
) => Promise<void>;
};
update?: {
@@ -1235,7 +1235,7 @@ export type BetterAuthOptions = {
*/
before?: (
verification: Partial<Verification> & Record<string, unknown>,
context?: GenericEndpointContext,
context: GenericEndpointContext | null,
) => Promise<
| boolean
| void
@@ -1248,7 +1248,7 @@ export type BetterAuthOptions = {
*/
after?: (
verification: Verification & Record<string, unknown>,
context?: GenericEndpointContext,
context: GenericEndpointContext | null,
) => Promise<void>;
};
delete?: {
@@ -1258,14 +1258,14 @@ export type BetterAuthOptions = {
*/
before?: (
verification: Verification & Record<string, unknown>,
context?: GenericEndpointContext,
context: GenericEndpointContext | null,
) => Promise<boolean | void>;
/**
* Hook that is called after a verification is deleted.
*/
after?: (
verification: Verification & Record<string, unknown>,
context?: GenericEndpointContext,
context: GenericEndpointContext | null,
) => Promise<void>;
};
};