Skip to content

Add deployment profiles and optional local-admin auth mode #35

Description

@a2975667

Triage

Current implementation appears Google-first:

  • .shared/developer_doc/setup/local-development.md lists MongoDB and Google OAuth credentials as required local prerequisites.
  • .shared/developer_doc/security/auth-security.md documents Google OAuth plus JWT only.
  • .shared/developer_doc/ops/env-and-secrets.md treats Google OAuth secrets as required production configuration.
  • server/src/app.module.ts connects to MONGO_URI without deployment-profile validation or remote-db guardrails.
  • server/src/auth/auth.module.ts always registers GoogleStrategy.
  • server/src/auth/auth.controller.ts exposes /api/v1/google-login and /api/v1/redirect unconditionally.
  • client/src/pages/login/Login.tsx hard-codes Google login.
  • server/src/schemas/user.schema.ts has a password field, but there is no local-admin login, password hashing, or bootstrap flow.

This is a feature-sized auth/config/deployment change. It should be implemented behind tests and docs updates, likely split into backend config validation, local-admin auth, setup diagnostics, frontend login switching, and deployment documentation.

Summary

Add explicit deployment profiles and an optional local-admin authentication mode so QSurvey can run safely in local, lab-hosted, and fully hosted deployments.

The goal is to let researchers and developers run QSurvey without Google OAuth unless they explicitly choose it. Local development should use a local database by default. Hosted deployments can use a remote database and either local-admin login or Google OAuth.

Problem

QSurvey currently treats MongoDB and Google OAuth as standard setup requirements. That creates friction for:

  • Local developers who should not need a cloud database.
  • Social science researchers who may not want to configure Google OAuth.
  • Lab-local deployments that only need one administrator.
  • IRB-sensitive deployments where local testing should not touch real participant data.

There are two separate deployment choices:

  1. Database scope

    • Local development should normally use local MongoDB.
    • Hosted production should normally use a remote or managed MongoDB.
    • Local development should not accidentally connect to production data.
  2. Authentication mode

    • Google OAuth is useful for multi-user or institutional login.
    • Google OAuth is unnecessary for local development, demos, and many single-admin deployments.
    • QSurvey should support a local admin account without requiring Google OAuth credentials.

Database location should not silently decide the whole application mode. Use explicit deployment profiles and validate unsafe combinations.

Goals

  • Add explicit deployment profiles.
  • Add local-admin authentication.
  • Make Google OAuth optional.
  • Disable Google OAuth unless explicitly enabled.
  • Block local development from using remote MongoDB by default.
  • Add conditional environment validation.
  • Add setup diagnostics that do not expose secrets.
  • Keep JWT as the shared downstream authorization mechanism.

Non-goals

  • Do not replace MongoDB.
  • Do not remove Google OAuth.
  • Do not implement multi-tenant hosting.
  • Do not infer all behavior from MONGO_URI.
  • Do not expose secrets in logs, API responses, or documentation.

Proposed environment model

Add these variables:

QSURVEY_PROFILE=local_dev | lab_local | hosted
AUTH_MODE=local_admin | google
MONGO_SCOPE=local | remote
ALLOW_REMOTE_DB_IN_LOCAL=false
CONFIRM_REMOTE_DB_IN_LOCAL=

Continue using existing variables:

MONGO_URI=
SECRET=
FRONTEND_URL=
REDIRECT_URL=
GOOGLE_CLIENTID=
GOOGLE_SECRET=

Add local-admin variables:

ADMIN_EMAIL=
ADMIN_PASSWORD=

Optional later:

ADMIN_PASSWORD_HASH=

Deployment profiles

QSURVEY_PROFILE=local_dev

Use for local development and demos.

Default behavior:

  • Requires local MongoDB.
  • Defaults to AUTH_MODE=local_admin.
  • Does not require Google OAuth variables.
  • Blocks remote MongoDB by default.
  • Allows survey creation, preview, test responses, and export against the local database.

Allowed local MongoDB examples:

mongodb://localhost:27017/qsurvey_dev
mongodb://127.0.0.1:27017/qsurvey_dev
mongodb://mongo:27017/qsurvey_dev

Remote MongoDB should fail in local_dev unless both override variables are set:

ALLOW_REMOTE_DB_IN_LOCAL=true
CONFIRM_REMOTE_DB_IN_LOCAL=I_UNDERSTAND_THIS_CAN_TOUCH_REAL_DATA

QSURVEY_PROFILE=lab_local

Use for lab servers, institutional servers, classroom use, and internal pilots.

Default behavior:

  • Allows local or institutional MongoDB.
  • Defaults to AUTH_MODE=local_admin.
  • Does not require Google OAuth variables unless AUTH_MODE=google.
  • Warns if public access is configured without HTTPS.

