Authentik automatic login support (#4600) * feat: authentik auto login * refactor: move SSO login configuration under sso section in config * Migrate SSO config keys read --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>

e68f9e482e2fab8eeea3b5b7372fca2d6c75da98

Jakub Jędrzejczyk <aegiruchan@gmail.com>

Signed
3 files changed, +55 -7Showing whitespace changes
default/config.yaml+16 -4
@@ -82,6 +82,13 @@ requestProxy:
8282enableUserAccounts: false
8383# Enable discreet login mode: hides user list on the login screen
8484enableDiscreetLogin: false
85+# If `basicAuthMode` and this are enabled then
86+# the username and passwords for basic auth are the same as those
87+# for the individual accounts
88+perUserBasicAuth: false
89+
90+# -- SSO LOGIN CONFIGURATION --
91+sso:
8592 # Enable's authlia based auto login. Only enable this if you
8693 # have setup and installed Authelia as a middle-ware on your
8794 # reverse proxy
@@ -90,10 +97,15 @@ enableDiscreetLogin: false
9097 # as that used for authlia. (Ensure the username in authlia
9198 # is an exact match in lowercase with that in sillytavern)
9299 autheliaAuth: false
93-# If `basicAuthMode` and this are enabled then
100+ # Enable's authentik based auto login. Only enable this if you
94-# the username and passwords for basic auth are the same as those
101+ # have setup and installed Authentik as a middle-ware on your
95-# for the individual accounts
102+ # reverse proxy.
96-perUserBasicAuth: false
103+ # https://goauthentik.io/
104+ # This will use auto login to an account with the same username
105+ # as that used for authentik. (Ensure the username in authentik
106+ # is an exact match in lowercase with that in sillytavern).
107+ authentikAuth: false
108+
97109# Host whitelist configuration. Recommended if you're using a listen mode
98110hostWhitelist:
99111 # Enable or disable host whitelisting
src/config-init.js+10 -0
@@ -124,6 +124,16 @@ const keyMigrationMap = [
124124 migrate: () => void 0,
125125 remove: true,
126126 },
127+ {
128+ oldKey: 'autheliaAuth',
129+ newKey: 'sso.autheliaAuth',
130+ migrate: (value) => value,
131+ },
132+ {
133+ oldKey: 'authentikAuth',
134+ newKey: 'sso.authentikAuth',
135+ migrate: (value) => value,
136+ },
127137];
128138
129139/**
src/users.js+29 -3
@@ -24,7 +24,8 @@ import { serverDirectory } from './server-directory.js';
2424export const KEY_PREFIX = 'user:';
2525const AVATAR_PREFIX = 'avatar:';
2626const ENABLE_ACCOUNTS = getConfigValue('enableUserAccounts', false, 'boolean');
2727const AUTHELIA_AUTH = getConfigValue('sso.autheliaAuth', false, 'boolean');
28+const AUTHENTIK_AUTH = getConfigValue('sso.authentikAuth', false, 'boolean');
2829const PER_USER_BASIC_AUTH = getConfigValue('perUserBasicAuth', false, 'boolean');
2930const ANON_CSRF_SECRET = crypto.randomBytes(64).toString('base64');
3031
@@ -716,6 +717,10 @@ export async function tryAutoLogin(request, basicAuthMode) {
716717 return true;
717718 }
718719
720+ if (AUTHENTIK_AUTH && await authentikUserLogin(request)) {
721+ return true;
722+ }
723+
719724 if (basicAuthMode && PER_USER_BASIC_AUTH && await basicUserLogin(request)) {
720725 return true;
721726 }
@@ -746,20 +751,41 @@ async function singleUserLogin(request) {
746751}
747752
748753/**
749754 * TriesAttempts auto-login withusing authliaan trustedAuthelia headersheader.
750755 * https://www.authelia.com/integration/trusted-header-sso/introduction/
751756 * @param {import('express').Request} request Request object
752757 * @returns {Promise<boolean>} Whether auto-login was performed
753758 */
754759async function autheliaUserLogin(request) {
760+ return headerUserLogin(request, 'Remote-User');
761+}
762+
763+/**
764+ * Attempts auto-login using an Authentik header.
765+ * https://docs.goauthentik.io/add-secure-apps/providers/proxy/forward_auth/
766+ * @param {import('express').Request} request Request object
767+ * @returns {Promise<boolean>} Whether auto-login was performed
768+ */
769+async function authentikUserLogin(request) {
770+ return headerUserLogin(request, 'X-Authentik-Username');
771+}
772+
773+/**
774+ * Tries auto-login with a given header.
775+ * @param {import('express').Request} request Request object
776+ * @param {string} [header='Remote-User'] The header to use for the trusted user
777+ * @returns {Promise<boolean>} Whether auto-login was performed
778+ */
779+async function headerUserLogin(request, header = 'Remote-User') {
755780 if (!request.session) {
756781 return false;
757782 }
758783
759784 const remoteUser = request.get('Remote-User'header);
760785 if (!remoteUser) {
761786 return false;
762787 }
788+ console.debug(`Attempting auto-login for user from header ${header}: ${remoteUser}`);
763789
764790 const userHandles = await getAllUserHandles();
765791 for (const userHandle of userHandles) {