|
|
@@ -0,0 +1,155 @@
|
|
|
+import express from 'express';
|
|
|
+import jwt from 'jsonwebtoken';
|
|
|
+import argon2 from 'argon2';
|
|
|
+import Mailer from "./mail.js"
|
|
|
+import * as crypto from "node:crypto";
|
|
|
+
|
|
|
+export const auth = express.Router();
|
|
|
+
|
|
|
+const mailer = new Mailer().init()
|
|
|
+
|
|
|
+let mfaCodes = []
|
|
|
+
|
|
|
+auth.get('/user', async (req, res) => {
|
|
|
+ const UserDB = req.app.locals.db;
|
|
|
+ const token = req.cookies.token;
|
|
|
+
|
|
|
+ if (!token) {
|
|
|
+ return res.status(401).json({error: 'No token'});
|
|
|
+ }
|
|
|
+
|
|
|
+ let decoded;
|
|
|
+ try {
|
|
|
+ decoded = jwt.verify(token, process.env.JWT_SECRET);
|
|
|
+ } catch {
|
|
|
+ res.clearCookie('token', {
|
|
|
+ httpOnly: true,
|
|
|
+ secure: false,
|
|
|
+ sameSite: 'lax',
|
|
|
+ path: '/'
|
|
|
+ });
|
|
|
+ return res.status(401).json({error: 'Invalid token'});
|
|
|
+ }
|
|
|
+
|
|
|
+ const row = await UserDB.find('users', {_id: decoded.id});
|
|
|
+
|
|
|
+ if (!row) {
|
|
|
+ res.clearCookie('token', {
|
|
|
+ httpOnly: true,
|
|
|
+ secure: false,
|
|
|
+ sameSite: 'lax',
|
|
|
+ path: '/'
|
|
|
+ });
|
|
|
+ return res.status(401).json({error: 'Invalid token'});
|
|
|
+ }
|
|
|
+
|
|
|
+ const {password, ...safeRow} = row;
|
|
|
+ res.json(safeRow);
|
|
|
+});
|
|
|
+
|
|
|
+auth.get('/login/:data', async (req, res) => {
|
|
|
+ const UserDB = req.app.locals.db;
|
|
|
+
|
|
|
+ if (!req.params.data) {
|
|
|
+ return res.status(401).send('Invalid token');
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const data = JSON.parse(atob(req.params.data));
|
|
|
+
|
|
|
+ const row = await UserDB.find('users', {_id: data.id});
|
|
|
+ if (!row) {
|
|
|
+ return res.status(401).send('Invalid token');
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log(mfaCodes)
|
|
|
+ const mfaRow = mfaCodes.find(obj => String(obj.id) === String(data.id));
|
|
|
+ if (!mfaRow) {
|
|
|
+ return res.status(401).send('Invalid token');
|
|
|
+ }
|
|
|
+
|
|
|
+ if (mfaRow.token !== data.token) {
|
|
|
+ return res.status(401).send('Invalid token');
|
|
|
+ }
|
|
|
+
|
|
|
+ const token = jwt.sign(
|
|
|
+ {id: row._id, username: row.username},
|
|
|
+ process.env.JWT_SECRET,
|
|
|
+ {expiresIn: '1h'}
|
|
|
+ );
|
|
|
+
|
|
|
+ res.cookie('token', token, {
|
|
|
+ httpOnly: true,
|
|
|
+ secure: false,
|
|
|
+ sameSite: 'lax',
|
|
|
+ maxAge: 60 * 60 * 1000,
|
|
|
+ path: '/'
|
|
|
+ });
|
|
|
+
|
|
|
+ const index = mfaCodes.findIndex(obj => String(obj.id) === String(data.id));
|
|
|
+ if (index !== -1) {
|
|
|
+ mfaCodes.splice(index, 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ res.redirect('http://localhost:5173/');
|
|
|
+ } catch (e) {
|
|
|
+ console.log(e);
|
|
|
+ res.status(401).send('Invalid token');
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+
|
|
|
+auth.post('/login', async (req, res) => {
|
|
|
+ let UserDB = req.app.locals.db;
|
|
|
+
|
|
|
+ const {username, password} = req.body;
|
|
|
+ if (!username || !password) {
|
|
|
+ return res.status(400).json({error: 'Missing credentials'});
|
|
|
+ }
|
|
|
+
|
|
|
+ const row = await UserDB.find('users', {username});
|
|
|
+ if (!row) return res.status(401).json({error: 'User doesnt exist'});
|
|
|
+
|
|
|
+ if (!await argon2.verify(row.password, password)) return res.status(401).json({error: 'Invalid password'});
|
|
|
+
|
|
|
+ let mfgen = crypto.randomBytes(64).toString('hex');
|
|
|
+
|
|
|
+ const mfaToken = {id: row._id, token: mfgen}
|
|
|
+
|
|
|
+
|
|
|
+ mfaCodes.push(mfaToken)
|
|
|
+
|
|
|
+ console.log(mfaCodes)
|
|
|
+
|
|
|
+ await mailer.send(row.email, "2FA Login Link", `Hey, ${row.username}!\n\nYour login link is: http://localhost:3000/api/auth/login/${btoa(JSON.stringify(mfaToken))}`)
|
|
|
+
|
|
|
+ res.json({success: true, user: {username}});
|
|
|
+});
|
|
|
+
|
|
|
+auth.post('/register', async (req, res) => {
|
|
|
+ let UserDB = req.app.locals.db;
|
|
|
+ const {email, password} = req.body;
|
|
|
+
|
|
|
+ const username = email.split("@")[0]
|
|
|
+
|
|
|
+ //await mailer.send(email, "signup request", "this worked?")
|
|
|
+
|
|
|
+ if (await UserDB.find('users', {username: username})) return res.status(400).json({error: 'User already exists'});
|
|
|
+
|
|
|
+ const hash = await hashPass(password)
|
|
|
+
|
|
|
+ UserDB.add('users', {username: username, email: email, password: hash})
|
|
|
+
|
|
|
+ res.json({success: true, user: {username}});
|
|
|
+
|
|
|
+})
|
|
|
+
|
|
|
+async function hashPass(input) {
|
|
|
+ return await argon2.hash(input, {
|
|
|
+ type: argon2.argon2id,
|
|
|
+ salt: Buffer.from(process.env.SALT, 'utf8'),
|
|
|
+ timeCost: 2,
|
|
|
+ memoryCost: 19456,
|
|
|
+ parallelism: 1
|
|
|
+ })
|
|
|
+}
|