diff --git a/aleksis/core/frontend/components/authorized_oauth_applications/AuthorizedApplication.vue b/aleksis/core/frontend/components/authorized_oauth_applications/AuthorizedApplication.vue
index 0fe07455e815f226dcbaf2414047cc623d5aa275..73d581937e36ff8f42133c25b73a967bff2ef503 100644
--- a/aleksis/core/frontend/components/authorized_oauth_applications/AuthorizedApplication.vue
+++ b/aleksis/core/frontend/components/authorized_oauth_applications/AuthorizedApplication.vue
@@ -37,7 +37,7 @@
             }}
           </v-list-item-content>
           <v-list-item-action>
-            <v-btn color="primary">
+            <v-btn color="primary" @click="deleteItem(accessToken)">
               {{ $t("oauth.authorized_application.revoke") }}
             </v-btn>
           </v-list-item-action>
@@ -73,5 +73,10 @@ export default {
       required: true,
     },
   },
+  methods: {
+    deleteItem(item) {
+      this.$emit("delete-item", item);
+    },
+  },
 };
 </script>
diff --git a/aleksis/core/frontend/components/authorized_oauth_applications/AuthorizedApplications.vue b/aleksis/core/frontend/components/authorized_oauth_applications/AuthorizedApplications.vue
index 5a6b42102c065783a6943282266abf7e28334173..bbf35c8e7f3c2f9e71479c03feb86799b3328d7f 100644
--- a/aleksis/core/frontend/components/authorized_oauth_applications/AuthorizedApplications.vue
+++ b/aleksis/core/frontend/components/authorized_oauth_applications/AuthorizedApplications.vue
@@ -1,10 +1,10 @@
 <template>
   <div>
     <h1 class="mb-4">{{ $t("oauth.authorized_application.title") }}</h1>
-    <div v-if="$apollo.queries.oauth.loading">
+    <div v-if="$apollo.queries.accessTokens.loading">
       <v-skeleton-loader type="card"></v-skeleton-loader>
     </div>
-    <div v-else-if="oauth">
+    <div v-else-if="accessTokens">
       <v-card class="mb-4">
         <v-card-title>
           {{ $t("oauth.authorized_application.subtitle") }}
@@ -14,25 +14,55 @@
         </v-card-text>
         <v-expansion-panels flat>
           <authorized-application
-            v-for="(accessToken, index) in oauth.accessTokens"
+            v-for="(accessToken, index) in accessTokens"
             :key="accessToken.id"
             :access-token="accessToken"
+            @delete-item="openDeleteDialog"
           />
         </v-expansion-panels>
       </v-card>
     </div>
+    <delete-dialog
+      :item="deleteItem"
+      :gql-mutation="require('./revokeOauthToken.graphql')"
+      :gql-query="require('./accessTokens.graphql')"
+      v-model="deleteDialog"
+    >
+      <template #title>
+        {{ $t("oauth.authorized_application.revoke_question") }}
+      </template>
+      <template #body>
+        <span v-if="deleteItem">{{ deleteItem.application.name }}</span>
+      </template>
+      <template #deleteContent>
+        {{ $t("oauth.authorized_application.revoke") }}
+      </template>
+    </delete-dialog>
   </div>
 </template>
 
 <script>
 import gqlAccessTokens from "./accessTokens.graphql";
 import AuthorizedApplication from "./AuthorizedApplication.vue";
