diff --git a/src/caosdb/configuration.py b/src/caosdb/configuration.py
index 8b4af56eb45561526bf8f45d9e4708631901ce6e..9f6b558abd7bd5e291eaa8bd89ca41ebe0c73bf8 100644
--- a/src/caosdb/configuration.py
+++ b/src/caosdb/configuration.py
@@ -24,20 +24,20 @@
 
 """Created on 20.09.2016."""
 
-from sys import hexversion
-if hexversion < 0x03000000:
-    from ConfigParser import ConfigParser  # @UnusedImport @UnresolvedImport
-else:
-    from configparser import ConfigParser  # @UnresolvedImport @Reimport
+try:
+    # python2
+    from ConfigParser import ConfigParser
+except ImportError:
+    # python3
+    from configparser import ConfigParser
 
 _DEFAULTS = {"Connection":
              {"url": None,
-                 "timeout": "200",
-                 "username": None,
-                 "password": None,
-                 "password_method": "plain",
-                 "debug": "0",
-                 "cacert": None},
+              "timeout": "200",
+              "username": None,
+              "password_method": "plain",
+              "debug": "0",
+              "cacert": None},
              "Container":
              {"debug": "0"}}
 
diff --git a/src/caosdb/connection/authentication/external_credentials_provider.py b/src/caosdb/connection/authentication/external_credentials_provider.py
new file mode 100644
index 0000000000000000000000000000000000000000..bebe43fedf215dfc2b744fbc0c96d7e9d50ba3e5
--- /dev/null
+++ b/src/caosdb/connection/authentication/external_credentials_provider.py
@@ -0,0 +1,93 @@
+# -*- coding: utf-8 -*-
+#
+# ** header v3.0
+# This file is a part of the CaosDB Project.
+#
+# Copyright (C) 2018 Research Group Biomedical Physics,
+# Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+# ** end header
+#
+"""external_credentials_provider.
+"""
+from __future__ import absolute_import, unicode_literals
+from abc import ABCMeta
+import logging
+from .plain import PlainTextCredentialsProvider
+
+# meta class compatible with Python 2 *and* 3:
+ABC = ABCMeta(str('ABC'), (object, ), {str('__slots__'): ()})
+
+
+class ExternalCredentialsProvider(PlainTextCredentialsProvider, ABC):
+    """ExternalCredentialsProvider.
+
+    Abstract subclass of PlainTextCredentialsProvider which should be used to
+    implement external credentials provider (e.g. pass, keyring, or any other call
+    to an external program, which presents the plain text password, which is to be
+    used for the authentication.
+
+    Parameters
+    ----------
+    callback: Function
+        A function which has **kwargs argument. This funktion will be called
+        each time a password is needed with the current connection
+        configuration as parameters.
+    """
+
+    def __init__(self, callback):
+        super(ExternalCredentialsProvider, self).__init__()
+        self._callback = callback
+        self._config = None
+
+    def configure(self, **config):
+        """configure.
+
+        Parameters
+        ----------
+        **config
+            Keyword arguments containing the necessary arguments for the
+            concrete implementation of this class.
+
+        Attributes
+        ----------
+        password : str
+            The password. This password is not stored in this class. A callback
+            is called to provide the password each time this property is
+            called.
+
+        Returns
+        -------
+        None
+        """
+        if "password" in config:
+            if "password_method" in config:
+                authm = "`{}`".format(config["password_method"])
+            else:
+                authm = "an external credentials provider"
+            self.logger.log(logging.WARNING,
+                            ("`password` defined. You configured caosdb to "
+                             "use %s as authentication method and yet "
+                             "provided a password yourself. This indicates "
+                             "a misconfiguration (e.g. in your "
+                             "pycaosdb.ini) and should be avoided."),
+                            authm)
+        self._config = dict(config)
+        super(ExternalCredentialsProvider, self).configure(**config)
+
+    @property
+    def password(self):
+        return self._callback(**self._config)
diff --git a/src/caosdb/connection/authentication/interface.py b/src/caosdb/connection/authentication/interface.py
index 975c43a4b8070944ec4a33fb2993a27b3047f3cb..008933caa37e1c0e5fe65ac12721df32fd0f2480 100644
--- a/src/caosdb/connection/authentication/interface.py
+++ b/src/caosdb/connection/authentication/interface.py
@@ -25,10 +25,6 @@
 caosdb server."""
 from abc import ABCMeta, abstractmethod, abstractproperty
 import logging
-try:
-    from urllib.parse import quote
-except ImportError:
-    from urllib import quote
 from caosdb.connection.utils import urlencode
 from caosdb.connection.interface import CaosDBServerConnection
 from caosdb.connection.utils import parse_auth_token
@@ -46,11 +42,17 @@ class AbstractAuthenticator(ABC):
     Interface for different authentication mechanisms. e.g. username/password
     authentication or SSH key authentication.
 
+    Attributes
+    ----------
+    logger : Logger
+        A logger which should be used for all logging which has to do with
+        authentication.
+
     Methods
     -------
-    login
-    logout
-    configure
+    login (abstract)
+    logout (abstract)
+    configure (abstract)
     on_request
     on_response
 
@@ -62,6 +64,7 @@ class AbstractAuthenticator(ABC):
 
     def __init__(self):
         self.auth_token = None
+        self.logger = _LOGGER
 
     @abstractmethod
     def login(self):
@@ -225,10 +228,21 @@ class CredentialsProvider(ABC):
 
     Attributes
     ----------
-    password
-    username
+    password (abstract)
+    username (abstract)
+    logger : Logger
+        A logger which should be used for all logging which has to do with the
+        provision of credentials. This is usually just the "authentication"
+        logger.
+
+    Methods
+    -------
+    configure (abstract)
     """
 
