diff --git a/internal/api/src/routes/auth/auth.server.test.ts b/internal/api/src/routes/auth/auth.server.test.ts index 817746af..d57c1d2a 100644 --- a/internal/api/src/routes/auth/auth.server.test.ts +++ b/internal/api/src/routes/auth/auth.server.test.ts @@ -834,6 +834,43 @@ test("POST /signup validates password length", async () => { expect(res.status).toBe(400); }); +test("POST /signup without email verification creates session and redirects to chat", async () => { + const { url, bindings } = await serve({ + bindings: { + sendEmail: undefined, // Disable email verification + }, + }); + const email = "noeverify@example.com"; + const password = "securepassword123"; + + const res = await fetch(`${url}/api/auth/signup`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email, password }), + }); + + expect(res.status).toBe(200); + const data = await res.json(); + expect(data.ok).toBe(true); + expect(data.redirect_url).toBe("/chat"); + + // Verify user was created + const db = await bindings.database(); + const user = await db.selectUserByEmail(email); + expect(user).toBeDefined(); + expect(user?.email).toBe(email); + + // Verify session cookie was set (auto-login) + const cookies = res.headers.getSetCookie?.() || [ + res.headers.get("Set-Cookie") || "", + ]; + const cookieString = cookies.join("; "); + expect(cookieString).toContain("blink_session_token="); + expect(cookieString).toContain("last_login_provider=credentials"); +}); + test("POST /resend-email-verification regenerates token", async () => { const { url, helpers, bindings } = await serve(); const { user } = await helpers.createUser({ diff --git a/internal/api/src/routes/auth/auth.server.ts b/internal/api/src/routes/auth/auth.server.ts index 4cc68e20..c56eb97f 100644 --- a/internal/api/src/routes/auth/auth.server.ts +++ b/internal/api/src/routes/auth/auth.server.ts @@ -1045,7 +1045,34 @@ export default function mountAuth(server: APIServer) { return c.json({ ok: true, redirect_url: redirectUrl }); } else { - // No email verification needed - redirect directly to chat + // No email verification needed - create session and redirect to chat + const sessionToken = await encode({ + secret: c.env.AUTH_SECRET, + token: { + sub: user.id, + id: user.id, + email: user.email, + name: user.display_name, + }, + salt: SESSION_COOKIE_NAME, + }); + + setCookie(c, SESSION_COOKIE_NAME, sessionToken, { + path: "/", + httpOnly: true, + sameSite: "Lax", + secure: SESSION_SECURE, + maxAge: 30 * 24 * 60 * 60, // 30 days + }); + + setCookie(c, "last_login_provider", "credentials", { + path: "/", + httpOnly: true, + sameSite: "Lax", + secure: SESSION_SECURE, + maxAge: 60 * 60 * 24 * 180, // 180 days + }); + return c.json({ ok: true, redirect_url: "/chat" }); } } diff --git a/internal/api/src/test.ts b/internal/api/src/test.ts index f7fe18b9..c2b5b484 100644 --- a/internal/api/src/test.ts +++ b/internal/api/src/test.ts @@ -271,11 +271,12 @@ export const serve = async (options?: ServeOptions) => { ...options?.bindings?.runtime, }, sendEmail: - options?.bindings?.sendEmail ?? - (async (email) => { - // Mock email service for tests - just log - console.log("Mock email sent:", email.type, email.email); - }), + options?.bindings && "sendEmail" in options.bindings + ? options.bindings.sendEmail + : async (email) => { + // Mock email service for tests - just log + console.log("Mock email sent:", email.type, email.email); + }, sendTelemetryEvent: options?.bindings?.sendTelemetryEvent ?? (async (event) => {