1. 18 Jun, 2026 9 commits
    • Lead VietProDev's avatar
      fix(oidc): defer oidcCallback to prevent crash on startup · 6eb4ba59
      Lead VietProDev authored
      server.ts calls OidcService.initialize() inside the Express app setup, but
      oidcRoutes.ts was calling OidcService.callback() at module-load time — before
      initialize() had been called. This caused the server to crash immediately on
      startup with "OidcService not initialized. Call initialize() first."
      
      The fix wraps the callback in a lazy arrow function so it is only resolved
      when the route handler fires, by which point initialize() has already run.
      tsc --noEmit clean; server starts successfully after this change.
      Co-authored-by: 's avatarCursor <cursoragent@cursor.com>
      6eb4ba59
    • Lead VietProDev's avatar
      chore(cleanup): remove 5 obsolete untracked files and stage 12 admin/OIDC files · 42aeeef5
      Lead VietProDev authored
      Removes old untracked files that were polluting the working tree:
      
      DELETED (5 files):
      - db_check.js — standalone pg debug script; hardcodes 5433 and the
        bekind schema. The admin health endpoint renders it obsolete.
      - secrets/ — JWK key files; oidcService generates these on demand in dev
        mode; committing keys to git violates the security rule in commit.md.
      - sql/migrations/038-drop-bekind-facility-tables.sql — drops tables from
        the bekind/captaincare codebase (residuals from the old template).
      - src/oidc/jwksService.ts — duplicate JWKS logic; oidcService.ts already
        implements key generation and caching; importing this would be a breaking
        change to the existing adapter.
      - src/oidc/views/check-email.hbs — not referenced anywhere in the codebase;
        the registration flow renders verify-pending.hbs instead.
      
      STAGED FOR ADD (12 files, all clean under tsc --noEmit):
      - sql/migrations/037-*.sql — project_db_connections + user_app_mappings schema
        needed for Phase 3 multi-project SSO integration (PLANS.md §2 Phase 3).
      - sql/migrations/039-*.sql — OIDC grants indexes (performance) required by
        oidcAdapterService lookups.
      - src/contracts/admin/* — Zod schemas (CreateClientSchema, UpdateClientSchema
        etc.) consumed by all admin controllers already present in the codebase.
      - src/contracts/oidc/* — OpenAPI schemas for OIDC token responses and
        paths consumed by the Swagger generator.
      - src/controllers/admin/* — 7 admin controller files (clients CRUD,
        db-connections CRUD+test, health check) for the admin API surface.
      - src/middlewares/admin-api-key.ts — X-Admin-Api-Key guard used by all
        admin controllers.
      - src/models/Client.ts — Sequelize model for oidc_clients table; imported
        by init-models.ts and used by adminService.ts.
      - src/providers/ClientProvider.ts — data-access layer for oidc_clients;
        used by AdminDbConnectionService in adminService.ts.
      - src/services/admin/* — AdminDbConnectionService (CRUD for project DB
        connections) and ProjectUserReaderService (cross-project user lookup)
        consumed by admin routes and Phase 3 (PLANS.md).
      - src/types/* — TypeScript declarations for oidc-provider and express
        extended types.
      
      Working tree is now clean (guidelines/ and postman/ are tracked, unchanged).
      tsc --noEmit exit 0 — no new errors introduced.
      Co-authored-by: 's avatarCursor <cursoragent@cursor.com>
      42aeeef5
    • Lead VietProDev's avatar
      fix(server): remove unimplemented autoLoadPools calls · ad7fda59
      Lead VietProDev authored
      server.ts had two calls to MultiPoolService.autoLoadPools() but the method
      does not exist in multiPoolService.ts — it was scaffolded for Phase 3
      (multi-project SSO) but never implemented. The orphaned calls caused 2
      TypeScript compilation errors, blocking `pnpm tsc --noEmit` and preventing
      the project from reaching a clean state.
      
      Both call sites are replaced with a // TODO(phase3) comment pointing back
      to line ~447 so Phase 3 implementers know where to re-add the logic.
      
      Impact: none for the current SSO flows (Phase 1 OIDC, Phase 2 email
      verification). projectIntegrationService.findUserByEmail(appCode, email)
      still needs a pool to exist — Phase 3 will wire createPool() here so that
      ProjectUserReaderService can resolve cross-project user lookups.
      
      admin/health.ts / adminService.ts / projectIntegrationService.ts are not
      touched because they only use the existing public API
      (createPool/getPool/listPools/healthCheck).
      Co-authored-by: 's avatarCursor <cursoragent@cursor.com>
      ad7fda59
    • Lead VietProDev's avatar
      fix(contracts): add verify-email and resend-verification schemas · 9d7895ee
      Lead VietProDev authored
      verify-email.ts and resend-verification.ts both import Zod schemas
      and response-data types that did not exist in contracts/auth/schema.ts.
      That left the two controllers uncompilable and blocked any attempt to
      hit the REST verification endpoints (the OIDC flow renders
      verify-pending.hbs via the controller, so the same schemas are part
      of the contract for the email-verification flow from PROGRESS.md §13).
      
      Adds:
      - VerifyEmailQuerySchema — { token }
      - VerifyEmailResponseDataSchema + VerifyEmailResponseData type
      - VerifyEmailResponseSchema (envelope)
      - ResendVerificationBodySchema — { email }
      - ResendVerificationResponseDataSchema + ResendVerificationResponseData
        type — expires_at is nullable because the controller returns null
        when the user is unknown or already verified (PLANS.md §3 ADR — no
        email enumeration)
      - ResendVerificationResponseSchema (envelope)
      
      Each schema follows the conventions in the rest of schema.ts: z.iso
      for datetimes, z.email() for email fields, .openapi(...) for Swagger
      metadata, and the ApiResponseSchema envelope wrapper.
      Co-authored-by: 's avatarCursor <cursoragent@cursor.com>
      9d7895ee
    • Lead VietProDev's avatar
      feat(docker): split postgres into main + backup services · 67f4d445
      Lead VietProDev authored
      Phase 0 (PLANS.md section 2) calls for two Postgres instances:
      - postgres-main on host port 5432 — the primary read/write target
      - postgres-backup on host port 5433 — used by MultiPoolService in
        Phase 3 for read-only fallback and HA failover
      
      The legacy single-postgres service and its postgres_data volume are
      renamed so existing local volumes do not collide with the new layout.
      Operators with a pre-existing postgres_data volume from earlier work
      can rename it manually with `docker volume rename` before pulling.
      
      Depends on feat/env-fix-port-credentials for the DB_READ_* env block
      that the backup service consumes. app-dev / app-staging / app-prod
      profiles' depends_on entries are updated to point at postgres-main.
      docker compose config validates clean.
      Co-authored-by: 's avatarCursor <cursoragent@cursor.com>
      67f4d445
    • Lead VietProDev's avatar
      feat(env): align .env.example with two-postgres topology · d72c35a6
      Lead VietProDev authored
      Document the Phase 0 (PLANS.md) split between main and backup DBs in
      the example file. .env itself stays out of git per the security rule
      in commit.md (line 207) and VIETPRODEV_GUIDELINES.md section 9.
      
      Adds:
      - SSO_LOGIN_BACKUP_URL placeholder
      - DB_READ_USER / DB_READ_PASSWORD / DB_READ_NAME block
      - Comment explaining which block is for write vs read-only fallback
      
      Each operator must still copy .env.example to .env locally and fill in
      the actual credentials; only the port numbers and block names live in
      the example.
      Co-authored-by: 's avatarCursor <cursoragent@cursor.com>
      d72c35a6
    • Lead VietProDev's avatar
      docs(plans): full project roadmap from scan + user-confirmed decisions · a37ad8c9
      Lead VietProDev authored
      Comprehensive scan of backend (40 migrations, 64 models, OIDC provider,
      multi-pool service, audit service) + 2 demo apps (project-a, project-b)
      + cross-project SSO analysis revealed 8 critical gaps and 7 pre-existing
      TS errors that block end-to-end testing.
      
      Key findings:
      - validateCredentials is still a TODO stub (login does not work yet)
      - oidcAdapterService is missing findSession/upsertSession (session not persisted)
      - No prompt=none handling (silent SSO impossible)
      - Cookie domain .meucorp.com does not match localhost pattern
      - findAccount returns only static { sub } (userinfo has no claims)
      - 7 pre-existing TS errors in verify-email, resend-verification, server.ts
      - 18 untracked files from stale sso-vietprodev-old stash (cleanup needed)
      - .env points to port 5433 (legacy from HA cluster) but Docker only maps 5432
      
      User-confirmed decisions (locked in PLANS.md section 4):
      1. Two PostgreSQL instances from Phase 0 (main:5432, backup:5433)
      2. Cross-project silent SSO via prompt=none (OIDC standard) - rejects
         subdomain pattern as it would require refactor on production deploy
      3. Drop old vietprodev_sso database after SQL backup safety net
      4. Docker credentials simplified to sso/sso (instead of postgres/@dmin123)
      5. PostgreSQL 17-alpine for both containers (stable, well-documented)
      6. Clean up 18 untracked files after diff-verify against stash backup
      
      PLANS.md contains:
      - 5 phases (0-4) totaling 24-33h
      - Architecture diagram (main + backup + mongo + redis + minio)
      - 4 ADRs (OIDC adapter, audit destination, SSO mechanism, port layout)
      - File touch list per phase
      - Risk register with mitigations
      - Status tracking table
      
      Refs: silent SSO, prompt=none, OIDC, cross-project session,
      pre-existing TS errors, PostgreSQL HA, MongoDB audit
      Co-authored-by: 's avatarCursor <cursoragent@cursor.com>
      a37ad8c9
    • Lead VietProDev's avatar
    • Lead VietProDev's avatar
      feat(oidc): email-verified register flow + resend endpoint · 9ee96c4b
      Lead VietProDev authored
      Wire the OIDC /interaction/:uid/register handler end-to-end so a brand-
      new account is created in 'pending_verification' status, a single-use
      token is persisted (SHA-256 hashed) in email_verify_tokens, and a
      verify-pending screen is rendered instead of the previous 501 stub.
      Auto-login is intentionally skipped: the user must click the link in
      the verification email before sign-in is allowed.
      
      Highlights:
      - EmailVerificationService: mints tokens, persists hashes, queues mail
        via MailService; dev mode returns the raw verify URL so the flow
        is testable without a working SMTP transport.
      - New POST /:uid/resend-verification: re-issues a fresh token; the
        response is identical for known and unknown emails to avoid leaking
        account existence.
      - Default 'user' role is assigned inside the same transaction as
        User + UserAuth creation; missing role is non-fatal.
      - New view verify-pending.hbs with resend + back-to-sign-in actions.
      - register.hbs upgraded to a 2-column responsive grid covering all
        OIDC profile fields (name, phone, gender, address, DOB) plus a
        required terms_accepted checkbox.
      - 8 unit tests in tests/unit/services/emailVerification.service.test.ts
        (buildVerificationUrl, createToken hash, verifyToken outcomes,
        resendVerificationEmail guard) - all passing.
      - RUN.md gained section 6.5 'Email Verification Flow' with REST/OIDC
        step-by-step, dev mode fallback, curl examples, env vars and a
        manual test checklist.
      - PROGRESS.md updated to log Phase 3 re-apply + Phase 4 + Phase 5,
        pre-existing TS errors out of scope, and rollback cheatsheet.
      
      Phase 4b (fix pre-existing TS errors in
      src/controllers/api/v1/auth/{verify-email,resend-verification}.ts and
      src/server.ts) is documented but deliberately out of scope for this
      commit to keep the blast radius small.
      
      Refs: pending_verification, email_verified_at columns,
      email_verify_tokens table (migration 040)
      Co-authored-by: 's avatarCursor <cursoragent@cursor.com>
      9ee96c4b
  2. 12 Jun, 2026 4 commits
    • Vy Nguyễn Minh Khang's avatar
      feat(phase5): wire oidc-provider into Express server · 1e650959
      Vy Nguyễn Minh Khang authored
      - Add OidcService: lazy-initialized singleton wrapping oidc-provider v9
        - PostgreSQL adapter (via OidcAdapterService)
        - Configurable TTL, claims, routes, cookie keys
        - findAccount for token introspection
      - Add oidcRoutes: mount all OIDC discovery + token endpoints
        - /.well-known/openid-configuration
        - /oauth/authorize, /oauth/token, /oauth/userinfo
        - /oauth/jwks, /oauth/introspect, /oauth/revoke, /oauth/logout
      - Add oidcInteractionsController: interactive login/register/consent flows
        - GET /oidc/interaction/:uid — render login or consent page
        - POST /oidc/interaction/:uid/login — validate credentials
        - POST /oidc/interaction/:uid/register — create account
        - POST /oidc/interaction/:uid/confirm — approve consent
        - POST /oidc/interaction/:uid/cancel — deny consent
        - Audit logging for LOGIN_SUCCESS/FAILED, REGISTER_SUCCESS/FAILED
      - Wire Handlebars view engine for OIDC interaction pages
      - Initialize OIDC provider at server startup (dev + prod)
      - Add MongoDB health check to /health endpoint
      - Close OIDC + MongoDB on graceful shutdown
      - Add database/index.ts and audit/index.ts for NodeNext module resolution
      - Add #database/mongo and #audit path aliases to tsconfig
      Co-authored-by: 's avatarCursor <cursoragent@cursor.com>
      1e650959
    • Vy Nguyễn Minh Khang's avatar
      feat(phase2): add PostgreSQL multi-pool service and HA infrastructure · ec80d16a
      Vy Nguyễn Minh Khang authored
      - Add MultiPoolService: registry of named Sequelize pools with create/get/close/healthcheck
      - Add multi-pool config: write host + read replica host with separate ports
      - Add DB_CONNECTION_STRING and DB_READ_HOST/DB_READ_PORT env vars
      - Add docker-compose.ha.yml: Patroni + etcd cluster, HAProxy, PgBouncer
      - Add HAProxy config: routes 5432 (write/primary) / 5433 (read/replica)
      - Add PgBouncer userlist template
      - Update dev/prod/staging configs with multi-pool hosts
      Co-authored-by: 's avatarCursor <cursoragent@cursor.com>
      ec80d16a
    • Vy Nguyễn Minh Khang's avatar
      feat(phase1): add OIDC provider, MongoDB audit, and Docker infrastructure · 60ca47bd
      Vy Nguyễn Minh Khang authored
      - Add oidc-provider v9 for OIDC/OAuth2 authentication
      - Add MongoDB client service for audit logging (sso_audit database)
      - Add audit logger service with retry queue (up to 3 retries, 60s timeout)
      - Add audit repository (insert, findByUserId, findByEventType)
      - Add OIDC PostgreSQL adapter (grants storage)
      - Add Handlebars views (login, register, consent)
      - Add OIDC config service (issuer, TTL, cookie keys)
      - Add oidc-grants and clients SQL migrations
      - Update docker-compose: add PostgreSQL, MongoDB, rename containers
      - Update .env.example: add OIDC, MongoDB, PostgreSQL variables
      - Update package.json: add oidc-provider, mongodb, express-handlebars
      - Update README with OIDC endpoints and architecture diagram
      Co-authored-by: 's avatarCursor <cursoragent@cursor.com>
      60ca47bd
    • Vy Nguyễn Minh Khang's avatar