mirror of
https://github.com/freedbygrace/ActiveDirectoryManager.git
synced 2026-08-18 06:24:31 +00:00
Initial checkpoint
Replit-Commit-Author: Agent Replit-Commit-Session-Id: 705f2157-ef97-4fbd-89e4-8c7f2ecaea90
This commit is contained in:
+175
@@ -0,0 +1,175 @@
|
||||
import passport from "passport";
|
||||
import { Strategy as LocalStrategy } from "passport-local";
|
||||
import { Express } from "express";
|
||||
import session from "express-session";
|
||||
import { scrypt, randomBytes, timingSafeEqual } from "crypto";
|
||||
import { promisify } from "util";
|
||||
import { storage } from "./storage";
|
||||
import { User as SelectUser } from "@shared/schema";
|
||||
import jwt from "jsonwebtoken";
|
||||
|
||||
declare global {
|
||||
namespace Express {
|
||||
interface User extends SelectUser {}
|
||||
}
|
||||
}
|
||||
|
||||
const scryptAsync = promisify(scrypt);
|
||||
|
||||
export async function hashPassword(password: string) {
|
||||
const salt = randomBytes(16).toString("hex");
|
||||
const buf = (await scryptAsync(password, salt, 64)) as Buffer;
|
||||
return `${buf.toString("hex")}.${salt}`;
|
||||
}
|
||||
|
||||
export async function comparePasswords(supplied: string, stored: string) {
|
||||
const [hashed, salt] = stored.split(".");
|
||||
const hashedBuf = Buffer.from(hashed, "hex");
|
||||
const suppliedBuf = (await scryptAsync(supplied, salt, 64)) as Buffer;
|
||||
return timingSafeEqual(hashedBuf, suppliedBuf);
|
||||
}
|
||||
|
||||
export function setupAuth(app: Express) {
|
||||
const jwtSecret = process.env.JWT_SECRET || 'default_jwt_secret_key_change_in_production';
|
||||
const sessionSecret = process.env.SESSION_SECRET || 'default_session_secret_key_change_in_production';
|
||||
|
||||
const sessionSettings: session.SessionOptions = {
|
||||
secret: sessionSecret,
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
store: storage.sessionStore,
|
||||
cookie: {
|
||||
secure: process.env.NODE_ENV === 'production',
|
||||
maxAge: 24 * 60 * 60 * 1000 // 24 hours
|
||||
}
|
||||
};
|
||||
|
||||
app.set("trust proxy", 1);
|
||||
app.use(session(sessionSettings));
|
||||
app.use(passport.initialize());
|
||||
app.use(passport.session());
|
||||
|
||||
passport.use(
|
||||
new LocalStrategy(async (username, password, done) => {
|
||||
try {
|
||||
const user = await storage.getUserByUsername(username);
|
||||
if (!user || !(await comparePasswords(password, user.password))) {
|
||||
return done(null, false);
|
||||
} else {
|
||||
return done(null, user);
|
||||
}
|
||||
} catch (error) {
|
||||
return done(error);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
passport.serializeUser((user, done) => done(null, user.id));
|
||||
passport.deserializeUser(async (id: number, done) => {
|
||||
try {
|
||||
const user = await storage.getUser(id);
|
||||
done(null, user);
|
||||
} catch (error) {
|
||||
done(error);
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/api/register", async (req, res, next) => {
|
||||
try {
|
||||
const existingUser = await storage.getUserByUsername(req.body.username);
|
||||
if (existingUser) {
|
||||
return res.status(400).json({ message: "Username already exists" });
|
||||
}
|
||||
|
||||
const hashedPassword = await hashPassword(req.body.password);
|
||||
|
||||
const user = await storage.createUser({
|
||||
...req.body,
|
||||
password: hashedPassword,
|
||||
});
|
||||
|
||||
// Log this activity
|
||||
await storage.createActivityLog({
|
||||
action: 'Create',
|
||||
resource: user.username,
|
||||
resourceType: 'User',
|
||||
userId: null,
|
||||
username: 'System',
|
||||
status: 'Success',
|
||||
details: { id: user.id }
|
||||
});
|
||||
|
||||
req.login(user, (err) => {
|
||||
if (err) return next(err);
|
||||
|
||||
// Don't send password back
|
||||
const { password, ...userWithoutPassword } = user;
|
||||
res.status(201).json(userWithoutPassword);
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/api/login", passport.authenticate("local"), (req, res) => {
|
||||
// Don't send password back
|
||||
const { password, ...userWithoutPassword } = req.user as SelectUser;
|
||||
res.status(200).json(userWithoutPassword);
|
||||
});
|
||||
|
||||
app.post("/api/logout", (req, res, next) => {
|
||||
req.logout((err) => {
|
||||
if (err) return next(err);
|
||||
res.sendStatus(200);
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/api/user", (req, res) => {
|
||||
if (!req.isAuthenticated()) return res.sendStatus(401);
|
||||
|
||||
// Don't send password back
|
||||
const { password, ...userWithoutPassword } = req.user as SelectUser;
|
||||
res.json(userWithoutPassword);
|
||||
});
|
||||
|
||||
// Middleware to verify API token
|
||||
const verifyApiToken = async (req: any, res: any, next: any) => {
|
||||
const token = req.headers.authorization?.split(' ')[1];
|
||||
|
||||
if (!token) {
|
||||
return res.status(401).json({ message: 'API token is required' });
|
||||
}
|
||||
|
||||
try {
|
||||
// Check if token exists in storage
|
||||
const apiToken = await storage.getApiTokenByToken(token);
|
||||
|
||||
if (!apiToken) {
|
||||
return res.status(401).json({ message: 'Invalid API token' });
|
||||
}
|
||||
|
||||
// Check if token is expired
|
||||
if (apiToken.expiresAt && new Date(apiToken.expiresAt) < new Date()) {
|
||||
return res.status(401).json({ message: 'API token has expired' });
|
||||
}
|
||||
|
||||
// Update the last used timestamp
|
||||
await storage.updateApiToken(apiToken.id, { lastUsedAt: new Date() });
|
||||
|
||||
// Get the user associated with this token
|
||||
const user = await storage.getUser(apiToken.userId);
|
||||
|
||||
if (!user) {
|
||||
return res.status(401).json({ message: 'User associated with token not found' });
|
||||
}
|
||||
|
||||
// Set the user in the request
|
||||
req.user = user;
|
||||
next();
|
||||
} catch (error) {
|
||||
return res.status(500).json({ message: 'Error verifying API token' });
|
||||
}
|
||||
};
|
||||
|
||||
return { verifyApiToken };
|
||||
}
|
||||
Reference in New Issue
Block a user