← back to Commercialrealestate
CRCP gate: multi-credential auth (CRCP_EXTRA_USERS) — add steve to main :9911
9bbc69347588c942e0881af5cbdf178eb930db04 · 2026-08-19 12:40:17 -0700 · Steve Abrams
Accept the primary CRCP_USER/CRCP_PASS plus an optional comma-separated
CRCP_EXTRA_USERS list so multiple people share one instance. Cookie check now
matches any accepted cred's token. Backward-compatible (no extras = old single
cred). Main :9911 launchd plist gets CRCP_EXTRA_USERS=steve:jef215 so steve logs
in alongside admin/DW2024!.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
Diff
commit 9bbc69347588c942e0881af5cbdf178eb930db04
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Aug 19 12:40:17 2026 -0700
CRCP gate: multi-credential auth (CRCP_EXTRA_USERS) — add steve to main :9911
Accept the primary CRCP_USER/CRCP_PASS plus an optional comma-separated
CRCP_EXTRA_USERS list so multiple people share one instance. Cookie check now
matches any accepted cred's token. Backward-compatible (no extras = old single
cred). Main :9911 launchd plist gets CRCP_EXTRA_USERS=steve:jef215 so steve logs
in alongside admin/DW2024!.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
scripts/serve.js | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/scripts/serve.js b/scripts/serve.js
index 773dd38..e88dea6 100644
--- a/scripts/serve.js
+++ b/scripts/serve.js
@@ -53,19 +53,30 @@ const AUTH_PASS = process.env.CRCP_PASS || 'DW2024!';
// automatically, and accept that cookie as an alternative to the header. Token is a stable
// non-reversible hash of the creds (never the plaintext password), so it survives restarts.
const crypto = require('crypto');
-const AUTH_TOKEN = crypto.createHash('sha256').update(AUTH_USER + ':' + AUTH_PASS).digest('hex');
+// Multi-credential gate (2026-08-19): accept the primary CRCP_USER/CRCP_PASS PLUS an optional
+// comma-separated CRCP_EXTRA_USERS list ("steve:jef215,foo:bar") so more than one person can log
+// into the same instance. Backward-compatible: with no extras this is exactly the old single cred.
+const GATE_CREDS = [[AUTH_USER, AUTH_PASS]];
+(process.env.CRCP_EXTRA_USERS || '').split(',').map(s => s.trim()).filter(Boolean).forEach(pair => {
+ const i = pair.indexOf(':'); if (i > 0) GATE_CREDS.push([pair.slice(0, i), pair.slice(i + 1)]);
+});
+const tokenOf = (u, p) => crypto.createHash('sha256').update(u + ':' + p).digest('hex');
+const AUTH_TOKEN = tokenOf(AUTH_USER, AUTH_PASS); // primary cred's token (kept for compatibility)
+const AUTH_TOKENS = new Set(GATE_CREDS.map(([u, p]) => tokenOf(u, p))); // every accepted cred's cookie token
app.get('/healthz', (req, res) => res.type('text').send('ok'));
app.use((req, res, next) => {
// 1) session cookie set after a prior successful Basic auth (fixes URL-creds subresource 401s)
const cookies = req.headers.cookie || '';
- if (cookies.split(/;\s*/).some(c => c === 'crcp_auth=' + AUTH_TOKEN)) return next();
+ const ck = cookies.split(/;\s*/).find(c => c.startsWith('crcp_auth='));
+ if (ck && AUTH_TOKENS.has(ck.slice('crcp_auth='.length))) return next();
// 2) the standard Authorization header (Basic-auth dialog, curl, or the navigation request)
const hdr = req.headers.authorization || '';
const [scheme, encoded] = hdr.split(' ');
if (scheme === 'Basic' && encoded) {
const [u, ...rest] = Buffer.from(encoded, 'base64').toString().split(':');
- if (u === AUTH_USER && rest.join(':') === AUTH_PASS) {
- res.cookie('crcp_auth', AUTH_TOKEN, { httpOnly: true, sameSite: 'Lax', path: '/', maxAge: 30 * 24 * 3600 * 1000 });
+ const p = rest.join(':');
+ if (GATE_CREDS.some(([cu, cp]) => cu === u && cp === p)) {
+ res.cookie('crcp_auth', tokenOf(u, p), { httpOnly: true, sameSite: 'Lax', path: '/', maxAge: 30 * 24 * 3600 * 1000 });
return next();
}
}
← c6ab3e8 gitignore data/sfr-notes.json (per-user runtime notes)
·
back to Commercialrealestate
·
Daily valley-pools refresh now pushes the fresh snapshot to 036e4b5 →