QSURVEY_PROFILE=hosted

Use for deployed study instances.

Default behavior:

  • Expects a remote or managed MongoDB database.
  • Allows either AUTH_MODE=local_admin or AUTH_MODE=google.
  • Requires public deployment URL configuration.
  • Requires HTTPS for production readiness.

Auth modes

AUTH_MODE=local_admin

Use one administrator account. Google OAuth is disabled.

Required variables:

QSURVEY_PROFILE=
AUTH_MODE=local_admin
MONGO_URI=
SECRET=
ADMIN_EMAIL=
ADMIN_PASSWORD=

Behavior:

  • Add email/password login endpoint, preferably POST /api/v1/auth/login.
  • Create exactly one initial admin account on first startup if no admin exists.
  • Store password hashes only.
  • Never store plaintext passwords.
  • If an admin already exists, ignore ADMIN_PASSWORD.
  • Do not overwrite an existing admin password from environment variables.
  • Return the same JWT shape used by Google-authenticated users.
  • Existing protected routes should continue using the JWT guard.
  • Disable Google OAuth routes.

Google routes in this mode:

GET /api/v1/google-login
GET /api/v1/redirect

These should return 404 or a clear disabled-auth-mode response.

These variables should not be required in local_admin mode:

GOOGLE_CLIENTID
GOOGLE_SECRET
REDIRECT_URL

AUTH_MODE=google

Use the existing Google OAuth flow.

Required variables:

QSURVEY_PROFILE=
AUTH_MODE=google
MONGO_URI=
SECRET=
GOOGLE_CLIENTID=
GOOGLE_SECRET=
REDIRECT_URL=
FRONTEND_URL=

Behavior:

  • Preserve the current Google login flow.
  • Enable /api/v1/google-login.
  • Enable /api/v1/redirect.
  • Continue issuing JWTs after successful Google login.

Conditional validation rules

At startup, always require:

QSURVEY_PROFILE
AUTH_MODE
MONGO_URI
SECRET

If AUTH_MODE=local_admin, require:

ADMIN_EMAIL
ADMIN_PASSWORD

If AUTH_MODE=google, require:

GOOGLE_CLIENTID
GOOGLE_SECRET
REDIRECT_URL
FRONTEND_URL

If QSURVEY_PROFILE=local_dev, reject remote MongoDB unless both override flags are present:

ALLOW_REMOTE_DB_IN_LOCAL=true
CONFIRM_REMOTE_DB_IN_LOCAL=I_UNDERSTAND_THIS_CAN_TOUCH_REAL_DATA

Startup errors should be readable and actionable.

Example:

QSurvey configuration error.

Missing required variables for AUTH_MODE=local_admin:
- ADMIN_EMAIL
- ADMIN_PASSWORD

Remote MongoDB is blocked in QSURVEY_PROFILE=local_dev.
To override, set:
ALLOW_REMOTE_DB_IN_LOCAL=true
CONFIRM_REMOTE_DB_IN_LOCAL=I_UNDERSTAND_THIS_CAN_TOUCH_REAL_DATA

Setup check endpoint

Add:

GET /api/v1/setup-check

The endpoint should report deployment state without exposing secrets.

Example response for local development:

{
  "profile": "local_dev",
  "authMode": "local_admin",
  "mongoScope": "local",
  "status": "ok",
  "checks": {
    "mongoConnected": true,
    "secretConfigured": true,
    "adminConfigured": true,
    "googleOAuthRequired": false,
    "googleOAuthConfigured": false,
    "remoteDbBlockedInLocal": true,
    "publicBaseUrlConfigured": false,
    "productionCollectionReady": false
  },
  "warnings": [
    "Google OAuth is disabled. Only the local admin account can manage surveys.",
    "Public participant links are not marked production-ready in local_dev mode."
  ]
}

Example response for hosted Google mode:

{
  "profile": "hosted",
  "authMode": "google",
  "mongoScope": "remote",
  "status": "ok",
  "checks": {
    "mongoConnected": true,
    "secretConfigured": true,
    "googleOAuthRequired": true,
    "googleOAuthConfigured": true,
    "redirectUrlConfigured": true,
    "frontendUrlConfigured": true,
    "publicBaseUrlConfigured": true,
    "productionCollectionReady": true
  },
  "warnings": []
}

Auth config endpoint

Add:

GET /api/v1/auth/config

Example response:

{
  "authMode": "local_admin",
  "localLoginEnabled": true,
  "googleLoginEnabled": false
}

Frontend behavior:

  • If localLoginEnabled=true, show email/password login.
  • If googleLoginEnabled=true, show Google login.
  • If both are false, show a configuration error.

