Files
SillyTavern/src/middleware/basicAuth.js
T
Cohee 8e8f501279 Immutable public and global content management (#5390)
* Use custom init script instead of postinstall

* Revert changes to start scripts in src\electron

* Add global data to content manager

* Add migration for public overrides and user.css location update

* Update npm publish workflow to use 'omit=dev' flag in npm ci commands

* Rename user.css readme file

* Fix indentation in userCssMiddleware function

* Add directory creation for content target

* Restore template compile location

* Move stylesheet up in index.json

* Use path.resolve for user.css file path in userCssMiddleware

* Correct capitalization in "Not Found" error page title and heading

* Remove init run from startup scripts

* Simplify user CSS file path resolution

* Update userCssMiddleware comment
2026-04-05 19:32:28 +03:00

58 lines
2.3 KiB
JavaScript

/**
* 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.
*/
import { Buffer } from 'node:buffer';
import path from 'node:path';
import storage from 'node-persist';
import { getAllUserHandles, toKey, getPasswordHash } from '../users.js';
import { getConfigValue, safeReadFileSync } from '../util.js';
const PER_USER_BASIC_AUTH = getConfigValue('perUserBasicAuth', false, 'boolean');
const ENABLE_ACCOUNTS = getConfigValue('enableUserAccounts', false, 'boolean');
const basicAuthMiddleware = async function (request, response, callback) {
const unauthorizedWebpage = safeReadFileSync(path.join(globalThis.DATA_ROOT, '_errors', 'unauthorized.html')) ?? '';
const unauthorizedResponse = (res) => {
res.set('WWW-Authenticate', 'Basic realm="SillyTavern", charset="UTF-8"');
return res.status(401).send(unauthorizedWebpage);
};
const basicAuthUserName = getConfigValue('basicAuthUser.username');
const basicAuthUserPassword = getConfigValue('basicAuthUser.password');
const authHeader = request.headers.authorization;
if (!authHeader) {
return unauthorizedResponse(response);
}
const [scheme, credentials] = authHeader.split(' ');
if (scheme !== 'Basic' || !credentials) {
return unauthorizedResponse(response);
}
const usePerUserAuth = PER_USER_BASIC_AUTH && ENABLE_ACCOUNTS;
const [username, ...passwordParts] = Buffer.from(credentials, 'base64')
.toString('utf8')
.split(':');
const password = passwordParts.join(':');
if (!usePerUserAuth && username === basicAuthUserName && password === basicAuthUserPassword) {
return callback();
} else if (usePerUserAuth) {
const userHandles = await getAllUserHandles();
for (const userHandle of userHandles) {
if (username === userHandle) {
const user = await storage.getItem(toKey(userHandle));
if (user && user.enabled && (user.password && user.password === getPasswordHash(password, user.salt))) {
return callback();
}
}
}
}
return unauthorizedResponse(response);
};
export default basicAuthMiddleware;