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