+import DeleteDialog from "../generic/dialogs/DeleteDialog.vue";
 
 export default {
   name: "AuthorizedApplications",
-  components: { AuthorizedApplication },
+  components: { DeleteDialog, AuthorizedApplication },
+  data() {
+    return {
+      deleteDialog: false,
+      deleteItem: null,
+    };
+  },
+  methods: {
+    openDeleteDialog(item) {
+      this.deleteItem = item;
+      this.deleteDialog = true;
+    },
+  },
   apollo: {
-    oauth: {
+    accessTokens: {
       query: gqlAccessTokens,
     },
   },
diff --git a/aleksis/core/frontend/components/authorized_oauth_applications/accessTokens.graphql b/aleksis/core/frontend/components/authorized_oauth_applications/accessTokens.graphql
index 3c40c63bdb89875972a03bed057466a28a7507c9..68ab08b05d479b8149b3772406a81dd0673c3169 100644
--- a/aleksis/core/frontend/components/authorized_oauth_applications/accessTokens.graphql
+++ b/aleksis/core/frontend/components/authorized_oauth_applications/accessTokens.graphql
@@ -1,20 +1,18 @@
 {
-  oauth {
-    accessTokens {
+  accessTokens: oauthAccessTokens {
+    id
+    created
+    updated
+    expires
+    scopes {
+      name
+      description
+    }
+    application {
       id
-      created
-      updated
-      expires
-      scopes {
-        name
-        description
-      }
-      application {
-        id
-        name
-        icon {
-          absoluteUrl
-        }
+      name
+      icon {
+        absoluteUrl
       }
     }
   }
diff --git a/aleksis/core/frontend/components/authorized_oauth_applications/revokeOauthToken.graphql b/aleksis/core/frontend/components/authorized_oauth_applications/revokeOauthToken.graphql
new file mode 100644
index 0000000000000000000000000000000000000000..6b2f8adbde00d9e0e4f3419673324f8962266683
--- /dev/null
+++ b/aleksis/core/frontend/components/authorized_oauth_applications/revokeOauthToken.graphql
@@ -0,0 +1,5 @@
+mutation ($id: ID!) {
+  revokeOauthToken(id: $id) {
+    ok
+  }
+}
diff --git a/aleksis/core/frontend/messages/en.json b/aleksis/core/frontend/messages/en.json
index 63af5033dd797a5c7a1191cbbf1868bf7921ba09..3a046e694bda219530c46e30d32e3d9ec1b44658 100644
--- a/aleksis/core/frontend/messages/en.json
+++ b/aleksis/core/frontend/messages/en.json
@@ -175,7 +175,8 @@
       "valid_until": "Valid until {date}",
       "access_since": "Access since {date}",
       "has_access_to": "Has access to:",
-      "revoke": "Revoke Access"
+      "revoke": "Revoke Access",
+      "revoke_question": "Are you sure you want to revoke access for this application?"
     }
   },
   "people": "People",
diff --git a/aleksis/core/schema/__init__.py b/aleksis/core/schema/__init__.py
index 5004c0080bc14665c0e557f898dbf4ab4e1069a8..24a68978aa2a40dce7aa6fbf3546e1e5929d62cb 100644
--- a/aleksis/core/schema/__init__.py
+++ b/aleksis/core/schema/__init__.py
@@ -9,7 +9,15 @@ from haystack.inputs import AutoQuery
 from haystack.query import SearchQuerySet
 from haystack.utils.loading import UnifiedIndex
 
-from ..models import CustomMenu, DynamicRoute, Notification, PDFFile, Person, TaskUserAssignment
+from ..models import (
+    CustomMenu,
+    DynamicRoute,
+    Notification,
+    OAuthAccessToken,
+    PDFFile,
+    Person,
+    TaskUserAssignment,
+)
 from ..util.apps import AppConfig
 from ..util.core_helpers import get_allowed_object_ids, get_app_module, get_app_packages, has_person
 from .celery_progress import CeleryProgressFetchedMutation, CeleryProgressType
@@ -19,7 +27,7 @@ from .group import GroupType  # noqa
 from .installed_apps import AppType
 from .message import MessageType
 from .notification import MarkNotificationReadMutation, NotificationType
-from .oauth import OAuthQuery
+from .oauth import OAuthAccessTokenType, OAuthRevokeTokenMutation
 from .pdf import PDFFileType
 from .person import PersonMutation, PersonType
 from .school_term import SchoolTermType  # noqa
@@ -60,7 +68,7 @@ class Query(graphene.ObjectType):
 
     two_factor = graphene.Field(TwoFactorType)
 
-    oauth = graphene.Field(OAuthQuery)
+    oauth_access_tokens = graphene.List(OAuthAccessTokenType)
 
     def resolve_ping(root, info, payload) -> str:
         return payload
@@ -161,8 +169,8 @@ class Query(graphene.ObjectType):
         return info.context.user
 
     @staticmethod
-    def resolve_oauth(root, info, **kwargs):
-        return True
+    def resolve_oauth_access_tokens(root, info, **kwargs):
+        return OAuthAccessToken.objects.filter(user=info.context.user)
 
 
 class Mutation(graphene.ObjectType):
@@ -172,6 +180,8 @@ class Mutation(graphene.ObjectType):
 
     celery_progress_fetched = CeleryProgressFetchedMutation.Field()
 
+    revoke_oauth_token = OAuthRevokeTokenMutation.Field()
+
 
 def build_global_schema():
     """Build global GraphQL schema from all apps."""
diff --git a/aleksis/core/schema/base.py b/aleksis/core/schema/base.py
index e3478a34ff62be4859d8d9e88a8d73322796ad82..36d4f0647bd25d56b58e14fc4b737a3893e20372 100644
--- a/aleksis/core/schema/base.py
+++ b/aleksis/core/schema/base.py
@@ -1,5 +1,5 @@
-from django.db.models import Model
 from django.core.exceptions import PermissionDenied
+from django.db.models import Model
 
 import graphene
 from graphene_django import DjangoObjectType
diff --git a/aleksis/core/schema/oauth.py b/aleksis/core/schema/oauth.py
index 74efa25615446097e758f57e16b6827bc275b2cf..c51527d0bb316f2768a844e71697a0eeb8c29100 100644
--- a/aleksis/core/schema/oauth.py
+++ b/aleksis/core/schema/oauth.py
@@ -31,8 +31,14 @@ class OAuthAccessTokenType(DjangoObjectType):
         fields = ["id", "application", "expires", "created", "updated"]
 
 
-class OAuthQuery(graphene.ObjectType):
-    access_tokens = graphene.List(OAuthAccessTokenType)
+class OAuthRevokeTokenMutation(graphene.Mutation):
+    class Arguments:
+        id = graphene.ID()  # noqa
 
-    def resolve_access_tokens(root, info, **kwargs):
-        return OAuthAccessToken.objects.filter(user=info.context.user)
+    ok = graphene.Boolean()
+
+    @staticmethod
+    def mutate(root, info, id):  # noqa
+        token = OAuthAccessToken.objects.get(id=id, user=info.context.user)
+        token.delete()
+        return OAuthRevokeTokenMutation(ok=True)