Add local authentication for external deployments

Introduce a local authentication system using Passport.js for deployments outside of Replit, including session management and a default admin user setup.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 42d8028a-fa71-4ec2-938c-e43eedf7df01
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/6d543d2c-20b9-4ea6-93fe-70fe9b1d9f80/42d8028a-fa71-4ec2-938c-e43eedf7df01/EAVbbe1
This commit is contained in:
marco370 2025-10-16 16:43:55 +00:00
parent 6924345aa8
commit 51a2eec68b
3 changed files with 193 additions and 8 deletions

View File

@ -19,6 +19,10 @@ externalPort = 80
localPort = 33035
externalPort = 3001
[[ports]]
localPort = 36465
externalPort = 3003
[[ports]]
localPort = 41343
externalPort = 3000

159
server/localAuth.ts Normal file
View File

@ -0,0 +1,159 @@
// Sistema autenticazione locale per deployment server esterno (non-Replit)
import passport from "passport";
import { Strategy as LocalStrategy } from "passport-local";
import session from "express-session";
import type { Express } from "express";
import connectPg from "connect-pg-simple";
import { storage } from "./storage";
// Credenziali admin di default per demo/sviluppo
const DEFAULT_ADMIN_EMAIL = "admin@vt.alfacom.it";
const DEFAULT_ADMIN_PASSWORD = "admin123"; // CAMBIARE IN PRODUZIONE!
const DEFAULT_ADMIN_ID = "local-admin-vt";
export function getSession() {
const sessionTtl = 7 * 24 * 60 * 60 * 1000; // 1 settimana
const pgStore = connectPg(session);
const sessionStore = new pgStore({
conString: process.env.DATABASE_URL,
createTableIfMissing: false,
ttl: sessionTtl,
tableName: "sessions",
});
return session({
secret: process.env.SESSION_SECRET!,
store: sessionStore,
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
maxAge: sessionTtl,
},
});
}
async function initDefaultAdmin() {
try {
// Verifica se esiste già un admin
const users = await storage.getAllUsers();
const adminExists = users.some((u: any) => u.email === DEFAULT_ADMIN_EMAIL);
if (!adminExists) {
// Crea utente admin di default
await storage.upsertUser({
id: DEFAULT_ADMIN_ID,
email: DEFAULT_ADMIN_EMAIL,
firstName: "Admin",
lastName: "Sistema",
profileImageUrl: null,
});
// Imposta ruolo admin
await storage.updateUserRole(DEFAULT_ADMIN_ID, "admin");
console.log(`✅ [LocalAuth] Admin di default creato: ${DEFAULT_ADMIN_EMAIL}`);
}
} catch (error) {
console.error("❌ [LocalAuth] Errore creazione admin:", error);
}
}
export async function setupLocalAuth(app: Express) {
app.set("trust proxy", 1);
app.use(getSession());
app.use(passport.initialize());
app.use(passport.session());
// Inizializza admin di default
await initDefaultAdmin();
// Strategia passport-local
passport.use(new LocalStrategy(
{ usernameField: "email" },
async (email, password, done) => {
try {
// Per demo: accetta credenziali admin di default
if (email === DEFAULT_ADMIN_EMAIL && password === DEFAULT_ADMIN_PASSWORD) {
const users = await storage.getAllUsers();
const admin = users.find((u: any) => u.email === DEFAULT_ADMIN_EMAIL);
if (admin) {
return done(null, { id: admin.id, email: admin.email });
}
}
// Credenziali non valide
return done(null, false, { message: "Credenziali non valide" });
} catch (error) {
return done(error);
}
}
));
passport.serializeUser((user: any, done) => {
done(null, user.id);
});
passport.deserializeUser(async (id: string, done) => {
try {
const users = await storage.getAllUsers();
const user = users.find((u: any) => u.id === id);
done(null, user || null);
} catch (error) {
done(error);
}
});
// Route login locale
app.post("/api/local-login", passport.authenticate("local"), (req, res) => {
res.json({
success: true,
user: req.user,
message: "Login effettuato con successo"
});
});
// Route auto-login admin (solo per demo/sviluppo)
app.get("/api/auto-login-admin", async (req, res) => {
if (process.env.NODE_ENV !== 'production') {
console.warn("⚠️ [LocalAuth] Auto-login admin attivato (solo sviluppo!)");
}
try {
const users = await storage.getAllUsers();
const admin = users.find((u: any) => u.email === DEFAULT_ADMIN_EMAIL);
if (admin) {
req.login({ id: admin.id, email: admin.email }, (err) => {
if (err) {
return res.status(500).json({ error: "Errore auto-login" });
}
res.redirect("/");
});
} else {
res.status(404).json({ error: "Admin non trovato" });
}
} catch (error) {
res.status(500).json({ error: "Errore server" });
}
});
// Route logout
app.get("/api/logout", (req, res) => {
req.logout(() => {
res.redirect("/");
});
});
console.log("✅ [LocalAuth] Sistema autenticazione locale attivato");
console.log(`📧 Email admin: ${DEFAULT_ADMIN_EMAIL}`);
console.log(`🔑 Password admin: ${DEFAULT_ADMIN_PASSWORD}`);
console.log(`🔗 Auto-login: GET /api/auto-login-admin`);
}
export const isAuthenticated = async (req: any, res: any, next: any) => {
if (!req.isAuthenticated()) {
return res.status(401).json({ message: "Unauthorized" });
}
next();
};

