mirror of
https://github.com/nicotsx/zerobyte.git
synced 2026-05-24 16:42:43 -04:00
fix(2fa): add missing 2fa column (#917)
This commit is contained in:
1
app/drizzle/20260522174225_organic_nehzno/migration.sql
Normal file
1
app/drizzle/20260522174225_organic_nehzno/migration.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE `two_factor` ADD `verified` integer DEFAULT true NOT NULL;
|
||||
2821
app/drizzle/20260522174225_organic_nehzno/snapshot.json
Normal file
2821
app/drizzle/20260522174225_organic_nehzno/snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -505,6 +505,7 @@ export const twoFactor = sqliteTable(
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => usersTable.id, { onDelete: "cascade" }),
|
||||
verified: integer("verified", { mode: "boolean" }).notNull().default(true),
|
||||
},
|
||||
(table) => [index("twoFactor_secret_idx").on(table.secret), index("twoFactor_userId_idx").on(table.userId)],
|
||||
);
|
||||
|
||||
1
bun.lock
1
bun.lock
@@ -71,6 +71,7 @@
|
||||
"zod": "^4.4.3",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@better-auth/utils": "0.4.0",
|
||||
"@effect/language-service": "^0.86.1",
|
||||
"@faker-js/faker": "^10.4.0",
|
||||
"@hey-api/openapi-ts": "^0.97.1",
|
||||
|
||||
51
e2e/0005-two-factor.spec.ts
Normal file
51
e2e/0005-two-factor.spec.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import type { Page } from "@playwright/test";
|
||||
import { base32 } from "@better-auth/utils/base32";
|
||||
import { createOTP } from "@better-auth/utils/otp";
|
||||
import { expect, test } from "./test";
|
||||
import { gotoAndWaitForAppReady } from "./helpers/page";
|
||||
|
||||
const workerPassword = "password123";
|
||||
|
||||
async function generateTotp(secret: string) {
|
||||
const decodedSecret = new TextDecoder().decode(base32.decode(secret));
|
||||
return createOTP(decodedSecret).totp();
|
||||
}
|
||||
|
||||
async function fillOtp(page: Page, code: string) {
|
||||
await page.locator('[data-slot="input-otp"]').click();
|
||||
await page.keyboard.type(code);
|
||||
}
|
||||
|
||||
test("user can enable 2FA and sign in with a TOTP code", async ({ page }) => {
|
||||
await gotoAndWaitForAppReady(page, "/settings");
|
||||
|
||||
const username = await page.locator("#username").inputValue();
|
||||
|
||||
await page.getByRole("button", { name: "Enable 2FA" }).click();
|
||||
await page.getByRole("textbox", { name: "Password" }).fill(workerPassword);
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
|
||||
await expect(page.getByRole("heading", { name: "Scan QR Code" })).toBeVisible();
|
||||
const secret = await page.locator("input[readonly]").inputValue();
|
||||
|
||||
await page.getByRole("button", { name: "Continue" }).click();
|
||||
await expect(page.getByRole("heading", { name: "Verify setup" })).toBeVisible();
|
||||
|
||||
await fillOtp(page, await generateTotp(secret));
|
||||
|
||||
await expect(page.getByText(/Status:\s*Enabled/)).toBeVisible();
|
||||
|
||||
await page.context().clearCookies();
|
||||
await gotoAndWaitForAppReady(page, "/login");
|
||||
|
||||
await page.getByRole("textbox", { name: "Username" }).fill(username);
|
||||
await page.getByRole("textbox", { name: "Password" }).fill(workerPassword);
|
||||
await page.getByRole("button", { name: "Login" }).click();
|
||||
|
||||
await expect(page.getByRole("heading", { name: "Two-Factor Authentication" })).toBeVisible();
|
||||
|
||||
await fillOtp(page, await generateTotp(secret));
|
||||
|
||||
await expect(page).toHaveURL("/volumes");
|
||||
await expect(page.getByRole("heading", { name: "No volume" })).toBeVisible();
|
||||
});
|
||||
@@ -102,6 +102,7 @@
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@better-auth/utils": "0.4.0",
|
||||
"@effect/language-service": "^0.86.1",
|
||||
"@faker-js/faker": "^10.4.0",
|
||||
"@hey-api/openapi-ts": "^0.97.1",
|
||||
|
||||
Reference in New Issue
Block a user