diff --git a/README.md b/README.md
index da496466539a2d8cf73e6f49f8c264eee142b57b..0a82674b82bff1dbecbdf04bbbc1c9c42555b9b8 100644
--- a/README.md
+++ b/README.md
@@ -44,17 +44,13 @@ You can automatically create required subscriptions based on your settings.py wi
 
 # Middlewares
 
-There is a `CurrentUserMiddleware` that catches the connected user of the last performed HTTP request and adds 
-to every model before it is saved. This is useful if you need to get the connected user that performed 
-the last HTTP request in a `pre_saved` signal. You can get it by using the following line :
+There is a `CurrentUserMiddleware` that catches the connected user of the current HTTP request and makes it available through the `get_current_user` function :
 
 ```python
-getattr(instance, MODEL_MODIFICATION_USER_FIELD, "Unknown user")
+from djangoldp_notification.middlewares import get_current_user
+get_current_user()
 ```
 
-`MODEL_MODIFICATION_USER_FIELD` is a constant that lies in `djangoldp_notification.middlewares` and 
-`instance` is the instance of your model before save in DB.
-
 # Signals
 
 ## Create notification on subscribed objects
diff --git a/djangoldp_notification/check_integrity.py b/djangoldp_notification/check_integrity.py
index 77839b4090e3ed6fcb73b9e9620bba3238f7f21f..b4f779eae5ada4c8ee2c45bd6f5759e52316bfbc 100644
--- a/djangoldp_notification/check_integrity.py
+++ b/djangoldp_notification/check_integrity.py
@@ -3,7 +3,6 @@ from django.conf import settings
 from django.db import models
 from djangoldp.models import Model
 from djangoldp_notification.models import send_request, Subscription
-from djangoldp_notification.middlewares import MODEL_MODIFICATION_USER_FIELD
 
 class technical_user:
   urlid = settings.BASE_URL
@@ -71,7 +70,6 @@ def check_integrity(options):
                 continue
               except ObjectDoesNotExist:
                 continue
-            setattr(resource, MODEL_MODIFICATION_USER_FIELD, technical_user)
             try:
               send_request(subscription.inbox, url_resource, resource, False)
               sent+=1
diff --git a/djangoldp_notification/filters.py b/djangoldp_notification/filters.py
deleted file mode 100644
index e3201fc74d1064a80644ca57400a301575327645..0000000000000000000000000000000000000000
--- a/djangoldp_notification/filters.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from djangoldp.filters import LDPPermissionsFilterBackend
-
-
-class InboxFilterBackend(LDPPermissionsFilterBackend):
-    def filter_queryset(self, request, queryset, view):
-        if not request.user.is_anonymous:
-            return queryset.filter(user=request.user)
-        else:
-            from djangoldp_notification.models import Notification
-            return Notification.objects.none()
-
-
-class SubscriptionsFilterBackend(LDPPermissionsFilterBackend):
-    def filter_queryset(self, request, queryset, view):
-        if request.method == "OPTIONS":
-            return queryset
-        else:
-            return super().filter_queryset(request, queryset, view)
diff --git a/djangoldp_notification/middlewares.py b/djangoldp_notification/middlewares.py
index 19ef25160248332047336f10399bc98601cf2bf3..1f0505fe85f87f61ec7216bc24db51ff1ef6580f 100644
--- a/djangoldp_notification/middlewares.py
+++ b/djangoldp_notification/middlewares.py
@@ -1,34 +1,12 @@
-from django.db.models import signals
-
-
-MODEL_MODIFICATION_USER_FIELD = 'modification_user'
-
+from threading import local
+_thread_locals = local()
 
 class CurrentUserMiddleware:
-    def __init__(self, get_response=None):
+    def __init__(self, get_response):
         self.get_response = get_response
-
     def __call__(self, request):
