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