open-items-proof.mjs166 lines · main
1/**
2 * Proof for remaining open items (Doltgres only, no deploy):
3 * 1) Passkey challenge/register/auth still works (simplewebauthn path + local fallback)
4 * 2) Google OAuth start URL (full browser path scaffolding)
5 * 3) Roles on Doltgres
6 * 4) Rate limit / captcha gates present
7 *
8 * bun scripts/open-items-proof.mjs
9 */
10
11process.env.BRIVEN_AUTH_CORE_ENABLED = 'true';
12process.env.BRIVEN_ENV = 'development';
13process.env.BRIVEN_ENGINE_DATABASE_URL =
14 process.env.BRIVEN_ENGINE_DATABASE_URL ??
15 'postgres://postgres:devpass@127.0.0.1:5434/briven_engine?sslmode=disable';
16process.env.BRIVEN_DATA_PLANE_URL =
17 process.env.BRIVEN_DATA_PLANE_URL ??
18 'postgres://postgres:devpass@127.0.0.1:5434/postgres?sslmode=disable';
19process.env.BRIVEN_GOOGLE_CLIENT_ID =
20 process.env.BRIVEN_GOOGLE_CLIENT_ID ?? 'open-items.apps.googleusercontent.com';
21process.env.BRIVEN_GOOGLE_CLIENT_SECRET =
22 process.env.BRIVEN_GOOGLE_CLIENT_SECRET ?? 'open-items-secret';
23
24const { ensureBrivenEngineDatabase } = await import(
25 '../src/services/auth-core/ensure-db.ts'
26);
27const { initAuthCoreSdk } = await import('../src/services/auth-core/engine.ts');
28const { signUpEmailPassword } = await import(
29 '../src/services/auth-core/emailpassword.ts'
30);
31const {
32 createRegistrationOptions,
33 finishRegistration,
34 createAuthenticationOptions,
35 finishAuthentication,
36} = await import('../src/services/auth-core/webauthn.ts');
37const {
38 createBrivenEngineRole,
39 assignBrivenEngineRole,
40 getBrivenEngineUserRoles,
41 listBrivenEngineRoles,
42 userHasPermission,
43} = await import('../src/services/auth-core/roles.ts');
44const { getAuthorisationUrl } = await import(
45 '../src/services/auth-core/thirdparty.ts'
46);
47const { requireTurnstileIfConfigured, AUTH_CORE_ABUSE } = await import(
48 '../src/services/auth-core/abuse.ts'
49);
50const { getEnginePool } = await import('../src/services/auth-core/db.ts');
51
52console.log('=== Open items proof (Doltgres) ===');
53
54if (!(await ensureBrivenEngineDatabase()).ok) process.exit(1);
55if (!(await initAuthCoreSdk())) process.exit(1);
56
57const projectId = 'p_open_items';
58const email = `open_${Date.now()}@example.com`;
59const su = await signUpEmailPassword({
60 email,
61 password: 'OpenItems!99',
62 projectId,
63});
64if (su.status !== 'OK') process.exit(1);
65const userId = su.user.id;
66
67// 1) Passkeys with simplewebauthn options generation
68const reg = await createRegistrationOptions({
69 userId,
70 userName: email,
71 projectId,
72});
73if (reg.status !== 'OK') {
74 console.error('FAIL reg options', reg);
75 process.exit(1);
76}
77console.log('passkey options challenge present', Boolean(reg.options.challenge));
78
79const fin = await finishRegistration({
80 userId,
81 challengeId: reg.challengeId,
82 credentialId: `cred_open_${Date.now()}`,
83 publicKey: Buffer.from('dev-pubkey').toString('base64url'),
84});
85if (fin.status !== 'OK') {
86 console.error('FAIL reg finish', fin);
87 process.exit(1);
88}
89console.log('passkey registered', fin.credentialDbId, 'verified', fin.verified);
90
91const authOpts = await createAuthenticationOptions({ userId, projectId });
92const listCred = (
93 await (await import('../src/services/auth-core/webauthn.ts')).listPasskeys(
94 userId,
95 )
96).credentials[0]?.credentialId;
97const auth = await finishAuthentication({
98 challengeId: authOpts.challengeId,
99 credentialId: listCred,
100});
101if (auth.status !== 'OK') {
102 console.error('FAIL auth', auth);
103 process.exit(1);
104}
105console.log('passkey auth session', auth.session.handle);
106
107// 2) Google full path — start URL (callback route lives in API + web page)
108const g = await getAuthorisationUrl({
109 thirdPartyId: 'google',
110 redirectURI: 'http://localhost:3000/auth/callback/google',
111 projectId,
112});
113if (g.status !== 'OK' || !g.urlWithQueryParams.includes('accounts.google.com')) {
114 console.error('FAIL google url', g);
115 process.exit(1);
116}
117console.log('google start URL OK', g.credentialsSource);
118
119// 3) Roles
120const role = await createBrivenEngineRole(
121 'editor',
122 ['content:read', 'content:write'],
123 { projectId },
124);
125const assign = await assignBrivenEngineRole(userId, 'editor', { projectId });
126const roles = await getBrivenEngineUserRoles(userId, { projectId });
127const canWrite = await userHasPermission(userId, 'content:write', { projectId });
128const listed = await listBrivenEngineRoles({ projectId });
129console.log('roles', { role, assign, roles, canWrite, listed: listed.roles });
130if (!assign.ok || !canWrite || !roles.roles.includes('editor')) process.exit(1);
131
132// 4) Captcha gate (no secret → allow in any env when unset)
133const cap = await requireTurnstileIfConfigured({});
134console.log('captcha without secret', cap);
135if (!cap.ok) process.exit(1);
136
137// Rate limit table exists
138const pool = getEnginePool();
139await pool.query(
140 `INSERT INTO be_rate_limits (bucket_key, hit_count, window_start)
141 VALUES ($1, 1, NOW())
142 ON CONFLICT (bucket_key) DO UPDATE SET hit_count = be_rate_limits.hit_count + 1`,
143 ['proof-ip'],
144).catch(async () => {
145 // Doltgres may lack ON CONFLICT — probe
146 const e = await pool.query(
147 `SELECT 1 FROM be_rate_limits WHERE bucket_key = $1`,
148 ['proof-ip'],
149 );
150 if (!e.rowCount) {
151 await pool.query(
152 `INSERT INTO be_rate_limits (bucket_key, hit_count, window_start) VALUES ($1, 1, NOW())`,
153 ['proof-ip'],
154 );
155 }
156});
157console.log('abuse config', AUTH_CORE_ABUSE);
158
159console.log('');
160console.log('✔ OPEN ITEMS PROOF OK');
161console.log(' 1 passkeys (simplewebauthn options + store): OK');
162console.log(' 2 Google OAuth start URL (browser path ready): OK');
163console.log(' 3 roles on Doltgres: OK');
164console.log(' 4 captcha/rate-limit scaffolding: OK');
165console.log(' 5 deploy: still blocked until you say ship');
166process.exit(0);