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:
-
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.
-
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:
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:
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:
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
- Add
QSURVEY_PROFILE and AUTH_MODE config parsing.
- Add conditional environment validation.
- Add local-admin password hashing.
- Add local-admin login endpoint.
- Bootstrap one admin account on first startup.
- Disable Google routes unless
AUTH_MODE=google.
- Add remote-database guardrail for
QSURVEY_PROFILE=local_dev.
- Add
/api/v1/setup-check.
- Add
/api/v1/auth/config.
- Update frontend login UI.
- 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.
Triage
Current implementation appears Google-first:
.shared/developer_doc/setup/local-development.mdlists MongoDB and Google OAuth credentials as required local prerequisites..shared/developer_doc/security/auth-security.mddocuments Google OAuth plus JWT only..shared/developer_doc/ops/env-and-secrets.mdtreats Google OAuth secrets as required production configuration.server/src/app.module.tsconnects toMONGO_URIwithout deployment-profile validation or remote-db guardrails.server/src/auth/auth.module.tsalways registersGoogleStrategy.server/src/auth/auth.controller.tsexposes/api/v1/google-loginand/api/v1/redirectunconditionally.client/src/pages/login/Login.tsxhard-codes Google login.server/src/schemas/user.schema.tshas apasswordfield, 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:
There are two separate deployment choices:
Database scope
Authentication mode
Database location should not silently decide the whole application mode. Use explicit deployment profiles and validate unsafe combinations.
Goals
Non-goals
MONGO_URI.Proposed environment model
Add these variables:
Continue using existing variables:
Add local-admin variables:
Optional later:
Deployment profiles
QSURVEY_PROFILE=local_devUse for local development and demos.
Default behavior:
AUTH_MODE=local_admin.Allowed local MongoDB examples:
Remote MongoDB should fail in
local_devunless both override variables are set:QSURVEY_PROFILE=lab_localUse for lab servers, institutional servers, classroom use, and internal pilots.
Default behavior:
AUTH_MODE=local_admin.AUTH_MODE=google.QSURVEY_PROFILE=hostedUse for deployed study instances.
Default behavior:
AUTH_MODE=local_adminorAUTH_MODE=google.Auth modes
AUTH_MODE=local_adminUse one administrator account. Google OAuth is disabled.
Required variables:
Behavior:
POST /api/v1/auth/login.ADMIN_PASSWORD.Google routes in this mode:
These should return 404 or a clear disabled-auth-mode response.
These variables should not be required in
local_adminmode:AUTH_MODE=googleUse the existing Google OAuth flow.
Required variables:
Behavior:
/api/v1/google-login./api/v1/redirect.Conditional validation rules
At startup, always require:
If
AUTH_MODE=local_admin, require:If
AUTH_MODE=google, require:If
QSURVEY_PROFILE=local_dev, reject remote MongoDB unless both override flags are present:Startup errors should be readable and actionable.
Example:
Setup check endpoint
Add:
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:
Example response:
{ "authMode": "local_admin", "localLoginEnabled": true, "googleLoginEnabled": false }Frontend behavior:
localLoginEnabled=true, show email/password login.googleLoginEnabled=true, show Google login.Functionality in local-admin mode
Local-admin mode should not disable core QSurvey functionality.
Allowed:
Limited or warned:
local_dev.Remote database guardrail
Local development should not accidentally connect to a remote database.
Implementation rule:
Local examples:
Remote examples:
Override requires both:
Security requirements
.envfiles.MONGO_URI.SECRET.ADMIN_PASSWORD.GOOGLE_SECRET.SECREToutsidelocal_dev.hosted.Suggested implementation order
QSURVEY_PROFILEandAUTH_MODEconfig parsing.AUTH_MODE=google.QSURVEY_PROFILE=local_dev./api/v1/setup-check./api/v1/auth/config.Tests to add
AUTH_MODE=local_adminwithout Google variables.AUTH_MODE=googlerequiring Google variables.QSURVEY_PROFILE=local_dev.local_adminmode.googlemode./api/v1/setup-checkresponse tests that verify no secrets are leaked./api/v1/auth/configresponse tests.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.mdAcceptance criteria
QSURVEY_PROFILE=local_devwith local MongoDB andAUTH_MODE=local_adminstarts without Google OAuth variables.QSURVEY_PROFILE=local_devwith remote MongoDB fails unless explicit override flags are set.AUTH_MODE=local_adminallows an admin to log in with email/password and receive a JWT.AUTH_MODE=local_admindoes not requireGOOGLE_CLIENTID,GOOGLE_SECRET, orREDIRECT_URL.AUTH_MODE=googlepreserves the existing Google login flow.AUTH_MODE=local_admin./api/v1/setup-checkreports configuration status without leaking secrets.local_dev,lab_local, andhosted.Open implementation decisions
MONGO_SCOPEshould be user-supplied, derived for diagnostics, or both with consistency validation.Userschema 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:
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.