From 63c71baa7877acf20f97cb80350b9a26e068e8f5 Mon Sep 17 00:00:00 2001 From: Kundan Sable Date: Wed, 22 Jul 2026 16:00:00 +0530 Subject: [PATCH] fix: don't hard-fail on an undecryptable saved password MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Connection._decode_password() let UnicodeDecodeError from decrypt() propagate as a hard error. For OIDC/OAuth2 users whose crypt key can differ between sessions, a password saved under a previous key can never be decoded, and every connection attempt surfaced "Failed to decrypt the saved password" — even after deleting and recreating the pgAdmin user, since the corrupted ciphertext is what's stored, not anything tied to the user record. Catch the decode failure, log a warning, and treat it as "no saved password" instead of propagating the error. connect() then falls through to the normal password prompt, so the account is recoverable instead of permanently stuck. Fixes #10140 --- .../utils/driver/psycopg3/connection.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/web/pgadmin/utils/driver/psycopg3/connection.py b/web/pgadmin/utils/driver/psycopg3/connection.py index ad544e99b25..57b9239dbaa 100644 --- a/web/pgadmin/utils/driver/psycopg3/connection.py +++ b/web/pgadmin/utils/driver/psycopg3/connection.py @@ -258,12 +258,20 @@ def _decode_password(self, encpass, manager, password, crypt_key): if isinstance(password, bytes): password = password.decode() except Exception as e: - manager.stop_ssh_tunnel() - current_app.logger.exception(e) - return True, \ - _( - "Failed to decrypt the saved password.\nError: {0}" - ).format(str(e)), password + # The saved password could not be decrypted. This happens + # when the stored ciphertext was encrypted with a different + # key (e.g. OIDC/OAuth2 logins where the derived encryption + # key changed between sessions), leaving un-decodable bytes + # (typically a "'utf-8' codec can't decode byte 0x.." error). + # Instead of failing every connection attempt permanently, + # discard the bad saved password and continue so the user is + # prompted for the password again. + current_app.logger.warning( + 'Ignoring the saved password as it could not be ' + 'decrypted. The user will be prompted for the password. ' + 'Error: {0}'.format(str(e)) + ) + return False, '', None return False, '', password def connect(self, **kwargs):