diff --git a/README.md b/README.md
index 859d4e16ccbc75f9bc331c0b77a7cd4bbfe534ba..610ae70efefb402994bacbb73a171465e2d1a53d 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,15 @@ Use uwsgi.
 
 tabs.
 
+## Bind with service account or as user?
+
+Uffd can use a dedicated service account for LDAP operations by setting `LDAP_SERVICE_BIND_DN`.
+Or it uses the credentials of the currently logged in user, by not setting `LDAP_SERVICE_BIND_DN`.
+
+If you choose to run with user credentials, some features are not available, like password resets
+or self signup, since in both cases, no user credentials can exist. 
+
+
 ## OAuth2 Single-Sign-On Provider
 
 Other services can use uffd as an OAuth2.0-based authentication provider.
diff --git a/uffd/__init__.py b/uffd/__init__.py
index d055976a05120c2526b7cf8d59818b10a69facdd..8c1d4e563ce24b994d2262233becdc48f9bd0b19 100644
--- a/uffd/__init__.py
+++ b/uffd/__init__.py
@@ -46,8 +46,13 @@ def create_app(test_config=None): # pylint: disable=too-many-locals
 	from uffd import user, selfservice, role, mail, session, csrf, mfa, oauth2, services, signup
 	# pylint: enable=C0415
 
-	for i in user.bp + selfservice.bp + role.bp + mail.bp + session.bp + csrf.bp + mfa.bp + oauth2.bp + services.bp + signup.bp:
-		app.register_blueprint(i)
+	if app.config['LDAP_SERVICE_BIND_DN']:
+		for i in user.bp + selfservice.bp + role.bp + mail.bp + session.bp + csrf.bp + mfa.bp + oauth2.bp + services.bp + signup.bp:
+			app.register_blueprint(i)
+	else:
+		app.config['ENABLE_PASSWORDRESET'] = False
+		for i in user.bp + selfservice.bp + role.bp + mail.bp + session.bp + csrf.bp + mfa.bp + oauth2.bp + services.bp:
+			app.register_blueprint(i)
 
 	@app.route("/")
 	def index():  #pylint: disable=unused-variable
diff --git a/uffd/default_config.cfg b/uffd/default_config.cfg
index 727cb849ae1e847f2f89309c1cc8dd3c03f46da9..5f9b2b026d4f8771edcc8577b99de6eb2c61e56b 100644
--- a/uffd/default_config.cfg
+++ b/uffd/default_config.cfg
@@ -37,6 +37,8 @@ LDAP_MAIL_UID_ATTRIBUTE="uid"
 LDAP_MAIL_RECEIVERS_ATTRIBUTE="mailacceptinggeneralid"
 LDAP_MAIL_DESTINATIONS_ATTRIBUTE="maildrop"
 
+# If you do not set the service bind_dn, connections use the user credentials.
+# When using a user connection, some features are not available, since they require a service connection
 LDAP_SERVICE_BIND_DN=""
 LDAP_SERVICE_BIND_PASSWORD=""
 LDAP_SERVICE_URL="ldapi:///"
@@ -60,6 +62,8 @@ MAIL_FROM_ADDRESS='foo@bar.com'
 
 # Do not enable this on a public service! There is no spam protection implemented at the moment.
 SELF_SIGNUP=False
+# PASSWORDRESET is not available when not using a service connection
+ENABLE_PASSWORDRESET=True
 
 #MFA_ICON_URL = 'https://example.com/logo.png'
 #MFA_RP_ID = 'example.com' # If unset, hostname from current request is used
diff --git a/uffd/ldap.py b/uffd/ldap.py
index 9d49a10c5b65dec6e83db4731aeca3a41d4c8cb8..855feea435b62dccf204a0dc313c9c5e52bb6bb5 100644
--- a/uffd/ldap.py
+++ b/uffd/ldap.py
@@ -1,10 +1,11 @@
 from flask import current_app, request, abort, session
 
 import ldap3
-from ldap3.core.exceptions import LDAPBindError
+from ldap3.core.exceptions import LDAPBindError, LDAPPasswordIsMandatoryError
 
 from ldapalchemy import LDAPMapper, LDAPCommitError # pylint: disable=unused-import
 from ldapalchemy.model import Query
+from ldapalchemy.core import encode_filter
 
 
 class FlaskQuery(Query):
