MeteoriteH/Back/routes/login.js

224 lines
5.5 KiB
JavaScript

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