diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index fe7a89aa0ed37c4d3b6bf684618a0f2f1c5ac001..5edb5daf763038840e0242bfe1c86006c5eec721 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -9,6 +9,11 @@ and this project adheres to `Semantic Versioning`_.
 Unreleased
 ----------
 
+Fixed
+~~~~~
+
+* The search bar in the sidenav menu is shown even though the user has no permission to see it.
+
 `3.0`_ - 2022-05-11
 -------------------
 
diff --git a/aleksis/core/frontend/components/app/SideNav.vue b/aleksis/core/frontend/components/app/SideNav.vue
index 0975c580cda88d03fd8a5d34b2fb16425ece69ee..70537e18342c56bd931f96adc89e796596dd5cb6 100644
--- a/aleksis/core/frontend/components/app/SideNav.vue
+++ b/aleksis/core/frontend/components/app/SideNav.vue
@@ -10,7 +10,7 @@
           <brand-logo :site-preferences="systemProperties.sitePreferences" />
         </a>
       </v-list-item>
-      <v-list-item class="search">
+      <v-list-item v-if="checkPermission('core.search_rule')" class="search">
         <sidenav-search />
       </v-list-item>
       <v-list-item-group :value="$route.name" v-if="sideNavMenu">
@@ -94,6 +94,8 @@ import BrandLogo from "./BrandLogo.vue";
 import LanguageForm from "./LanguageForm.vue";
 import SidenavSearch from "./SidenavSearch.vue";
 