+    def __init__(self):
+        self.logger = _LOGGER
+
     @abstractmethod
     def configure(self, **config):
         """configure.
diff --git a/src/caosdb/connection/authentication/keyring.py b/src/caosdb/connection/authentication/keyring.py
index da9e7d41000009c1ba26c1faa080f84a5ab2e91f..abdbf112e20279d9c7211d01bebd296869edea5d 100644
--- a/src/caosdb/connection/authentication/keyring.py
+++ b/src/caosdb/connection/authentication/keyring.py
@@ -30,7 +30,8 @@ retrieve the password.
 import sys
 import imp
 from getpass import getpass
-from .plain import PlainTextCredentialsProvider
+from caosdb.exceptions import ConfigurationException
+from .external_credentials_provider import ExternalCredentialsProvider
 from .interface import CredentialsAuthenticator
 
 
@@ -46,7 +47,7 @@ def get_authentication_provider():
     CredentialsAuthenticator
         with a 'KeyringCaller' as back-end.
     """
-    return CredentialsAuthenticator(KeyringCaller())
+    return CredentialsAuthenticator(KeyringCaller(callback=_call_keyring))
 
 
 def _get_external_keyring():
@@ -64,7 +65,16 @@ def _get_external_keyring():
         fil.close()
 
 
-def _call_keyring(app, username):
+def _call_keyring(**config):
+    if "username" not in config:
+        raise ConfigurationException("Your configuration did not provide a "
+                                     "`username` which is needed by the "
+                                     "`KeyringCaller` to retrieve the "
+                                     "password in question.")
+    url = config.get("url")
+    username = config.get("username")
+    app = "PyCaosDB — {}".format(url)
+    password = _call_keyring(app=app, username=username)
     external_keyring = _get_external_keyring()
     password = external_keyring.get_password(app, username)
     if password is None:
@@ -76,7 +86,7 @@ def _call_keyring(app, username):
     return password
 
 
-class KeyringCaller(PlainTextCredentialsProvider):
+class KeyringCaller(ExternalCredentialsProvider):
     """KeyringCaller.
 
     A class for retrieving the password from the external 'gnome keyring' and
@@ -91,22 +101,3 @@ class KeyringCaller(PlainTextCredentialsProvider):
     password
     username
     """
