diff --git a/api/config/settings/common.py b/api/config/settings/common.py
index fe68a2746c5807decf33bff7b8c192ad6e3c0744..ef52f6643df1b73ca1dd78250731d5237518e322 100644
--- a/api/config/settings/common.py
+++ b/api/config/settings/common.py
@@ -686,7 +686,7 @@ REST_FRAMEWORK = {
         "funkwhale_api.federation.parsers.ActivityParser",
     ),
     "DEFAULT_AUTHENTICATION_CLASSES": (
-        "oauth2_provider.contrib.rest_framework.OAuth2Authentication",
+        "funkwhale_api.common.authentication.OAuth2Authentication",
         "funkwhale_api.common.authentication.JSONWebTokenAuthenticationQS",
         "funkwhale_api.common.authentication.BearerTokenHeaderAuth",
         "funkwhale_api.common.authentication.JSONWebTokenAuthentication",
diff --git a/api/funkwhale_api/common/authentication.py b/api/funkwhale_api/common/authentication.py
index 6d2b433bc9eab9c6db7efc454a0c088e3ab750af..f826b0c122fccea619d91c7f26b097490e30f2c3 100644
--- a/api/funkwhale_api/common/authentication.py
+++ b/api/funkwhale_api/common/authentication.py
@@ -1,6 +1,13 @@
 from django.conf import settings
 from django.utils.encoding import smart_text
 from django.utils.translation import ugettext as _
+
+from django.core.cache import cache
+
+from allauth.account.utils import send_email_confirmation
+from oauth2_provider.contrib.rest_framework.authentication import (
+    OAuth2Authentication as BaseOAuth2Authentication,
+)
 from rest_framework import exceptions
 from rest_framework_jwt import authentication
 from rest_framework_jwt.settings import api_settings
@@ -14,7 +21,40 @@ def should_verify_email(user):
     return has_unverified_email and mandatory_verification
 
 
+class UnverifiedEmail(Exception):
+    def __init__(self, user):
+        self.user = user
+
+
+def resend_confirmation_email(request, user):
+    THROTTLE_DELAY = 500
+    cache_key = "auth:resent-email-confirmation:{}".format(user.pk)
+    if cache.get(cache_key):
+        return False
+
+    done = send_email_confirmation(request, user)
+    cache.set(cache_key, True, THROTTLE_DELAY)
+    return done
+
+
+class OAuth2Authentication(BaseOAuth2Authentication):
+    def authenticate(self, request):
+        try:
+            return super().authenticate(request)
+        except UnverifiedEmail as e:
+            request.oauth2_error = {"error": "unverified_email"}
+            resend_confirmation_email(request, e.user)
+
+
 class BaseJsonWebTokenAuth(object):
+    def authenticate(self, request):
+        try:
+            return super().authenticate(request)
+        except UnverifiedEmail as e:
+            msg = _("You need to verify your email address.")
+            resend_confirmation_email(request, e.user)
+            raise exceptions.AuthenticationFailed(msg)
+
     def authenticate_credentials(self, payload):
         """
         We have to implement this method by hand to ensure we can check that the
@@ -38,9 +78,7 @@ class BaseJsonWebTokenAuth(object):
             raise exceptions.AuthenticationFailed(msg)
 
         if should_verify_email(user):
-
-            msg = _("You need to verify your email address.")
-            raise exceptions.AuthenticationFailed(msg)
+            raise UnverifiedEmail(user)
 
         return user
 
diff --git a/api/funkwhale_api/users/adapters.py b/api/funkwhale_api/users/adapters.py
index c44a7ce9f52890805f8dee8c13099ee114aa9177..e52892bd98dda1267915a0484c2507c72312411c 100644
--- a/api/funkwhale_api/users/adapters.py
+++ b/api/funkwhale_api/users/adapters.py
@@ -25,3 +25,7 @@ class FunkwhaleAccountAdapter(DefaultAccountAdapter):
 
     def get_login_redirect_url(self, request):
         return "noop"
+
+    def add_message(self, *args, **kwargs):
+        # disable message sending
+        return
diff --git a/api/funkwhale_api/users/auth_backends.py b/api/funkwhale_api/users/auth_backends.py
index b274bcee255264dc3071c47a16a410c353a5631a..cb7d3deaf12604df71cc342bd099c7d1a2209565 100644
--- a/api/funkwhale_api/users/auth_backends.py
+++ b/api/funkwhale_api/users/auth_backends.py
@@ -43,9 +43,11 @@ class ModelBackend(backends.ModelBackend):
         return user if self.user_can_authenticate(user) else None
 
     def user_can_authenticate(self, user):
-        return super().user_can_authenticate(
-            user
-        ) and not authentication.should_verify_email(user)
+        can_authenticate = super().user_can_authenticate(user)
+        if authentication.should_verify_email(user):
+            raise authentication.UnverifiedEmail(user)
+
+        return can_authenticate
 
 
 class AllAuthBackend(auth_backends.AuthenticationBackend, ModelBackend):
diff --git a/api/funkwhale_api/users/jwt_views.py b/api/funkwhale_api/users/jwt_views.py
index 532653abd1ac140c1d77a789a5bf70e2c101b6e3..7d797a9b9d74bf901fd6d8b3d9cade63fd65a288 100644
--- a/api/funkwhale_api/users/jwt_views.py
+++ b/api/funkwhale_api/users/jwt_views.py
@@ -1,8 +1,11 @@
 from rest_framework_jwt import views as jwt_views
 
+from . import serializers
+
 
 class ObtainJSONWebToken(jwt_views.ObtainJSONWebToken):
     throttling_scopes = {"*": {"anonymous": "jwt-login", "authenticated": "jwt-login"}}
+    serializer_class = serializers.JSONWebTokenSerializer
 
 
 class RefreshJSONWebToken(jwt_views.RefreshJSONWebToken):
diff --git a/api/funkwhale_api/users/oauth/server.py b/api/funkwhale_api/users/oauth/server.py
index e4f0947ec379a9dfce069345fed095fbef928103..cf24544200fad61c411cc5ff773e3d32f653a457 100644
--- a/api/funkwhale_api/users/oauth/server.py
+++ b/api/funkwhale_api/users/oauth/server.py
@@ -8,8 +8,7 @@ def check(request):
     user = request.user
     request.user = user.__class__.objects.all().for_auth().get(pk=user.pk)
     if authentication.should_verify_email(request.user):
-        setattr(request, "oauth2_error", {"error": "unverified_email"})
-        return False
+        raise authentication.UnverifiedEmail(user)
     return True
 
 
diff --git a/api/funkwhale_api/users/serializers.py b/api/funkwhale_api/users/serializers.py
index 2027ee8c9b07d69f44d6c59c5306637125b45d1b..8952c5a1940892298296a45dc1a8accb0b045dc2 100644
--- a/api/funkwhale_api/users/serializers.py
+++ b/api/funkwhale_api/users/serializers.py
@@ -7,8 +7,10 @@ from django.utils.translation import gettext_lazy as _
 from rest_auth.serializers import PasswordResetSerializer as PRS
 from rest_auth.registration.serializers import RegisterSerializer as RS, get_adapter
 from rest_framework import serializers
+from rest_framework_jwt import serializers as jwt_serializers
 
 from funkwhale_api.activity import serializers as activity_serializers
+from funkwhale_api.common import authentication
 from funkwhale_api.common import models as common_models
 from funkwhale_api.common import preferences
 from funkwhale_api.common import serializers as common_serializers
@@ -36,6 +38,15 @@ username_validators = [ASCIIUsernameValidator()]
 NOOP = object()
 
 
+class JSONWebTokenSerializer(jwt_serializers.JSONWebTokenSerializer):
+    def validate(self, data):
+        try:
+            return super().validate(data)
+        except authentication.UnverifiedEmail as e:
+            authentication.send_email_confirmation(self.context["request"], e.user)
+            raise serializers.ValidationError("Please verify your email address.")
+
+
 class RegisterSerializer(RS):
     invitation = serializers.CharField(
         required=False, allow_null=True, allow_blank=True
diff --git a/api/tests/users/oauth/test_views.py b/api/tests/users/oauth/test_views.py
index 0bbbe0c570aede12cbfa1c00a1613054cade439e..bf78c83b4cfe6f58db32f6576f8cc6fdb968b2e4 100644
--- a/api/tests/users/oauth/test_views.py
+++ b/api/tests/users/oauth/test_views.py
@@ -381,9 +381,15 @@ def test_grant_delete(factories, logged_in_api_client, mocker, now):
     ],
 )
 def test_token_auth(
-    setting_value, verified_email, expected_status_code, api_client, factories, settings
+    setting_value,
+    verified_email,
+    expected_status_code,
+    api_client,
+    factories,
+    settings,
+    mailoutbox,
 ):
-
+    sent_emails = len(mailoutbox)
     user = factories["users.User"](verified_email=verified_email)
     token = factories["users.AccessToken"](user=user)
     settings.ACCOUNT_EMAIL_VERIFICATION = setting_value
@@ -392,3 +398,7 @@ def test_token_auth(
         HTTP_AUTHORIZATION="Bearer {}".format(token.token),
     )
     assert response.status_code == expected_status_code
+
+    if expected_status_code != 200:
+        # confirmation email should have been sent again
+        assert len(mailoutbox) == sent_emails + 1
diff --git a/deploy/env.prod.sample b/deploy/env.prod.sample
index fc88505e4951cf44745366f0466a6f26cf1112ac..4a184e833d29d5ea68524a23601b62dfdc8e876d 100644
--- a/deploy/env.prod.sample
+++ b/deploy/env.prod.sample
@@ -52,6 +52,10 @@ FUNKWHALE_PROTOCOL=https
 # EMAIL_CONFIG=smtp+ssl://user@:password@youremail.host:465
 # EMAIL_CONFIG=smtp+tls://user@:password@youremail.host:587
 
+# Make email verification mandatory before using the service
+# Doesn't apply to admins.
+# ACCOUNT_EMAIL_VERIFICATION_ENFORCE=false
+
 # The email address to use to send system emails.
 # DEFAULT_FROM_EMAIL=noreply@yourdomain