mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-03-06 04:38:21 +01:00
Add additional login methods
This commit is contained in:
parent
030313c584
commit
69a604044d
@ -51,6 +51,19 @@ requestProxy:
|
||||
enableUserAccounts: false
|
||||
# Enable discreet login mode: hides user list on the login screen
|
||||
enableDiscreetLogin: false
|
||||
# Enable's authlia based auto login. Only enable this if you
|
||||
# have setup and installed Authelia as a middle-ware on your
|
||||
# reverse proxy
|
||||
# https://www.authelia.com/
|
||||
# This will use auto login to an account with the same username
|
||||
# as that used for authlia. (Ensure the username in authlia
|
||||
# is an exact match with that in sillytavern)
|
||||
autheliaAuth: false
|
||||
# If `basicAuthMode` and this are enabled then
|
||||
# the username and passwords for basic auth are the same as those
|
||||
# for the individual accounts
|
||||
perUserBasicAuth: false
|
||||
|
||||
# User session timeout *in seconds* (defaults to 24 hours).
|
||||
## Set to a positive number to expire session after a certain time of inactivity
|
||||
## Set to 0 to expire session when the browser is closed
|
||||
|
10
server.js
10
server.js
@ -65,6 +65,7 @@ const DEFAULT_WHITELIST = true;
|
||||
const DEFAULT_ACCOUNTS = false;
|
||||
const DEFAULT_CSRF_DISABLED = false;
|
||||
const DEFAULT_BASIC_AUTH = false;
|
||||
const DEFAULT_PERUSER_BASIC_AUTH = false;
|
||||
|
||||
const DEFAULT_ENABLE_IPV6 = false;
|
||||
const DEFAULT_ENABLE_IPV4 = true;
|
||||
@ -184,6 +185,7 @@ const enableWhitelist = cliArguments.whitelist ?? getConfigValue('whitelistMode'
|
||||
const dataRoot = cliArguments.dataRoot ?? getConfigValue('dataRoot', './data');
|
||||
const disableCsrf = cliArguments.disableCsrf ?? getConfigValue('disableCsrfProtection', DEFAULT_CSRF_DISABLED);
|
||||
const basicAuthMode = cliArguments.basicAuthMode ?? getConfigValue('basicAuthMode', DEFAULT_BASIC_AUTH);
|
||||
const PERUSER_BASIC_AUTH = getConfigValue('perUserBasicAuth', DEFAULT_PERUSER_BASIC_AUTH);
|
||||
const enableAccounts = getConfigValue('enableUserAccounts', DEFAULT_ACCOUNTS);
|
||||
|
||||
const uploadsPath = path.join(dataRoot, require('./src/constants').UPLOADS_DIRECTORY);
|
||||
@ -756,9 +758,11 @@ const postSetupTasks = async function (v6Failed, v4Failed) {
|
||||
}
|
||||
|
||||
if (basicAuthMode) {
|
||||
const basicAuthUser = getConfigValue('basicAuthUser', {});
|
||||
if (!basicAuthUser?.username || !basicAuthUser?.password) {
|
||||
console.warn(color.yellow('Basic Authentication is enabled, but username or password is not set or empty!'));
|
||||
if (!PERUSER_BASIC_AUTH) {
|
||||
const basicAuthUser = getConfigValue('basicAuthUser', {});
|
||||
if (!basicAuthUser?.username || !basicAuthUser?.password) {
|
||||
console.warn(color.yellow('Basic Authentication is enabled, but username or password is not set or empty!'));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -2,14 +2,18 @@
|
||||
* 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 { getConfig } = require('../util.js');
|
||||
const { getAllUserHandles, toKey, getPasswordHash } = require('../users.js');
|
||||
const { getConfig, getConfigValue } = require('../util.js');
|
||||
const storage = require('node-persist');
|
||||
|
||||
const PERUSER_BASIC_AUTH = getConfigValue('perUserBasicAuth', false);
|
||||
|
||||
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 basicAuthMiddleware = async function (request, response, callback) {
|
||||
const config = getConfig();
|
||||
const authHeader = request.headers.authorization;
|
||||
|
||||
@ -27,11 +31,25 @@ const basicAuthMiddleware = function (request, response, callback) {
|
||||
.toString('utf8')
|
||||
.split(':');
|
||||
|
||||
if (username === config.basicAuthUser.username && password === config.basicAuthUser.password) {
|
||||
|
||||
if (! PERUSER_BASIC_AUTH && username === config.basicAuthUser.username && password === config.basicAuthUser.password) {
|
||||
return callback();
|
||||
} else {
|
||||
return unauthorizedResponse(response);
|
||||
} else if (PERUSER_BASIC_AUTH) {
|
||||
const userHandles = await getAllUserHandles();
|
||||
for (const userHandle of userHandles) {
|
||||
if (username == userHandle) {
|
||||
const user = await storage.getItem(toKey(userHandle));
|
||||
if (user && (user.password && user.password === getPasswordHash(password, user.salt))) {
|
||||
return callback();
|
||||
}
|
||||
else if (user && !user.password && !password) {
|
||||
// Login to an account without password
|
||||
return callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return unauthorizedResponse(response);
|
||||
};
|
||||
|
||||
module.exports = basicAuthMiddleware;
|
||||
|
98
src/users.js
98
src/users.js
@ -19,6 +19,8 @@ const { readSecret, writeSecret } = require('./endpoints/secrets');
|
||||
const KEY_PREFIX = 'user:';
|
||||
const AVATAR_PREFIX = 'avatar:';
|
||||
const ENABLE_ACCOUNTS = getConfigValue('enableUserAccounts', false);
|
||||
const AUTHELIA_AUTH = getConfigValue('autheliaAuth', false);
|
||||
const PERUSER_BASIC_AUTH = getConfigValue('perUserBasicAuth', false);
|
||||
const ANON_CSRF_SECRET = crypto.randomBytes(64).toString('base64');
|
||||
|
||||
/**
|
||||
@ -575,6 +577,31 @@ async function tryAutoLogin(request) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (await singler_user_login(request)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (AUTHELIA_AUTH && await authelia_user_login(request)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (PERUSER_BASIC_AUTH && await basic_user_login(request)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries auto-login if there is only one user and it's not password protected.
|
||||
* @param {import('express').Request} request Request object
|
||||
* @returns {Promise<boolean>} Whether auto-login was performed
|
||||
*/
|
||||
async function singler_user_login(request) {
|
||||
if (!request.session) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const userHandles = await getAllUserHandles();
|
||||
if (userHandles.length === 1) {
|
||||
const user = await storage.getItem(toKey(userHandles[0]));
|
||||
@ -583,7 +610,78 @@ async function tryAutoLogin(request) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries auto-login with authlia trusted headers.
|
||||
* https://www.authelia.com/integration/trusted-header-sso/introduction/
|
||||
* @param {import('express').Request} request Request object
|
||||
* @returns {Promise<boolean>} Whether auto-login was performed
|
||||
*/
|
||||
async function authelia_user_login(request) {
|
||||
if (!request.session) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const remote_user = request.get("Remote-User");
|
||||
if (!remote_user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const userHandles = await getAllUserHandles();
|
||||
for (const userHandle of userHandles) {
|
||||
if (remote_user == userHandle) {
|
||||
const user = await storage.getItem(toKey(userHandle));
|
||||
if (user) {
|
||||
request.session.handle = userHandle;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries auto-login with basic auth username.
|
||||
* @param {import('express').Request} request Request object
|
||||
* @returns {Promise<boolean>} Whether auto-login was performed
|
||||
*/
|
||||
async function basic_user_login(request) {
|
||||
if (!request.session) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auth_header = request.get("Authorization");
|
||||
if (!auth_header) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const parts = auth_header.split(' ');
|
||||
if (!parts || parts.length < 2 || parts[0].toLowerCase() != "basic") {
|
||||
return false;
|
||||
}
|
||||
|
||||
const b64auth = parts[1];
|
||||
const [login, password] = Buffer.from(b64auth, 'base64').toString().split(':')
|
||||
|
||||
const userHandles = await getAllUserHandles();
|
||||
for (const userHandle of userHandles) {
|
||||
if (login == userHandle) {
|
||||
const user = await storage.getItem(toKey(userHandle));
|
||||
// Verify pass again here just to be sure
|
||||
if (user && user.password && user.password === getPasswordHash(password, user.salt)) {
|
||||
request.session.handle = userHandle;
|
||||
return true;
|
||||
}
|
||||
else if (user && !user.password && !password) {
|
||||
// Login to an account without password
|
||||
request.session.handle = userHandle;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user