-
-    def configure(self, **config):
-        """configure.
-
-        Parameters
-        ----------
-        **config
-            Keyword arguments which contain at least the keywords "username" and
-            "url".
-
-        Returns
-        -------
-        None
-        """
-        url = config.get("url")
-        username = config.get("username")
-        app = "PyCaosDB — {}".format(url)
-        password = _call_keyring(app=app, username=username)
-        super(KeyringCaller, self).configure(password=password, **config)
diff --git a/src/caosdb/connection/authentication/pass.py b/src/caosdb/connection/authentication/pass.py
index 5aa9a81a24ed02584b51ae30cb1de0d69e11d9f4..9399fc4f4a76407ad94618785adcfbb945d4c788 100644
--- a/src/caosdb/connection/authentication/pass.py
+++ b/src/caosdb/connection/authentication/pass.py
@@ -28,8 +28,9 @@ password.
 """
 
 from subprocess import check_output, CalledProcessError
+from caosdb.exceptions import ConfigurationException
 from .interface import CredentialsAuthenticator
-from .plain import PlainTextCredentialsProvider
+from .external_credentials_provider import ExternalCredentialsProvider
 
 
 def get_authentication_provider():
@@ -44,13 +45,19 @@ def get_authentication_provider():
     CredentialsAuthenticator
         with a 'PassCaller' as back-end.
     """
-    return CredentialsAuthenticator(PassCaller())
+    return CredentialsAuthenticator(PassCaller(callback=_call_pass))
 
 
-def _call_pass(password_identifier):
+def _call_pass(**config):
+    if "password_identifier" not in config:
+        raise ConfigurationException("Your configuration did not provide a "
+                                     "`password_identifier` which is needed "
+                                     "by the `PassCaller` to retrieve the "
+                                     "password in question.")
+
     try:
         return check_output(
-            "pass " + password_identifier,
+            "pass " + config["password_identifier"],
             shell=True).splitlines()[0].decode("UTF-8")
     except CalledProcessError as exc:
         raise RuntimeError(
@@ -59,7 +66,7 @@ def _call_pass(password_identifier):
             "incorrect or missing.".format(exc.returncode))
 
 
-class PassCaller(PlainTextCredentialsProvider):
+class PassCaller(ExternalCredentialsProvider):
     """PassCaller.
 
     A class for retrieving the password from the external program 'pass' and
@@ -74,20 +81,5 @@ class PassCaller(PlainTextCredentialsProvider):
     password
     username
     """
