diff --git a/src/auth.ts b/src/auth.ts index 1534afb..e9ba737 100644 --- a/src/auth.ts +++ b/src/auth.ts @@ -40,7 +40,7 @@ if (process.env.APPLE_CLIENT_ID && process.env.APPLE_CLIENT_SECRET) { // Shares the MCP product's CNPG database. Better Auth's tables (user, session, // account, jwks, oauthApplication, ...) don't collide with the app's `users` // table, so they coexist in the default schema — same DB, no second instance. -const pool = new Pool({ +export const pool = new Pool({ connectionString: required("AUTH_DATABASE_URL"), }); diff --git a/src/migrate.ts b/src/migrate.ts index c346439..630fad7 100644 --- a/src/migrate.ts +++ b/src/migrate.ts @@ -4,7 +4,7 @@ * the MCP server's Alembic step. Idempotent — a no-op once the schema is current. */ import { getMigrations } from "better-auth/db/migration"; -import { auth } from "./auth.js"; +import { auth, pool } from "./auth.js"; const { toBeCreated, toBeAdded, runMigrations } = await getMigrations(auth.options); @@ -19,4 +19,31 @@ if (toBeCreated.length === 0 && toBeAdded.length === 0) { console.log("better-auth schema applied"); } +// Seed the portal as a stable public (PKCE) OIDC client. Better Auth hashes +// confidential secrets, so the portal uses PKCE with no secret. Idempotent. +const portalClientId = process.env.PORTAL_CLIENT_ID ?? "intervalsicu-portal"; +const portalRedirect = + process.env.PORTAL_REDIRECT_URI ?? "https://intervalsicu.farhoodlabs.com/portal/auth/callback"; +await pool.query( + `INSERT INTO "oauthClient" + (id, "clientId", "clientSecret", public, "requirePKCE", "tokenEndpointAuthMethod", + "redirectUris", "grantTypes", "responseTypes", scopes, type, "skipConsent", disabled, + name, "createdAt", "updatedAt") + VALUES ($1, $1, NULL, true, true, 'none', + $2::jsonb, $3::jsonb, $4::jsonb, $5::jsonb, 'web', true, false, + 'Intervals.icu MCP portal', now(), now()) + ON CONFLICT ("clientId") DO UPDATE SET + "redirectUris" = EXCLUDED."redirectUris", public = true, "requirePKCE" = true, + "tokenEndpointAuthMethod" = 'none', "skipConsent" = true, disabled = false, + "updatedAt" = now()`, + [ + portalClientId, + JSON.stringify([portalRedirect]), + JSON.stringify(["authorization_code", "refresh_token"]), + JSON.stringify(["code"]), + JSON.stringify(["openid", "email", "profile"]), + ], +); +console.log(`seeded portal OAuth client '${portalClientId}' (public + PKCE)`); + process.exit(0);