diff --git a/backend/app.py b/backend/app.py
index 8394a1fff033ab738fe5bfc01beee90624e1c1c7..242861b2e634df92473ba31e864fe96a503b80b5 100644
--- a/backend/app.py
+++ b/backend/app.py
@@ -70,7 +70,11 @@ db.init_app(app)
 # We need this app context in order to talk the database, which is managed by
 # flask-sqlalchemy, which assumes a flask app context.
 with app.app_context():
+    # Load the list of apps from a configmap and store any missing ones in the
+    # database.
     cluster_config.populate_apps()
+    # Same for the list of oauthclients.
+    cluster_config.populate_oauthclients()
 
 app.register_blueprint(api_v1)
 app.register_blueprint(web)
diff --git a/backend/areas/apps/models.py b/backend/areas/apps/models.py
index 0f2b00eca24c11d58a20944bd670003a7d48ca93..069c73751643f42af0619e65c7d6425d4802138d 100644
--- a/backend/areas/apps/models.py
+++ b/backend/areas/apps/models.py
@@ -309,6 +309,7 @@ class OAuthClientApp(db.Model):  # pylint: disable=too-few-public-methods
     This mapping exists so that
     * you can have a different name for the OAuth client than for the app, and
     * you can have multiple OAuth clients that belong to the same app.
+    Also, some apps might have no OAuth client at all.
     """
 
     __tablename__ = "oauthclient_app"
diff --git a/backend/cluster_config.py b/backend/cluster_config.py
index 1ea44e1b4c9daccd7afa76b67f94b230fea07ce3..20ec9e98023dfd01b46a30492f74057aea1a1aed 100644
--- a/backend/cluster_config.py
+++ b/backend/cluster_config.py
@@ -1,5 +1,5 @@
 from database import db
-from areas.apps.models import App
+from areas.apps.models import App, OAuthClientApp
 import helpers.kubernetes as k8s
 
 import logging
@@ -43,3 +43,41 @@ def _populate_apps_from(database_apps, configmap_name):
                 new_app = App(slug=app_slug, name=name, external=external, url=url)
                 db.session.add(new_app)
         db.session.commit()
+
+# Read in two configmaps from the cluster, which specify which oauthclients
+# should be present in the database.
+def populate_oauthclients():
+    logging.info("cluster_config: populating oauthclients")
+    database_oauthclients = {}
+    for client in OAuthClientApp.query.all():
+        id = client.oauthclient_id
+        database_oauthclients[id] = client
+        logging.info(f"database oauthclient: {id}")
+    _populate_oauthclients_from(database_oauthclients, "stackspin-oauthclients")
+    _populate_oauthclients_from(database_oauthclients, "stackspin-oauthclients-custom")
+
+# Read a list of oauthclients from a configmap. Check if they are already
+# present in the database, and if not, add missing ones there. The value of the
+# mapping is taken to be the slug of the app the oauthclient belongs to.
+def _populate_oauthclients_from(database_oauthclients, configmap_name):
+    cm_oauthclients = k8s.get_kubernetes_config_map_data(configmap_name, "flux-system")
+    if cm_oauthclients is None:
+        logging.info(f"Could not find configmap '{configmap_name}' in namespace 'flux-system'; ignoring.")
+    else:
+        for client_id, client_app in cm_oauthclients.items():
+            logging.info(f"configmap oauthclient: {client_id}")
+            if client_id in database_oauthclients:
+                logging.info(f"  already present in database")
+            else:
+                logging.info(f"  not present in database, adding!")
+                # Take the value of the configmap mapping (`client_app`) and
+                # interpret it as the slug of the app that this oauthclient
+                # belongs to.
+                app = App.query.filter_by(slug=client_app).first()
+                if not app:
+                    logging.error(f"  could not find app with slug {client_app}")
+                    continue
+                new_client = OAuthClientApp(oauthclient_id=client_id, app_id=app.id)
+                logging.info(f"  new oauth client: {new_client}")
+                db.session.add(new_client)
+        db.session.commit()