-        self.process_request(request)
-        response = self.get_response(request)
-        signals.pre_save.disconnect(dispatch_uid=request)
-        signals.pre_delete.disconnect(dispatch_uid=request)
-        return response
-
-    def process_request(self, request):
-        if request.method in ('GET', 'HEAD', 'OPTION'):
-            # this request shouldn't update anything
-            # so no signal handler should be attached
-            return
-
-        if hasattr(request, 'user') and request.user.is_authenticated:
-            user = request.user
-        else:
-            user = None
-
-        def _update_users(sender, instance, **kwargs):
-            if(type(instance).__name__ != "ScheduledActivity" and type(instance).__name__ != "LogEntry" and type(instance).__name__ != "Activity"):
-                setattr(instance, MODEL_MODIFICATION_USER_FIELD, user)
+        _thread_locals._current_user = getattr(request, 'user', None)
+        return self.get_response(request)
 
-        signals.pre_save.connect(_update_users, dispatch_uid=request, weak=False)
-        signals.pre_delete.connect(_update_users, dispatch_uid=request, weak=False)
+def get_current_user():
+    return getattr(_thread_locals, '_current_user', None)
\ No newline at end of file
diff --git a/djangoldp_notification/migrations/0016_alter_notification_options_and_more.py b/djangoldp_notification/migrations/0016_alter_notification_options_and_more.py
new file mode 100644
index 0000000000000000000000000000000000000000..8371f7094950e5b209effdf4aa32c9f79fd7e5c2
--- /dev/null
+++ b/djangoldp_notification/migrations/0016_alter_notification_options_and_more.py
@@ -0,0 +1,37 @@
+# Generated by Django 4.2.3 on 2023-10-12 09:14
+
+from django.db import migrations
+import djangoldp.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('djangoldp_notification', '0015_alter_subscription_options'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='notification',
+            options={'default_permissions': {'control', 'delete', 'view', 'add', 'change'}, 'ordering': ['-date']},
+        ),
+        migrations.AlterModelOptions(
+            name='subscription',
+            options={'default_permissions': {'control', 'delete', 'view', 'add', 'change'}, 'ordering': ['pk']},
+        ),
+        migrations.AlterField(
+            model_name='notification',
+            name='urlid',
+            field=djangoldp.fields.LDPUrlField(blank=True, db_index=True, null=True, unique=True),
+        ),
+        migrations.AlterField(
+            model_name='notificationsetting',
+            name='urlid',
+            field=djangoldp.fields.LDPUrlField(blank=True, db_index=True, null=True, unique=True),
+        ),
+        migrations.AlterField(
+            model_name='subscription',
+            name='urlid',
+            field=djangoldp.fields.LDPUrlField(blank=True, db_index=True, null=True, unique=True),
+        ),
+    ]
diff --git a/djangoldp_notification/models.py b/djangoldp_notification/models.py
index 5e7010541631ae4276adc694e01fa7ea1709ae08..3ac8eadb3f02d45cfc65cf5325601ca6c6128197 100644
--- a/djangoldp_notification/models.py
+++ b/djangoldp_notification/models.py
@@ -4,17 +4,16 @@ from django.contrib.auth import get_user_model
 from django.core.mail import send_mail
 from django.core.exceptions import ObjectDoesNotExist
 from django.db import models
-from django.db.models.signals import post_save, post_delete
+from django.db.models.signals import post_save, post_delete, m2m_changed
 from django.dispatch import receiver
 from django.template import loader
-from django.urls import NoReverseMatch, get_resolver, reverse
+from django.urls import NoReverseMatch, get_resolver
 from django.utils.translation import gettext_lazy as _
 from djangoldp.fields import LDPUrlField
 from djangoldp.models import Model
-from djangoldp.views import LDPViewSet
-from djangoldp.activities.services import ActivityQueueService, ActivityPubService, activity_sending_finished
-from djangoldp_notification.middlewares import MODEL_MODIFICATION_USER_FIELD
-from djangoldp_notification.permissions import InboxPermissions, SubscriptionsPermissions
+from djangoldp.permissions import CreateOnly, AuthenticatedOnly, ReadAndCreate, OwnerPermissions
+from djangoldp.activities.services import ActivityQueueService, activity_sending_finished
+from djangoldp_notification.middlewares import get_current_user
 from djangoldp_notification.views import LDPNotificationsViewSet
 import logging
 
