From 0aa6fabdd94e723ec64b2cfdd25e4129897da5f4 Mon Sep 17 00:00:00 2001 From: IrosTheBeggar Date: Mon, 7 Dec 2020 21:47:15 -0500 Subject: [PATCH] new admin api --- mstream.js | 4 ++-- src/api/auth.js | 28 ++++++++++++++++++++++++++++ src/util/auth.js | 31 +++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 src/api/auth.js create mode 100644 src/util/auth.js diff --git a/mstream.js b/mstream.js index 87cd2f2..0a92a8f 100755 --- a/mstream.js +++ b/mstream.js @@ -86,7 +86,8 @@ exports.serveIt = config => { // Login functionality program.auth = false; if (program.users && Object.keys(program.users).length !== 0) { - require('./modules/login.js').setup(mstream, program, express); + require('./src/api/auth.js').setup(mstream, program); + require('./modules/login.js').setup(mstream, program); program.auth = true; } else { program.users = { @@ -154,7 +155,6 @@ exports.serveIt = config => { server.listen(program.port, program.address, () => { const protocol = program.ssl && program.ssl.cert && program.ssl.key ? 'https' : 'http'; winston.info(`Access mStream locally: ${protocol}://${program.address}:${program.port}`); - winston.info(`Try the WinAmp Demo: ${protocol}://${program.address}:${program.port}/winamp`); dbModule.runAfterBoot(program); ddns.setup(program); diff --git a/src/api/auth.js b/src/api/auth.js new file mode 100644 index 0000000..6f0a0ee --- /dev/null +++ b/src/api/auth.js @@ -0,0 +1,28 @@ +const jwt = require('jsonwebtoken'); +const Joi = require('joi'); +const winston = require('winston'); +const auth = require('../util/auth'); + +exports.setup = (mstream, program) => { + mstream.post('/api/v1/auth/login', async (req, res) => { + try { + const schema = Joi.object({ + username: Joi.string().required(), + password: Joi.string().required() + }); + await schema.validateAsync(req.body); + + if (!program.users[req.body.username]) { throw 'user not found'; } + + await auth.authenticateUser(program.users[req.body.username].password, program.users[req.body.username].salt, req.body.password) + + res.json({ + vpaths: program.users[req.body.username].vpaths, + token: jwt.sign({ username: req.body.username }, program.secret) + }); + }catch (err) { + winston.warn(`Failed login attempt from ${req.ip}. Username: ${req.body.username}`); + setTimeout(() => { res.status(401).json({ error: 'Login Failed' }); }, 800); + } + }); +} \ No newline at end of file diff --git a/src/util/auth.js b/src/util/auth.js new file mode 100644 index 0000000..02396fb --- /dev/null +++ b/src/util/auth.js @@ -0,0 +1,31 @@ +const crypto = require('crypto'); + +const hashConfig = { + hashBytes: 32, + saltBytes: 16, + iterations: 15000 +}; + +exports.hashPassword = password => { + return new Promise((resolve, reject) => { + crypto.randomBytes(hashConfig.saltBytes, (err, salt) => { + if (err) { return reject('Failed to hash password'); } + crypto.pbkdf2(password, salt.toString('base64'), hashConfig.iterations, hashConfig.hashBytes, 'sha512', (err, hash) => { + if (err) { return reject('Failed to hash password'); } + resolve({ salt: salt.toString('base64'), hashPassword: hash.toString('base64') }); + }); + }); + }); +} + +exports.authenticateUser = (password, salt, givenPassword) => { + return new Promise((resolve, reject) => { + crypto.pbkdf2(givenPassword, salt, hashConfig.iterations, hashConfig.hashBytes, 'sha512', (err, verifyHash) => { + if (err) { reject('Unknown Authentication Error'); } + if (verifyHash.toString('base64') !== password) { + return reject('Authentication Error: Passwords do not match'); + } + resolve(); + }); + }); +} \ No newline at end of file