We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Login template does not respect non_fields_errors display
When using a custom `WAGTAILADMIN_USER_LOGIN_FORM`, you can't set form-wide errors as they will always be displayed as `Your {{ username_field }} and password didn't match. Please try again.` from `"wagtailadmin/login.html"`

As the default LoginForm (`wagtail.admin.forms.auth.LoginForm`) subclasses `django.contrib.auth.forms.AuthenticationForm` which already has an `'invalid_login'` error message (that is usename_field-aware) we could just use that, but if we want this particular message we could just override this error message"

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `wagtail/admin/forms/auth.py`
Content:
```
1 from django import forms
2 from django.contrib.auth.forms import AuthenticationForm
3 from django.contrib.auth.forms import PasswordChangeForm as DjangoPasswordChangeForm
4 from django.contrib.auth.forms import PasswordResetForm as DjangoPasswordResetForm
5 from django.utils.translation import gettext_lazy
6 
7 
8 class LoginForm(AuthenticationForm):
9     username = forms.CharField(max_length=254, widget=forms.TextInput())
10 
11     password = forms.CharField(
12         widget=forms.PasswordInput(
13             attrs={
14                 "placeholder": gettext_lazy("Enter password"),
15             }
16         )
17     )
18 
19     remember = forms.BooleanField(required=False)
20 
21     def __init__(self, request=None, *args, **kwargs):
22         super().__init__(request=request, *args, **kwargs)
23         self.fields["username"].widget.attrs["placeholder"] = gettext_lazy(
24             "Enter your %(username_field_name)s"
25         ) % {"username_field_name": self.username_field.verbose_name}
26         self.fields["username"].widget.attrs["autofocus"] = ""
27 
28     @property
29     def extra_fields(self):
30         for field_name in self.fields.keys():
31             if field_name not in ["username", "password", "remember"]:
32                 yield field_name, self[field_name]
33 
34 
35 class PasswordResetForm(DjangoPasswordResetForm):
36     email = forms.EmailField(
37         label=gettext_lazy("Enter your email address to reset your password"),
38         max_length=254,
39         required=True,
40     )
41 
42     @property
43     def extra_fields(self):
44         for field_name in self.fields.keys():
45             if field_name not in ["email"]:
46                 yield field_name, self[field_name]
47 
48 
49 class PasswordChangeForm(DjangoPasswordChangeForm):
50     """
51     Since this is displayed as part of a larger form, this differs from the vanilla Django
52     PasswordChangeForm as follows:
53     * the old-password field is not auto-focused
54     * Fields are not marked as required
55     """
56 
57     def __init__(self, *args, **kwargs):
58         super().__init__(*args, **kwargs)
59         try:
60             del self.fields["old_password"].widget.attrs["autofocus"]
61         except KeyError:
62             pass
63 
64         self.fields["old_password"].required = False
65         self.fields["new_password1"].required = False
66         self.fields["new_password2"].required = False
67
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/wagtail/admin/forms/auth.py b/wagtail/admin/forms/auth.py
--- a/wagtail/admin/forms/auth.py
+++ b/wagtail/admin/forms/auth.py
@@ -18,6 +18,13 @@
 
     remember = forms.BooleanField(required=False)
 