Functionality in local-admin mode

Local-admin mode should not disable core QSurvey functionality.

Allowed:

  • Create surveys.
  • Edit surveys.
  • Preview surveys.
  • Submit test responses.
  • Export data.
  • Run local or lab-local studies.

Limited or warned:

  • Disable Google OAuth routes.
  • Disable Google-account-based researcher onboarding.
  • Show a local/admin-mode banner in the designer/admin UI.
  • Warn before public participant collection in local_dev.
  • Mark production readiness as false unless hosted settings are configured.

Remote database guardrail

Local development should not accidentally connect to a remote database.

Implementation rule:

If QSURVEY_PROFILE=local_dev and MONGO_URI is remote:
  fail startup unless explicit override flags are set.

Local examples:

localhost
127.0.0.1
::1
mongo

Remote examples:

mongodb+srv://...
mongodb://<public-host>:27017/...

Override requires both:

ALLOW_REMOTE_DB_IN_LOCAL=true
CONFIRM_REMOTE_DB_IN_LOCAL=I_UNDERSTAND_THIS_CAN_TOUCH_REAL_DATA

Security requirements

  • Never commit .env files.
  • Never print MONGO_URI.
  • Never print SECRET.
  • Never print ADMIN_PASSWORD.
  • Never print GOOGLE_SECRET.
  • Never print generated database passwords.
  • Store password hashes only.
  • Require a strong SECRET outside local_dev.
  • Require HTTPS for production readiness in hosted.
  • Avoid default admin passwords.
  • Add login rate limiting if feasible.

Suggested implementation order

  1. Add QSURVEY_PROFILE and AUTH_MODE config parsing.
  2. Add conditional environment validation.
  3. Add local-admin password hashing.
  4. Add local-admin login endpoint.
  5. Bootstrap one admin account on first startup.
  6. Disable Google routes unless AUTH_MODE=google.
  7. Add remote-database guardrail for QSURVEY_PROFILE=local_dev.
  8. Add /api/v1/setup-check.
  9. Add /api/v1/auth/config.
  10. Update frontend login UI.
  11. Update deployment docs.

Tests to add

  • Config validation tests for missing always-required variables.
  • Config validation tests for AUTH_MODE=local_admin without Google variables.
  • Config validation tests for AUTH_MODE=google requiring Google variables.
  • Remote MongoDB guardrail tests for QSURVEY_PROFILE=local_dev.
  • Local-admin bootstrap tests covering first startup and existing admin behavior.
  • Local-admin login success and invalid-password tests.
  • Google route disabled tests in local_admin mode.
  • Google flow preservation tests in google mode.
  • /api/v1/setup-check response tests that verify no secrets are leaked.
  • /api/v1/auth/config response tests.
  • Frontend login UI tests for local-admin mode, Google mode, and invalid config.

Documentation to update

  • .shared/developer_doc/setup/local-development.md
  • .shared/developer_doc/security/auth-security.md
  • .shared/developer_doc/ops/env-and-secrets.md
  • .shared/developer_doc/ops/deployment-guide.md
  • Any repository setup or sample env documentation that lists required auth variables.

Acceptance criteria

  • QSURVEY_PROFILE=local_dev with local MongoDB and AUTH_MODE=local_admin starts without Google OAuth variables.
  • QSURVEY_PROFILE=local_dev with remote MongoDB fails unless explicit override flags are set.
  • AUTH_MODE=local_admin allows an admin to log in with email/password and receive a JWT.
  • AUTH_MODE=local_admin does not require GOOGLE_CLIENTID, GOOGLE_SECRET, or REDIRECT_URL.
  • AUTH_MODE=google preserves the existing Google login flow.
  • Google login routes are disabled when AUTH_MODE=local_admin.
  • Protected API routes work with JWTs from either auth mode.
  • /api/v1/setup-check reports configuration status without leaking secrets.
  • Frontend login UI switches between local-admin login and Google login based on server config.
  • Documentation explains local_dev, lab_local, and hosted.

Open implementation decisions

  • Choose whether disabled Google routes should return 404 or a clear 4xx disabled-auth-mode response.
  • Decide whether login rate limiting is required in the first implementation or tracked as a follow-up.
  • Decide whether MONGO_SCOPE should be user-supplied, derived for diagnostics, or both with consistency validation.
  • Decide how to mark and identify the single local admin account in the existing User schema without disrupting Google-authenticated users.

Rationale

This lowers setup friction for social science researchers and local developers while protecting real study data.

The safest default is:

Local development uses local MongoDB and local admin login.

Hosted deployments can still use Google OAuth when a team wants institutional login, but Google OAuth should not be required for running, testing, or deploying a simple single-admin QSurvey instance.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions