Improve security and deployment flexibility for external access
Update README and application configurations to support reverse proxy setups, including TLS termination and proper cookie handling, and add an option to seed demo meetings. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 77cfe984-2c65-4152-bb7a-0df28274fe66 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: c15da2be-cee7-4cbb-8d58-f9fcc3c38ed7 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/c3c252e4-c83d-40ca-9fff-99a3ea60701e/77cfe984-2c65-4152-bb7a-0df28274fe66/FvHcc7z Replit-Helium-Checkpoint-Created: true
This commit is contained in:
@@ -13,6 +13,25 @@ const app: Express = express();
|
||||
|
||||
app.set("trust proxy", 1);
|
||||
|
||||
// Self-hosted deployments are commonly fronted by a TLS-terminating
|
||||
// proxy that does NOT forward `X-Forwarded-Proto: https` to the
|
||||
// upstream (e.g. `tailscale serve`, ngrok free tier, some Cloudflare
|
||||
// Tunnel configs). Without that header, `req.secure` stays false and
|
||||
// the session cookie (when marked `secure: true`) is silently dropped
|
||||
// — login appears to succeed but the very next request is unauth'd.
|
||||
//
|
||||
// Setting `TRUST_PROXY_HTTPS=true` tells us to treat every incoming
|
||||
// request as if it arrived over HTTPS. Only enable this when the
|
||||
// edge proxy you control actually terminates TLS in front of Tx OS.
|
||||
const trustProxyHttps =
|
||||
(process.env.TRUST_PROXY_HTTPS ?? "").toLowerCase() === "true";
|
||||
if (trustProxyHttps) {
|
||||
app.use((req, _res, next) => {
|
||||
req.headers["x-forwarded-proto"] = "https";
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
app.use(
|
||||
pinoHttp({
|
||||
logger,
|
||||
@@ -55,11 +74,30 @@ app.use(
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
|
||||
// The session cookie should only be marked `secure` when the user is
|
||||
// actually reaching us over HTTPS. We derive that from PUBLIC_BASE_URL
|
||||
// instead of NODE_ENV so a self-hosted production install that's only
|
||||
// reachable over plain HTTP (e.g. internal LAN, no TLS yet) still
|
||||
// works without operator surgery.
|
||||
const publicBaseUrl = process.env.PUBLIC_BASE_URL ?? "";
|
||||
const sessionCookieSecure =
|
||||
publicBaseUrl.startsWith("https://") || trustProxyHttps;
|
||||
|
||||
export const sessionMiddleware = session({
|
||||
store: new PgSession({
|
||||
pool,
|
||||
tableName: "user_sessions",
|
||||
// Auto-create the session table on first boot. Drizzle's migrations
|
||||
// intentionally exclude `user_sessions` (see lib/db/drizzle.config.ts)
|
||||
// because the schema is owned by connect-pg-simple, not by us. Without
|
||||
// this flag, the very first login on a fresh install 500s.
|
||||
createTableIfMissing: true,
|
||||
}),
|
||||
// Honour `X-Forwarded-Proto` from upstream proxies when deciding
|
||||
// whether to set the secure cookie. Combined with `app.set("trust
|
||||
// proxy", 1)` above, this makes the cookie work end-to-end behind
|
||||
// Caddy / nginx / Cloudflare / Tailscale.
|
||||
proxy: true,
|
||||
secret: process.env.SESSION_SECRET ?? (() => {
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
throw new Error("SESSION_SECRET must be set in production");
|
||||
@@ -69,7 +107,7 @@ export const sessionMiddleware = session({
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
cookie: {
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
secure: sessionCookieSecure,
|
||||
httpOnly: true,
|
||||
maxAge: 7 * 24 * 60 * 60 * 1000,
|
||||
sameSite: "lax",
|
||||
|
||||
Reference in New Issue
Block a user