when i enabled 2fa and added the secret to google authenticator on my phone i try to login, but i keep getting an error message that says invalid 2fa code. how can i fix it? the code is indeed ok…
this is my code:
const express = require("express");
const session = require("express-session");
const passport = require("passport");
const LocalStrategy = require("passport-local").Strategy;
const speakeasy = require("speakeasy");
const flash = require("express-flash");
const app = express();
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
const authenticator = require("otplib");
// Use sessions to track login status
app.use(
session({
secret: "your-secret-key",
resave: false,
saveUninitialized: false,
})
);
// Initialize Passport and session
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
app.use(bodyParser.urlencoded({ extended: true }));
mongoose
.connect("mongodb://localhost:27017/2fa", {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => {
console.log("MongoDB connected successfully");
})
.catch((err) => {
console.error("MongoDB connection error:", err);
});
// Create a User model/schema
const User = mongoose.model("User", {
username: String,
password: String,
secret: String,
is2FAEnabled: Boolean,
token: String,
});
// Passport local strategy for authentication
passport.use(
new LocalStrategy(async function (username, password, done) {
try {
const user = await User.findOne({
username: username,
password: password,
}).exec();
if (!user) {
return done(null, false, { message: "Incorrect username or password" });
}
return done(null, user);
} catch (err) {
return done(err);
}
})
);
// Serialize user for session
passport.serializeUser(function (user, done) {
done(null, user.id);
});
// Deserialize user from session
passport.deserializeUser(async function (id, done) {
try {
const user = await User.findById(id).exec();
done(null, user);
} catch (err) {
done(err);
}
});
// Middleware to check if user is authenticated
function isAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect("/login");
}
// Routes
// Dashboard route
app.get("/dashboard", isAuthenticated, async (req, res) => {
try {
const user = await User.findById(req.user.id);
if (user && user.is2FAEnabled) {
return res.redirect("/verify-2fa"); // Redirect to verify-2fa if 2FA is enabled
} else {
return res.send("Dashboard Page"); // Render dashboard if 2FA is not enabled
}
} catch (error) {
console.log(error);
res.redirect("/"); // Fallback redirect to home in case of an error
}
});
app.get("/", (req, res) => {
res.send("Home Page");
});
// Login route
app.post(
"/login",
passport.authenticate("local", {
successRedirect: "/dashboard", // Redirect to dashboard by default
failureRedirect: "/login",
failureFlash: true,
})
);
// Logout route
app.get("/logout", (req, res) => {
req.logout(function (err) {
if (err) {
// Handle error, if any
return res.status(500).send("Internal Server Error");
}
// Redirect to the home page or any other page after successful logout
res.redirect("/");
});
});
// Enable 2FA for a user user1 secret = MZFXORCDNZKFOL23INLTSSBQMEVHG6BQ / HBLVIKDLN4SEE6TSKJSWWY3SGVKDO3DX
app.get("/enable-2fa", isAuthenticated, async (req, res) => {
try {
const secret = speakeasy.generateSecret({ length: 20 });
// Use findOneAndUpdate to update the user in the database
await User.findOneAndUpdate(
{ _id: req.user.id },
{ secret: secret.base32, is2FAEnabled: true }
).exec();
res.send(`2FA Enabled. Save this secret: ${secret.base32}`);
} catch (error) {
console.error(error);
res.status(500).send("Internal Server Error");
}
});
// Authenticate using 2FA
app.post(
"/login-2fa",
passport.authenticate("local", {
successRedirect: "/verify-2fa",
failureRedirect: "/login",
failureFlash: true,
})
);
// Verify 2FA page
app.get("/verify-2fa", isAuthenticated, (req, res) => {
res.send(`
<html>
<head>
<title>Verify 2FA</title>
</head>
<body>
<h2>Verify 2FA Page</h2>
<form method="post" action="/verify-2fa">
<label for="totp">Enter TOTP Code:</label>
<input type="text" id="totp" name="totp" required>
<button type="submit">Verify</button>
</form>
</body>
</html>
`);
});
// Verify 2FA code
app.post("/verify-2fa", isAuthenticated, async (req, res) => {
try {
const user = await User.findById(req.user.id);
const isValid = speakeasy.totp.verify({
secret: user.secret,
encoding: "base32",
token: req.body.totp,
});
//console.log("User ID:", req.user.id);
//console.log("User Secret:", user.secret);
//console.log("is2FAEnabled:", user.is2FAEnabled);
//console.log("Received TOTP:", req.body.totp);
if (isValid) {
res.redirect("/dashboard");
} else if (!isValid) {
res.send("Invalid 2FA code. Try again.");
} else {
res.send("some error my nigga");
}
} catch (error) {
console.error(error);
res.status(500).send("Internal Server Error");
}
});
// Login page
app.get("/login", (req, res) => {
res.send(`
<html>
<head>
<title>Login</title>
</head>
<body>
<h2>Login Page</h2>
<form method="post" action="/login">
<label for="username">Username:</label>
<input type="text" id="username" name="username" required>
<br>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required>
<br>
<button type="submit">Login</button>
</form>
</body>
</html>
`);
});
app.get("/register", (req, res) => {
res.send(`
<html>
<head>
<title>Register</title>
</head>
<body>
<h2>Registration Page</h2>
<form method="post" action="/register">
<label for="username">Username:</label>
<input type="text" id="username" name="username" required>
<br>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required>
<br>
<button type="submit">Register</button>
</form>
</body>
</html>
`);
});
app.post("/register", (req, res) => {
const { username, password } = req.body;
// Check if the username already exists
User.findOne({ username: username })
.exec()
.then((existingUser) => {
if (existingUser) {
return res.send(
"Username already exists. Please choose a different one."
);
}
// Create a new user and save to the database
const newUser = new User({
username: username,
password: password,
secret: speakeasy.generateSecret({ length: 20 }).base32,
is2FAEnabled: false,
});
return newUser.save();
})
.then(() => {
res.send("Registration successful. You can now log in.");
})
.catch((err) => {
console.error(err);
res.status(500).send("Internal Server Error");
});
});
// Start server
const PORT = 80;
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
without 2fa enabled a user can login and logout without any problem. but for some reason i keep getting the “invalid 2fa code” error message… what to do?