const express = require("express") const router = express.Router() const bodyParser = require("body-parser") var sanitize = require("mongo-sanitize") const mongoose = require("mongoose") const User = require("./../model/user.js") const bcrypt = require("bcrypt") const jwt = require("jsonwebtoken") require("dotenv").config() const JWT_SECRET = process.env.JWT_SECRET const fetch = (...args) => import("node-fetch").then(({ default: fetch }) => fetch(...args)) const speakeasy = require("speakeasy") const rateLimit = require("express-rate-limit") const limiter = rateLimit({ windowMs: 5 * 1000, // 5 seconds max: 1, // Limit each IP to 1 requests per `window` standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers legacyHeaders: false, // Disable the `X-RateLimit-*` headers handler: (request, response, next, options) => { return response.json({ status: "error", error: "Too many requests try again later.", }) }, }) router.use(bodyParser.json()) router.get("/", (req, res) => { res.redirect("/") }) router.get(["/RequestAuth", "/RequestAuth.ashx"], (req, res) => { if (!req.cookies[".ROBLOSECURITY"]) { res.status(403).end() } res.send( "https://www.mete0r.xyz/Login/Negotiate.ashx?suggest=" + req.cookies[".ROBLOSECURITY"], ) }) // studio router.get(["/Negotiate", "/Negotiate.ashx"], (req, res) => { if (!req.query.suggest) { res.status(403).end() } //res.cookie('jwt', req.query.suggest) // maxage is 24 hours res.cookie(".ROBLOSECURITY", req.query.suggest) res.cookie(".RBXID", req.query.suggest) res.send(req.query.suggest) }) // studio router.post("/", limiter, async (req, res) => { //console.log(req.headers) let { username, password, _2fa } = req.body if ( !username && req.headers?.["user-agent"]?.includes("RobloxStudio/WinInet") === true ) { // Studio login username = req.body.cvalue ?? req.body.username password = req.body.password ?? req.body.ticket _2fa = req.body.code } if (!username || typeof username !== "string") { return res.json({ status: "error", error: "Usernames needs to be sent and it needs to be a string", }) } if (!password || typeof password !== "string") { return res.json({ status: "error", error: "Password needs to be sent and it needs to be a string", }) } if (password.length < 4) { return res.json({ status: "error", error: "Password needs to be at least 5 characters", }) } sanitizedusername = sanitize(username) const user = await User.findOne({ username: sanitizedusername }) /*.lean()*/ if (!user) { if (req.headers?.["user-agent"] === "RobloxStudio/WinInet") { // studio response return res.json({ errors: [ { code: 1, message: "Incorrect password", }, ], }) } return res.json({ status: "error", error: "Invalid username/password" }) } if (user.twofasecrets) { const json = JSON.parse(user.twofasecrets) if (json.verified === true) { if (!_2fa) { if (req.headers?.["user-agent"] === "RobloxStudio/WinInet") { // studio response return res.json({ user: { id: user.userid, name: user.username, }, twoStepVerificationData: { mediaType: "Email", ticket: password, }, isBanned: false, }) } return res.json({ status: "error", error: "2FA Enabled on account but 2fa not sent", }) } const valid = speakeasy.totp.verify({ secret: json.secret, encoding: "ascii", token: _2fa, }) if (valid === false) { if (req.headers?.["user-agent"] === "RobloxStudio/WinInet") { // studio response return res.json({ errors: [ { code: 6, message: "Invalid two step verify code.", }, ], }) } return res.json({ status: "error", error: "Invalid 2FA Code" }) } } else { // basically if they haven't verified that they know the secret before we will just remove it for them user.twofasecrets = undefined user.markModified("twofasecrets") user.save() } } if ( (await bcrypt.compare(password, user.password)) || password === user.password ) { // the username and password match // lets make a token for them using the data from our database const ip = req.headers["cf-connecting-ip"] || req.socket.remoteAddress const token = jwt.sign( { id: user._id, username: user.username, admin: user.admin, userid: user.userid, ip, furry: true, }, JWT_SECRET, { expiresIn: "24h" }, ) if (req.headers?.["user-agent"] != "RobloxStudio/WinInet") { res.cookie("jwt", token, { SameSite: "Strict", httpOnly: true, maxAge: 24 * 60 * 60 * 1000, }) // maxage is 24 hours } res.cookie(".ROBLOSECURITY", token, { SameSite: "Strict", httpOnly: true, maxAge: 24 * 60 * 60 * 1000, }) res.cookie(".RBXID", token, { SameSite: "Strict", httpOnly: true, maxAge: 24 * 60 * 60 * 1000, }) if (req.url === "/v2/twostepverification/verify") { return res.json({}) } if (req.headers?.["user-agent"] === "RobloxStudio/WinInet") { // studio response return res.json({ user: { id: user.userid, name: user.username, }, isBanned: false, }) } return res.json({ status: "ok", cookie: token }) } if (req.headers?.["user-agent"] === "RobloxStudio/WinInet") { // studio response return res.json({ errors: [ { code: 1, message: "Incorrect password", }, ], }) } res.status(403).json({ status: "error", error: "Invalid username/password", }) }) module.exports = router