@@ -21,6 +22,30 @@ class FlaskQuery(Query):
 		return res
 
 
+def test_user_bind(bind_dn, bind_pw):
+	try:
+		if current_app.config.get('LDAP_SERVICE_MOCK', False):
+			# Since we reuse the same conn for all calls to `user_conn()` we
+			# simulate the password check by rebinding. Note that ldap3's mocking
+			# implementation just compares the string in the objects's userPassword
+			# field with the password, no support for hashing or OpenLDAP-style
+			# password-prefixes ("{PLAIN}..." or "{ssha512}...").
+			conn = ldap.get_connection()
+			if not conn.rebind(bind_dn, bind_pw):
+				return False
+		else:
+			server = ldap3.Server(current_app.config["LDAP_SERVICE_URL"])
+			conn = connect_and_bind_to_ldap(server, bind_dn, bind_pw)
+			if not conn:
+				return False
+	except (LDAPBindError, LDAPPasswordIsMandatoryError):
+		return False
+
+	conn.search(conn.user, encode_filter(current_app.config["LDAP_USER_SEARCH_FILTER"]))
+	conn.unbind()
+	return len(conn.entries) == 1
+
+
 def connect_and_bind_to_ldap(server, bind_dn, bind_pw):
 	# Using auto_bind cannot close the connection, so define the connection with extra steps
 	_connection = ldap3.Connection(server, bind_dn, bind_pw)
@@ -30,7 +55,7 @@ def connect_and_bind_to_ldap(server, bind_dn, bind_pw):
 		_connection.start_tls(read_server_info=False)
 	if not _connection.bind(read_server_info=True):
 		_connection.unbind()
-		return None
+		raise LDAPBindError
 	return _connection
 
 
@@ -65,6 +90,7 @@ class FlaskLDAPMapper(LDAPMapper):
 				current_app.ldap_mock.bind()
 			return current_app.ldap_mock
 		server = ldap3.Server(current_app.config["LDAP_SERVICE_URL"], get_info=ldap3.ALL)
+
 		# If the configured LDAP service bind_dn is empty, connect to LDAP as a user
 		if current_app.config['LDAP_SERVICE_BIND_DN']:
 			bind_dn = current_app.config["LDAP_SERVICE_BIND_DN"]
@@ -72,12 +98,10 @@ class FlaskLDAPMapper(LDAPMapper):
 		else:
 			bind_dn = session['user_dn']
 			bind_pw = session['user_pw']
-
-		_connection = connect_and_bind_to_ldap(server, bind_dn, bind_pw)
-		if not _connection:
-			raise LDAPBindError
-
-		request.ldap_connection = _connection
+		try:
+			request.ldap_connection = connect_and_bind_to_ldap(server, bind_dn, bind_pw)
+		except (LDAPBindError, LDAPPasswordIsMandatoryError):
+			return None
 
 		return request.ldap_connection
 
diff --git a/uffd/session/templates/login.html b/uffd/session/templates/login.html
index e3a17c5716c9b1f4a90f4f86de72a4b9c69db7bd..15c0f8f03e402965ae5ba497e1973f236a47ed37 100644
--- a/uffd/session/templates/login.html
+++ b/uffd/session/templates/login.html
@@ -25,7 +25,9 @@
 			{% if config['SELF_SIGNUP'] %}
 			<a href="{{ url_for("signup.signup_start") }}" class="float-left">Register</a>
 			{% endif %}
+			{% if config['ENABLE_PASSWORDRESET'] %}
 			<a href="{{ url_for("selfservice.forgot_password") }}" class="float-right">Forgot Password?</a>
+			{% endif %}
 		</div>
 	</div>
 </div>
diff --git a/uffd/session/views.py b/uffd/session/views.py
index 3b5027eeaaa1451c28221eb405426d112866ea88..f1b16f8a12152d1bf61734497849a77048f65b7b 100644
--- a/uffd/session/views.py
+++ b/uffd/session/views.py
@@ -4,12 +4,8 @@ import functools
 
 from flask import Blueprint, render_template, request, url_for, redirect, flash, current_app, session, abort
 
-import ldap3
-from ldap3.core.exceptions import LDAPBindError, LDAPPasswordIsMandatoryError
-from ldapalchemy.core import encode_filter
-
 from uffd.user.models import User
