isupports: _UpdatingDict
     nickname_confirmed: bool
     user_modes: str
+    sasl_auth_state: str
     _completable_motd: _CompletableStringsList
     _channels: dict[str, _ChannelDb]
 
         self.conn = None
         self._db.isupports.clear()
         self._db.nickname_confirmed = False
+        self._db.sasl_auth_state = ''
 
     def on_handled_loop_exception(self, e: IrcConnAbortException) -> None:
         'Gracefully handle broken connection.'
             # '@'-split because <https://defs.ircdocs.horse/defs/numerics>
             # claims: "<hostname> can also be in the form <user@hostname>"
             self._db.client_host = msg.params[1].split('@')[-1]
-        elif msg.match('903', 2):  # RPL_SASLSUCESS
-            self._log('SASL auth succeeded')
-            self._caps.end_negotiation()
-        elif msg.match('904', 2):  # ERR_SASLFAIL
-            self._log('SASL auth failed', alert=True)
-            self._caps.end_negotiation()
+        elif msg.match('903', 2) or msg.match('904', 2):  # RPL_SUCESS, or …
+            self._db.sasl_auth_state = 'WIN' if msg.verb == '903' else 'FAIL'
+            self._caps.end_negotiation()                  # … ERR_SASLFAIL
         elif msg.match('AUTHENTICATE') and msg.params[0] == '+':
             auth = b64encode((self._db.nickname + '\0'
                               + self._db.nickname + '\0'
                               + self._db.password
                               ).encode('utf-8')).decode('utf-8')
             self.send(IrcMessage('AUTHENTICATE', (auth,)))
-        elif msg.match('CAP', len_is_min=True)\
-                and self._caps.process_msg(msg.params[1:])\
-                and self._db.caps.has('sasl')\
-                and 'PLAIN' in self._db.caps['sasl'].data.split(','):
-            if self._db.password:
-                self._log('trying to authenticate via SASL/plain')
-                self.send(IrcMessage('AUTHENTICATE', ('PLAIN',)))
-            else:
-                self._caps.end_negotiation()
+        elif msg.match('CAP', len_is_min=True):
+            if (self._caps.process_msg(msg.params[1:])
+                    and self._db.caps.has('sasl')
+                    and 'PLAIN' in self._db.caps['sasl'].data.split(',')):
+                if self._db.password:
+                    self._db.sasl_auth_state = 'attempting'
+                    self.send(IrcMessage('AUTHENTICATE', ('PLAIN',)))
+                else:
+                    self._caps.end_negotiation()
         elif msg.match('ERROR'):
             self.close()
         elif msg.match('MODE', 2) and msg.params[0] == self._db.nickname: