diff --git a/README.md b/README.md
index 067ff96d3871e6b9937566116ac4004aaf44bcfc..c496b54345880adc9f23fe961bd5cbd93b7fd4e4 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 LDAP Proxy for Uffd
 ===================
 
-LDAP server that provides a view on user data exposed by uffd's API. Based on python-ldapserver.
+LDAP server that provides user and group data exposed by uffd's API. Based on python-ldapserver.
 
-**Prototype! Do not use in production!**
+Requires at least uffd v1.2.0!
diff --git a/debian/contrib/uffd-ldap.conf b/debian/contrib/uffd-ldap.conf
index d63bc0470e2b6d1a477fbfc2508abc13963f477b..03a8fa08cf870a438ce3dbb4a403fca5fa84f018 100644
--- a/debian/contrib/uffd-ldap.conf
+++ b/debian/contrib/uffd-ldap.conf
@@ -1,6 +1,7 @@
-# Both options must be set
+# All of these options must be set:
 #SERVER_API_URL="https://localhost"
-#SERVER_API_KEY="my_secret_api_token"
+#SERVER_API_USER="client_id"
+#SERVER_API_SECRET="my_secret_api_client_secret"
 #SERVER_BASE_DN="dc=example,dc=com"
 
 # If bind password is not set, anonymous binds have full read permissions.
diff --git a/server.py b/server.py
index a55095970bebe3f7a423382e33c9a06f3a6f486c..587c449e73021c409b7b66a3dacf60fd65ccc5ab 100755
--- a/server.py
+++ b/server.py
@@ -22,11 +22,14 @@ CUSTOM_SCHEMA = (RFC2307BIS_SCHEMA|RFC2798_SCHEMA).extend(attribute_type_definit
 ])
 
 class UffdAPI:
-	def __init__(self, baseurl, key, cache_ttl=60):
+	def __init__(self, baseurl, client_id, client_secret, cache_ttl=60):
 		self.baseurl = baseurl
-		self.key = key
-		self.session = CacheControl(requests.Session(), heuristic=ExpiresAfter(seconds=cache_ttl))
-		self.session.headers['Authorization'] = 'Bearer '+self.key
+		self.client_id = client_id
+		self.client_secret = client_secret
+		self.session = requests.Session()
+		self.session.auth = (client_id, client_secret)
+		if cache_ttl:
+			self.session = CacheControl(self.session, heuristic=ExpiresAfter(seconds=cache_ttl))
 
 	def get(self, endpoint, **kwargs):
 		resp = self.session.get(self.baseurl + endpoint, params=kwargs)
@@ -263,11 +266,12 @@ class StdoutFilter(logging.Filter):
 @click.option('--socket-path', type=click.Path(), help='Path for UNIX domain socket')
 @click.option('--socket-fd', type=int, help='Use fd number as server socket (alternative to --socket-path)')
 @click.option('--api-url', required=True, help='Uffd base URL without API prefix or trailing slash (e.g. https://example.com)')
-@click.option('--api-key', required=True, help='API secret, do not set this on the command-line, use environment variable SERVER_API_KEY instead')
+@click.option('--api-user', required=True, help='API user/client id')
+@click.option('--api-secret', required=True, help='API secret, do not set this on the command-line, use environment variable SERVER_API_SECRET instead')
 @click.option('--cache-ttl', default=60, help='Time-to-live for API response caching in seconds')
 @click.option('--base-dn', required=True, help='Base DN for user, group and system objects. E.g. "dc=example,dc=com"')
 @click.option('--bind-password', help='Authentication password for the service connection to LDAP. Bind DN is always "cn=service,ou=system,BASEDN". If set, anonymous access is disabled.')
-def main(socket_address, socket_path, socket_fd, api_url, api_key, cache_ttl, base_dn, bind_password):
+def main(socket_address, socket_path, socket_fd, api_url, api_user, api_secret, cache_ttl, base_dn, bind_password):
 	# pylint: disable=too-many-locals
 	if (socket_address is not None) \
 	   + (socket_path is not None) \
@@ -284,7 +288,7 @@ def main(socket_address, socket_path, socket_fd, api_url, api_key, cache_ttl, ba
 	root_logger.addHandler(stdout_handler)
 	root_logger.addHandler(stderr_handler)
 
-	api = UffdAPI(api_url, api_key, cache_ttl)
+	api = UffdAPI(api_url, api_user, api_secret, cache_ttl)
 	RequestHandler = make_requesthandler(api, base_dn, bind_password)
 	if socket_address is not None:
 		host, port = parse_network_address(socket_address)