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..8164d49b4d50203c4de4d1ecd946bb309e74dd4d
--- /dev/null
+++ b/src/caosdb/connection/authentication/external_credentials_provider.py
@@ -0,0 +1,91 @@
+# -*- 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)
+
+    @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 d3512174c405f0d86bc4edc11ca10962936f3093..fe9cb9170933cc12f76747d9d3c62a1a1fd2e8bd 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_session_token
@@ -46,17 +42,24 @@ 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
     """
 
     def __init__(self):
         self._session_token = None
+        self.logger = _LOGGER
 
     @abstractmethod
     def login(self):
@@ -141,6 +144,7 @@ class AbstractAuthenticator(ABC):
             headers['Cookie'] = self._session_token
 
 
+
 class CredentialsAuthenticator(AbstractAuthenticator):
     """CredentialsAuthenticator
 
@@ -220,10 +224,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 ed8c1991f9e234a7ea00f9597c6d59bc845b1fa5..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,21 +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"])
-            config["password"] = password
-        super(PassCaller, self).configure(**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/src/caosdb/exceptions.py b/src/caosdb/exceptions.py
index 581acc2cb4a447c423309f66e2a8ae218cb1a7c0..485ad908b18310d9f8346beb2261b528a8a45cce 100644
--- a/src/caosdb/exceptions.py
+++ b/src/caosdb/exceptions.py
@@ -32,6 +32,29 @@ class CaosDBException(Exception):
         Exception.__init__(self, msg)
         self.msg = msg
 
+class ConfigurationException(CaosDBException):
+    """ConfigurationException.
+
+    Indicates a misconfiguration.
+
+    Parameters
+    ----------
+    msg : str
+        A descriptin of the misconfiguration. The constructor adds
+        a few lines with explainingg where to find the configuration.
+
+    Attributes
+    ----------
+    msg : str
+        A description of the misconfiguration.
+    """
+    def __init__(self, msg):
+        super(ConfigurationException, self).__init__(msg +
+                                                     ConfigurationException._INFO)
+
+    _INFO = ("\n\nPlease check your ~/.pycaosdb.ini and your $PWD/"
+             ".pycaosdb.ini. Do at least one of them exist and are they correct?")
+
 
 class ClientErrorException(CaosDBException):
     def __init__(self, msg, status, body):
diff --git a/tox.ini b/tox.ini
index 5a7cee258b4936038d36d59cadd38534bbe7c941..11866fb924e728c719f14a9440455198f23dcbeb 100644
--- a/tox.ini
+++ b/tox.ini
@@ -2,5 +2,6 @@
 envlist= py27, py34, py35, py36
 skip_missing_interpreters = true
 [testenv]
-deps=nose
-commands= nosetests {posargs}
+deps=pytest
+     nose
+commands=py.test {posargs}
diff --git a/unittests/test_authentication_external.py b/unittests/test_authentication_external.py
new file mode 100644
index 0000000000000000000000000000000000000000..c595d38f83f6994f62baca924b1225832b9a659b
--- /dev/null
+++ b/unittests/test_authentication_external.py
@@ -0,0 +1,35 @@
+"""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..721adefde1df3a819c72ec61433debd9856e6b9f
--- /dev/null
+++ b/unittests/test_authentication_keyring.py
@@ -0,0 +1,40 @@
+# -*- 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..968fdfd94b75eef27f76bb0e0f07370bca3e5150
--- /dev/null
+++ b/unittests/test_authentication_pass.py
@@ -0,0 +1,42 @@
+# -*- 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
index 5e9c3c3d338047eb69c58094c6325699bdcff537..6b59a23101111ee3592ee5395560a33ab1382b17 100644
--- a/unittests/test_authentication_plain.py
+++ b/unittests/test_authentication_plain.py
@@ -52,3 +52,8 @@ def test_subclass_configure():
         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"