step3-social-proof.mjs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. /**
  2. * Step 3 proof: Google/GitHub social login on Doltgres.
  3. *
  4. * Without real OAuth client secrets, we prove the Doltgres user/link/session
  5. * path using a post-exchange profile (same as after Google/GitHub returns).
  6. * Authorisation URL shape is checked with dummy env credentials.
  7. *
  8. * cd apps/api
  9. * BRIVEN_ENGINE_DATABASE_URL=postgres://postgres:devpass@127.0.0.1:5434/briven_engine?sslmode=disable \
  10. * BRIVEN_DATA_PLANE_URL=postgres://postgres:devpass@127.0.0.1:5434/postgres?sslmode=disable \
  11. * bun scripts/step3-social-proof.mjs
  12. */
  13. process.env.BRIVEN_AUTH_CORE_ENABLED = 'true';
  14. process.env.BRIVEN_ENV = 'development';
  15. process.env.BRIVEN_ENGINE_DATABASE_URL =
  16. process.env.BRIVEN_ENGINE_DATABASE_URL ??
  17. 'postgres://postgres:devpass@127.0.0.1:5434/briven_engine?sslmode=disable';
  18. process.env.BRIVEN_DATA_PLANE_URL =
  19. process.env.BRIVEN_DATA_PLANE_URL ??
  20. 'postgres://postgres:devpass@127.0.0.1:5434/postgres?sslmode=disable';
  21. // Dummy credentials so authorisation URL can be built (not used for real HTTP)
  22. process.env.BRIVEN_GOOGLE_CLIENT_ID =
  23. process.env.BRIVEN_GOOGLE_CLIENT_ID ?? 'test-google-client-id.apps.googleusercontent.com';
  24. process.env.BRIVEN_GOOGLE_CLIENT_SECRET =
  25. process.env.BRIVEN_GOOGLE_CLIENT_SECRET ?? 'test-google-secret';
  26. process.env.BRIVEN_GITHUB_CLIENT_ID =
  27. process.env.BRIVEN_GITHUB_CLIENT_ID ?? 'test-github-client-id';
  28. process.env.BRIVEN_GITHUB_CLIENT_SECRET =
  29. process.env.BRIVEN_GITHUB_CLIENT_SECRET ?? 'test-github-secret';
  30. const { ensureBrivenEngineDatabase } = await import(
  31. '../src/services/auth-core/ensure-db.ts'
  32. );
  33. const { initAuthCoreSdk } = await import('../src/services/auth-core/engine.ts');
  34. const {
  35. getAuthorisationUrl,
  36. signInUpWithThirdPartyProfile,
  37. } = await import('../src/services/auth-core/thirdparty.ts');
  38. const { getEnginePool } = await import('../src/services/auth-core/db.ts');
  39. const projectId = 'p_step3_local';
  40. console.log('=== Phase 4: social login (Google/GitHub) on Doltgres ===');
  41. console.log({ projectId });
  42. const ensured = await ensureBrivenEngineDatabase();
  43. if (!ensured.ok) {
  44. console.error('FAIL ensure', ensured);
  45. process.exit(1);
  46. }
  47. if (!(await initAuthCoreSdk())) {
  48. console.error('FAIL init');
  49. process.exit(1);
  50. }
  51. // 1) Authorisation URLs
  52. const googleUrl = await getAuthorisationUrl({
  53. thirdPartyId: 'google',
  54. redirectURI: 'http://localhost:3000/auth/callback/google',
  55. projectId,
  56. });
  57. console.log('google auth url', {
  58. status: googleUrl.status,
  59. hasGoogle:
  60. googleUrl.status === 'OK' &&
  61. googleUrl.urlWithQueryParams.includes('accounts.google.com'),
  62. hasClientId:
  63. googleUrl.status === 'OK' &&
  64. googleUrl.urlWithQueryParams.includes('test-google-client-id'),
  65. credentialsSource:
  66. googleUrl.status === 'OK' ? googleUrl.credentialsSource : null,
  67. });
  68. if (googleUrl.status !== 'OK') {
  69. console.error('FAIL google url', googleUrl);
  70. process.exit(1);
  71. }
  72. const githubUrl = await getAuthorisationUrl({
  73. thirdPartyId: 'github',
  74. redirectURI: 'http://localhost:3000/auth/callback/github',
  75. projectId,
  76. });
  77. console.log('github auth url', {
  78. status: githubUrl.status,
  79. hasGithub:
  80. githubUrl.status === 'OK' &&
  81. githubUrl.urlWithQueryParams.includes('github.com/login/oauth'),
  82. });
  83. if (githubUrl.status !== 'OK') {
  84. console.error('FAIL github url', githubUrl);
  85. process.exit(1);
  86. }
  87. // 2) Simulated Google profile (after successful OAuth exchange)
  88. const googleTpId = `google-sub-${Date.now()}`;
  89. const googleEmail = `step3_google_${Date.now()}@example.com`;
  90. const g1 = await signInUpWithThirdPartyProfile({
  91. profile: {
  92. thirdPartyId: 'google',
  93. thirdPartyUserId: googleTpId,
  94. email: googleEmail,
  95. emailVerified: true,
  96. name: 'Step3 Google User',
  97. },
  98. projectId,
  99. });
  100. console.log('google first sign-in', {
  101. status: g1.status,
  102. createdNewUser: g1.status === 'OK' ? g1.createdNewUser : null,
  103. userId: g1.status === 'OK' ? g1.user.id : null,
  104. session: g1.status === 'OK' ? g1.session.handle : null,
  105. });
  106. if (g1.status !== 'OK' || !g1.createdNewUser) {
  107. console.error('FAIL google first', g1);
  108. process.exit(1);
  109. }
  110. // 3) Same Google account again → same user, not new
  111. const g2 = await signInUpWithThirdPartyProfile({
  112. profile: {
  113. thirdPartyId: 'google',
  114. thirdPartyUserId: googleTpId,
  115. email: googleEmail,
  116. emailVerified: true,
  117. },
  118. projectId,
  119. });
  120. console.log('google second sign-in', {
  121. status: g2.status,
  122. createdNewUser: g2.status === 'OK' ? g2.createdNewUser : null,
  123. sameUser: g2.status === 'OK' && g2.user.id === g1.user.id,
  124. });
  125. if (g2.status !== 'OK' || g2.createdNewUser || g2.user.id !== g1.user.id) {
  126. console.error('FAIL google second', g2);
  127. process.exit(1);
  128. }
  129. // 4) GitHub profile
  130. const githubTpId = `gh-${Date.now()}`;
  131. const gh = await signInUpWithThirdPartyProfile({
  132. profile: {
  133. thirdPartyId: 'github',
  134. thirdPartyUserId: githubTpId,
  135. email: `step3_gh_${Date.now()}@example.com`,
  136. emailVerified: true,
  137. name: 'Step3 GH',
  138. },
  139. projectId,
  140. });
  141. console.log('github sign-in', {
  142. status: gh.status,
  143. createdNewUser: gh.status === 'OK' ? gh.createdNewUser : null,
  144. userId: gh.status === 'OK' ? gh.user.id : null,
  145. });
  146. if (gh.status !== 'OK') {
  147. console.error('FAIL github', gh);
  148. process.exit(1);
  149. }
  150. // 5) SQL proof
  151. const pool = getEnginePool();
  152. const links = await pool.query(
  153. `SELECT third_party_id, third_party_user_id, user_id, tenant_id
  154. FROM be_third_party_links
  155. WHERE tenant_id = $1
  156. ORDER BY created_at`,
  157. ['proj-p-step3-local'],
  158. );
  159. const sessions = await pool.query(
  160. `SELECT COUNT(*)::int AS n FROM be_sessions
  161. WHERE user_id = ANY($1::text[])`,
  162. [[g1.user.id, gh.user.id]],
  163. );
  164. console.log('SQL third_party_links', links.rows);
  165. console.log('SQL sessions for social users', sessions.rows[0]);
  166. const hasGoogle = links.rows.some(
  167. (r) => r.third_party_id === 'google' && r.third_party_user_id === googleTpId,
  168. );
  169. const hasGithub = links.rows.some(
  170. (r) => r.third_party_id === 'github' && r.third_party_user_id === githubTpId,
  171. );
  172. if (!hasGoogle || !hasGithub) {
  173. console.error('FAIL links missing');
  174. process.exit(1);
  175. }
  176. console.log('');
  177. console.log('✔ PHASE 4 LOCAL PROOF OK (social)');
  178. console.log(' storage: Doltgres');
  179. console.log(' Google authorisation URL: OK');
  180. console.log(' GitHub authorisation URL: OK');
  181. console.log(' Google sign-up + re-login same user: OK');
  182. console.log(' GitHub sign-up: OK');
  183. console.log(' be_third_party_links rows: OK');
  184. console.log(' sessions: OK');
  185. console.log(' note: real browser OAuth needs live Google/GitHub redirect URIs;');
  186. console.log(' platform env BRIVEN_GOOGLE_* / BRIVEN_GITHUB_* already on France.');
  187. process.exit(0);