-from uffd.ldap import ldap, connect_and_bind_to_ldap
+from uffd.ldap import ldap, test_user_bind
 from uffd.ratelimit import Ratelimit, host_ratelimit, format_delay
 
 bp = Blueprint("session", __name__, template_folder='templates', url_prefix='/')
@@ -18,41 +14,19 @@ login_ratelimit = Ratelimit('login', 1*60, 3)
 
 def login_get_user(loginname, password):
 	dn = User(loginname=loginname).dn
-	if current_app.config.get('LDAP_SERVICE_MOCK', False):
-		conn = ldap.get_connection()
-		# Since we reuse the same conn for all calls to `user_conn()` we
-		# simulate the password check by rebinding. Note that ldap3's mocking
-		# implementation just compares the string in the objects's userPassword
-		# field with the password, no support for hashing or OpenLDAP-style
-		# password-prefixes ("{PLAIN}..." or "{ssha512}...").
-		try:
-			if not conn.rebind(dn, password):
-				return None
-		except (LDAPBindError, LDAPPasswordIsMandatoryError):
+
+	# If we use a service connection, test user bind seperately
+	if current_app.config['LDAP_SERVICE_BIND_DN']:
+		if not test_user_bind(dn, password):
 			return None
+	# If we use a user connection, just create the connection normally
 	else:
-		# When using a LDAP service connection, try bind with separate user connection
-		if current_app.config['LDAP_SERVICE_BIND_DN']:
-			server = ldap3.Server(current_app.config["LDAP_SERVICE_URL"])
-			try:
-				conn = connect_and_bind_to_ldap(server, dn, password)
-				if conn is None:
-					raise LDAPBindError
-			except (LDAPBindError, LDAPPasswordIsMandatoryError):
-				return None
-		else:
-			session.clear()
-			session['user_dn'] = dn
-			session['user_pw'] = password
-			try:
-				conn = ldap.get_connection()
-			except (LDAPBindError, LDAPPasswordIsMandatoryError):
-				session.clear()
-				return None
+		# ldap.get_connection gets the credentials from the session, so set it here initially
+		session['user_dn'] = dn
+		session['user_pw'] = password
+		if not ldap.get_connection():
+			return None
 
-	conn.search(conn.user, encode_filter(current_app.config["LDAP_USER_SEARCH_FILTER"]))
-	if len(conn.entries) != 1:
-		return None
 	return User.query.get(dn)
 
 @bp.route("/logout")
@@ -63,10 +37,10 @@ def logout():
 	session.clear()
 	return resp
 
-def set_session(user, skip_mfa=False, password=''):
-	session.clear()
+def set_session(user, password='', skip_mfa=False):
 	session['user_dn'] = user.dn
-	if password:
+	# only save the password if we use a user connection
+	if password and not current_app.config['LDAP_SERVICE_BIND_DN']:
 		session['user_pw'] = password
 	session['logintime'] = datetime.datetime.now().timestamp()
 	session['_csrf_token'] = secrets.token_hex(128)
@@ -97,9 +71,7 @@ def login():
 	if not user.is_in_group(current_app.config['ACL_SELFSERVICE_GROUP']):
 		flash('You do not have access to this service')
 		return render_template('login.html', ref=request.values.get('ref'))
-	# If the configured LDAP bind_dn is empty, connect to LDAP as a user
-	# Therefore, we save the password for future binds in the session
-	set_session(user, password=password if not current_app.config['LDAP_SERVICE_BIND_DN'] else '')
+	set_session(user, password=password)
 	return redirect(url_for('mfa.auth', ref=request.values.get('ref', url_for('index'))))
 
 def get_current_user():
diff --git a/uffd/signup/views.py b/uffd/signup/views.py
index ce9b139e83f729dffb93be72b83a6b633bb41f49..ba0f58c69ecc2015be42c0975f85332a99d57ac4 100644
--- a/uffd/signup/views.py
+++ b/uffd/signup/views.py
@@ -97,6 +97,6 @@ def signup_confirm_submit(token):
 		return render_template('signup/confirm.html', signup=signup, error=msg)
 	db.session.commit()
 	ldap.session.commit()
-	set_session(user, skip_mfa=True)
+	set_session(user, password=request.form['password'], skip_mfa=True)
 	flash('Your account was successfully created')
 	return redirect(url_for('selfservice.index'))