diff --git a/config.conf b/config.conf index 8cb18a2b2..77c6b85ad 100644 --- a/config.conf +++ b/config.conf @@ -2,10 +2,12 @@ const port = 8000; const whitelist = ['127.0.0.1']; //Example for add several IP in whitelist: ['127.0.0.1', '192.168.0.10'] const whitelistMode = true; //Disabling enabling the ip whitelist mode. true/false +const basicAuthMode = false; //Toggle basic authentication for endpoints. +const basicAuthUser = {username: "user", password: "password"}; //Login credentials when basicAuthMode is true. const autorun = true; //Autorun in the browser. true/false const enableExtensions = true; //Enables support for TavernAI-extras project const listen = true; // If true, Can be access from other device or PC. otherwise can be access only from hosting machine. module.exports = { - port, whitelist, whitelistMode, autorun, enableExtensions, listen + port, whitelist, whitelistMode, basicAuthMode, basicAuthUser, autorun, enableExtensions, listen }; diff --git a/server.js b/server.js index c7fd4a199..4ccadf8a9 100644 --- a/server.js +++ b/server.js @@ -35,6 +35,7 @@ const rimraf = require("rimraf"); const multer = require("multer"); const http = require("http"); const https = require('https'); +const basicAuthMiddleware = require('./src/middleware/basicAuthMiddleware'); //const PNG = require('pngjs').PNG; const extract = require('png-chunks-extract'); const encode = require('png-chunks-encode'); @@ -194,6 +195,8 @@ const CORS = cors({ app.use(CORS); +if (listen && config.basicAuthMode) app.use(basicAuthMiddleware); + app.use(function (req, res, next) { //Security let clientIp = req.connection.remoteAddress; let ip = ipaddr.parse(clientIp); @@ -2395,6 +2398,10 @@ const setupTasks = async function () { if (autorun) open(autorunUrl.toString()); console.log('SillyTavern is listening on: ' + tavernUrl); + if (listen && + !config.whitelistMode && + !config.basicAuthMode) + console.log('Your SillyTavern is currently open to the public. To increase security, consider enabling whitelisting or basic authentication.') if (fs.existsSync('public/characters/update.txt') && !is_colab) { convertStage1(); diff --git a/src/middleware/basicAuthMiddleware.js b/src/middleware/basicAuthMiddleware.js new file mode 100644 index 000000000..2f368214c --- /dev/null +++ b/src/middleware/basicAuthMiddleware.js @@ -0,0 +1,39 @@ +/** + * When applied, this middleware will ensure the request contains the required header for basic authentication and only + * allow access to the endpoint after successful authentication. + */ + +const {dirname} = require('path'); +const appDir = dirname(require.main.filename); +const config = require(appDir + '/config.conf'); + +const unauthorizedResponse = (res) => { + res.set('WWW-Authenticate', 'Basic realm="SillyTavern", charset="UTF-8"'); + return res.status(401).send('Authentication required'); +}; + +const basicAuthMiddleware = function (request, response, callback) { + const authHeader = request.headers.authorization; + + if (!authHeader) { + return unauthorizedResponse(response); + } + + const [scheme, credentials] = authHeader.split(' '); + + if (scheme !== 'Basic' || !credentials) { + return unauthorizedResponse(response); + } + + const [username, password] = Buffer.from(credentials, 'base64') + .toString('utf8') + .split(':'); + + if (username === config.basicAuthUser.username && password === config.basicAuthUser.password) { + return callback(); + } else { + return unauthorizedResponse(response); + } +} + +module.exports = basicAuthMiddleware; \ No newline at end of file