+    error_messages = {
+        **AuthenticationForm.error_messages,
+        "invalid_login": gettext_lazy(
+            "Your %(username_field)s and password didn't match. Please try again."
+        ),
+    }
+
     def __init__(self, request=None, *args, **kwargs):
         super().__init__(request=request, *args, **kwargs)
         self.fields["username"].widget.attrs["placeholder"] = gettext_lazy(
@@ -31,6 +38,13 @@
             if field_name not in ["username", "password", "remember"]:
                 yield field_name, self[field_name]
 
+    def get_invalid_login_error(self):
+        return forms.ValidationError(
+            self.error_messages["invalid_login"],
+            code="invalid_login",
+            params={"username_field": self.username_field.verbose_name},
+        )
+
 
 class PasswordResetForm(DjangoPasswordResetForm):
     email = forms.EmailField(

{"golden_diff": "diff --git a/wagtail/admin/forms/auth.py b/wagtail/admin/forms/auth.py\n--- a/wagtail/admin/forms/auth.py\n+++ b/wagtail/admin/forms/auth.py\n@@ -18,6 +18,13 @@\n \n     remember = forms.BooleanField(required=False)\n \n+    error_messages = {\n+        **AuthenticationForm.error_messages,\n+        \"invalid_login\": gettext_lazy(\n+            \"Your %(username_field)s and password didn't match. Please try again.\"\n+        ),\n+    }\n+\n     def __init__(self, request=None, *args, **kwargs):\n         super().__init__(request=request, *args, **kwargs)\n         self.fields[\"username\"].widget.attrs[\"placeholder\"] = gettext_lazy(\n@@ -31,6 +38,13 @@\n             if field_name not in [\"username\", \"password\", \"remember\"]:\n                 yield field_name, self[field_name]\n \n+    def get_invalid_login_error(self):\n+        return forms.ValidationError(\n+            self.error_messages[\"invalid_login\"],\n+            code=\"invalid_login\",\n+            params={\"username_field\": self.username_field.verbose_name},\n+        )\n+\n \n class PasswordResetForm(DjangoPasswordResetForm):\n     email = forms.EmailField(\n", "issue": "Login template does not respect non_fields_errors display\nWhen using a custom `WAGTAILADMIN_USER_LOGIN_FORM`, you can't set form-wide errors as they will always be displayed as `Your {{ username_field }} and password didn't match. Please try again.` from `\"wagtailadmin/login.html\"`\r\n\r\nAs the default LoginForm (`wagtail.admin.forms.auth.LoginForm`) subclasses `django.contrib.auth.forms.AuthenticationForm` which already has an `'invalid_login'` error message (that is usename_field-aware) we could just use that, but if we want this particular message we could just override this error message\"\n", "before_files": [{"content": "from django import forms\nfrom django.contrib.auth.forms import AuthenticationForm\nfrom django.contrib.auth.forms import PasswordChangeForm as DjangoPasswordChangeForm\nfrom django.contrib.auth.forms import PasswordResetForm as DjangoPasswordResetForm\nfrom django.utils.translation import gettext_lazy\n\n\nclass LoginForm(AuthenticationForm):\n    username = forms.CharField(max_length=254, widget=forms.TextInput())\n\n    password = forms.CharField(\n        widget=forms.PasswordInput(\n            attrs={\n                \"placeholder\": gettext_lazy(\"Enter password\"),\n            }\n        )\n    )\n\n    remember = forms.BooleanField(required=False)\n\n    def __init__(self, request=None, *args, **kwargs):\n        super().__init__(request=request, *args, **kwargs)\n        self.fields[\"username\"].widget.attrs[\"placeholder\"] = gettext_lazy(\n            \"Enter your %(username_field_name)s\"\n        ) % {\"username_field_name\": self.username_field.verbose_name}\n        self.fields[\"username\"].widget.attrs[\"autofocus\"] = \"\"\n\n    @property\n    def extra_fields(self):\n        for field_name in self.fields.keys():\n            if field_name not in [\"username\", \"password\", \"remember\"]:\n                yield field_name, self[field_name]\n\n\nclass PasswordResetForm(DjangoPasswordResetForm):\n    email = forms.EmailField(\n        label=gettext_lazy(\"Enter your email address to reset your password\"),\n        max_length=254,\n        required=True,\n    )\n\n    @property\n    def extra_fields(self):\n        for field_name in self.fields.keys():\n            if field_name not in [\"email\"]:\n                yield field_name, self[field_name]\n\n\nclass PasswordChangeForm(DjangoPasswordChangeForm):\n    \"\"\"\n    Since this is displayed as part of a larger form, this differs from the vanilla Django\n    PasswordChangeForm as follows:\n    * the old-password field is not auto-focused\n    * Fields are not marked as required\n    \"\"\"\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        try:\n            del self.fields[\"old_password\"].widget.attrs[\"autofocus\"]\n        except KeyError:\n            pass\n\n        self.fields[\"old_password\"].required = False\n        self.fields[\"new_password1\"].required = False\n        self.fields[\"new_password2\"].required = False\n", "path": "wagtail/admin/forms/auth.py"}], "after_files": [{"content": "from django import forms\nfrom django.contrib.auth.forms import AuthenticationForm\nfrom django.contrib.auth.forms import PasswordChangeForm as DjangoPasswordChangeForm\nfrom django.contrib.auth.forms import PasswordResetForm as DjangoPasswordResetForm\nfrom django.utils.translation import gettext_lazy\n\n\nclass LoginForm(AuthenticationForm):\n    username = forms.CharField(max_length=254, widget=forms.TextInput())\n\n    password = forms.CharField(\n        widget=forms.PasswordInput(\n            attrs={\n                \"placeholder\": gettext_lazy(\"Enter password\"),\n            }\n        )\n    )\n\n    remember = forms.BooleanField(required=False)\n\n    error_messages = {\n        **AuthenticationForm.error_messages,\n        \"invalid_login\": gettext_lazy(\n            \"Your %(username_field)s and password didn't match. Please try again.\"\n        ),\n    }\n\n    def __init__(self, request=None, *args, **kwargs):\n        super().__init__(request=request, *args, **kwargs)\n        self.fields[\"username\"].widget.attrs[\"placeholder\"] = gettext_lazy(\n            \"Enter your %(username_field_name)s\"\n        ) % {\"username_field_name\": self.username_field.verbose_name}\n        self.fields[\"username\"].widget.attrs[\"autofocus\"] = \"\"\n\n    @property\n    def extra_fields(self):\n        for field_name in self.fields.keys():\n            if field_name not in [\"username\", \"password\", \"remember\"]:\n                yield field_name, self[field_name]\n\n    def get_invalid_login_error(self):\n        return forms.ValidationError(\n            self.error_messages[\"invalid_login\"],\n            code=\"invalid_login\",\n            params={\"username_field\": self.username_field.verbose_name},\n        )\n\n\nclass PasswordResetForm(DjangoPasswordResetForm):\n    email = forms.EmailField(\n        label=gettext_lazy(\"Enter your email address to reset your password\"),\n        max_length=254,\n        required=True,\n    )\n\n    @property\n    def extra_fields(self):\n        for field_name in self.fields.keys():\n            if field_name not in [\"email\"]:\n                yield field_name, self[field_name]\n\n\nclass PasswordChangeForm(DjangoPasswordChangeForm):\n    \"\"\"\n    Since this is displayed as part of a larger form, this differs from the vanilla Django\n    PasswordChangeForm as follows:\n    * the old-password field is not auto-focused\n    * Fields are not marked as required\n    \"\"\"\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        try:\n            del self.fields[\"old_password\"].widget.attrs[\"autofocus\"]\n        except KeyError:\n            pass\n\n        self.fields[\"old_password\"].required = False\n        self.fields[\"new_password1\"].required = False\n        self.fields[\"new_password2\"].required = False\n", "path": "wagtail/admin/forms/auth.py"}]}