@@ -34,10 +33,7 @@ class Notification(Model):
     class Meta(Model.Meta):
         owner_field = 'user'
         ordering = ['-date']
-        permission_classes = [InboxPermissions]
-        anonymous_perms = ['add']
-        authenticated_perms = ['inherit']
-        owner_perms = ['view', 'change', 'control']
+        permission_classes = [CreateOnly|OwnerPermissions]
         view_set = LDPNotificationsViewSet
 
     # NOTE: this would be our ideal cache behaviour
@@ -81,9 +77,7 @@ class NotificationSetting(Model):
     class Meta:
         auto_author = 'user'
         owner_field = 'user'
-        anonymous_perms = []
-        authenticated_perms = []
-        owner_perms = ['view', 'change']
+        permission_classes = [OwnerPermissions]
         container_path = 'settings/'
         serializer_fields = ['@id', 'receiveMail']
         rdf_type = 'sib:usersettings'
@@ -104,9 +98,7 @@ class Subscription(Model):
 
     class Meta(Model.Meta):
         ordering = ['pk']
-        anonymous_perms = []
-        authenticated_perms = ["add", "view", "delete"]
-        permission_classes = [SubscriptionsPermissions]
+        permission_classes = [AuthenticatedOnly, ReadAndCreate]
 
 
 @receiver(post_save, sender=Subscription, dispatch_uid="nested_subscriber_check")
@@ -116,7 +108,7 @@ def create_nested_subscribers(sender, instance, created, **kwargs):
         try:
             # object is a WebID.. convert to local representation
             local = Model.resolve(instance.object.replace(settings.SITE_URL, ''))[0]
-            nested_fields = Model.get_meta(local, 'nested_fields', [])
+            nested_fields = getattr(local._meta, 'nested_fields', [])
 
             # Don't create nested subscriptions for user model (Notification loop issue)
             if local._meta.model_name == get_user_model()._meta.model_name:
@@ -153,63 +145,55 @@ def create_nested_subscribers(sender, instance, created, **kwargs):
 # --- SUBSCRIPTION SYSTEM ---
 @receiver(post_save, dispatch_uid="callback_notif")
 @receiver(post_delete, dispatch_uid="delete_callback_notif")
-def send_notification(sender, instance, **kwargs):
-    if(type(instance).__name__ != "ScheduledActivity" and type(instance).__name__ != "LogEntry" and type(instance).__name__ != "Activity"):
-        if sender != Notification:
-            # don't send notifications for foreign resources
-            if hasattr(instance, 'urlid') and Model.is_external(instance.urlid):
-                return
-
-            recipients = []
-            try:
-                url_container = settings.BASE_URL + Model.container_id(instance)
-                url_resource = settings.BASE_URL + Model.resource_id(instance)
-            except NoReverseMatch:
-                return
-
-            # dispatch a notification for every Subscription on this resource
-            for subscription in Subscription.objects.filter(models.Q(disable_automatic_notifications=False) & (models.Q(object=url_resource) | models.Q(object=url_container))):
-                if subscription.inbox not in recipients and (not subscription.is_backlink or not kwargs.get("created")):
-                    # I may have configured to send the subscription to a foreign key
-                    if subscription.field is not None and len(subscription.field) > 1:
-                        try:
-                            if kwargs.get("created"):
-                                continue
+@receiver(m2m_changed, dispatch_uid="m2m_callback_notif")
+def notify(sender, instance, created=None, model=None, pk_set=set(), action='', **kwargs):
+    if type(instance).__name__ not in ["ScheduledActivity", "LogEntry", "Activity", "Migration"] and sender != Notification \
+        and action not in ['pre_add', 'pre_remove']:
+        if action or created is False:
+            request_type = 'update' #M2M change or post_save
+        elif created:
+            request_type = 'creation'
+        else:
+            request_type = "deletion"
+        send_notifications(instance, request_type)
+        if model and pk_set:
+            # Notify the reverse relations
+            send_notifications(model.objects.get(id=pk_set.pop()), 'update')
 
