pediatric-ai-scribe-v3/src/utils/platform.js
Daniel 6dffdf91e5 Sliding 24h idle timeout (web) + persistent mobile + 2FA backup codes
Session model:
  Web     — 24h sliding idle timeout enforced server-side via
             user_sessions.last_activity. 30-day JWT + cookie are a
             safety net; middleware is the real clock. Cookie is
             re-set on active use so browsers match the sliding window.
  Mobile  — 365-day JWT, no idle timeout (stays persistent via Keychain
             / Keystore). Detected via User-Agent ("PedScribe" /
             "Capacitor") or X-Client: mobile header.

2FA backup codes:
  - 10 single-use codes generated when 2FA is first enabled
  - Stored as bcrypt hashes in new users.totp_backup_codes column
  - Consumed atomically on successful login fallback (when TOTP fails)
  - Regenerate endpoint (POST /api/auth/2fa/backup-codes) requires
    current password; invalidates prior codes
  - Count endpoint (GET /api/auth/2fa/backup-codes/count) powers a
    "N codes remaining" indicator on the 2FA settings card
  - Modal shows codes exactly once with Copy + Download .txt actions
  - Codes cleared when 2FA is disabled

New files:
  src/utils/platform.js — isMobileClient() helper

Schema migration (idempotent):
  ALTER TABLE users ADD COLUMN IF NOT EXISTS totp_backup_codes TEXT
2026-04-14 04:24:54 +02:00

12 lines
530 B
JavaScript

// Simple mobile-vs-web client detection based on the User-Agent
// appended by Capacitor config (appendUserAgent: "PedScribe-Android",
// similarly for iOS) and an optional X-Client header.
function isMobileClient(req) {
if (!req) return false;
var xc = req.headers && req.headers['x-client'];
if (xc === 'mobile' || xc === 'ios' || xc === 'android') return true;
var ua = (req.headers && req.headers['user-agent']) || '';
return /PedScribe|Capacitor/i.test(ua);
}
module.exports = { isMobileClient: isMobileClient };