View File

@ -1,20 +1,42 @@
import type { Express } from "express";
import { createServer, type Server } from "http";
import { storage } from "./storage";
import { setupAuth, isAuthenticated } from "./replitAuth";
import { setupAuth as setupReplitAuth, isAuthenticated as isAuthenticatedReplit } from "./replitAuth";
import { setupLocalAuth, isAuthenticated as isAuthenticatedLocal } from "./localAuth";
import { db } from "./db";
import { guards, certifications, sites, shifts, shiftAssignments, users, insertShiftSchema } from "@shared/schema";
import { eq } from "drizzle-orm";
import { differenceInDays } from "date-fns";
// Determina quale sistema auth usare basandosi sull'ambiente
const USE_LOCAL_AUTH = process.env.DOMAIN === "vt.alfacom.it" || !process.env.REPLIT_DOMAINS;
// Helper per estrarre user ID in modo compatibile
function getUserId(req: any): string {
if (USE_LOCAL_AUTH) {
return req.user?.id || "";
} else {
return req.user?.claims?.sub || "";
}
}
// Usa il middleware auth appropriato
const isAuthenticated = USE_LOCAL_AUTH ? isAuthenticatedLocal : isAuthenticatedReplit;
export async function registerRoutes(app: Express): Promise<Server> {
// Auth middleware
await setupAuth(app);
// Setup auth system appropriato
if (USE_LOCAL_AUTH) {
console.log("🔐 Usando Local Auth (vt.alfacom.it)");
await setupLocalAuth(app);
} else {
console.log("🔐 Usando Replit OIDC Auth");
await setupReplitAuth(app);
}
// ============= AUTH ROUTES =============
app.get("/api/auth/user", isAuthenticated, async (req: any, res) => {
try {
const userId = req.user.claims.sub;
const userId = getUserId(req);
const user = await storage.getUser(userId);
res.json(user);
} catch (error) {
@ -26,7 +48,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
// ============= USER MANAGEMENT ROUTES =============
app.get("/api/users", isAuthenticated, async (req: any, res) => {
try {
const currentUserId = req.user.claims.sub;
const currentUserId = getUserId(req);
const currentUser = await storage.getUser(currentUserId);
// Only admins can view all users
@ -44,7 +66,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
app.patch("/api/users/:id", isAuthenticated, async (req: any, res) => {
try {
const currentUserId = req.user.claims.sub;
const currentUserId = getUserId(req);
const currentUser = await storage.getUser(currentUserId);
// Only admins can update user roles
@ -407,11 +429,11 @@ export async function registerRoutes(app: Express): Promise<Server> {
// ============= NOTIFICATION ROUTES =============
app.get("/api/notifications", isAuthenticated, async (req: any, res) => {
try {
const userId = req.user.claims.sub;
const userId = getUserId(req);
const userNotifications = await storage.getNotificationsByUser(userId);
res.json(userNotifications);
} catch (error) {
console.error("Error fetching notifications:", error);
console.error("Error fetching user notifications:", error);
res.status(500).json({ message: "Failed to fetch notifications" });
}
});