-                            instance = getattr(instance, subscription.field, instance)
 
-                            # don't send notifications for foreign resources
-                            if hasattr(instance, 'urlid') and Model.is_external(instance.urlid):
-                                continue
+def send_notifications(instance, request_type):
+    try:
+        url_container = settings.BASE_URL + Model.container_id(instance)
+        url_resource = settings.BASE_URL + Model.resource_id(instance)
+    except NoReverseMatch:
+        return
+    recipients = []
+    # don't send notifications for foreign resources
+    if hasattr(instance, 'urlid') and Model.is_external(instance.urlid):
+        return
+    # dispatch a notification for every Subscription on this resource
+    for subscription in Subscription.objects.filter(models.Q(disable_automatic_notifications=False) & (models.Q(object=url_resource) | models.Q(object=url_container))):
+        if subscription.inbox not in recipients and (not subscription.is_backlink or request_type != 'creation'):
+            # I may have configured to send the subscription to a foreign key
+            if subscription.field is not None and len(subscription.field) > 1 and request_type != 'creation':
+                try:
+                    instance = getattr(instance, subscription.field, instance)
+                    # don't send notifications for foreign resources
+                    if hasattr(instance, 'urlid') and Model.is_external(instance.urlid):
+                        continue
 
-                            url_resource = settings.BASE_URL + Model.resource_id(instance)
-                        except NoReverseMatch:
-                            continue
-                        except ObjectDoesNotExist:
-                            continue
+                    url_resource = settings.BASE_URL + Model.resource_id(instance)
+                except NoReverseMatch:
+                    continue
+                except ObjectDoesNotExist:
+                    continue
 
-                    send_request(subscription.inbox, url_resource, instance, kwargs.get("created"))
-                    recipients.append(subscription.inbox)
+            send_request(subscription.inbox, url_resource, instance, request_type)
+            recipients.append(subscription.inbox)
 
 
-@receiver(activity_sending_finished, sender=ActivityQueueService)
-def _handle_prosody_response(sender, response, saved_activity, **kwargs):
-    '''callback function for handling a response from Prosody on a notification'''
-    # if text is defined in the response body then it's an error
-    if saved_activity is not None:
-        response_body = saved_activity.response_to_json()
-        if 'condition' in response_body:
-            logger.error("[DjangoLDP-Notification.models._handle_prosody_response] error in Prosody response " +
-                         str(response_body))
-
-
-def send_request(target, object_iri, instance, created):
-    author = getattr(getattr(instance, MODEL_MODIFICATION_USER_FIELD, None), "urlid", str(_("Auteur inconnu")))
-    if(created is not None):
-        request_type = "creation" if created else "update"
-    else:
-        request_type = "deletion"
-
+def send_request(target, object_iri, instance, request_type):
+    author = getattr(get_current_user(), 'urlid', 'unknown')
     # local inbox
     if target.startswith(settings.SITE_URL):
         user = Model.resolve_parent(target.replace(settings.SITE_URL, ''))
@@ -225,6 +209,17 @@ def send_request(target, object_iri, instance, created):
         ActivityQueueService.send_activity(target, json)
 
 
