diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index f810de6455bb5bcae59fa33aa2b39a71d44fb078..37ce94292fc02e9530fb1ea996d246a415cd6eee 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -26,6 +26,8 @@ Fixed
 * Due to a merge error, the once removed account menu in the sidenav appeared again.
 * Scheduled notifications were shown on dashboard before time.
 * Remove broken notifications menu item in favor of item next to account menu.
+* [OAuth2] Resources which are protected with client credentials 
+  allowed access if no scopes were allowed.
 * The site logo could overlap with the menu for logos with an unexpected aspect ratio.
 
 Changed
diff --git a/aleksis/core/tests/regression/test_regression.py b/aleksis/core/tests/regression/test_regression.py
index b9d33e6981312817324b92d4fe67176b96763cda..c2417a56ebd02c015e84f18401d39cfc582b4252 100644
--- a/aleksis/core/tests/regression/test_regression.py
+++ b/aleksis/core/tests/regression/test_regression.py
@@ -1,10 +1,16 @@
+import base64
+
 from django.contrib.auth import get_user_model
 
 import pytest
 
-from aleksis.core.models import Group, Person
+from aleksis.core.models import Group, OAuthApplication, Person
 
 pytestmark = pytest.mark.django_db
+from django.http import HttpResponse
+from django.test import override_settings
+from django.urls import path, reverse
+from django.views.generic import View
 
 
 def test_all_settigns_registered():
@@ -82,3 +88,61 @@ def test_reassign_user_to_person():
     assert user2.groups.count() == 1
     assert user1.groups.first().name == "Group 2"
     assert user2.groups.first().name == "Group 1"
+
+
+@override_settings(ROOT_URLCONF="aleksis.core.tests.regression.view_oauth")
+def test_no_access_oauth2_client_credentials_without_allowed_scopes(client):
+    """Tests that ClientProtectedResourceMixin doesn't allow access if no allowed scopes are set.
+
+    https://edugit.org/AlekSIS/official/AlekSIS-Core/-/issues/688
+    """
+
+    wrong_application = OAuthApplication.objects.create(
+        name="Test Application",
+        allowed_scopes=[],
+        authorization_grant_type=OAuthApplication.GRANT_CLIENT_CREDENTIALS,
+        client_type=OAuthApplication.CLIENT_CONFIDENTIAL,
+        redirect_uris=["http://localhost:8000/"],
+    )
+    wrong_application_2 = OAuthApplication.objects.create(
+        name="Test Application",
+        allowed_scopes=["read"],
+        authorization_grant_type=OAuthApplication.GRANT_CLIENT_CREDENTIALS,
+        client_type=OAuthApplication.CLIENT_CONFIDENTIAL,
+        redirect_uris=["http://localhost:8000/"],
+    )
+    correct_application = OAuthApplication.objects.create(
+        name="Test Application",
+        allowed_scopes=["write"],
+        authorization_grant_type=OAuthApplication.GRANT_CLIENT_CREDENTIALS,
+        client_type=OAuthApplication.CLIENT_CONFIDENTIAL,
+        redirect_uris=["http://localhost:8000/"],
+    )
+
+    url = reverse("client_protected_resource_mixin_test")
+    auth_header = (
+        "Basic "
+        + base64.b64encode(
+            f"{wrong_application.client_id}:{wrong_application.client_secret}".encode()
+        ).decode()
+    )
+    r = client.get(url, HTTP_AUTHORIZATION=auth_header)
+    assert r.status_code == 403
+
+    auth_header = (
+        "Basic "
+        + base64.b64encode(
+            f"{wrong_application_2.client_id}:{wrong_application_2.client_secret}".encode()
+        ).decode()
+    )
+    r = client.get(url, HTTP_AUTHORIZATION=auth_header)
+    assert r.status_code == 403
+
+    auth_header = (
+        "Basic "
+        + base64.b64encode(
+            f"{correct_application.client_id}:{correct_application.client_secret}".encode()
+        ).decode()
+    )
+    r = client.get(url, HTTP_AUTHORIZATION=auth_header)
+    assert r.status_code == 200
diff --git a/aleksis/core/tests/regression/view_oauth.py b/aleksis/core/tests/regression/view_oauth.py
new file mode 100644
index 0000000000000000000000000000000000000000..d5671208e127e79c30d7bb179bbf2e82a35616fb
--- /dev/null
+++ b/aleksis/core/tests/regression/view_oauth.py
@@ -0,0 +1,24 @@
+from django.http import HttpResponse
+from django.test import override_settings
+from django.urls import path, reverse
+from django.views.generic import View
+
+from oauth2_provider.views.mixins import ScopedResourceMixin
+
+from aleksis.core.util.auth_helpers import ClientProtectedResourceMixin
+
+
+class TestViewClientProtectedResourceMixin(ScopedResourceMixin, ClientProtectedResourceMixin, View):
+    required_scopes = ["write"]
+
+    def get(self, request):
+        return HttpResponse("OK")
+
+
+urlpatterns = [
+    path(
+        "client_protected_resource_mixin_test/",
+        TestViewClientProtectedResourceMixin.as_view(),
+        name="client_protected_resource_mixin_test",
+    ),
+]
diff --git a/aleksis/core/util/auth_helpers.py b/aleksis/core/util/auth_helpers.py
index 6edfac83373882d077d0a588d822fd3a0d0cc9b4..ca80aeae4a59ac069023465559d599f929bab6d8 100644
--- a/aleksis/core/util/auth_helpers.py
+++ b/aleksis/core/util/auth_helpers.py
@@ -134,6 +134,10 @@ class ClientProtectedResourceMixin(_ClientProtectedResourceMixin):
         # Verify scopes of configured application
         # The OAuth request was enriched with a reference to the Application when using the
         #  validator above.
+        if not oauth_request.client.allowed_scopes:
+            # If there are no allowed scopes, the client is not allowed to access this resource
+            return False
+
         required_scopes = set(self.get_scopes() or [])
         allowed_scopes = set(AppScopes().get_available_scopes(oauth_request.client) or [])
         return required_scopes.issubset(allowed_scopes)