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
12 lines
530 B
JavaScript
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 };
|