fix(two-factor): wire twoFactorTable option to schema modelName (#8443)

This commit is contained in:
Alex Yang
2026-03-06 00:12:48 -08:00
committed by GitHub
parent 71c3a85d2f
commit a92a71ef8a
3 changed files with 55 additions and 1 deletions

View File

@@ -441,7 +441,15 @@ export const twoFactor = <O extends TwoFactorOptions>(options?: O) => {
},
],
},
schema: mergeSchema(schema, options?.schema),
schema: mergeSchema(schema, {
...options?.schema,
twoFactor: {
...options?.schema?.twoFactor,
...(options?.twoFactorTable
? { modelName: options.twoFactorTable }
: {}),
},
}),
rateLimit: [
{
pathMatcher(path) {

View File

@@ -1394,6 +1394,45 @@ describe("twoFactorCookieMaxAge", async () => {
});
});
/**
* @see https://github.com/better-auth/better-auth/issues/8424
*/
describe("twoFactorTable option", async () => {
const { auth, signInWithTestUser, testUser, db } = await getTestInstance({
secret: DEFAULT_SECRET,
plugins: [
twoFactor({
twoFactorTable: "custom_two_factor",
skipVerificationOnEnable: true,
}),
],
});
let { headers } = await signInWithTestUser();
it("should use custom table name for two factor data", async () => {
const enableRes = await auth.api.enableTwoFactor({
body: { password: testUser.password },
headers,
asResponse: true,
});
expect(enableRes.status).toBe(200);
headers = convertSetCookieToCookie(enableRes.headers);
const twoFactorRecord = await db.findOne<TwoFactorTable>({
model: "twoFactor",
where: [
{
field: "userId",
value: (await auth.api.getSession({ headers }))?.user.id as string,
},
],
});
expect(twoFactorRecord).toBeDefined();
expect(twoFactorRecord?.secret).toBeDefined();
});
});
describe("OTP storage modes", async () => {
describe("hashed OTP storage", async () => {
let OTP = "";

View File

@@ -10,6 +10,13 @@ export interface TwoFactorOptions {
* Application Name
*/
issuer?: string | undefined;
/**
* The name of the table that stores the two factor
* authentication data.
*
* @default "twoFactor"
*/
twoFactorTable?: string | undefined;
/**
* TOTP OPtions
*/