VigilanzaTurni/server/replitAuth.ts
marco370 c82fafd438 Improve authentication by supporting external servers
Update Replit authentication to use environment variables for domains and REPL ID.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 42d8028a-fa71-4ec2-938c-e43eedf7df01
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/6d543d2c-20b9-4ea6-93fe-70fe9b1d9f80/42d8028a-fa71-4ec2-938c-e43eedf7df01/AwjfjCW
2025-10-16 16:05:48 +00:00

158 lines
4.2 KiB
TypeScript

// Integration: javascript_log_in_with_replit blueprint
import * as client from "openid-client";
import { Strategy, type VerifyFunction } from "openid-client/passport";
import passport from "passport";
import session from "express-session";
import type { Express, RequestHandler } from "express";
import memoize from "memoizee";
import connectPg from "connect-pg-simple";
import { storage } from "./storage";
// Supporto deployment Replit e server esterni
const REPLIT_DOMAINS = process.env.REPLIT_DOMAINS || process.env.DOMAIN || "vt.alfacom.it";
const REPL_ID = process.env.REPL_ID || "vigilanza-turni";
const getOidcConfig = memoize(
async () => {
return await client.discovery(
new URL(process.env.ISSUER_URL ?? "https://replit.com/oidc"),
REPL_ID
);
},
{ maxAge: 3600 * 1000 }
);
export function getSession() {
const sessionTtl = 7 * 24 * 60 * 60 * 1000; // 1 week
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: true,
maxAge: sessionTtl,
},
});
}
function updateUserSession(
user: any,
tokens: client.TokenEndpointResponse & client.TokenEndpointResponseHelpers
) {
user.claims = tokens.claims();
user.access_token = tokens.access_token;
user.refresh_token = tokens.refresh_token;
user.expires_at = user.claims?.exp;
}
async function upsertUser(
claims: any,
) {
await storage.upsertUser({
id: claims["sub"],
email: claims["email"],
firstName: claims["first_name"],
lastName: claims["last_name"],
profileImageUrl: claims["profile_image_url"],
});
}
export async function setupAuth(app: Express) {
app.set("trust proxy", 1);
app.use(getSession());
app.use(passport.initialize());
app.use(passport.session());
const config = await getOidcConfig();
const verify: VerifyFunction = async (
tokens: client.TokenEndpointResponse & client.TokenEndpointResponseHelpers,
verified: passport.AuthenticateCallback
) => {
const user = {};
updateUserSession(user, tokens);
await upsertUser(tokens.claims());
verified(null, user);
};
for (const domain of REPLIT_DOMAINS.split(",")) {
const strategy = new Strategy(
{
name: `replitauth:${domain}`,
config,
scope: "openid email profile offline_access",
callbackURL: `https://${domain}/api/callback`,
},
verify,
);
passport.use(strategy);
}
passport.serializeUser((user: Express.User, cb) => cb(null, user));
passport.deserializeUser((user: Express.User, cb) => cb(null, user));
app.get("/api/login", (req, res, next) => {
passport.authenticate(`replitauth:${req.hostname}`, {
prompt: "login consent",
scope: ["openid", "email", "profile", "offline_access"],
})(req, res, next);
});
app.get("/api/callback", (req, res, next) => {
passport.authenticate(`replitauth:${req.hostname}`, {
successReturnToOrRedirect: "/",
failureRedirect: "/api/login",
})(req, res, next);
});
app.get("/api/logout", (req, res) => {
req.logout(() => {
res.redirect(
client.buildEndSessionUrl(config, {
client_id: REPL_ID,
post_logout_redirect_uri: `${req.protocol}://${req.hostname}`,
}).href
);
});
});
}
export const isAuthenticated: RequestHandler = async (req, res, next) => {
const user = req.user as any;
if (!req.isAuthenticated() || !user.expires_at) {
return res.status(401).json({ message: "Unauthorized" });
}
const now = Math.floor(Date.now() / 1000);
if (now <= user.expires_at) {
return next();
}
const refreshToken = user.refresh_token;
if (!refreshToken) {
res.status(401).json({ message: "Unauthorized" });
return;
}
try {
const config = await getOidcConfig();
const tokenResponse = await client.refreshTokenGrant(config, refreshToken);
updateUserSession(user, tokenResponse);
return next();
} catch (error) {
res.status(401).json({ message: "Unauthorized" });
return;
}
};