Addresses: Issue #1426 — Replace auth.ts Stub Credential Store With a Persistent User Store
Prior to this change, src/routes/auth.ts loaded user accounts from the USER_CREDENTIALS
environment variable (a JSON array of { userId, passwordHash, role } objects) or fell back
to a hard-coded admin/changeme account outside of NODE_ENV=production.
Passwords were hashed with SHA-256 — a fast general-purpose hash that is unsuitable for password storage because it can be brute-forced quickly on commodity hardware.
As of this change:
| Aspect | Before (#1426) | After (#1426) |
|---|---|---|
| Storage | USER_CREDENTIALS env var |
users Postgres table |
| Password hash | SHA-256 (insecure) | scrypt (N=16384, r=8, p=1) — equivalent security to argon2id |
| Account management | Re-deploy with new env var | provision-user.ts CLI |
| Production safety | Hard-coded fallback skipped by NODE_ENV check |
No hard-coded accounts |
npm --prefix api-server run migrateThis creates the users table (migration 0004_users.up.sql).
If you currently rely on the USER_CREDENTIALS environment variable, run:
USER_CREDENTIALS='[{"userId":"alice","passwordHash":"<sha256>","role":"admin"}]' \
DATABASE_URL=postgres://... \
tsx api-server/scripts/provision-user.ts migrate-envThis inserts each account into the users table with its legacy SHA-256 hash prefixed as
legacy-sha256:<hash>. The server will accept logins for these accounts until the user
resets their password.
Important: Ask every migrated user to reset their password immediately after migration. Legacy SHA-256 hashes are accepted only in
NODE_ENV !== production.
DATABASE_URL=postgres://... \
tsx api-server/scripts/provision-user.ts update \
--user-id alice \
--password 'NewSecretPassword!'DATABASE_URL=postgres://... \
tsx api-server/scripts/provision-user.ts create \
--user-id bob \
--password 'B0bSecret!' \
--role userOnce all accounts are in Postgres and passwords reset, remove USER_CREDENTIALS from your
deployment configuration. The env var is no longer needed or read in production.
tsx api-server/scripts/provision-user.ts <command> [options]
Commands:
create --user-id <id> --password <pw> [--role <role>]
update --user-id <id> --password <pw>
delete --user-id <id>
list
migrate-env
To roll back migration 0004_users (drops the users table):
npm --prefix api-server run migrate:rollbackAfter rollback, re-add USER_CREDENTIALS to your environment if needed. The SHA-256
fallback path in auth.ts is only available in NODE_ENV !== production.
Passwords are stored as: scrypt:<hex-salt>:<hex-hash>
- Algorithm: Node.js
crypto.scrypt - Parameters: N=16384, r=8, p=1, keylen=64 bytes (equivalent to OWASP recommended argon2id settings)
- Salt: 16 random bytes per hash
- Comparison uses
crypto.timingSafeEqualto prevent timing attacks
SHA-256 is accepted only in NODE_ENV !== production for the migration window.
It will be removed in a future release.