-
-    def configure(self, **config):
-        """configure.
-
-        Parameters
-        ----------
-        **config
-            Keyword arguments which contain at least the keywords "username" and
-            "password_identifier".
-
-        Returns
-        -------
-        None
-        """
-        if "password_identifier" in config:
-            password = _call_pass(config["password_identifier"])
-        super(PassCaller, self).configure(password=password, **config)
+    # all the work is done in _call_pass and the super class
+    pass
diff --git a/src/caosdb/connection/authentication/plain.py b/src/caosdb/connection/authentication/plain.py
index 6cb7b1b896d8d32f71437c527385f269b37cdb51..83dd592940a7010d07112f73b9bd5bcf3741a168 100644
--- a/src/caosdb/connection/authentication/plain.py
+++ b/src/caosdb/connection/authentication/plain.py
@@ -59,6 +59,7 @@ class PlainTextCredentialsProvider(CredentialsProvider):
     """
 
     def __init__(self):
+        super(PlainTextCredentialsProvider, self).__init__()
         self._password = None
         self._username = None
 
diff --git a/unittests/test_authentication_external.py b/unittests/test_authentication_external.py
new file mode 100644
index 0000000000000000000000000000000000000000..a8fc6f79578c9812dab52dd1fa3807f62fd710fb
--- /dev/null
+++ b/unittests/test_authentication_external.py
@@ -0,0 +1,61 @@
+#! -*- coding: utf-8 -*-
+#
+# ** header v3.0
+# This file is a part of the CaosDB Project.
+#
+# Copyright (C) 2018 Research Group Biomedical Physics,
+# Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+# ** end header
+#
+"""test_authentication_external.
+
+Tests for the external_credentials_provider modul.
+"""
+
+from __future__ import unicode_literals
+import logging
+from pytest import raises
+from caosdb.connection.authentication import (
+    external_credentials_provider as ecp
+)
+
+
+class _TestCaller(ecp.ExternalCredentialsProvider):
+    pass
+
+
+def test_callback():
+    def _callback(**config):
+        assert "opt" in config
+        return "secret"
+    t = _TestCaller(callback=_callback)
+    t.configure(opt=None)
+    assert t.password == "secret"
+
+
+def test_log_password_incident():
+    class _mylogger(logging.Logger):
+        def log(self, level, msg, *args, **kwargs):
+            assert level == logging.WARNING
+            assert "`password` defined." in msg
+            raise Exception("log")
+
+    t = _TestCaller(callback=None)
+    t.logger = _mylogger("mylogger")
+    with raises(Exception) as exc_info:
+        t.configure(password="password")
+    assert exc_info.value.args[0] == "log"
diff --git a/unittests/test_authentication_keyring.py b/unittests/test_authentication_keyring.py
new file mode 100644
index 0000000000000000000000000000000000000000..1105b9d29604e7809c1f6ab6d6a6d027c5115025
--- /dev/null
+++ b/unittests/test_authentication_keyring.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+#
+# ** header v3.0
+# This file is a part of the CaosDB Project.
+#
+# Copyright (C) 2018 Research Group Biomedical Physics,
+# Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+# ** end header
+#
+"""test_authentication_keyring
+
+Tests for the caosdb.connection.authentication.keyring module.
+"""
+import sys
+from pytest import raises
+from caosdb.connection.authentication.keyring import KeyringCaller
+
+
+def test_initialization():
+    def _callback(**config):
+        assert not config
+        raise Exception("_callback")
+    k = KeyringCaller(callback=_callback)
+    k.configure()
+    with raises(Exception) as exc_info:
+        assert k.password is None
+    assert exc_info.value.args[0] == "_callback"
diff --git a/unittests/test_authentication_pass.py b/unittests/test_authentication_pass.py
new file mode 100644
index 0000000000000000000000000000000000000000..e5e4870c890a6a19effe672a3fde127cb4ea1fd7
--- /dev/null
+++ b/unittests/test_authentication_pass.py
@@ -0,0 +1,43 @@
+# -*- coding: utf-8 -*-
+#
+# ** header v3.0
+# This file is a part of the CaosDB Project.
+#
+# Copyright (C) 2018 Research Group Biomedical Physics,
+# Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+# ** end header
+#
+"""test_authentication_pass
+
+Tests for the caosdb.connection.authentication.pass module.
+"""
+import sys
+from pytest import raises
+_PASSCALLER = "caosdb.connection.authentication.pass"
+__import__(_PASSCALLER)
+PassCaller = sys.modules[_PASSCALLER].PassCaller
+
+
+def test_initialization():
+    def _callback(**config):
+        assert not config
+        raise Exception("_callback")
+    p = PassCaller(callback=_callback)
+    p.configure()
+    with raises(Exception) as exc_info:
+        p.password
+    assert exc_info.value.args[0] == "_callback"
diff --git a/unittests/test_authentication_plain.py b/unittests/test_authentication_plain.py
new file mode 100644
index 0000000000000000000000000000000000000000..26c57e953f28a2a0ab80e35026c27ff74f18d370
--- /dev/null
+++ b/unittests/test_authentication_plain.py
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+#
+# ** header v3.0
+# This file is a part of the CaosDB Project.
+#
+# Copyright (C) 2018 Research Group Biomedical Physics,
+# Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+#
+# ** end header
+#
+"""test_authentication_plain.
+
+Unit tests for the modul caosdb.connection.authentication.plain.
+"""
+
+from __future__ import unicode_literals
+from pytest import raises
+from caosdb.connection.authentication.plain import PlainTextCredentialsProvider
+
+
+def test_subclass_configure():
+    """Test the correct passing of the password argument."""
+    class SubClassOf(PlainTextCredentialsProvider):
+        """A simple subclass of PlainTextCredentialsProvider."""
+
+        def configure(self, **config):
+            super(SubClassOf, self).configure(password="added in subclass", **config)
+
+    instance = SubClassOf()
+    instance.configure()
+    assert instance.password == "added in subclass"
+
+    instance.configure(**{"somearg": "BLA"})
+    assert instance.password == "added in subclass"
+
+    instance.configure(somearg="BLA")
+    assert instance.password == "added in subclass"
+
+    with raises(TypeError) as exc_info:
+        instance.configure(password="OH NO!")
+    assert exc_info.value.args[0] == ("configure() got multiple values for "
+                                      "keyword argument 'password'")
+
+
+def test_plain_has_logger():
+    p = PlainTextCredentialsProvider()
+    assert hasattr(p, "logger")
+    assert p.logger.name == "authentication"