import { eq } from 'drizzle-orm'; import { db, schema } from '@linkdr/db'; /** * A fixed test account for local development. * * Signing in normally needs a real handset to receive a real SMS, which makes * the whole app untestable without a phone in your hand and Twilio credits. This * pins one number to one known code so `pnpm dev` is usable. * * THREE independent guards, because a login bypass reaching production is the * worst bug this codebase could ship: * * 1. NODE_ENV must not be 'production'. * 2. ALLOW_DEV_LOGIN must be explicitly 'true' — being in dev is not enough. * 3. The phone number must match exactly. * * Any one of them failing falls straight back to the real OTP path. */ const DEV_PHONE = '+525500000000'; const DEV_CODE = '000000'; /** * DEMO_LOGIN — the same fixed login, deliberately permitted in a production * BUILD, for the client-demo deployment at linkdr.serfaty.site. * * This is a login bypass running under NODE_ENV=production and there is no way * to dress that up. It is a separate variable from ALLOW_DEV_LOGIN on purpose: * the two say different things, and someone copying a dev `.env` into a real * environment must not be able to enable this by accident. Guard 3 still holds * — only DEV_PHONE is affected, every other number goes through Twilio. * * What makes it acceptable HERE and nowhere else: that deployment contains * nothing but seeded fixtures, and the account it opens is a seeded customer. * There is no real person's data behind it. * * Before this platform takes a real signup, DEMO_LOGIN must be unset and this * block deleted. The boot warning below exists so that is impossible to * forget: it prints on every single start. */ const demoLogin = process.env.DEMO_LOGIN === 'true'; if (demoLogin && process.env.NODE_ENV === 'production') { console.warn( ` ############################################################ ` + ` # DEMO_LOGIN IS ON IN A PRODUCTION BUILD. # ` + ` # ${DEV_PHONE} signs in with a fixed code and NO SMS. # ` + ` # This is for the client demo only. Unset DEMO_LOGIN # ` + ` # before this platform accepts a real signup. # ` + ` ############################################################ `, ); } export function isDevLoginEnabled(): boolean { // Local development: as before. if (process.env.NODE_ENV !== 'production') { return process.env.ALLOW_DEV_LOGIN === 'true'; } // Production: only the explicit demo flag, never ALLOW_DEV_LOGIN. return demoLogin; } export function isDevLoginPhone(phone: string): boolean { return isDevLoginEnabled() && phone === DEV_PHONE; } /** * Replace the freshly-generated random code with the fixed one. * * better-auth writes the verification row (identifier = the phone number, * value = ":") and only then calls sendOTP, so by the time this * runs there is a row to overwrite. Rewriting the value rather than intercepting * the comparison means the real verify path still runs in full — same expiry, * same attempt cap, same single-use consumption. */ export async function pinDevLoginCode(phone: string): Promise { if (!isDevLoginPhone(phone)) return; await db .update(schema.verifications) .set({ value: `${DEV_CODE}:0` }) .where(eq(schema.verifications.identifier, phone)); console.info(`\n [dev login] ${DEV_PHONE} → code ${DEV_CODE}\n`); } export const DEV_LOGIN = { phone: DEV_PHONE, code: DEV_CODE } as const;