From f380f261a53f05e4af94a02b9904da48b96c804b Mon Sep 17 00:00:00 2001 From: Ventz Petkov <901168+ventz@users.noreply.github.com> Date: Fri, 12 Apr 2024 12:39:11 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=82=20fix:=20OIDC=20Username=20Array?= =?UTF-8?q?=20Edge=20Case=20(#2394)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Patch for OpenID username `username` is generally based on email, rather than `given_name`. The challenge with `given_name` is that it can be a multi-value array (ex: "Nick, Fullname"), which completely breaks the system with: ``` LibreChat | ValidationError: User validation failed: username: Cast to string failed for value "[ 'Nickname', 'Firstname' ]" (type Array) at path "username" LibreChat | at Document.invalidate (/app/node_modules/mongoose/lib/document.js:3200:32) LibreChat | at model.$set (/app/node_modules/mongoose/lib/document.js:1459:12) LibreChat | at model.set [as username] (/app/node_modules/mongoose/lib/helpers/document/compile.js:205:19) LibreChat | at OpenIDConnectStrategy._verify (/app/api/strategies/openidStrategy.js:127:27) LibreChat | at process.processTicksAndRejections (node:internal/process/task_queues:95:5) ``` * Update openidStrategy.js * refactor(openidStrategy): add helper function for stringy username --------- Co-authored-by: Danny Avila --- api/strategies/openidStrategy.js | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/api/strategies/openidStrategy.js b/api/strategies/openidStrategy.js index b6abd8f705..cb56d42db0 100644 --- a/api/strategies/openidStrategy.js +++ b/api/strategies/openidStrategy.js @@ -37,6 +37,26 @@ const downloadImage = async (url, imagePath, accessToken) => { } }; +/** + * Converts an input into a string suitable for a username. + * If the input is a string, it will be returned as is. + * If the input is an array, elements will be joined with underscores. + * In case of undefined or other falsy values, a default value will be returned. + * + * @param {string | string[] | undefined} input - The input value to be converted into a username. + * @param {string} [defaultValue=''] - The default value to return if the input is falsy. + * @returns {string} The processed input as a string suitable for a username. + */ +function convertToUsername(input, defaultValue = '') { + if (typeof input === 'string') { + return input; + } else if (Array.isArray(input)) { + return input.join('_'); + } + + return defaultValue; +} + async function setupOpenId() { try { const issuer = await Issuer.discover(process.env.OPENID_ISSUER); @@ -104,11 +124,13 @@ async function setupOpenId() { } } + const username = convertToUsername(userinfo.username || userinfo.given_name || userinfo.email); + if (!user) { user = new User({ provider: 'openid', openidId: userinfo.sub, - username: userinfo.username || userinfo.given_name || '', + username, email: userinfo.email || '', emailVerified: userinfo.email_verified || false, name: fullName, @@ -116,7 +138,7 @@ async function setupOpenId() { } else { user.provider = 'openid'; user.openidId = userinfo.sub; - user.username = userinfo.username || userinfo.given_name || ''; + user.username = username; user.name = fullName; }