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
158 lines
4.2 KiB
TypeScript
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;
|
|
}
|
|
};
|