+@receiver(activity_sending_finished, sender=ActivityQueueService)
+def _handle_prosody_response(sender, response, saved_activity, **kwargs):
+    '''callback function for handling a response from Prosody on a notification'''
+    # if text is defined in the response body then it's an error
+    if saved_activity is not None:
+        response_body = saved_activity.response_to_json()
+        if 'condition' in response_body:
+            logger.error("[DjangoLDP-Notification.models._handle_prosody_response] error in Prosody response " +
+                         str(response_body))
+
+
 def get_default_email_sender_djangoldp_instance():
     '''
     :return: the configured email host if it can find one, or None
diff --git a/djangoldp_notification/permissions.py b/djangoldp_notification/permissions.py
deleted file mode 100644
index d9fce393310027ea7fbccc62fd2d8f8b90f37911..0000000000000000000000000000000000000000
--- a/djangoldp_notification/permissions.py
+++ /dev/null
@@ -1,33 +0,0 @@
-from django.contrib.auth import get_user_model
-
-from djangoldp.permissions import LDPPermissions
-from djangoldp_notification.filters import InboxFilterBackend, SubscriptionsFilterBackend
-from rest_framework.reverse import reverse
-
-
-class InboxPermissions(LDPPermissions):
-    filter_backends = [InboxFilterBackend]
-
-
-class SubscriptionsPermissions(LDPPermissions):
-    filter_backends = [SubscriptionsFilterBackend]
-
-    def has_permission(self, request, view):
-        if request.user.is_anonymous and not request.method == "OPTIONS":
-            return False
-
-        if request.method in ["GET", "PATCH", "DELETE", "PUT"]:
-            return True
-
-        return super().has_permission(request, view)
-
-    def has_object_permission(self, request, view, obj):
-        if request.user.is_anonymous and not request.method == "OPTIONS":
-            return False
-
-        reverse_path_key = "{}-notification-list".format(get_user_model()._meta.object_name.lower())
-        user_inbox = reverse(reverse_path_key, kwargs={"slug": request.user.slug}, request=request)
-        if obj.inbox == user_inbox:
-            return True
-
-        return False
diff --git a/djangoldp_notification/tests/test_cache.py b/djangoldp_notification/tests/test_cache.py
index ffe1f9df9510997c990c6fa9d5006aa2c74dd377..e49ab2b4e4dafe2c071edc08c66d99570af4d02a 100644
--- a/djangoldp_notification/tests/test_cache.py
+++ b/djangoldp_notification/tests/test_cache.py
@@ -1,10 +1,6 @@
 import uuid
 import json
 from rest_framework.test import APITestCase, APIClient
-
-from django.conf import settings
-from djangoldp.models import Model
-from djangoldp.serializers import GLOBAL_SERIALIZER_CACHE
 from djangoldp_account.models import LDPUser
 from djangoldp_notification.models import Notification
 
@@ -74,13 +70,13 @@ class TestSubscription(APITestCase):
         my_container_urlid = '{}/users/{}/inbox/'.format(settings.SITE_URL, self.user.username)
         their_container_urlid = '{}/users/{}/inbox/'.format(settings.SITE_URL, other_user.username)
 
-        self.assertTrue(GLOBAL_SERIALIZER_CACHE.has(Model.get_meta(Notification, 'label'), my_container_urlid))
-        self.assertTrue(GLOBAL_SERIALIZER_CACHE.has(Model.get_meta(Notification, 'label'), their_container_urlid))
+        self.assertTrue(GLOBAL_SERIALIZER_CACHE.has(getattr(Notification._meta, 'label', None), my_container_urlid))
+        self.assertTrue(GLOBAL_SERIALIZER_CACHE.has(getattr(Notification._meta, 'label', None), their_container_urlid))
 
         # save my notification - should wipe the cache for my inbox...
         notification.unread = False
         notification.save()
-        self.assertFalse(GLOBAL_SERIALIZER_CACHE.has(Model.get_meta(Notification, 'label'), my_container_urlid))
+        self.assertFalse(GLOBAL_SERIALIZER_CACHE.has(getattr(Notification._meta, 'label', None), my_container_urlid))
 
         # ...but not for theirs
-        self.assertTrue(GLOBAL_SERIALIZER_CACHE.has(Model.get_meta(Notification, 'label'), their_container_urlid))'''
+        self.assertTrue(GLOBAL_SERIALIZER_CACHE.has(getattr(Notification._meta, 'label', None), their_container_urlid))'''
diff --git a/setup.cfg b/setup.cfg
index 854a0b520fa1ca55bdbcd0a41cfb2e065cb63327..4f5d16405127a9722e31a166abc24a0fbb85684a 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -10,8 +10,8 @@ license = MIT
 [options]
 packages = find:
 install_requires =
-    djangoldp~=3.0
-    djangoldp_account>=3.0
+    djangoldp~=3.1.0
+    djangoldp_account~=3.1.0
 
 [options.extras_require]
 include_package_data = True