+import permissionsMixin from "../../mixins/permissions.js";
+
 export default {
   name: "SideNav",
   components: {
@@ -106,6 +108,10 @@ export default {
     systemProperties: { type: Object, required: true },
     value: { type: Boolean, required: true },
   },
+  mixins: [permissionsMixin],
+  mounted() {
+    this.fetchPermissions(["core.search_rule"]);
+  },
 };
 </script>
 
diff --git a/aleksis/core/frontend/components/app/permissions.graphql b/aleksis/core/frontend/components/app/permissions.graphql
new file mode 100644
index 0000000000000000000000000000000000000000..fcb6133e0afdb78e9a2fb750e1972d64aa0968e7
--- /dev/null
+++ b/aleksis/core/frontend/components/app/permissions.graphql
@@ -0,0 +1,8 @@
+query gqlPermissions($permissions: [String]!) {
+  whoAmI {
+    permissions: globalPermissionsByName(permissions: $permissions) {
+      name
+      result
+    }
+  }
+}
diff --git a/aleksis/core/frontend/components/app/whoAmI.graphql b/aleksis/core/frontend/components/app/whoAmI.graphql
index 0b2877bd2cf15b5134c8e70978fb6b2218414e3a..57e0292d37ea4d221c9f263621647bfdabe38b6f 100644
--- a/aleksis/core/frontend/components/app/whoAmI.graphql
+++ b/aleksis/core/frontend/components/app/whoAmI.graphql
@@ -1,4 +1,4 @@
-query ($permissions: [String]!) {
+query whoAmI {
   whoAmI {
     username
     isAuthenticated
@@ -12,9 +12,5 @@ query ($permissions: [String]!) {
       avatarUrl
       isDummy
     }
-    permissions: globalPermissionsByName(permissions: $permissions) {
-      name
-      result
-    }
   }
 }
diff --git a/aleksis/core/frontend/index.js b/aleksis/core/frontend/index.js
index f59aa8578b59addcb420d6165c6ef1672c669c30..d82922996539f353ec2aa9a7ecb4faaf68d08322 100644
--- a/aleksis/core/frontend/index.js
+++ b/aleksis/core/frontend/index.js
@@ -70,6 +70,7 @@ const app = new Vue({
     backgroundActive: true,
     invalidation: false,
     snackbarItems: [],
+    permissions: [],
   }),
   computed: {
     matchedComponents() {
diff --git a/aleksis/core/frontend/mixins/menus.js b/aleksis/core/frontend/mixins/menus.js
index 9ba58bcaa84e72917e3057614fca6058df39af3a..7b6317a5aa3f625501bd51119d0fc4c6f633b379 100644
--- a/aleksis/core/frontend/mixins/menus.js
+++ b/aleksis/core/frontend/mixins/menus.js
@@ -1,15 +1,17 @@
 import gqlCustomMenu from "../components/app/customMenu.graphql";
 
+import permissionsMixin from "./permissions.js";
+
 /**
  * Vue mixin containing menu generation code.
  *
  * Only used by main App component, but factored out for readability.
  */
 const menusMixin = {
+  mixins: [permissionsMixin],
   data() {
     return {
       footerMenu: null,
-      permissionNames: [],
       sideNavMenu: null,
       accountMenu: null,
     };
@@ -35,8 +37,7 @@ const menusMixin = {
         }
       }
 
-      this.permissionNames = permArray;
-      this.$apollo.queries.whoAmI.refetch();
+      this.fetchPermissions(permArray);
     },
     buildMenu(routes, menuKey) {
       let menu = {};
@@ -99,14 +100,6 @@ const menusMixin = {
 
       return Object.values(menu);
     },
-    checkPermission(permissionName) {
-      return (
-        this.whoAmI &&
-        this.whoAmI.permissions &&
-        this.whoAmI.permissions.find((p) => p.name === permissionName) &&
-        this.whoAmI.permissions.find((p) => p.name === permissionName).result
-      );
-    },
     checkValidators(validators) {
       for (const validator of validators) {
         if (!validator(this.whoAmI)) {
@@ -118,14 +111,9 @@ const menusMixin = {
     buildMenus() {
       this.accountMenu = this.buildMenu(
         this.$router.getRoutes(),
-        "inAccountMenu",
-        this.whoAmI ? this.whoAmI.permissions : []
-      );
-      this.sideNavMenu = this.buildMenu(
-        this.$router.getRoutes(),
-        "inMenu",
-        this.whoAmI ? this.whoAmI.permissions : []
+        "inAccountMenu"
       );
+      this.sideNavMenu = this.buildMenu(this.$router.getRoutes(), "inMenu");
     },
   },
   apollo: {
diff --git a/aleksis/core/frontend/mixins/permissions.js b/aleksis/core/frontend/mixins/permissions.js
new file mode 100644
index 0000000000000000000000000000000000000000..55bc94f30a39e6712a6e74863b045707c3ae3bba
--- /dev/null
+++ b/aleksis/core/frontend/mixins/permissions.js
@@ -0,0 +1,55 @@
+import gqlPermissions from "../components/app/permissions.graphql";
+
+/**
+ * Vue mixin containing permission checking code.
+ */
+
+const permissionsMixin = {
+  apollo: {
+    permissions: {
+      query: gqlPermissions,
+      update(data) {
+        this.$root.permissions = data.whoAmI.permissions;
+      },
+      variables: {
+        permissions: [],
+      },
+    },
+  },
+  methods: {
+    checkPermission(permissionName) {
+      return (
+        this.$root.permissions &&
+        this.$root.permissions.find((p) => p.name === permissionName) &&
+        this.$root.permissions.find((p) => p.name === permissionName).result
+      );
+    },
+    fetchPermissions(permissionNames) {
+      this.$apollo.queries.permissions.fetchMore({
+        variables: {
+          permissions: permissionNames,
+        },
+        updateQuery: (previousResult, { fetchMoreResult }) => {
+          const oldPermissions = previousResult.whoAmI.permissions;
+          const newPermissions = fetchMoreResult.whoAmI.permissions;
+
+          const keepPermissions = oldPermissions.filter(
+            (oldPermission) =>
+              !newPermissions.find(
+                (newPermission) => newPermission.name === oldPermission.name
+              )
+          );
+
+          return {
+            whoAmI: {
+              __typename: previousResult.whoAmI.__typename,
+              permissions: [...keepPermissions, ...newPermissions],
+            },
+          };
+        },
+      });
+    },
+  },
+};
+
+export default permissionsMixin;