deepen-mfa-passkeys-proof.mjs179 lines · main
1/**
2 * Deepen proof: TOTP MFA + passkeys on Doltgres + Google auth URL with env secrets.
3 *
4 * cd apps/api && bun scripts/deepen-mfa-passkeys-proof.mjs
5 */
6
7process.env.BRIVEN_AUTH_CORE_ENABLED = 'true';
8process.env.BRIVEN_ENV = 'development';
9process.env.BRIVEN_ENGINE_DATABASE_URL =
10 process.env.BRIVEN_ENGINE_DATABASE_URL ??
11 'postgres://postgres:devpass@127.0.0.1:5434/briven_engine?sslmode=disable';
12process.env.BRIVEN_DATA_PLANE_URL =
13 process.env.BRIVEN_DATA_PLANE_URL ??
14 'postgres://postgres:devpass@127.0.0.1:5434/postgres?sslmode=disable';
15process.env.BRIVEN_GOOGLE_CLIENT_ID =
16 process.env.BRIVEN_GOOGLE_CLIENT_ID ?? 'deepen-google.apps.googleusercontent.com';
17process.env.BRIVEN_GOOGLE_CLIENT_SECRET =
18 process.env.BRIVEN_GOOGLE_CLIENT_SECRET ?? 'deepen-google-secret';
19
20const { ensureBrivenEngineDatabase } = await import(
21 '../src/services/auth-core/ensure-db.ts'
22);
23const { initAuthCoreSdk } = await import('../src/services/auth-core/engine.ts');
24const { signUpEmailPassword } = await import(
25 '../src/services/auth-core/emailpassword.ts'
26);
27const {
28 createTotpDevice,
29 generateTotpCode,
30 verifyAndEnableTotpDevice,
31 verifyUserTotp,
32 listTotpDevices,
33} = await import('../src/services/auth-core/mfa.ts');
34const {
35 createRegistrationOptions,
36 finishRegistration,
37 createAuthenticationOptions,
38 finishAuthentication,
39 listPasskeys,
40} = await import('../src/services/auth-core/webauthn.ts');
41const { getAuthorisationUrl } = await import(
42 '../src/services/auth-core/thirdparty.ts'
43);
44const { getEnginePool } = await import('../src/services/auth-core/db.ts');
45
46console.log('=== Phase 5: MFA + passkeys on Doltgres ===');
47
48if (!(await ensureBrivenEngineDatabase()).ok) process.exit(1);
49if (!(await initAuthCoreSdk())) process.exit(1);
50
51const projectId = 'p_deepen_local';
52const email = `deepen_${Date.now()}@example.com`;
53const su = await signUpEmailPassword({
54 email,
55 password: 'Deepen!Pass99',
56 projectId,
57});
58if (su.status !== 'OK') {
59 console.error('FAIL signup', su);
60 process.exit(1);
61}
62const userId = su.user.id;
63
64// ── TOTP ────────────────────────────────────────────────────────────
65const created = await createTotpDevice(userId, 'phone-app', { projectId });
66console.log('totp create', {
67 ok: created.ok,
68 hasSecret: Boolean(created.secret),
69 hasOtpauth: Boolean(created.otpauthUrl),
70});
71if (!created.ok || !created.secret || !created.deviceId) {
72 console.error('FAIL totp create', created);
73 process.exit(1);
74}
75
76const code = generateTotpCode(created.secret);
77const bad = await verifyAndEnableTotpDevice({
78 userId,
79 deviceId: created.deviceId,
80 code: '000000',
81});
82console.log('totp wrong code', bad.ok);
83if (bad.ok) process.exit(1);
84
85const good = await verifyAndEnableTotpDevice({
86 userId,
87 deviceId: created.deviceId,
88 code,
89});
90console.log('totp enable', good.ok);
91if (!good.ok) {
92 console.error('FAIL totp enable', good);
93 process.exit(1);
94}
95
96const check = await verifyUserTotp(userId, generateTotpCode(created.secret));
97console.log('totp login check', check.ok);
98if (!check.ok) process.exit(1);
99
100const devices = await listTotpDevices(userId);
101console.log('totp devices', devices.devices);
102
103// ── Passkeys ────────────────────────────────────────────────────────
104const reg = await createRegistrationOptions({
105 userId,
106 userName: email,
107 projectId,
108});
109console.log('passkey reg options', reg.status, reg.status === 'OK' ? reg.challengeId : null);
110if (reg.status !== 'OK') process.exit(1);
111
112const fin = await finishRegistration({
113 userId,
114 challengeId: reg.challengeId,
115 credentialId: `cred_${Date.now()}`,
116 publicKey: Buffer.from('fake-public-key-for-local-proof').toString('base64url'),
117 transports: ['internal'],
118});
119console.log('passkey register finish', fin.status);
120if (fin.status !== 'OK') process.exit(1);
121
122const authOpts = await createAuthenticationOptions({ userId, projectId });
123if (authOpts.status !== 'OK') process.exit(1);
124const authFin = await finishAuthentication({
125 challengeId: authOpts.challengeId,
126 credentialId: `cred_${Date.now()}`.replace(/\d+$/, '') + // wrong id test first
127 '',
128});
129// use the real credential id from list
130const keys = await listPasskeys(userId);
131const realId = keys.credentials[0]?.credentialId;
132const authOk = await finishAuthentication({
133 challengeId: (
134 await createAuthenticationOptions({ userId, projectId })
135 ).challengeId,
136 credentialId: realId,
137});
138// fix: need challenge from fresh options
139const authOpts2 = await createAuthenticationOptions({ userId, projectId });
140const authOk2 = await finishAuthentication({
141 challengeId: authOpts2.challengeId,
142 credentialId: realId,
143});
144console.log('passkey authenticate', authOk2.status, authOk2.status === 'OK' ? authOk2.userId : authOk2);
145if (authOk2.status !== 'OK' || authOk2.userId !== userId) process.exit(1);
146
147// ── Google URL with real-shaped env secrets ─────────────────────────
148const gUrl = await getAuthorisationUrl({
149 thirdPartyId: 'google',
150 redirectURI: 'http://localhost:3000/auth/callback/google',
151 projectId,
152});
153console.log('google authorisation', {
154 status: gUrl.status,
155 source: gUrl.status === 'OK' ? gUrl.credentialsSource : null,
156 hasGoogleHost:
157 gUrl.status === 'OK' && gUrl.urlWithQueryParams.includes('accounts.google.com'),
158});
159if (gUrl.status !== 'OK') process.exit(1);
160
161const pool = getEnginePool();
162const totpRows = await pool.query(
163 `SELECT COUNT(*)::int AS n FROM be_totp_devices WHERE user_id = $1 AND verified = TRUE`,
164 [userId],
165);
166const pkRows = await pool.query(
167 `SELECT COUNT(*)::int AS n FROM be_webauthn_credentials WHERE user_id = $1`,
168 [userId],
169);
170
171console.log('SQL totp verified', totpRows.rows[0]);
172console.log('SQL passkeys', pkRows.rows[0]);
173
174console.log('');
175console.log('✔ PHASE 5 LOCAL PROOF OK (MFA + passkeys)');
176console.log(' storage: Doltgres');
177console.log(' TOTP enroll + verify: OK');
178console.log(' passkey register + authenticate: OK');
179process.exit(0);