open-items-proof.mjs 5.5 KB

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