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>
Signed| @@ -82,18 +82,30 @@ requestProxy: | ||
| 82 | 82 | enableUserAccounts: false |
| 83 | 83 | # Enable discreet login mode: hides user list on the login screen |
| 84 | 84 | enableDiscreetLogin: false |
| 85 | -# Enable's authlia based auto login. Only enable this if you | |
| 86 | -# have setup and installed Authelia as a middle-ware on your | |
| 87 | -# reverse proxy | |
| 88 | -# https://www.authelia.com/ | |
| 89 | -# This will use auto login to an account with the same username | |
| 90 | -# as that used for authlia. (Ensure the username in authlia | |
| 91 | -# is an exact match in lowercase with that in sillytavern) | |
| 92 | -autheliaAuth: false | |
| 93 | 85 | # If `basicAuthMode` and this are enabled then |
| 94 | 86 | # the username and passwords for basic auth are the same as those |
| 95 | 87 | # for the individual accounts |
| 96 | 88 | perUserBasicAuth: false |
| 89 | + | |
| 90 | +# -- SSO LOGIN CONFIGURATION -- | |
| 91 | +sso: | |
| 92 | + # Enable's authlia based auto login. Only enable this if you | |
| 93 | + # have setup and installed Authelia as a middle-ware on your | |
| 94 | + # reverse proxy | |
| 95 | + # https://www.authelia.com/ | |
| 96 | + # This will use auto login to an account with the same username | |
| 97 | + # as that used for authlia. (Ensure the username in authlia | |
| 98 | + # is an exact match in lowercase with that in sillytavern) | |
| 99 | + autheliaAuth: false | |
| 100 | + # Enable's authentik based auto login. Only enable this if you | |
| 101 | + # have setup and installed Authentik as a middle-ware on your | |
| 102 | + # reverse proxy. | |
| 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 | + | |
| 97 | 109 | # Host whitelist configuration. Recommended if you're using a listen mode |
| 98 | 110 | hostWhitelist: |
| 99 | 111 | # Enable or disable host whitelisting |
| @@ -124,6 +124,16 @@ const keyMigrationMap = [ | ||
| 124 | 124 | migrate: () => void 0, |
| 125 | 125 | remove: true, |
| 126 | 126 | }, |
| 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 | + }, | |
| 127 | 137 | ]; |
| 128 | 138 | |
| 129 | 139 | /** |
| @@ -24,7 +24,8 @@ import { serverDirectory } from './server-directory.js'; | ||
| 24 | 24 | export const KEY_PREFIX = 'user:'; |
| 25 | 25 | const AVATAR_PREFIX = 'avatar:'; |
| 26 | 26 | const ENABLE_ACCOUNTS = getConfigValue('enableUserAccounts', false, 'boolean'); |
| 27 | 27 | const AUTHELIA_AUTH = getConfigValue('sso.autheliaAuth', false, 'boolean'); |
| 28 | +const AUTHENTIK_AUTH = getConfigValue('sso.authentikAuth', false, 'boolean'); | |
| 28 | 29 | const PER_USER_BASIC_AUTH = getConfigValue('perUserBasicAuth', false, 'boolean'); |
| 29 | 30 | const ANON_CSRF_SECRET = crypto.randomBytes(64).toString('base64'); |
| 30 | 31 | |
| @@ -716,6 +717,10 @@ export async function tryAutoLogin(request, basicAuthMode) { | ||
| 716 | 717 | return true; |
| 717 | 718 | } |
| 718 | 719 | |
| 720 | + if (AUTHENTIK_AUTH && await authentikUserLogin(request)) { | |
| 721 | + return true; | |
| 722 | + } | |
| 723 | + | |
| 719 | 724 | if (basicAuthMode && PER_USER_BASIC_AUTH && await basicUserLogin(request)) { |
| 720 | 725 | return true; |
| 721 | 726 | } |
| @@ -746,20 +751,41 @@ async function singleUserLogin(request) { | ||
| 746 | 751 | } |
| 747 | 752 | |
| 748 | 753 | /** |
| 749 | 754 | * TriesAttempts auto-login withusing authliaan trustedAuthelia headersheader. |
| 750 | 755 | * https://www.authelia.com/integration/trusted-header-sso/introduction/ |
| 751 | 756 | * @param {import('express').Request} request Request object |
| 752 | 757 | * @returns {Promise<boolean>} Whether auto-login was performed |
| 753 | 758 | */ |
| 754 | 759 | async 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') { | |
| 755 | 780 | if (!request.session) { |
| 756 | 781 | return false; |
| 757 | 782 | } |
| 758 | 783 | |
| 759 | 784 | const remoteUser = request.get('Remote-User'header); |
| 760 | 785 | if (!remoteUser) { |
| 761 | 786 | return false; |
| 762 | 787 | } |
| 788 | + console.debug(`Attempting auto-login for user from header ${header}: ${remoteUser}`); | |
| 763 | 789 | |
| 764 | 790 | const userHandles = await getAllUserHandles(); |
| 765 | 791 | for (const userHandle of userHandles) { |