feat(mam-api): wire express-session + tighten CORS allowlist
This commit is contained in:
parent
1a723fe4c2
commit
a094df03ea
1 changed files with 36 additions and 1 deletions
|
|
@ -1,6 +1,9 @@
|
||||||
import 'dotenv/config';
|
import 'dotenv/config';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
|
import session from 'express-session';
|
||||||
|
import connectPgSimple from 'connect-pg-simple';
|
||||||
|
const PgStore = connectPgSimple(session);
|
||||||
import os from 'node:os';
|
import os from 'node:os';
|
||||||
import { exec } from 'node:child_process';
|
import { exec } from 'node:child_process';
|
||||||
import pool from './db/pool.js';
|
import pool from './db/pool.js';
|
||||||
|
|
@ -34,9 +37,41 @@ const app = express();
|
||||||
const PORT = process.env.PORT || 3000;
|
const PORT = process.env.PORT || 3000;
|
||||||
|
|
||||||
// ── Middleware ────────────────────────────────────────────────────────────────
|
// ── Middleware ────────────────────────────────────────────────────────────────
|
||||||
app.use(cors({ origin: true, credentials: true }));
|
// Tightened CORS — once cookies carry authority, `origin: true` would let
|
||||||
|
// any site forge requests with the cookie. Drive the allowlist from env.
|
||||||
|
const allowedOrigins = (process.env.ALLOWED_ORIGINS || '')
|
||||||
|
.split(',').map(s => s.trim()).filter(Boolean);
|
||||||
|
app.use(cors({
|
||||||
|
origin: (origin, cb) => {
|
||||||
|
// No Origin header (same-origin or curl) — allow.
|
||||||
|
if (!origin) return cb(null, true);
|
||||||
|
if (allowedOrigins.length === 0 || allowedOrigins.includes(origin)) return cb(null, true);
|
||||||
|
return cb(new Error('CORS: origin not allowed: ' + origin));
|
||||||
|
},
|
||||||
|
credentials: true,
|
||||||
|
}));
|
||||||
app.use(express.json({ limit: '50mb' }));
|
app.use(express.json({ limit: '50mb' }));
|
||||||
|
|
||||||
|
// Trust the reverse proxy only when explicitly told to (production HTTPS).
|
||||||
|
if (process.env.TRUST_PROXY === 'true') app.set('trust proxy', 1);
|
||||||
|
|
||||||
|
// Session — actually wired this time. See specs/2026-05-27-auth-system-design.md.
|
||||||
|
app.use(session({
|
||||||
|
store: new PgStore({ pool, tableName: 'sessions', pruneSessionInterval: 60 * 15 }),
|
||||||
|
secret: process.env.SESSION_SECRET,
|
||||||
|
name: 'dragonflight.sid',
|
||||||
|
cookie: {
|
||||||
|
httpOnly: true,
|
||||||
|
sameSite: 'lax',
|
||||||
|
secure: process.env.TRUST_PROXY === 'true',
|
||||||
|
path: '/',
|
||||||
|
maxAge: 8 * 3600 * 1000,
|
||||||
|
},
|
||||||
|
rolling: false, // sliding renewal handled in requireAuth so idle + absolute can be enforced separately
|
||||||
|
resave: false,
|
||||||
|
saveUninitialized: false,
|
||||||
|
}));
|
||||||
|
|
||||||
// ── Health ────────────────────────────────────────────────────────────────────
|
// ── Health ────────────────────────────────────────────────────────────────────
|
||||||
app.get('/health', (_req, res) => res.json({ status: 'ok' }));
|
app.get('/health', (_req, res) => res.json({ status: 'ok' }));
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue