Repository: pennersr/django-allauth
Pull request #3946 (2024-07-05 08:41:35)

Render checkboxes and radio buttons before label
The `<label>` element should be placed before its corresponding <input> in most cases. However, in the case of a checkbox input or a radio input, the <label> element should be placed after it.

To illustrate:

Bad:

    I agree to terms and conditions ☑

Good:

    ☑ I agree to terms and conditions

django-allauth ships with the two templates that will be affected positively by this change:

allauth/templates/account/email.html
allauth/templates/socialaccount/connections.html


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1eb78d33..c7d674f5 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -14,7 +14,7 @@ jobs:
       matrix:
         project: ['regular', 'headless_only']
         python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
-        django-version: ['main', '4.0', '4.1', '4.2', '5.0']
+        django-version: ['main', '4.2', '5.0']
         exclude:
           - python-version: '3.8'
             django-version: '5.0'
diff --git a/ChangeLog.rst b/ChangeLog.rst
index 5e7a7cd1..8a7f95a6 100644
--- a/ChangeLog.rst
+++ b/ChangeLog.rst
@@ -5,8 +5,7 @@ Note worthy changes
 -------------------
 
 - Added support for WebAuthn based security keys and passkey login. Note that
-  this is currently disabled by default, and headless support is not supported,
-  yet.
+  this is currently disabled by default.
 
 - The 0.x.y version numbers really did not do justice to the state of the
   project, and we are way past the point where a version 1.0 would be
@@ -22,6 +21,38 @@ Note worthy changes
   a 409.
 
 
+Backwards incompatible changes
+------------------------------
+
+- Dropped support for Django 3.2, 4.0 and 4.1 (which all reached end of life).
+  As Django 3.2 was the last to support Python 3.7, support for Python 3.7 is
+  now dropped as well.
+
+
+0.63.6 (2024-07-12)
+*******************
+
+Security notice
+---------------
+
+- When the Facebook provider was configured to use the ``js_sdk`` method the
+  login page could become vulnerable to an XSS attack.
+
+
+0.63.5 (2024-07-11)
+*******************
+
+Fixes
+-----
+
+- The security fix in 0.63.4 that altered the ``__str__()`` of ``SocialToken``
+  caused issues within the Amazon Cognito, Atlassian, JupyterHub, LemonLDAP,
+  Nextcloud and OpenID Connect providers. Fixed.
+
+
+0.63.4 (2024-07-10)
+*******************
+
 Security notice
 ---------------
 
@@ -31,12 +62,8 @@ Security notice
   log/print tokens, you will now have to explicitly log the ``token`` field of
   the ``SocialToken`` instance.
 
-
-Backwards incompatible changes
-------------------------------
-
-- Dropped support for Django 3.2 (which reached end of life).  As Django 3.2 was
-  the last to support Python 3.7, support for Python 3.7 is now dropped as well.
+- Enumeration prevention: the behavior on the outside of an actual signup versus
+  a signup where the user already existed was not fully identical, fixed.
 
 
 0.63.3 (2024-05-31)
diff --git a/allauth/account/adapter.py b/allauth/account/adapter.py
index 1b05cb1f..73e0c694 100644
--- a/allauth/account/adapter.py
+++ b/allauth/account/adapter.py
@@ -727,38 +727,34 @@ class DefaultAccountAdapter(BaseAdapter):
         """The order of the methods returned matters. The first method is the
         default when using the `@reauthentication_required` decorator.
         """
+        from allauth.account.internal.flows.reauthentication import (
+            get_reauthentication_flows,
+        )
+
+        flow_by_id = {f["id"]: f for f in get_reauthentication_flows(user)}
         ret = []
-        if not user.is_authenticated:
-            return ret
-        if user.has_usable_password():
+        if "reauthenticate" in flow_by_id:
             entry = {
                 "id": "reauthenticate",
                 "description": _("Use your password"),
+                "url": reverse("account_reauthenticate"),
             }
-            if not allauth_app_settings.HEADLESS_ONLY:
-                entry["url"] = reverse("account_reauthenticate")
             ret.append(entry)
-        if allauth_app_settings.MFA_ENABLED:
-            from allauth.mfa.models import Authenticator
-            from allauth.mfa.utils import is_mfa_enabled
-
-            if is_mfa_enabled(
-                user, types=[Authenticator.Type.TOTP, Authenticator.Type.RECOVERY_CODES]
-            ):
+        if "mfa_reauthenticate" in flow_by_id:
+            types = flow_by_id["mfa_reauthenticate"]["types"]
+            if "recovery_codes" in types or "totp" in types:
                 entry = {
                     "id": "mfa_reauthenticate",
                     "description": _("Use authenticator app or code"),
+                    "url": reverse("mfa_reauthenticate"),
                 }
-                if not allauth_app_settings.HEADLESS_ONLY:
-                    entry["url"] = reverse("mfa_reauthenticate")
                 ret.append(entry)
-            if is_mfa_enabled(user, types=[Authenticator.Type.WEBAUTHN]):
+            if "webauthn" in types:
                 entry = {
-                    "id": "mfa_reauthenticate_webauthn",
+                    "id": "mfa_reauthenticate:webauthn",
                     "description": _("Use security key or device"),
+                    "url": reverse("mfa_reauthenticate_webauthn"),
                 }
-                if not allauth_app_settings.HEADLESS_ONLY:
-                    entry["url"] = reverse("mfa_reauthenticate_webauthn")
                 ret.append(entry)
         return ret
 
diff --git a/allauth/account/forms.py b/allauth/account/forms.py
index 3ada4527..637f15a1 100644
--- a/allauth/account/forms.py
+++ b/allauth/account/forms.py
@@ -381,10 +381,8 @@ class BaseSignupForm(_base_signup_form_class()):  # type: ignore[misc]
             # Don't create a new account, only send an email informing the user
             # that (s)he already has one...
             email = self.cleaned_data["email"]
-            adapter = get_adapter()
-            adapter.send_account_already_exists_mail(email)
+            resp = flows.signup.prevent_enumeration(request, email)
             user = None
-            resp = adapter.respond_email_verification_sent(request, None)
             request.session[flows.login.LOGIN_SESSION_KEY] = EmailVerificationStage.key
         else:
             user = self.save(request)
diff --git a/allauth/account/internal/flows/reauthentication.py b/allauth/account/internal/flows/reauthentication.py
index f744463e..4c4e731d 100644
--- a/allauth/account/internal/flows/reauthentication.py
+++ b/allauth/account/internal/flows/reauthentication.py
@@ -1,13 +1,13 @@
 import time
-from typing import Optional
+from typing import Dict, List, Optional
 
 from django.contrib.auth import REDIRECT_FIELD_NAME
 from django.http import HttpRequest, HttpResponseRedirect
 from django.urls import resolve, reverse
 from django.utils.http import urlencode
 
+from allauth import app_settings as allauth_settings
 from allauth.account import app_settings
-from allauth.account.adapter import get_adapter
 from allauth.account.authentication import (
     get_authentication_records,
     record_authentication,
@@ -73,7 +73,7 @@ def raise_if_reauthentication_required(request: HttpRequest) -> None:
 def did_recently_authenticate(request: HttpRequest) -> bool:
     if request.user.is_anonymous:
         return False
-    if not get_adapter().get_reauthentication_methods(request.user):
+    if not get_reauthentication_flows(request.user):
         # TODO: This user only has social accounts attached. Now, ideally, you
         # would want to reauthenticate over at the social account provider. For
         # now, this is not implemented. Although definitely suboptimal, this
@@ -86,3 +86,25 @@ def did_recently_authenticate(request: HttpRequest) -> bool:
         return False
     authenticated_at = methods[-1]["at"]
     return time.time() - authenticated_at < app_settings.REAUTHENTICATION_TIMEOUT
+
+
+def get_reauthentication_flows(user) -> List[Dict]:
+    ret: List[Dict] = []
+    if not user.is_authenticated:
+        return ret
+    if user.has_usable_password():
+        entry = {
+            "id": "reauthenticate",
+        }
+        ret.append(entry)
+    if allauth_settings.MFA_ENABLED:
+        from allauth.mfa.models import Authenticator
+        from allauth.mfa.utils import is_mfa_enabled
+
+        types = []
+        for typ in Authenticator.Type:
+            if is_mfa_enabled(user, types=[typ]):
+                types.append(typ)
+        if types:
+            ret.append({"id": "mfa_reauthenticate", "types": types})
+    return ret
diff --git a/allauth/account/internal/flows/signup.py b/allauth/account/internal/flows/signup.py
index ceb1a179..d8d757c0 100644
--- a/allauth/account/internal/flows/signup.py
+++ b/allauth/account/internal/flows/signup.py
@@ -1,4 +1,5 @@
-from django.http import HttpRequest
+from django.contrib import messages
+from django.http import HttpRequest, HttpResponse
 from django.urls import reverse
 
 from allauth.account import app_settings
@@ -7,6 +8,19 @@ from allauth.core.internal.httpkit import get_frontend_url
 from allauth.utils import build_absolute_uri
 
 
+def prevent_enumeration(request: HttpRequest, email: str) -> HttpResponse:
+    adapter = get_adapter(request)
+    adapter.send_account_already_exists_mail(email)
+    adapter.add_message(
+        request,
+        messages.INFO,
+        "account/messages/email_confirmation_sent.txt",
+        {"email": email, "login": False, "signup": True},
+    )
+    resp = adapter.respond_email_verification_sent(request, None)
+    return resp
+
+
 def send_unknown_account_mail(request: HttpRequest, email: str) -> None:
     if not app_settings.EMAIL_UNKNOWN_ACCOUNTS:
         return None
diff --git a/allauth/headless/account/urls.py b/allauth/headless/account/urls.py
index 3bccc224..6b1dbf5f 100644
--- a/allauth/headless/account/urls.py
+++ b/allauth/headless/account/urls.py
@@ -1,93 +1,96 @@
 from django.urls import include, path
 
+from allauth import app_settings as allauth_settings
 from allauth.account import app_settings as account_settings
 from allauth.headless.account import views
 
 
 def build_urlpatterns(client):
-    return [
+    account_patterns = []
+    auth_patterns = [
         path(
-            "auth/",
-            include(
-                [
-                    path(
-                        "session",
-                        views.SessionView.as_api_view(client=client),
-                        name="current_session",
-                    ),
-                    path(
-                        "password/",
-                        include(
-                            [
-                                path(
-                                    "request",
-                                    views.RequestPasswordResetView.as_api_view(
-                                        client=client
-                                    ),
-                                    name="request_password_reset",
-                                ),
-                                path(
-                                    "reset",
-                                    views.ResetPasswordView.as_api_view(client=client),
-                                    name="reset_password",
+            "session",
+            views.SessionView.as_api_view(client=client),
+            name="current_session",
+        ),
+        path(
+            "reauthenticate",
+            views.ReauthenticateView.as_api_view(client=client),
+            name="reauthenticate",
+        ),
+    ]
+    if not allauth_settings.SOCIALACCOUNT_ONLY:
+        account_patterns.extend(
+            [
+                path(
+                    "password/change",
+                    views.ChangePasswordView.as_api_view(client=client),
+                    name="change_password",
+                ),
+                path(
+                    "email",
+                    views.ManageEmailView.as_api_view(client=client),
+                    name="manage_email",
+                ),
+            ]
+        )
+        auth_patterns.extend(
+            [
+                path(
+                    "password/",
+                    include(
+                        [
+                            path(
+                                "request",
+                                views.RequestPasswordResetView.as_api_view(
+                                    client=client
                                 ),
-                            ]
-                        ),
-                    ),
-                    path(
-                        "login",
-                        views.LoginView.as_api_view(client=client),
-                        name="login",
+                                name="request_password_reset",
+                            ),
+                            path(
+                                "reset",
+                                views.ResetPasswordView.as_api_view(client=client),
+                                name="reset_password",
+                            ),
+                        ]
                     ),
-                    path(
-                        "reauthenticate",
-                        views.ReauthenticateView.as_api_view(client=client),
-                        name="reauthenticate",
-                    ),
-                    path(
-                        "signup",
-                        views.SignupView.as_api_view(client=client),
-                        name="signup",
-                    ),
-                    path(
-                        "email/verify",
-                        views.VerifyEmailView.as_api_view(client=client),
-                        name="verify_email",
-                    ),
-                ]
-                + (
-                    [
-                        path(
-                            "code/request",
-                            views.RequestLoginCodeView.as_api_view(client=client),
-                            name="request_login_code",
-                        ),
-                        path(
-                            "code/confirm",
-                            views.ConfirmLoginCodeView.as_api_view(client=client),
-                            name="confirm_login_code",
-                        ),
-                    ]
-                    if account_settings.LOGIN_BY_CODE_ENABLED
-                    else []
-                )
-            ),
-        ),
+                ),
+                path(
+                    "login",
+                    views.LoginView.as_api_view(client=client),
+                    name="login",
+                ),
+                path(
+                    "signup",
+                    views.SignupView.as_api_view(client=client),
+                    name="signup",
+                ),
+                path(
+                    "email/verify",
+                    views.VerifyEmailView.as_api_view(client=client),
+                    name="verify_email",
+                ),
+            ]
+        )
+    if account_settings.LOGIN_BY_CODE_ENABLED:
+        auth_patterns.extend(
+            [
+                path(
+                    "code/request",
+                    views.RequestLoginCodeView.as_api_view(client=client),
+                    name="request_login_code",
+                ),
+                path(
+                    "code/confirm",
+                    views.ConfirmLoginCodeView.as_api_view(client=client),
+                    name="confirm_login_code",
+                ),
+            ]
+        )
+    return [
+        path("auth/", include(auth_patterns)),
         path(
             "account/",
-            include(
-                [
-                    path(
-                        "password/change",
-                        views.ChangePasswordView.as_api_view(client=client),
-                        name="change_password",
-                    ),
-                    path(
-                        "email",
-                        views.ManageEmailView.as_api_view(client=client),
-                        name="manage_email",
-                    ),
-                ]
-            ),
+            include(account_patterns),
         ),
     ]
diff --git a/allauth/headless/base/response.py b/allauth/headless/base/response.py
index 3ff235b8..d6f384c5 100644
--- a/allauth/headless/base/response.py
+++ b/allauth/headless/base/response.py
@@ -8,6 +8,7 @@ from allauth.account.utils import user_display, user_username
 from allauth.headless.constants import Flow
 from allauth.headless.internal import authkit, sessionkit
 from allauth.headless.internal.restkit.response import APIResponse
+from allauth.mfa import app_settings as mfa_settings
 
 
 class BaseAuthenticationResponse(APIResponse):
@@ -44,21 +45,20 @@ class BaseAuthenticationResponse(APIResponse):
         auth_status = authkit.AuthenticationStatus(request)
         ret = []
         if user and user.is_authenticated:
-            ret.extend(
-                [
-                    {"id": m["id"]}
-                    for m in get_account_adapter().get_reauthentication_methods(user)
-                ]
-            )
+            ret.extend(flows.reauthentication.get_reauthentication_flows(user))
         else:
-            ret.append({"id": Flow.LOGIN})
+            if not allauth_settings.SOCIALACCOUNT_ONLY:
+                ret.append({"id": Flow.LOGIN})
             if account_settings.LOGIN_BY_CODE_ENABLED:
                 code_flow = {"id": Flow.LOGIN_BY_CODE}
                 _, data = flows.login_by_code.get_pending_login(request, peek=True)
                 if data:
                     code_flow["is_pending"] = True
                 ret.append(code_flow)
-            if get_account_adapter().is_open_for_signup(request):
+            if (
+                get_account_adapter().is_open_for_signup(request)
+                and not allauth_settings.SOCIALACCOUNT_ONLY
+            ):
                 ret.append({"id": Flow.SIGNUP})
             if allauth_settings.SOCIALACCOUNT_ENABLED:
                 from allauth.headless.socialaccount.response import (
@@ -66,6 +66,9 @@ class BaseAuthenticationResponse(APIResponse):
                 )
 
                 ret.extend(provider_flows(request))
+            if allauth_settings.MFA_ENABLED:
+                if mfa_settings.PASSKEY_LOGIN_ENABLED:
+                    ret.append({"id": Flow.MFA_LOGIN_WEBAUTHN})
         stage_key = None
         stage = auth_status.get_pending_stage()
         if stage:
@@ -75,9 +78,23 @@ class BaseAuthenticationResponse(APIResponse):
             if isinstance(lsk, str):
                 stage_key = lsk
         if stage_key:
-            ret.append({"id": stage_key, "is_pending": True})
+            pending_flow = {"id": stage_key, "is_pending": True}
+            if stage and stage_key == Flow.MFA_AUTHENTICATE:
+                self._enrich_mfa_flow(stage, pending_flow)
+            ret.append(pending_flow)
         return ret
 
+    def _enrich_mfa_flow(self, stage, flow: dict) -> None:
+        from allauth.mfa.adapter import get_adapter as get_mfa_adapter
+        from allauth.mfa.models import Authenticator
+
+        adapter = get_mfa_adapter()
+        types = []
+        for typ in Authenticator.Type:
+            if adapter.is_mfa_enabled(stage.login.user, types=[typ]):
+                types.append(typ)
+        flow["types"] = types
+
 
 class AuthenticationResponse(BaseAuthenticationResponse):
     def __init__(self, request):
diff --git a/allauth/headless/constants.py b/allauth/headless/constants.py
index 53baf2c9..dca9f28c 100644
--- a/allauth/headless/constants.py
+++ b/allauth/headless/constants.py
@@ -1,6 +1,5 @@
 from enum import Enum
 
-from allauth import app_settings as allauth_settings
 from allauth.account.stages import EmailVerificationStage
 
 
@@ -19,7 +18,5 @@ class Flow(str, Enum):
     PROVIDER_TOKEN = "provider_token"
     REAUTHENTICATE = "reauthenticate"
     MFA_REAUTHENTICATE = "mfa_reauthenticate"
-    if allauth_settings.MFA_ENABLED:
-        from allauth.mfa.stages import AuthenticateStage
-
-        MFA_AUTHENTICATE = AuthenticateStage.key  # type: ignore[has-type]
+    MFA_AUTHENTICATE = "mfa_authenticate"  # NOTE: Equal to `allauth.mfa.stages.AuthenticationStage.key`
+    MFA_LOGIN_WEBAUTHN = "mfa_login_webauthn"
diff --git a/allauth/headless/internal/restkit/inputs.py b/allauth/headless/internal/restkit/inputs.py
index 4badf9c6..3bf9ac08 100644
--- a/allauth/headless/internal/restkit/inputs.py
+++ b/allauth/headless/internal/restkit/inputs.py
@@ -5,6 +5,7 @@ from django.forms import (
     EmailField,
     Field,
     Form,
+    ModelChoiceField,
     ModelMultipleChoiceField,
 )
 
@@ -16,6 +17,7 @@ __all__ = [
     "EmailField",
     "BooleanField",
     "ModelMultipleChoiceField",
+    "ModelChoiceField",
 ]
 
 
diff --git a/allauth/headless/mfa/inputs.py b/allauth/headless/mfa/inputs.py
index b761b000..9d0c6747 100644
--- a/allauth/headless/mfa/inputs.py
+++ b/allauth/headless/mfa/inputs.py
@@ -1,7 +1,14 @@
 from allauth.headless.internal.restkit import inputs
 from allauth.mfa.base.forms import AuthenticateForm
+from allauth.mfa.models import Authenticator
 from allauth.mfa.recovery_codes.forms import GenerateRecoveryCodesForm
 from allauth.mfa.totp.forms import ActivateTOTPForm
+from allauth.mfa.webauthn.forms import (
+    AddWebAuthnForm,
+    AuthenticateWebAuthnForm,
+    LoginWebAuthnForm,
+    ReauthenticateWebAuthnForm,
+)
 
 
 class AuthenticateInput(AuthenticateForm, inputs.Input):
@@ -14,3 +21,44 @@ class ActivateTOTPInput(ActivateTOTPForm, inputs.Input):
 
 class GenerateRecoveryCodesInput(GenerateRecoveryCodesForm, inputs.Input):
     pass
+
+
+class AddWebAuthnInput(AddWebAuthnForm, inputs.Input):
+    pass
+
+
+class UpdateWebAuthnInput(inputs.Input):
+    id = inputs.ModelChoiceField(queryset=Authenticator.objects.none())
+    name = inputs.CharField(required=True, max_length=100)
+
+    def __init__(self, *args, **kwargs):
+        self.user = kwargs.pop("user")
+        super().__init__(*args, **kwargs)
+        self.fields["id"].queryset = Authenticator.objects.filter(
+            user=self.user, type=Authenticator.Type.WEBAUTHN
+        )
+
+
+class DeleteWebAuthnInput(inputs.Input):
+    authenticators = inputs.ModelMultipleChoiceField(
+        queryset=Authenticator.objects.none()
+    )
+
+    def __init__(self, *args, **kwargs):
+        self.user = kwargs.pop("user")
+        super().__init__(*args, **kwargs)
+        self.fields["authenticators"].queryset = Authenticator.objects.filter(
+            user=self.user, type=Authenticator.Type.WEBAUTHN
+        )
+
+
+class ReauthenticateWebAuthnInput(ReauthenticateWebAuthnForm, inputs.Input):
+    pass
+
+
+class AuthenticateWebAuthnInput(AuthenticateWebAuthnForm, inputs.Input):
+    pass
+
+
+class LoginWebAuthnInput(LoginWebAuthnForm, inputs.Input):
+    pass
diff --git a/allauth/headless/mfa/response.py b/allauth/headless/mfa/response.py
index 830fe0a9..ab99adec 100644
--- a/allauth/headless/mfa/response.py
+++ b/allauth/headless/mfa/response.py
@@ -1,9 +1,14 @@
 from allauth.headless.base.response import APIResponse
-from allauth.mfa import app_settings
+from allauth.mfa import app_settings as mfa_settings
 
 
 def get_config_data(request):
-    data = {"mfa": {"supported_types": app_settings.SUPPORTED_TYPES}}
+    data = {
+        "mfa": {
+            "supported_types": mfa_settings.SUPPORTED_TYPES,
+            "passkey_login_enabled": mfa_settings.PASSKEY_LOGIN_ENABLED,
+        }
+    }
     return data
 
 
@@ -30,6 +35,13 @@ def _authenticator_data(authenticator, sensitive=False):
         )
         if sensitive:
             data["unused_codes"] = unused_codes
+    elif authenticator.type == authenticator.Type.WEBAUTHN:
+        wrapped = authenticator.wrap()
+        data["id"] = authenticator.pk
+        data["name"] = wrapped.name
+        passwordless = wrapped.is_passwordless
+        if passwordless is not None:
+            data["is_passwordless"] = passwordless
     return data
 
 
@@ -37,6 +49,10 @@ class AuthenticatorDeletedResponse(APIResponse):
     pass
 
 
+class AuthenticatorsDeletedResponse(APIResponse):
+    pass
+
+
 class TOTPNotFoundResponse(APIResponse):
     def __init__(self, request, secret, totp_url):
         super().__init__(
@@ -61,6 +77,12 @@ class AuthenticatorsResponse(APIResponse):
         super().__init__(request, data=data)
 
 
+class AuthenticatorResponse(APIResponse):
+    def __init__(self, request, authenticator, meta=None):
+        data = _authenticator_data(authenticator)
+        super().__init__(request, data=data, meta=meta)
+
+
 class RecoveryCodesNotFoundResponse(APIResponse):
     def __init__(self, request):
         super().__init__(request, status=404)
@@ -70,3 +92,13 @@ class RecoveryCodesResponse(APIResponse):
     def __init__(self, request, authenticator):
         data = _authenticator_data(authenticator, sensitive=True)
         super().__init__(request, data=data)
+
+
+class AddWebAuthnResponse(APIResponse):
+    def __init__(self, request, registration_data):
+        super().__init__(request, data={"creation_options": registration_data})
+
+
+class WebAuthnRequestOptionsResponse(APIResponse):
+    def __init__(self, request, request_options):
+        super().__init__(request, data={"request_options": request_options})
diff --git a/allauth/headless/mfa/urls.py b/allauth/headless/mfa/urls.py
index a7d3a8ef..4d29ed27 100644
--- a/allauth/headless/mfa/urls.py
+++ b/allauth/headless/mfa/urls.py
@@ -1,26 +1,77 @@
 from django.urls import include, path
 
 from allauth.headless.mfa import views
+from allauth.mfa import app_settings as mfa_settings
 
 
 def build_urlpatterns(client):
+    auth_patterns = [
+        path(
+            "2fa/authenticate",
+            views.AuthenticateView.as_api_view(client=client),
+            name="authenticate",
+        ),
+        path(
+            "2fa/reauthenticate",
+            views.ReauthenticateView.as_api_view(client=client),
+            name="reauthenticate",
+        ),
+    ]
+
+    authenticators = []
+    if "totp" in mfa_settings.SUPPORTED_TYPES:
+        authenticators.append(
+            path(
+                "totp",
+                views.ManageTOTPView.as_api_view(client=client),
+                name="manage_totp",
+            )
+        )
+    if "recovery_codes" in mfa_settings.SUPPORTED_TYPES:
+        authenticators.append(
+            path(
+                "recovery-codes",
+                views.ManageRecoveryCodesView.as_api_view(client=client),
+                name="manage_recovery_codes",
+            )
+        )
+    if "webauthn" in mfa_settings.SUPPORTED_TYPES:
+        authenticators.extend(
+            [
+                path(
+                    "webauthn",
+                    views.ManageWebAuthnView.as_api_view(client=client),
+                    name="manage_webauthn",
+                ),
+            ]
+        )
+        auth_patterns.extend(
+            [
+                path(
+                    "webauthn/authenticate",
+                    views.AuthenticateWebAuthnView.as_api_view(client=client),
+                    name="authenticate_webauthn",
+                ),
+                path(
+                    "webauthn/reauthenticate",
+                    views.ReauthenticateWebAuthnView.as_api_view(client=client),
+                    name="reauthenticate_webauthn",
+                ),
+            ]
+        )
+        if mfa_settings.PASSKEY_LOGIN_ENABLED:
+            auth_patterns.append(
+                path(
+                    "webauthn/login",
+                    views.LoginWebAuthnView.as_api_view(client=client),
+                    name="login_webauthn",
+                )
+            )
+
     return [
         path(
             "auth/",
-            include(
-                [
-                    path(
-                        "2fa/authenticate",
-                        views.AuthenticateView.as_api_view(client=client),
-                        name="authenticate",
-                    ),
-                    path(
-                        "2fa/reauthenticate",
-                        views.ReauthenticateView.as_api_view(client=client),
-                        name="reauthenticate",
-                    ),
-                ]
-            ),
+            include(auth_patterns),
         ),
         path(
             "account/",
@@ -32,14 +83,8 @@ def build_urlpatterns(client):
                         name="authenticators",
                     ),
                     path(
-                        "authenticators/totp",
-                        views.ManageTOTPView.as_api_view(client=client),
-                        name="manage_totp",
-                    ),
-                    path(
-                        "authenticators/recovery-codes",
-                        views.ManageRecoveryCodesView.as_api_view(client=client),
-                        name="manage_recovery_codes",
+                        "authenticators/",
+                        include(authenticators),
                     ),
                 ]
             ),
diff --git a/allauth/headless/mfa/views.py b/allauth/headless/mfa/views.py
index e8d1e60e..c1d97872 100644
--- a/allauth/headless/mfa/views.py
+++ b/allauth/headless/mfa/views.py
@@ -1,19 +1,31 @@
-from allauth.headless.base.response import AuthenticationResponse
+from allauth.account.models import Login
+from allauth.headless.base.response import APIResponse, AuthenticationResponse
 from allauth.headless.base.views import (
+    APIView,
     AuthenticatedAPIView,
     AuthenticationStageAPIView,
 )
 from allauth.headless.mfa import response
 from allauth.headless.mfa.inputs import (
     ActivateTOTPInput,
+    AddWebAuthnInput,
     AuthenticateInput,
+    AuthenticateWebAuthnInput,
+    DeleteWebAuthnInput,
     GenerateRecoveryCodesInput,
+    LoginWebAuthnInput,
+    ReauthenticateWebAuthnInput,
+    UpdateWebAuthnInput,
 )
 from allauth.mfa.adapter import DefaultMFAAdapter, get_adapter
 from allauth.mfa.models import Authenticator
 from allauth.mfa.recovery_codes.internal import flows as recovery_codes_flows
 from allauth.mfa.stages import AuthenticateStage
 from allauth.mfa.totp.internal import auth as totp_auth, flows as totp_flows
+from allauth.mfa.webauthn.internal import (
+    auth as webauthn_auth,
+    flows as webauthn_flows,
+)
 
 
 class AuthenticateView(AuthenticationStageAPIView):
@@ -48,7 +60,7 @@ class AuthenticatorsView(AuthenticatedAPIView):
 class ManageTOTPView(AuthenticatedAPIView):
     input_class = {"POST": ActivateTOTPInput}
 
-    def get(self, request, *args, **kwargs):
+    def get(self, request, *args, **kwargs) -> APIResponse:
         authenticator = self._get_authenticator()
         if not authenticator:
             adapter: DefaultMFAAdapter = get_adapter()
@@ -91,3 +103,99 @@ class ManageRecoveryCodesView(AuthenticatedAPIView):
 
     def get_input_kwargs(self):
         return {"user": self.request.user}
+
+
+class ManageWebAuthnView(AuthenticatedAPIView):
+    input_class = {
+        "POST": AddWebAuthnInput,
+        "PUT": UpdateWebAuthnInput,
+        "DELETE": DeleteWebAuthnInput,
+    }
+
+    def get(self, request, *args, **kwargs):
+        passwordless = "passwordless" in request.GET
+        creation_options = webauthn_flows.begin_registration(
+            request, request.user, passwordless
+        )
+        return response.AddWebAuthnResponse(request, creation_options)
+
+    def get_input_kwargs(self):
+        return {"user": self.request.user}
+
+    def post(self, request, *args, **kwargs):
+        auth, rc_auth = webauthn_flows.add_authenticator(
+            request,
+            name=self.input.cleaned_data["name"],
+            credential=self.input.cleaned_data["credential"],
+        )
+        did_generate_recovery_codes = bool(rc_auth)
+        return response.AuthenticatorResponse(
+            request,
+            auth,
+            meta={"recovery_codes_generated": did_generate_recovery_codes},
+        )
+
+    def put(self, request, *args, **kwargs):
+        authenticator = self.input.cleaned_data["id"]
+        webauthn_flows.rename_authenticator(
+            request, authenticator, self.input.cleaned_data["name"]
+        )
+        return response.AuthenticatorResponse(request, authenticator)
+
+    def delete(self, request, *args, **kwargs):
+        authenticators = self.input.cleaned_data["authenticators"]
+        webauthn_flows.remove_authenticators(request, authenticators)
+        return response.AuthenticatorsDeletedResponse(request)
+
+
+class ReauthenticateWebAuthnView(AuthenticatedAPIView):
+    input_class = {
+        "POST": ReauthenticateWebAuthnInput,
+    }
+
+    def get(self, request, *args, **kwargs):
+        request_options = webauthn_auth.begin_authentication(request.user)
+        return response.WebAuthnRequestOptionsResponse(request, request_options)
+
+    def get_input_kwargs(self):
+        return {"user": self.request.user}
+
+    def post(self, request, *args, **kwargs):
+        authenticator = self.input.cleaned_data["credential"]
+        webauthn_flows.reauthenticate(request, authenticator)
+        return AuthenticationResponse(self.request)
+
+
+class AuthenticateWebAuthnView(AuthenticationStageAPIView):
+    input_class = {
+        "POST": AuthenticateWebAuthnInput,
+    }
+    stage_class = AuthenticateStage
+
+    def get(self, request, *args, **kwargs):
+        request_options = webauthn_auth.begin_authentication(self.stage.login.user)
+        return response.WebAuthnRequestOptionsResponse(request, request_options)
+
+    def get_input_kwargs(self):
+        return {"user": self.stage.login.user}
+
+    def post(self, request, *args, **kwargs):
+        self.input.save()
+        return self.respond_next_stage()
+
+
+class LoginWebAuthnView(APIView):
+    input_class = {
+        "POST": LoginWebAuthnInput,
+    }
+
+    def get(self, request, *args, **kwargs):
+        request_options = webauthn_auth.begin_authentication()
+        return response.WebAuthnRequestOptionsResponse(request, request_options)
+
+    def post(self, request, *args, **kwargs):
+        authenticator = self.input.cleaned_data["credential"]
+        redirect_url = None
+        login = Login(user=authenticator.user, redirect_url=redirect_url)
+        webauthn_flows.perform_passwordless_login(request, authenticator, login)
+        return AuthenticationResponse(request)
diff --git a/allauth/locale/ar/LC_MESSAGES/django.po b/allauth/locale/ar/LC_MESSAGES/django.po
index 9598cb91..f40ab633 100644
--- a/allauth/locale/ar/LC_MESSAGES/django.po
+++ b/allauth/locale/ar/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: 0.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-06-30 16:36+0200\n"
 "Last-Translator: Ali Amin <aliamin01009007110@gmail.com>\n"
 "Language-Team: Arabic <https://hosted.weblate.org/projects/allauth/django-"
@@ -92,19 +92,19 @@ msgstr "اسم المستخدم غير مسموح به. الرجاء اختيا
 msgid "The username and/or password you specified are not correct."
 msgstr "اسم المستخدم و / أو كلمة المرور غير صحيحة."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "هل نسيت كلمة المرور؟"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "Use your authenticator app"
 msgid "Use authenticator app or code"
 msgstr "استخدم تطبيق المصادقة الخاص بك"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -302,13 +302,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr "كود المصدق"
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "كلمة المرور"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
@@ -545,7 +545,8 @@ msgstr "أدخل رمز تسجيل الدخول"
 msgid ""
 "We’ve sent a code to %(email_link)s. The code expires shortly, so please "
 "enter it soon."
-msgstr "لقد أرسلنا رمزًا إلى %(email_link)s. الرمز ينتهي قريبًا، لذا يرجى إدخاله قريبًا."
+msgstr ""
+"لقد أرسلنا رمزًا إلى %(email_link)s. الرمز ينتهي قريبًا، لذا يرجى إدخاله قريبًا."
 
 #: templates/account/confirm_login_code.html:34
 #: templates/account/request_login_code.html:31
@@ -661,7 +662,8 @@ msgid ""
 "- Browser: %(user_agent)s\n"
 "- Date: %(timestamp)s"
 msgstr ""
-"0 إذا لم تتعرف على هذا التغيير، فيرجى اتخاذ الاحتياطات الأمنية المناسبة على الفور. ينشأ التغيير في حسابك من:.\n"
+"0 إذا لم تتعرف على هذا التغيير، فيرجى اتخاذ الاحتياطات الأمنية المناسبة على "
+"الفور. ينشأ التغيير في حسابك من:.\n"
 "- عنوان IP: %(ip)s.\n"
 "- المتصفح: %(user_agent)s.\n"
 "- التاريخ: %(timestamp)s"
diff --git a/allauth/locale/az/LC_MESSAGES/django.po b/allauth/locale/az/LC_MESSAGES/django.po
index 831602fc..9fbcec4f 100644
--- a/allauth/locale/az/LC_MESSAGES/django.po
+++ b/allauth/locale/az/LC_MESSAGES/django.po
@@ -9,7 +9,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: django-allauth\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 22:02+0200\n"
 "Last-Translator: Jeyhun Piriyev <jeihunpiriyev@gmail.com>\n"
 "Language-Team: Azerbaijani (Azerbaijan)\n"
@@ -91,17 +91,17 @@ msgstr ""
 msgid "The username and/or password you specified are not correct."
 msgstr "Göstərdiyiniz istifadəçi adı və/yaxud şifrə düzgün deyil."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 msgid "Use your password"
 msgstr "Şifrənizi istifadə edin"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "Use your authenticator app"
 msgid "Use authenticator app or code"
 msgstr "Autentifikator tətbiqindən istifadə edin"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -311,13 +311,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr "Doğrulama kodu"
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Şifrə"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/bg/LC_MESSAGES/django.po b/allauth/locale/bg/LC_MESSAGES/django.po
index c3b02985..916a4aaf 100644
--- a/allauth/locale/bg/LC_MESSAGES/django.po
+++ b/allauth/locale/bg/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 22:14+0200\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -91,19 +91,19 @@ msgstr ""
 msgid "The username and/or password you specified are not correct."
 msgstr "Потребителското име и/или паролата, които въведохте, са грешни."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "Забравена парола?"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "token secret"
 msgid "Use authenticator app or code"
 msgstr "таен код"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -299,13 +299,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Парола"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/ca/LC_MESSAGES/django.po b/allauth/locale/ca/LC_MESSAGES/django.po
index 5dc9cc54..2a5b6f17 100644
--- a/allauth/locale/ca/LC_MESSAGES/django.po
+++ b/allauth/locale/ca/LC_MESSAGES/django.po
@@ -9,7 +9,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: django-allauth\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-06-01 11:09+0000\n"
 "Last-Translator: Ajordat <alexjortri@gmail.com>\n"
 "Language-Team: Catalan <https://hosted.weblate.org/projects/allauth/django-"
@@ -90,17 +90,17 @@ msgstr ""
 msgid "The username and/or password you specified are not correct."
 msgstr "L'usuari i/o la contrasenya que heu especificat no són correctes."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 msgid "Use your password"
 msgstr "Utilitzeu la vostra paraula de pas"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "Use your authenticator app"
 msgid "Use authenticator app or code"
 msgstr "Utilitzeu la vostra aplicació d’autenticar"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -298,13 +298,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr "Codi de l’autenticador"
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Contrasenya"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/cs/LC_MESSAGES/django.po b/allauth/locale/cs/LC_MESSAGES/django.po
index 74cfc2c4..b12f5ff7 100644
--- a/allauth/locale/cs/LC_MESSAGES/django.po
+++ b/allauth/locale/cs/LC_MESSAGES/django.po
@@ -9,7 +9,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: 0.55\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 22:03+0200\n"
 "Last-Translator: Filip Dobrovolny <dobrovolny.filip@gmail.com>\n"
 "Language-Team: Czech <>\n"
@@ -91,19 +91,19 @@ msgstr "Toto uživatelské jméno nemůže být zvoleno. Prosím, zvolte si jin
 msgid "The username and/or password you specified are not correct."
 msgstr "Zadané uživatelské jméno nebo heslo není správné."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "Zapomenuté heslo?"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "Authenticator App"
 msgid "Use authenticator app or code"
 msgstr "Autentifikátor"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -312,13 +312,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr "Kód autentifikátoru"
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Heslo"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/da/LC_MESSAGES/django.po b/allauth/locale/da/LC_MESSAGES/django.po
index fb0281cc..e59548bf 100644
--- a/allauth/locale/da/LC_MESSAGES/django.po
+++ b/allauth/locale/da/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 21:40+0200\n"
 "Last-Translator: b'Tuk Bredsdorff <tukodeb@gmail.com>'\n"
 "Language-Team: \n"
@@ -93,19 +93,19 @@ msgstr "Brugernavn kan ikke bruges. Brug venligst et andet brugernavn."
 msgid "The username and/or password you specified are not correct."
 msgstr "Det angivne brugernavn og/eller adgangskoden er ikke korrekt."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "Glemt password?"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "token secret"
 msgid "Use authenticator app or code"
 msgstr "token hemmelighed"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -301,13 +301,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Adgangskode"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/de/LC_MESSAGES/django.po b/allauth/locale/de/LC_MESSAGES/django.po
index 7faf1120..8cceb9fc 100644
--- a/allauth/locale/de/LC_MESSAGES/django.po
+++ b/allauth/locale/de/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: django-allauth\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-06-08 10:54+0000\n"
 "Last-Translator: hentoe <hendriktoedter@gmx.de>\n"
 "Language-Team: German <https://hosted.weblate.org/projects/allauth/django-"
@@ -88,17 +88,17 @@ msgstr ""
 msgid "The username and/or password you specified are not correct."
 msgstr "Der Anmeldename und/oder das Passwort sind leider falsch."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 msgid "Use your password"
 msgstr "Verwenden Sie Ihr Passwort"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "Use your authenticator app"
 msgid "Use authenticator app or code"
 msgstr "Verwenden Sie Ihre Authenticator-App"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -296,13 +296,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr "Authentifizierungscode"
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Passwort"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/el/LC_MESSAGES/django.po b/allauth/locale/el/LC_MESSAGES/django.po
index 34d07b78..97c15304 100644
--- a/allauth/locale/el/LC_MESSAGES/django.po
+++ b/allauth/locale/el/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 21:38+0200\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -91,19 +91,19 @@ msgstr "Δεν μπορεί να χρησιμοποιηθεί αυτό το όν
 msgid "The username and/or password you specified are not correct."
 msgstr "Το όνομα χρήστη ή/και το συνθηματικό που δόθηκαν δεν είναι σωστά."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "Ξέχασα το συνθηματικό μου"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "token secret"
 msgid "Use authenticator app or code"
 msgstr "token secret"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -305,13 +305,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Συνθηματικό"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/en/LC_MESSAGES/django.po b/allauth/locale/en/LC_MESSAGES/django.po
index 2939c1ce..5097ca3b 100644
--- a/allauth/locale/en/LC_MESSAGES/django.po
+++ b/allauth/locale/en/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2023-07-24 22:28+0200\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -84,15 +84,15 @@ msgstr ""
 msgid "The username and/or password you specified are not correct."
 msgstr ""
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 msgid "Use your password"
 msgstr ""
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 msgid "Use authenticator app or code"
 msgstr ""
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -282,11 +282,11 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 msgid "Passwordless"
 msgstr ""
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/es/LC_MESSAGES/django.po b/allauth/locale/es/LC_MESSAGES/django.po
index d7d75aa2..a98d187e 100644
--- a/allauth/locale/es/LC_MESSAGES/django.po
+++ b/allauth/locale/es/LC_MESSAGES/django.po
@@ -6,9 +6,9 @@ msgid ""
 msgstr ""
 "Project-Id-Version: 0.61.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
-"PO-Revision-Date: 2024-06-01 11:09+0000\n"
-"Last-Translator: Ajordat <alexjortri@gmail.com>\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
+"PO-Revision-Date: 2024-07-01 15:09+0000\n"
+"Last-Translator: gallegonovato <fran-carro@hotmail.es>\n"
 "Language-Team: Spanish <https://hosted.weblate.org/projects/allauth/django-"
 "allauth/es/>\n"
 "Language: es\n"
@@ -16,7 +16,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
-"X-Generator: Weblate 5.6-dev\n"
+"X-Generator: Weblate 5.7-dev\n"
 
 #: account/adapter.py:58
 msgid "This account is currently inactive."
@@ -88,20 +88,18 @@ msgstr "No se puede usar este nombre de usuario. Por favor, utilice otro."
 msgid "The username and/or password you specified are not correct."
 msgstr "El usuario y/o la contraseña que ha indicado no son correctos."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 msgid "Use your password"
 msgstr "Utilice su contraseña"
 
-#: account/adapter.py:750
-#, fuzzy
-#| msgid "Use your authenticator app"
+#: account/adapter.py:748
 msgid "Use authenticator app or code"
-msgstr "Utilice su aplicación de autenticación"
+msgstr "Utiliza la aplicación o el código de autenticación"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
-msgstr ""
+msgstr "Usar clave o dispositivo de seguridad"
 
 #: account/admin.py:23
 msgid "Mark selected email addresses as verified"
@@ -263,19 +261,17 @@ msgstr ""
 "su dirección de correo electrónico."
 
 #: mfa/adapter.py:141
-#, fuzzy
-#| msgid "secret key"
 msgid "Master key"
-msgstr "clave secreta"
+msgstr "Llave maestra"
 
 #: mfa/adapter.py:143
 msgid "Backup key"
-msgstr ""
+msgstr "Clave de respaldo"
 
 #: mfa/adapter.py:144
 #, python-brace-format
 msgid "Key nr. {number}"
-msgstr ""
+msgstr "Clave n° {number}"
 
 #: mfa/apps.py:9
 msgid "MFA"
@@ -291,24 +287,25 @@ msgstr "Verificador TOTP"
 
 #: mfa/models.py:26
 msgid "WebAuthn"
-msgstr ""
+msgstr "WebAuthn"
 
 #: mfa/totp/forms.py:11
 msgid "Authenticator code"
 msgstr "Código de del autenticador"
 
-#: mfa/webauthn/forms.py:15
-#, fuzzy
-#| msgid "Password"
+#: mfa/webauthn/forms.py:16
 msgid "Passwordless"
-msgstr "Contraseña"
+msgstr "Passwordless"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
 "protection."
 msgstr ""
+"Habilitar la operación sin contraseña le permite iniciar sesión usando solo "
+"esta clave/dispositivo, pero impone requisitos adicionales como biometría o "
+"protección con PIN."
 
 #: socialaccount/adapter.py:36
 #, python-format
@@ -875,7 +872,7 @@ msgstr ""
 
 #: templates/account/login.html:42
 msgid "Sign in with a passkey"
-msgstr ""
+msgstr "Iniciar sesión con un passkey"
 
 #: templates/account/login.html:47 templates/account/request_login_code.html:9
 msgid "Mail me a sign-in code"
@@ -1177,24 +1174,20 @@ msgid "Authenticator App Deactivated"
 msgstr "Aplicación de autenticación desactivada"
 
 #: templates/mfa/email/webauthn_added_message.txt:4
-#, fuzzy
-#| msgid "A new set of recovery codes has been generated."
 msgid "A new security key has been added."
-msgstr "Se ha generado un nuevo conjunto de código de recuperación."
+msgstr "Se ha agregado una nueva clave de seguridad."
 
 #: templates/mfa/email/webauthn_added_subject.txt:3
 msgid "Security Key Added"
-msgstr ""
+msgstr "Clave de seguridad añadida"
 
 #: templates/mfa/email/webauthn_removed_message.txt:4
-#, fuzzy
-#| msgid "Your email has been confirmed."
 msgid "A security key has been removed."
-msgstr "Su correo electrónico ha sido verificado."
+msgstr "Se ha eliminado una clave de seguridad."
 
 #: templates/mfa/email/webauthn_removed_subject.txt:3
 msgid "Security Key Removed"
-msgstr ""
+msgstr "Clave de seguridad eliminada"
 
 #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4
 msgid "Authenticator App"
@@ -1218,27 +1211,27 @@ msgstr "Activar"
 
 #: templates/mfa/index.html:45 templates/mfa/webauthn/base.html:4
 msgid "Security Keys"
-msgstr ""
+msgstr "Claves de seguridad"
 
 #: templates/mfa/index.html:50
 #, python-format
 msgid "You have added %(count)s security key/device."
 msgid_plural "You have added %(count)s security keys/devices."
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "Has añadido %(count)s clave/dispositivo de seguridad."
+msgstr[1] "Has añadido %(count)s claves/dispositivos de seguridad."
 
 #: templates/mfa/index.html:53
 msgid "No security keys have been added."
-msgstr ""
+msgstr "No se han agregado claves de seguridad."
 
 #: templates/mfa/index.html:60
 msgid "Manage"
-msgstr ""
+msgstr "Administrar"
 
 #: templates/mfa/index.html:65
 #: templates/mfa/webauthn/authenticator_list.html:68
 msgid "Add"
-msgstr ""
+msgstr "Añadir"
 
 #: templates/mfa/index.html:75 templates/mfa/recovery_codes/base.html:4
 #: templates/mfa/recovery_codes/generate.html:6
@@ -1279,11 +1272,11 @@ msgstr "Se ha generado un nuevo conjunto de código de recuperación."
 
 #: templates/mfa/messages/webauthn_added.txt:2
 msgid "Security key added."
-msgstr ""
+msgstr "Clave de seguridad añadida."
 
 #: templates/mfa/messages/webauthn_removed.txt:2
 msgid "Security key removed."
-msgstr ""
+msgstr "Clave de seguridad eliminada."
 
 #: templates/mfa/reauthenticate.html:6
 msgid "Enter an authenticator code:"
@@ -1357,79 +1350,73 @@ msgstr ""
 
 #: templates/mfa/webauthn/add_form.html:7
 msgid "Add Security Key or Device"
-msgstr ""
+msgstr "Agregar clave de seguridad o dispositivo"
 
 #: templates/mfa/webauthn/add_form.html:18
 msgid "Add key or device"
-msgstr ""
+msgstr "Agregar clave o dispositivo"
 
 #: templates/mfa/webauthn/authenticator_confirm_delete.html:6
 msgid "Remove Security Key or Device"
-msgstr ""
+msgstr "Eliminar clave de seguridad o dispositivo"
 
 #: templates/mfa/webauthn/authenticator_confirm_delete.html:8
-#, fuzzy
-#| msgid "Are you sure you want to sign out?"
 msgid "Are you sure you want to remove this security key?"
-msgstr "¿Seguro que desea cerrar sesión?"
+msgstr "¿Estás seguro de que deseas eliminar esta clave de seguridad?"
 
 #: templates/mfa/webauthn/authenticator_list.html:8
 msgid "Security Keys and Devices"
-msgstr ""
+msgstr "Dispositivos y claves de seguridad"
 
 #: templates/mfa/webauthn/authenticator_list.html:11
 msgid "You have not added any security keys/devices."
-msgstr ""
+msgstr "No ha agregado ninguna clave/dispositivo de seguridad."
 
 #: templates/mfa/webauthn/authenticator_list.html:19
 msgid "Usage"
-msgstr ""
+msgstr "Uso"
 
 #: templates/mfa/webauthn/authenticator_list.html:31
 msgid "Passkey"
-msgstr ""
+msgstr "Passkey"
 
 #: templates/mfa/webauthn/authenticator_list.html:35
-#, fuzzy
-#| msgid "secret key"
 msgid "Security key"
-msgstr "clave secreta"
+msgstr "Clave de seguridad"
 
 #: templates/mfa/webauthn/authenticator_list.html:38
 msgid "This key does not specify whether is a passkey."
-msgstr ""
+msgstr "Esta clave no especifica si es un passkey."
 
 #: templates/mfa/webauthn/authenticator_list.html:39
-#, fuzzy
-#| msgid "Unverified"
 msgid "Unspecified"
-msgstr "Sin verificar"
+msgstr "Sin especificar"
 
 #: templates/mfa/webauthn/authenticator_list.html:44
 #, python-format
 msgid "Added on %(created_at)s"
-msgstr ""
+msgstr "Añadida el %(created_at)s"
 
 #: templates/mfa/webauthn/authenticator_list.html:46
 #, python-format
 msgid "Last used %(last_used)s"
-msgstr ""
+msgstr "Usada por última vez %(last_used)s"
 
 #: templates/mfa/webauthn/authenticator_list.html:54
 msgid "Edit"
-msgstr ""
+msgstr "Editar"
 
 #: templates/mfa/webauthn/edit_form.html:7
 msgid "Edit Security Key or Device"
-msgstr ""
+msgstr "Editar clave o dispositivo de seguridad"
 
 #: templates/mfa/webauthn/edit_form.html:18
 msgid "Save"
-msgstr ""
+msgstr "Guardar"
 
 #: templates/mfa/webauthn/snippets/scripts.html:2
 msgid "This functionality requires JavaScript."
-msgstr ""
+msgstr "Esta funcionalidad requiere JavaScript."
 
 #: templates/socialaccount/authentication_error.html:5
 #: templates/socialaccount/authentication_error.html:9
diff --git a/allauth/locale/et/LC_MESSAGES/django.po b/allauth/locale/et/LC_MESSAGES/django.po
index 765affe9..c23447c8 100644
--- a/allauth/locale/et/LC_MESSAGES/django.po
+++ b/allauth/locale/et/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 21:55+0200\n"
 "Last-Translator: Gregor Grigorjan <gregor@grigorjan.net>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -88,17 +88,17 @@ msgstr "Kasutajanime pole lubatud kasutada. Palun kasuta muud kasutajanime."
 msgid "The username and/or password you specified are not correct."
 msgstr "Sisestatud kasutajanimi või salasõna ei ole õige."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 msgid "Use your password"
 msgstr "Kasuta oma salasõna"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "Use your authenticator app"
 msgid "Use authenticator app or code"
 msgstr "Kasuta oma autentimisrakendust"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -294,13 +294,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr "Autentimiskood"
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Salasõna"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/eu/LC_MESSAGES/django.po b/allauth/locale/eu/LC_MESSAGES/django.po
index 749c4914..4df55eee 100644
--- a/allauth/locale/eu/LC_MESSAGES/django.po
+++ b/allauth/locale/eu/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: django-allauth\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 22:11+0200\n"
 "Last-Translator: Eneko Illarramendi <eneko@illarra.com>\n"
 "Language-Team: Basque <translate@tropela.eus>\n"
@@ -92,19 +92,19 @@ msgstr ""
 msgid "The username and/or password you specified are not correct."
 msgstr "Sartutako erabiltzailea eta/edo pasahitza ez dira zuzenak."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "Pasahitza ahaztu duzu?"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "token secret"
 msgid "Use authenticator app or code"
 msgstr "\"token secret\"-a"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -300,13 +300,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Pasahitza"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/fa/LC_MESSAGES/django.po b/allauth/locale/fa/LC_MESSAGES/django.po
index 64d98473..5358627b 100644
--- a/allauth/locale/fa/LC_MESSAGES/django.po
+++ b/allauth/locale/fa/LC_MESSAGES/django.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-05-18 20:01+0000\n"
 "Last-Translator: Samad Valipour <samadvalipour76@gmail.com>\n"
 "Language-Team: Persian <https://hosted.weblate.org/projects/allauth/django-"
@@ -88,19 +88,19 @@ msgstr "نام‌کاربری قابل استفاده نیست. لطفا نام
 msgid "The username and/or password you specified are not correct."
 msgstr "نام‌کاربری یا گذرواژه نادرست است."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "گذرواژه‌ات را فراموش کرده‌ای؟"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "token secret"
 msgid "Use authenticator app or code"
 msgstr "رمز توکن"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -303,13 +303,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "گذرواژه"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/fi/LC_MESSAGES/django.po b/allauth/locale/fi/LC_MESSAGES/django.po
index 30e81221..0eae2faa 100644
--- a/allauth/locale/fi/LC_MESSAGES/django.po
+++ b/allauth/locale/fi/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 21:30+0200\n"
 "Last-Translator: Anonymous User <seppo.erviala@iki.fi>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -93,17 +93,17 @@ msgstr "Käyttäjänimeä ei voi käyttää. Valitse toinen käyttäjänimi."
 msgid "The username and/or password you specified are not correct."
 msgstr "Annettu käyttäjänimi tai salasana ei ole oikein."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "Salasana unohtunut?"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 msgid "Use authenticator app or code"
 msgstr ""
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -305,13 +305,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Salasana"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/fr/LC_MESSAGES/django.po b/allauth/locale/fr/LC_MESSAGES/django.po
index a553fc56..9b6646f9 100644
--- a/allauth/locale/fr/LC_MESSAGES/django.po
+++ b/allauth/locale/fr/LC_MESSAGES/django.po
@@ -10,7 +10,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: django-allauth\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 21:42+0200\n"
 "Last-Translator: Gilou <contact+dev@gilouweb.com>\n"
 "Language-Team: français <>\n"
@@ -93,17 +93,17 @@ msgstr "Ce pseudonyme ne peut pas être utilisé. Veuillez en choisir un autre."
 msgid "The username and/or password you specified are not correct."
 msgstr "Le pseudo et/ou le mot de passe sont incorrects."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 msgid "Use your password"
 msgstr "Utilisez votre mot de passe"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "Use your authenticator app"
 msgid "Use authenticator app or code"
 msgstr "Utilisez votre application d'authentification"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -301,13 +301,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr "Code de l'authentificateur"
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Mot de passe"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/he/LC_MESSAGES/django.po b/allauth/locale/he/LC_MESSAGES/django.po
index de2df986..f1780ad1 100644
--- a/allauth/locale/he/LC_MESSAGES/django.po
+++ b/allauth/locale/he/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: 0.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-06-15 10:09+0000\n"
 "Last-Translator: Eyal Cherevatsky <5116133+eyalch@users.noreply.github.com>\n"
 "Language-Team: Hebrew <https://hosted.weblate.org/projects/allauth/django-"
@@ -85,17 +85,17 @@ msgstr "לא ניתן להשתמש בשם משתמש זה. אנא בחר שם מ
 msgid "The username and/or password you specified are not correct."
 msgstr "שם המשתמש ו/או הסיסמה אינם נכונים."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 msgid "Use your password"
 msgstr "להשתמש בסיסמה"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "Use your authenticator app"
 msgid "Use authenticator app or code"
 msgstr "להשתמש ביישומון אימות"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -287,13 +287,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr "קוד אימות"
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "סיסמה"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/hr/LC_MESSAGES/django.po b/allauth/locale/hr/LC_MESSAGES/django.po
index faa1d516..a5043482 100644
--- a/allauth/locale/hr/LC_MESSAGES/django.po
+++ b/allauth/locale/hr/LC_MESSAGES/django.po
@@ -10,7 +10,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 21:48+0200\n"
 "Last-Translator:   <goran.cetusic@gmail.com>\n"
 "Language-Team: Bojan Mihelac <bmihelac@mihelac.org>\n"
@@ -96,17 +96,17 @@ msgstr "Nije moguće koristiti upisano korisničko ime. Molimo odaberite drugo."
 msgid "The username and/or password you specified are not correct."
 msgstr "Korisničko ime i/ili lozinka nisu ispravni."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "Zaboravili ste lozinku?"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 msgid "Use authenticator app or code"
 msgstr ""
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -306,13 +306,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Lozinka"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/hu/LC_MESSAGES/django.po b/allauth/locale/hu/LC_MESSAGES/django.po
index eecc89ba..deff5327 100644
--- a/allauth/locale/hu/LC_MESSAGES/django.po
+++ b/allauth/locale/hu/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 21:45+0200\n"
 "Last-Translator: Tamás Makó <tom@greenplug.hu>\n"
 "Language-Team: \n"
@@ -91,17 +91,17 @@ msgstr "Ez a felhasználói azonosító nem használható. Kérlek válassz más
 msgid "The username and/or password you specified are not correct."
 msgstr "A megadott felhasználó vagy a jelszó hibás."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "Elfelejtett jelszó?"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 msgid "Use authenticator app or code"
 msgstr ""
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -301,13 +301,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Jelszó"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/id/LC_MESSAGES/django.po b/allauth/locale/id/LC_MESSAGES/django.po
index a411edf6..dafb526c 100644
--- a/allauth/locale/id/LC_MESSAGES/django.po
+++ b/allauth/locale/id/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 21:35+0200\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -91,19 +91,19 @@ msgstr ""
 msgid "The username and/or password you specified are not correct."
 msgstr "Nama pengguna dan/atau kata sandi yang anda masukkan tidak benar."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "Lupa Kata Sandi?"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "token secret"
 msgid "Use authenticator app or code"
 msgstr "rahasia token"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -299,13 +299,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Kata sandi"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/it/LC_MESSAGES/django.po b/allauth/locale/it/LC_MESSAGES/django.po
index b3adae04..c496f27d 100644
--- a/allauth/locale/it/LC_MESSAGES/django.po
+++ b/allauth/locale/it/LC_MESSAGES/django.po
@@ -10,7 +10,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: django-allauth\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 21:29+0200\n"
 "Last-Translator: Sandro <sandro@e-den.it>\n"
 "Language: it\n"
@@ -90,17 +90,17 @@ msgstr "Questo username non può essere usato. Per favore scegline un altro."
 msgid "The username and/or password you specified are not correct."
 msgstr "Lo username e/o la password che hai usato non sono corretti."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 msgid "Use your password"
 msgstr "Usa la tua password"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "Use your authenticator app"
 msgid "Use authenticator app or code"
 msgstr "Usa la tua app di autenticazione"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -298,13 +298,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr "Codice autenticatore"
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Password"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/ja/LC_MESSAGES/django.po b/allauth/locale/ja/LC_MESSAGES/django.po
index 4724e4d0..7b59718a 100644
--- a/allauth/locale/ja/LC_MESSAGES/django.po
+++ b/allauth/locale/ja/LC_MESSAGES/django.po
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
-"PO-Revision-Date: 2024-06-20 03:09+0000\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
+"PO-Revision-Date: 2024-07-01 15:09+0000\n"
 "Last-Translator: SATOH Fumiyasu <fumiyas@osstech.co.jp>\n"
 "Language-Team: Japanese <https://hosted.weblate.org/projects/allauth/django-"
 "allauth/ja/>\n"
@@ -17,17 +17,15 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 5.6-rc\n"
+"X-Generator: Weblate 5.7-dev\n"
 
 #: account/adapter.py:58
 msgid "This account is currently inactive."
 msgstr "このアカウントは現在無効です。"
 
 #: account/adapter.py:60
-#, fuzzy
-#| msgid "You cannot remove your primary email address (%(email)s)."
 msgid "You cannot remove your primary email address."
-msgstr "メインのメールアドレス (%(email)s) を削除することはできません。"
+msgstr "メインのメールアドレスを削除することはできません。"
 
 #: account/adapter.py:63
 msgid "This email address is already associated with this account."
@@ -54,10 +52,8 @@ msgid "Incorrect password."
 msgstr "不正なパスワードです。"
 
 #: account/adapter.py:72
-#, fuzzy
-#| msgid "Bad Token"
 msgid "Invalid or expired key."
-msgstr "不正なトークン"
+msgstr "不正または期限切れのキーです。"
 
 #: account/adapter.py:73
 msgid "The password reset token was invalid."
@@ -74,7 +70,7 @@ msgstr "ログイン失敗が連続しています。時間が経ってからや
 
 #: account/adapter.py:78
 msgid "The email address is not assigned to any user account"
-msgstr "このメールアドレスで登録されたユーザーアカウントがありません。"
+msgstr "このメールアドレスで登録されたユーザーアカウントがありません"
 
 #: account/adapter.py:79
 #: templates/account/messages/unverified_primary_email.txt:2
@@ -89,20 +85,18 @@ msgstr "このユーザー名は使用できません。他のユーザー名を
 msgid "The username and/or password you specified are not correct."
 msgstr "入力されたユーザー名もしくはパスワードが正しくありません。"
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 msgid "Use your password"
 msgstr "パスワードを使用する"
 
-#: account/adapter.py:750
-#, fuzzy
-#| msgid "Use your authenticator app"
+#: account/adapter.py:748
 msgid "Use authenticator app or code"
-msgstr "認証アプリを利用する"
+msgstr "認証アプリまたは認証コードを利用する"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
-msgstr ""
+msgstr "セキュリティキー/デバイスを使用"
 
 #: account/admin.py:23
 msgid "Mark selected email addresses as verified"
@@ -232,48 +226,44 @@ msgstr "メールアドレスの確認"
 
 #: headless/apps.py:7
 msgid "Headless"
-msgstr ""
+msgstr "ヘッドレス"
 
 #: mfa/adapter.py:32
 msgid ""
 "You cannot add an email address to an account protected by two-factor "
 "authentication."
 msgstr ""
-"二要素認証で保護されているアカウントには、メールアドレスを追加できません。"
+"多要素認証で保護されているアカウントには、メールアドレスを追加できません。"
 
 #: mfa/adapter.py:35
 msgid "You cannot deactivate two-factor authentication."
-msgstr "二要素認証は無効化できません。"
+msgstr "多要素認証は無効化できません。"
 
 #: mfa/adapter.py:38
-#, fuzzy
-#| msgid "You cannot deactivate two-factor authentication."
 msgid ""
 "You cannot generate recovery codes without having two-factor authentication "
 "enabled."
-msgstr "二要素認証は無効化できません。"
+msgstr "リカバリーキーを生成するには多要素認証の有効化が必要です。"
 
 #: mfa/adapter.py:42
 msgid ""
 "You cannot activate two-factor authentication until you have verified your "
 "email address."
 msgstr ""
-"メールアドレスの確認が完了するまで、二要素認証を有効にすることはできません。"
+"メールアドレスの確認が完了するまで、多要素認証を有効にすることはできません。"
 
 #: mfa/adapter.py:141
-#, fuzzy
-#| msgid "secret key"
 msgid "Master key"
-msgstr "シークレットキー"
+msgstr "マスターキー"
 
 #: mfa/adapter.py:143
 msgid "Backup key"
-msgstr ""
+msgstr "バックアップキー"
 
 #: mfa/adapter.py:144
 #, python-brace-format
 msgid "Key nr. {number}"
-msgstr ""
+msgstr "キー番号 {number}"
 
 #: mfa/apps.py:9
 msgid "MFA"
@@ -289,24 +279,24 @@ msgstr "TOTP 認証アプリ"
 
 #: mfa/models.py:26
 msgid "WebAuthn"
-msgstr ""
+msgstr "WebAuthn"
 
 #: mfa/totp/forms.py:11
 msgid "Authenticator code"
 msgstr "認証アプリコード"
 
-#: mfa/webauthn/forms.py:15
-#, fuzzy
-#| msgid "Password"
+#: mfa/webauthn/forms.py:16
 msgid "Passwordless"
-msgstr "パスワード"
+msgstr "パスワードレス"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
 "protection."
 msgstr ""
+"パスワードレス機能を有効化すると、このキー/デバイスを使用してログイン可能にな"
+"ります。その代わりに生態認証や PIN コードなどによる保護が必要になります。"
 
 #: socialaccount/adapter.py:36
 #, python-format
@@ -318,10 +308,8 @@ msgstr ""
 "ンしてから %s アカウントを接続してください。"
 
 #: socialaccount/adapter.py:40
-#, fuzzy
-#| msgid "Bad Token"
 msgid "Invalid token."
-msgstr "不正なトークン"
+msgstr "不正なトークンです。"
 
 #: socialaccount/adapter.py:41
 msgid "Your account has no password set up."
@@ -332,12 +320,8 @@ msgid "Your account has no verified email address."
 msgstr "確認済みのメールアドレスの登録が必要です。"
 
 #: socialaccount/adapter.py:44
-#, fuzzy
-#| msgid ""
-#| "You can sign in to your account using any of the following third-party "
-#| "accounts:"
 msgid "You cannot disconnect your last remaining third-party account."
-msgstr "以下の外部アカウントを使ってログインすることができます:"
+msgstr "唯一残っている外部アカウントとの接続を解除することはできません。"
 
 #: socialaccount/adapter.py:47
 #: templates/socialaccount/messages/account_connected_other.txt:2
@@ -446,9 +430,6 @@ msgid "Invalid profile data"
 msgstr "無効なプロファイルデータ"
 
 #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:17
-#, fuzzy
-#| msgctxt "field label"
-#| msgid "Login"
 msgid "Login"
 msgstr "ログイン"
 
@@ -462,7 +443,7 @@ msgstr "キャンセル"
 msgid ""
 "Invalid response while obtaining request token from \"%s\". Response was: %s."
 msgstr ""
-"「%s」からのリクエストトークン取得中に不正な応答がありました。レスポンス: %s"
+"リクエストトークン取得中に「%s」から不正な応答がありました。レスポンス: %s"
 
 #: socialaccount/providers/oauth/client.py:119
 #: socialaccount/providers/pocket/client.py:86
@@ -796,10 +777,8 @@ msgstr ""
 "もしそれがあなたなら、以下のリンクからアカウントを登録することができます。"
 
 #: templates/account/email/unknown_account_subject.txt:3
-#, fuzzy
-#| msgid "Accounts"
 msgid "Unknown Account"
-msgstr "アカウント"
+msgstr "不明なアカウント"
 
 #: templates/account/email_change.html:5 templates/account/email_change.html:9
 msgid "Email Address"
@@ -879,7 +858,7 @@ msgstr ""
 
 #: templates/account/login.html:42
 msgid "Sign in with a passkey"
-msgstr ""
+msgstr "パスキーでログイン"
 
 #: templates/account/login.html:47 templates/account/request_login_code.html:9
 msgid "Mail me a sign-in code"
@@ -1016,10 +995,8 @@ msgid ""
 msgstr "パスワード不要のログイン用特別コードが記載されたメールをお送りします。"
 
 #: templates/account/request_login_code.html:24
-#, fuzzy
-#| msgid "Recovery Codes"
 msgid "Request Code"
-msgstr "リカバリーコード"
+msgstr "コードを要求"
 
 #: templates/account/signup.html:4 templates/socialaccount/signup.html:5
 msgid "Signup"
@@ -1130,7 +1107,7 @@ msgstr "メニュー:"
 #: templates/allauth/layouts/base.html:43 templates/mfa/authenticate.html:10
 #: templates/mfa/index.html:5 templates/mfa/index.html:9
 msgid "Two-Factor Authentication"
-msgstr "二要素認証"
+msgstr "多要素認証"
 
 #: templates/allauth/layouts/base.html:49
 #: templates/usersessions/usersession_list.html:6
@@ -1143,17 +1120,17 @@ msgid ""
 "Your account is protected by two-factor authentication. Please enter an "
 "authenticator code:"
 msgstr ""
-"あなたのアカウントは二要素認証で保護されています。認証コードを入力してくださ"
+"あなたのアカウントは多要素認証で保護されています。認証コードを入力してくださ"
 "い:"
 
 #: templates/mfa/email/recovery_codes_generated_message.txt:4
 msgid ""
 "A new set of Two-Factor Authentication recovery codes has been generated."
-msgstr "二要素認証用のリカバリーコードを新たに生成しました。"
+msgstr "多要素認証用のリカバリーコードを新たに生成しました。"
 
 #: templates/mfa/email/recovery_codes_generated_subject.txt:3
 msgid "New Recovery Codes Generated"
-msgstr "二要素認証用のリカバリーコードを生成しました"
+msgstr "新しいリカバリーコードを生成しました"
 
 #: templates/mfa/email/totp_activated_message.txt:4
 #: templates/mfa/messages/totp_activated.txt:2
@@ -1174,24 +1151,20 @@ msgid "Authenticator App Deactivated"
 msgstr "認証アプリを無効化しました"
 
 #: templates/mfa/email/webauthn_added_message.txt:4
-#, fuzzy
-#| msgid "A new set of recovery codes has been generated."
 msgid "A new security key has been added."
-msgstr "新しいリカバリーコードを生成しました。"
+msgstr "新しいセキュリティキーを登録しました。"
 
 #: templates/mfa/email/webauthn_added_subject.txt:3
 msgid "Security Key Added"
-msgstr ""
+msgstr "セキュリティキーを追加しました"
 
 #: templates/mfa/email/webauthn_removed_message.txt:4
-#, fuzzy
-#| msgid "Your email has been confirmed."
 msgid "A security key has been removed."
-msgstr "あなたのメールアドレスを確認しました。"
+msgstr "セキュリティキーを削除しました。"
 
 #: templates/mfa/email/webauthn_removed_subject.txt:3
 msgid "Security Key Removed"
-msgstr ""
+msgstr "セキュリティキーを削除しました"
 
 #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4
 msgid "Authenticator App"
@@ -1215,26 +1188,26 @@ msgstr "有効化する"
 
 #: templates/mfa/index.html:45 templates/mfa/webauthn/base.html:4
 msgid "Security Keys"
-msgstr ""
+msgstr "セキュリティキー"
 
 #: templates/mfa/index.html:50
 #, python-format
 msgid "You have added %(count)s security key/device."
 msgid_plural "You have added %(count)s security keys/devices."
-msgstr[0] ""
+msgstr[0] "%(count)s 個のセキュリティキー/デバイスを登録しました。"
 
 #: templates/mfa/index.html:53
 msgid "No security keys have been added."
-msgstr ""
+msgstr "登録されているセキュリティキーはありません。"
 
 #: templates/mfa/index.html:60
 msgid "Manage"
-msgstr ""
+msgstr "管理"
 
 #: templates/mfa/index.html:65
 #: templates/mfa/webauthn/authenticator_list.html:68
 msgid "Add"
-msgstr ""
+msgstr "登録"
 
 #: templates/mfa/index.html:75 templates/mfa/recovery_codes/base.html:4
 #: templates/mfa/recovery_codes/generate.html:6
@@ -1253,7 +1226,7 @@ msgstr[0] ""
 
 #: templates/mfa/index.html:84
 msgid "No recovery codes set up."
-msgstr "リカバリーコードがセットアップされていません。"
+msgstr "リカバリーコードが設定されていません。"
 
 #: templates/mfa/index.html:94
 msgid "View"
@@ -1273,11 +1246,11 @@ msgstr "新しいリカバリーコードを生成しました。"
 
 #: templates/mfa/messages/webauthn_added.txt:2
 msgid "Security key added."
-msgstr ""
+msgstr "セキュリティーキーを登録しました。"
 
 #: templates/mfa/messages/webauthn_removed.txt:2
 msgid "Security key removed."
-msgstr ""
+msgstr "セキュリティキーを削除しました。"
 
 #: templates/mfa/reauthenticate.html:6
 msgid "Enter an authenticator code:"
@@ -1318,7 +1291,7 @@ msgid ""
 "below with your authenticator app. Then, input the verification code "
 "generated by the app below."
 msgstr ""
-"アカウントを二要素認証で保護するために、認証アプリで以下の QR コードをスキャ"
+"アカウントを多要素認証で保護するために、認証アプリで以下の QR コードをスキャ"
 "ンしてください。次に、アプリで生成された確認コードを以下に入力してください。"
 
 #: templates/mfa/totp/activate_form.html:21
@@ -1346,29 +1319,27 @@ msgstr "認証アプリによる認証を無効化しようとしています。
 
 #: templates/mfa/webauthn/add_form.html:7
 msgid "Add Security Key or Device"
-msgstr ""
+msgstr "セキュリティキー/デバイスの登録"
 
 #: templates/mfa/webauthn/add_form.html:18
 msgid "Add key or device"
-msgstr ""
+msgstr "キー/デバイスを登録"
 
 #: templates/mfa/webauthn/authenticator_confirm_delete.html:6
 msgid "Remove Security Key or Device"
-msgstr ""
+msgstr "セキュリティキー/デバイスの解除"
 
 #: templates/mfa/webauthn/authenticator_confirm_delete.html:8
-#, fuzzy
-#| msgid "Are you sure you want to sign out?"
 msgid "Are you sure you want to remove this security key?"
-msgstr "ログアウトしますか?"
+msgstr "セキュリティキーを解除しますか?"
 
 #: templates/mfa/webauthn/authenticator_list.html:8
 msgid "Security Keys and Devices"
-msgstr ""
+msgstr "セキュリティキー/デバイス"
 
 #: templates/mfa/webauthn/authenticator_list.html:11
 msgid "You have not added any security keys/devices."
-msgstr ""
+msgstr "登録されたセキュリティキー/デバイスはありません。"
 
 #: templates/mfa/webauthn/authenticator_list.html:19
 msgid "Usage"
@@ -1376,59 +1347,55 @@ msgstr ""
 
 #: templates/mfa/webauthn/authenticator_list.html:31
 msgid "Passkey"
-msgstr ""
+msgstr "パスキー"
 
 #: templates/mfa/webauthn/authenticator_list.html:35
-#, fuzzy
-#| msgid "secret key"
 msgid "Security key"
-msgstr "シークレットキー"
+msgstr "セキュリティキー"
 
 #: templates/mfa/webauthn/authenticator_list.html:38
 msgid "This key does not specify whether is a passkey."
-msgstr ""
+msgstr "このキーはパスキーかどうか記載されていません。"
 
 #: templates/mfa/webauthn/authenticator_list.html:39
-#, fuzzy
-#| msgid "Unverified"
 msgid "Unspecified"
-msgstr "未確認"
+msgstr "未指定"
 
 #: templates/mfa/webauthn/authenticator_list.html:44
 #, python-format
 msgid "Added on %(created_at)s"
-msgstr ""
+msgstr "登録日: %(created_at)s"
 
 #: templates/mfa/webauthn/authenticator_list.html:46
 #, python-format
 msgid "Last used %(last_used)s"
-msgstr ""
+msgstr "最終使用日: %(last_used)s"
 
 #: templates/mfa/webauthn/authenticator_list.html:54
 msgid "Edit"
-msgstr ""
+msgstr "編集"
 
 #: templates/mfa/webauthn/edit_form.html:7
 msgid "Edit Security Key or Device"
-msgstr ""
+msgstr "セキュリティキー/デバイスの編集"
 
 #: templates/mfa/webauthn/edit_form.html:18
 msgid "Save"
-msgstr ""
+msgstr "保存"
 
 #: templates/mfa/webauthn/snippets/scripts.html:2
 msgid "This functionality requires JavaScript."
-msgstr ""
+msgstr "この機能を利用するには JavaScript が必要です。"
 
 #: templates/socialaccount/authentication_error.html:5
 #: templates/socialaccount/authentication_error.html:9
 msgid "Third-Party Login Failure"
-msgstr "ソーシャルログインに失敗しました"
+msgstr "外部アカウントによるログインに失敗しました"
 
 #: templates/socialaccount/authentication_error.html:12
 msgid ""
 "An error occurred while attempting to login via your third-party account."
-msgstr "ソーシャルアカウントでのログイン時ににエラーが発生しました。"
+msgstr "外部アカウントでのログイン時ににエラーが発生しました。"
 
 #: templates/socialaccount/connections.html:5
 #: templates/socialaccount/connections.html:9
@@ -1464,11 +1431,11 @@ msgstr "外部アカウントを接続しました"
 msgid ""
 "A third-party account from %(provider)s has been disconnected from your "
 "account."
-msgstr "%(provider)s の外部アカウントをあなたのアカウントから切断しました。"
+msgstr "%(provider)s の外部アカウントへの接続を解除しました。"
 
 #: templates/socialaccount/email/account_disconnected_subject.txt:3
 msgid "Third-Party Account Disconnected"
-msgstr "外部アカウントを接続解除しました"
+msgstr "外部アカウントとの接続を解除しました"
 
 #: templates/socialaccount/login.html:10
 #, python-format
@@ -1478,7 +1445,7 @@ msgstr "%(provider)s と接続する"
 #: templates/socialaccount/login.html:13
 #, python-format
 msgid "You are about to connect a new third-party account from %(provider)s."
-msgstr "%(provider)sの新しい外部アカウントを接続しようとしています。"
+msgstr "%(provider)s の新しい外部アカウントを接続しようとしています。"
 
 #: templates/socialaccount/login.html:17
 #, python-format
@@ -1516,7 +1483,7 @@ msgstr "外部アカウントを接続しました。"
 
 #: templates/socialaccount/messages/account_disconnected.txt:2
 msgid "The third-party account has been disconnected."
-msgstr "外部アカウントの接続を解除しました。"
+msgstr "外部アカウントとの接続を解除しました。"
 
 #: templates/socialaccount/signup.html:12
 #, python-format
diff --git a/allauth/locale/ka/LC_MESSAGES/django.po b/allauth/locale/ka/LC_MESSAGES/django.po
index 30dea89f..85e21a10 100644
--- a/allauth/locale/ka/LC_MESSAGES/django.po
+++ b/allauth/locale/ka/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 21:31+0200\n"
 "Last-Translator: Nikoloz Naskidashvili <nikoloz2005@gmail.com>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -93,19 +93,19 @@ msgstr ""
 msgid "The username and/or password you specified are not correct."
 msgstr "თქვენს მიერ მითითებული მომხმარებლის სახელი ან პაროლი არასწორია."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "დაგავიწყდა პაროლი?"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "token secret"
 msgid "Use authenticator app or code"
 msgstr "საიდუმლო კოდი"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -301,13 +301,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "პაროლი"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/ko/LC_MESSAGES/django.po b/allauth/locale/ko/LC_MESSAGES/django.po
index 5eaea094..64ca04f3 100644
--- a/allauth/locale/ko/LC_MESSAGES/django.po
+++ b/allauth/locale/ko/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 21:50+0200\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -91,19 +91,19 @@ msgstr "해당 아이디는 이미 사용중입니다. 다른 사용자명을 
 msgid "The username and/or password you specified are not correct."
 msgstr "아이디 또는 비밀번호가 올바르지 않습니다."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "비밀번호를 잊으셨나요?"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "token secret"
 msgid "Use authenticator app or code"
 msgstr "시크릿 토큰"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -299,13 +299,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "비밀번호"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/ky/LC_MESSAGES/django.po b/allauth/locale/ky/LC_MESSAGES/django.po
index fdaf9f9c..e653ddd8 100644
--- a/allauth/locale/ky/LC_MESSAGES/django.po
+++ b/allauth/locale/ky/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 21:44+0200\n"
 "Last-Translator: Murat Jumashev <jumasheff at gmail dot com>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -91,19 +91,19 @@ msgstr "Бул атты колдонуу мүмкүн эмес. Башкасын
 msgid "The username and/or password you specified are not correct."
 msgstr "Сиз берген колдонуучу аты жана/же купуя туура эмес."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "Купуяңызды унуттуңузбу?"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "token secret"
 msgid "Use authenticator app or code"
 msgstr "токендин жашыруун ачкычы"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -305,13 +305,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Купуя"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/lt/LC_MESSAGES/django.po b/allauth/locale/lt/LC_MESSAGES/django.po
index a2cb915b..ad46cab3 100644
--- a/allauth/locale/lt/LC_MESSAGES/django.po
+++ b/allauth/locale/lt/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 22:02+0200\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -94,19 +94,19 @@ msgstr ""
 msgid "The username and/or password you specified are not correct."
 msgstr "Pateiktas naudotojo vardas ir/arba slaptažodis yra neteisingi."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "Pamiršote slaptažodį?"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "token secret"
 msgid "Use authenticator app or code"
 msgstr "token secret"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -308,13 +308,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Slaptažodis"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/lv/LC_MESSAGES/django.po b/allauth/locale/lv/LC_MESSAGES/django.po
index 587336a9..8f546375 100644
--- a/allauth/locale/lv/LC_MESSAGES/django.po
+++ b/allauth/locale/lv/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 21:51+0200\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -94,19 +94,19 @@ msgstr ""
 msgid "The username and/or password you specified are not correct."
 msgstr "Nepareizs lietotāja vārds un/vai parole."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "Aizmirsāt paroli?"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "token secret"
 msgid "Use authenticator app or code"
 msgstr "token secret"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -308,13 +308,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Parole"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/mn/LC_MESSAGES/django.po b/allauth/locale/mn/LC_MESSAGES/django.po
index 8a93c6d9..bca39496 100644
--- a/allauth/locale/mn/LC_MESSAGES/django.po
+++ b/allauth/locale/mn/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: 0.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 21:32+0200\n"
 "Last-Translator: Bilgutei Erdenebayar <beke0130@gmail.com>\n"
 "Language-Team: Mongolian <beke0130@gmail.com>\n"
@@ -90,19 +90,19 @@ msgstr "Хэрэглэгчийн нэрийг ашиглах боломжгүй.
 msgid "The username and/or password you specified are not correct."
 msgstr "Таны оруулсан имэйл хаяг болон/эсвэл нууц үг буруу байна."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "Нууц Үг Мартсан?"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "token secret"
 msgid "Use authenticator app or code"
 msgstr "токен нууц"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -298,13 +298,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Нууц үг"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/nb/LC_MESSAGES/django.po b/allauth/locale/nb/LC_MESSAGES/django.po
index 27a22b36..7b31b0da 100644
--- a/allauth/locale/nb/LC_MESSAGES/django.po
+++ b/allauth/locale/nb/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 21:52+0200\n"
 "Last-Translator: Allan Nordhøy <epost@anotheragency.no>\n"
 "Language-Team: Norwegian Bokmål <https://hosted.weblate.org/projects/django-"
@@ -92,19 +92,19 @@ msgstr "Brukernavnet kan ikke benyttes. Vennligst velg et annet brukernavn."
 msgid "The username and/or password you specified are not correct."
 msgstr "Brukernavnet og/eller passordet du oppgav er feil."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "Glemt passord?"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "token secret"
 msgid "Use authenticator app or code"
 msgstr "hemmelig nøkkel"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -300,13 +300,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Passord"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/nl/LC_MESSAGES/django.po b/allauth/locale/nl/LC_MESSAGES/django.po
index 7441edb0..b916039b 100644
--- a/allauth/locale/nl/LC_MESSAGES/django.po
+++ b/allauth/locale/nl/LC_MESSAGES/django.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 22:01+0200\n"
 "Last-Translator: Raymond Penners <raymond.penners@intenct.nl>\n"
 "Language-Team: Dutch <https://hosted.weblate.org/projects/django-allauth/"
@@ -88,17 +88,17 @@ msgstr "Deze gebruikersnaam mag je niet gebruiken, kies een andere."
 msgid "The username and/or password you specified are not correct."
 msgstr "Je gebruikersnaam en/of wachtwoord zijn incorrect."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 msgid "Use your password"
 msgstr "Gebruik je wachtwoord"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "Use your authenticator app"
 msgid "Use authenticator app or code"
 msgstr "Gebruik je authenticator-app"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -296,13 +296,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr "Authenticator code"
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Wachtwoord"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/pl/LC_MESSAGES/django.po b/allauth/locale/pl/LC_MESSAGES/django.po
index ca23908f..469b2254 100644
--- a/allauth/locale/pl/LC_MESSAGES/django.po
+++ b/allauth/locale/pl/LC_MESSAGES/django.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 22:09+0200\n"
 "Last-Translator: Krzysztof Hoffmann <krzysiekpl@gmail.com>\n"
 "Language-Team: \n"
@@ -91,19 +91,19 @@ msgstr "Nie możesz użyć tej nazwy użytkownika. Proszę wybierz inną."
 msgid "The username and/or password you specified are not correct."
 msgstr "Podana nazwa użytkownika i/lub hasło są niepoprawne."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "Nie pamiętasz hasła?"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "Authenticator App"
 msgid "Use authenticator app or code"
 msgstr "Aplikacja autoryzacyjna"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -315,13 +315,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr "Kod autentykacyjny"
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Hasło"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/pt_BR/LC_MESSAGES/django.po b/allauth/locale/pt_BR/LC_MESSAGES/django.po
index 3b073dcb..1aee28a4 100644
--- a/allauth/locale/pt_BR/LC_MESSAGES/django.po
+++ b/allauth/locale/pt_BR/LC_MESSAGES/django.po
@@ -12,8 +12,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: django-allauth\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
-"PO-Revision-Date: 2024-06-29 17:09+0000\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
+"PO-Revision-Date: 2024-07-01 15:09+0000\n"
 "Last-Translator: Allan Silva Barroso de Oliveira <allan.sbo@hotmail.com>\n"
 "Language-Team: Portuguese (Brazil) <https://hosted.weblate.org/projects/"
 "allauth/django-allauth/pt_BR/>\n"
@@ -91,20 +91,18 @@ msgstr "Este nome de usuário não pode ser usado. É preciso escolher outro."
 msgid "The username and/or password you specified are not correct."
 msgstr "O nome de usuário e/ou senha especificados não estão corretos."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 msgid "Use your password"
 msgstr "Insira sua senha"
 
-#: account/adapter.py:750
-#, fuzzy
-#| msgid "Use your authenticator app"
+#: account/adapter.py:748
 msgid "Use authenticator app or code"
-msgstr "Use seu aplicativo autenticador"
+msgstr "Use um aplicativo autenticador ou código"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
-msgstr ""
+msgstr "Use uma chave de segurança ou dispositivo"
 
 #: account/admin.py:23
 msgid "Mark selected email addresses as verified"
@@ -265,19 +263,17 @@ msgstr ""
 "e-mail seja verificado."
 
 #: mfa/adapter.py:141
-#, fuzzy
-#| msgid "secret key"
 msgid "Master key"
-msgstr "Chave secreta"
+msgstr "Chave mestre"
 
 #: mfa/adapter.py:143
 msgid "Backup key"
-msgstr ""
+msgstr "Chave de backup"
 
 #: mfa/adapter.py:144
 #, python-brace-format
 msgid "Key nr. {number}"
-msgstr ""
+msgstr "Chave número. {number}"
 
 #: mfa/apps.py:9
 msgid "MFA"
@@ -293,24 +289,25 @@ msgstr "Autenticador TOTP"
 
 #: mfa/models.py:26
 msgid "WebAuthn"
-msgstr ""
+msgstr "Autenticação web"
 
 #: mfa/totp/forms.py:11
 msgid "Authenticator code"
 msgstr "Código autenticador"
 
-#: mfa/webauthn/forms.py:15
-#, fuzzy
-#| msgid "Password"
+#: mfa/webauthn/forms.py:16
 msgid "Passwordless"
-msgstr "Senha"
+msgstr "Sem senha"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
 "protection."
 msgstr ""
+"A ativação da operação sem senha permite que você faça login usando apenas "
+"esta chave/dispositivo, mas impõe requisitos adicionais, como biometria ou "
+"proteção por PIN."
 
 #: socialaccount/adapter.py:36
 #, python-format
@@ -865,7 +862,7 @@ msgstr ""
 
 #: templates/account/login.html:42
 msgid "Sign in with a passkey"
-msgstr ""
+msgstr "Entrar com uma chave de acesso"
 
 #: templates/account/login.html:47 templates/account/request_login_code.html:9
 msgid "Mail me a sign-in code"
@@ -1159,24 +1156,20 @@ msgid "Authenticator App Deactivated"
 msgstr "Aplicativo autenticador desativado"
 
 #: templates/mfa/email/webauthn_added_message.txt:4
-#, fuzzy
-#| msgid "A new set of recovery codes has been generated."
 msgid "A new security key has been added."
-msgstr "Um novo conjunto de códigos de recuperação foi gerado."
+msgstr "Uma nova chave de segurança foi adicionada."
 
 #: templates/mfa/email/webauthn_added_subject.txt:3
 msgid "Security Key Added"
-msgstr ""
+msgstr "Chave de segurança adicionada"
 
 #: templates/mfa/email/webauthn_removed_message.txt:4
-#, fuzzy
-#| msgid "Your email has been confirmed."
 msgid "A security key has been removed."
-msgstr "Seu e-mail foi confirmado."
+msgstr "Uma chave de segurança foi removida."
 
 #: templates/mfa/email/webauthn_removed_subject.txt:3
 msgid "Security Key Removed"
-msgstr ""
+msgstr "Chave de Segurança Removida"
 
 #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4
 msgid "Authenticator App"
@@ -1200,27 +1193,27 @@ msgstr "Ativar"
 
 #: templates/mfa/index.html:45 templates/mfa/webauthn/base.html:4
 msgid "Security Keys"
-msgstr ""
+msgstr "Chaves de Segurança"
 
 #: templates/mfa/index.html:50
 #, python-format
 msgid "You have added %(count)s security key/device."
 msgid_plural "You have added %(count)s security keys/devices."
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "Você adicionou %(count)s chave ou dispositivo de segurança."
+msgstr[1] "Você adicionou %(count)s chaves ou dispositivos de segurança."
 
 #: templates/mfa/index.html:53
 msgid "No security keys have been added."
-msgstr ""
+msgstr "Nenhuma chave de segurança foi adicionada."
 
 #: templates/mfa/index.html:60
 msgid "Manage"
-msgstr ""
+msgstr "Gerenciar"
 
 #: templates/mfa/index.html:65
 #: templates/mfa/webauthn/authenticator_list.html:68
 msgid "Add"
-msgstr ""
+msgstr "Adicionar"
 
 #: templates/mfa/index.html:75 templates/mfa/recovery_codes/base.html:4
 #: templates/mfa/recovery_codes/generate.html:6
@@ -1261,11 +1254,11 @@ msgstr "Um novo conjunto de códigos de recuperação foi gerado."
 
 #: templates/mfa/messages/webauthn_added.txt:2
 msgid "Security key added."
-msgstr ""
+msgstr "Chave de segurança adicionada."
 
 #: templates/mfa/messages/webauthn_removed.txt:2
 msgid "Security key removed."
-msgstr ""
+msgstr "Chave de segurança removida."
 
 #: templates/mfa/reauthenticate.html:6
 msgid "Enter an authenticator code:"
@@ -1338,79 +1331,73 @@ msgstr ""
 
 #: templates/mfa/webauthn/add_form.html:7
 msgid "Add Security Key or Device"
-msgstr ""
+msgstr "Adicionar chave ou dispositivo de segurança"
 
 #: templates/mfa/webauthn/add_form.html:18
 msgid "Add key or device"
-msgstr ""
+msgstr "Adicionar chave ou dispositivo"
 
 #: templates/mfa/webauthn/authenticator_confirm_delete.html:6
 msgid "Remove Security Key or Device"
-msgstr ""
+msgstr "Remover chave ou dispositivo de segurança"
 
 #: templates/mfa/webauthn/authenticator_confirm_delete.html:8
-#, fuzzy
-#| msgid "Are you sure you want to sign out?"
 msgid "Are you sure you want to remove this security key?"
-msgstr "Tem certeza que deseja sair?"
+msgstr "Tem certeza que deseja remover esta chave de segurança?"
 
 #: templates/mfa/webauthn/authenticator_list.html:8
 msgid "Security Keys and Devices"
-msgstr ""
+msgstr "Chaves e dispositivos de segurança"
 
 #: templates/mfa/webauthn/authenticator_list.html:11
 msgid "You have not added any security keys/devices."
-msgstr ""
+msgstr "Você não adicionou nenhuma chave ou dispositivo de segurança."
 
 #: templates/mfa/webauthn/authenticator_list.html:19
 msgid "Usage"
-msgstr ""
+msgstr "Uso"
 
 #: templates/mfa/webauthn/authenticator_list.html:31
 msgid "Passkey"
-msgstr ""
+msgstr "Chave de acesso"
 
 #: templates/mfa/webauthn/authenticator_list.html:35
-#, fuzzy
-#| msgid "secret key"
 msgid "Security key"
-msgstr "Chave secreta"
+msgstr "Chave de segurança"
 
 #: templates/mfa/webauthn/authenticator_list.html:38
 msgid "This key does not specify whether is a passkey."
-msgstr ""
+msgstr "Esta chave não especifica se é uma chave de acesso."
 
 #: templates/mfa/webauthn/authenticator_list.html:39
-#, fuzzy
-#| msgid "Unverified"
 msgid "Unspecified"
-msgstr "Não verificado"
+msgstr "Não especificado"
 
 #: templates/mfa/webauthn/authenticator_list.html:44
 #, python-format
 msgid "Added on %(created_at)s"
-msgstr ""
+msgstr "Adicionado em %(created_at)s"
 
 #: templates/mfa/webauthn/authenticator_list.html:46
 #, python-format
 msgid "Last used %(last_used)s"
-msgstr ""
+msgstr "Último uso %(last_used)s"
 
 #: templates/mfa/webauthn/authenticator_list.html:54
 msgid "Edit"
-msgstr ""
+msgstr "Editar"
 
 #: templates/mfa/webauthn/edit_form.html:7
 msgid "Edit Security Key or Device"
-msgstr ""
+msgstr "Editar chave ou dispositivo de segurança"
 
 #: templates/mfa/webauthn/edit_form.html:18
 msgid "Save"
-msgstr ""
+msgstr "Salvar"
 
 #: templates/mfa/webauthn/snippets/scripts.html:2
 msgid "This functionality requires JavaScript."
-msgstr ""
+msgstr "Esta funcionalidade requer JavaScript."
 
 #: templates/socialaccount/authentication_error.html:5
 #: templates/socialaccount/authentication_error.html:9
diff --git a/allauth/locale/pt_PT/LC_MESSAGES/django.po b/allauth/locale/pt_PT/LC_MESSAGES/django.po
index 9e88e258..ab1624e1 100644
--- a/allauth/locale/pt_PT/LC_MESSAGES/django.po
+++ b/allauth/locale/pt_PT/LC_MESSAGES/django.po
@@ -10,7 +10,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: django-allauth\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 22:05+0200\n"
 "Last-Translator: Emanuel Angelo <emanuel.angelo@gmail.com>\n"
 "Language-Team: Portuguese (Portugal)\n"
@@ -95,17 +95,17 @@ msgid "The username and/or password you specified are not correct."
 msgstr ""
 "O nome de utilizador e/ou a palavra-passe que especificou não estão corretos."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 msgid "Use your password"
 msgstr "Utilizar palavra-passe"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "Use your authenticator app"
 msgid "Use authenticator app or code"
 msgstr "Utilizar autenticador"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -303,13 +303,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr "Código do autenticador"
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Palavra-passe"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/ro/LC_MESSAGES/django.po b/allauth/locale/ro/LC_MESSAGES/django.po
index 0273e9ed..1217fec1 100644
--- a/allauth/locale/ro/LC_MESSAGES/django.po
+++ b/allauth/locale/ro/LC_MESSAGES/django.po
@@ -10,7 +10,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: django-allauthrr\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 21:34+0200\n"
 "Last-Translator: Gilou <contact+dev@gilouweb.com>\n"
 "Language-Team: \n"
@@ -100,19 +100,19 @@ msgstr ""
 msgid "The username and/or password you specified are not correct."
 msgstr "Numele de utilizator si / sau parola sunt incorecte."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "Ai uitat parola?"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "token secret"
 msgid "Use authenticator app or code"
 msgstr "token secret"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -310,13 +310,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Parola"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/ru/LC_MESSAGES/django.po b/allauth/locale/ru/LC_MESSAGES/django.po
index 0aaa85ad..5c4bef4c 100644
--- a/allauth/locale/ru/LC_MESSAGES/django.po
+++ b/allauth/locale/ru/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-06-18 13:09+0000\n"
 "Last-Translator: Andrei Satsevich <mr.ackid@gmail.com>\n"
 "Language-Team: Russian <https://hosted.weblate.org/projects/allauth/django-"
@@ -88,17 +88,17 @@ msgstr "Такое имя пользователя не может быть ис
 msgid "The username and/or password you specified are not correct."
 msgstr "Имя пользователя и/или пароль неверны."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 msgid "Use your password"
 msgstr "Используйте ваш пароль"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "Use your authenticator app"
 msgid "Use authenticator app or code"
 msgstr "Используйте приложение-аутентификатор"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -296,13 +296,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr "Код аутентификатора"
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Пароль"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/sk/LC_MESSAGES/django.po b/allauth/locale/sk/LC_MESSAGES/django.po
index 196be0f2..df142d5d 100644
--- a/allauth/locale/sk/LC_MESSAGES/django.po
+++ b/allauth/locale/sk/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 21:23+0200\n"
 "Last-Translator: Erik Telepovský <erik@pragmaticmates.com>\n"
 "Language-Team: \n"
@@ -90,17 +90,17 @@ msgstr "Užívateľské meno nemôže byť použité. Prosím, použite iné men
 msgid "The username and/or password you specified are not correct."
 msgstr "Uvedené užívateľské meno alebo heslo nie je správne."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 msgid "Use your password"
 msgstr "Použite svoje heslo"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "Use your authenticator app"
 msgid "Use authenticator app or code"
 msgstr "použite autentifikačnú aplikáciu"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -310,13 +310,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr "Kód z autentifikátora"
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Heslo"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/sl/LC_MESSAGES/django.po b/allauth/locale/sl/LC_MESSAGES/django.po
index cb667183..58ebd872 100644
--- a/allauth/locale/sl/LC_MESSAGES/django.po
+++ b/allauth/locale/sl/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 21:25+0200\n"
 "Last-Translator: Klemen Štrajhar <klemen.strajhar@gmail.com>\n"
 "Language-Team: Bojan Mihelac <bmihelac@mihelac.org>, Lev Predan Kowarski, "
@@ -95,19 +95,19 @@ msgstr ""
 msgid "The username and/or password you specified are not correct."
 msgstr "Uporabniško ime in/ali geslo nista pravilna."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "Ste pozabili geslo?"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "token secret"
 msgid "Use authenticator app or code"
 msgstr "žeton skrivnost"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -301,13 +301,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Geslo"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/sr/LC_MESSAGES/django.po b/allauth/locale/sr/LC_MESSAGES/django.po
index 81a9f52e..7fab35f3 100644
--- a/allauth/locale/sr/LC_MESSAGES/django.po
+++ b/allauth/locale/sr/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-06-30 16:28+0200\n"
 "Last-Translator: dex girl <saraciric73@gmail.com>\n"
 "Language-Team: Serbian <https://hosted.weblate.org/projects/allauth/django-"
@@ -87,17 +87,17 @@ msgstr ""
 msgid "The username and/or password you specified are not correct."
 msgstr "Корисничко име и/или лозинка коју сте навели нису тачни."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 msgid "Use your password"
 msgstr "Koristi vašu lozinku"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "Use your authenticator app"
 msgid "Use authenticator app or code"
 msgstr "Koristi tvoju autentikatorsku aplikaciju"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -296,13 +296,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr "Autentikatorski kod"
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Лозинка"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
@@ -603,11 +603,14 @@ msgid ""
 "\n"
 "%(password_reset_url)s"
 msgstr ""
-"Dobijate ovaj e mail zato što vi ili neko drugi pokušava da se registruje  nalog koristeći e mail adresu.  \n"
+"Dobijate ovaj e mail zato što vi ili neko drugi pokušava da se registruje  "
+"nalog koristeći e mail adresu.  \n"
 "\n"
 "%(email)s\n"
 "\n"
-"Svakako, nalog koji koristi tu e mail adresu već postoji. U slučaju da ste zaboravili na ovo, molim vas koristite šifra zaboravljena proceduru da vratite vaš nalog. \n"
+"Svakako, nalog koji koristi tu e mail adresu već postoji. U slučaju da ste "
+"zaboravili na ovo, molim vas koristite šifra zaboravljena proceduru da "
+"vratite vaš nalog. \n"
 "\n"
 "%(password_reset_url)s"
 
@@ -846,7 +849,9 @@ msgstr "Neuspela potvrda %(email)s zato što je već potvrđen drugim nalogom."
 msgid ""
 "This email confirmation link expired or is invalid. Please <a "
 "href=\"%(email_url)s\">issue a new email confirmation request</a>."
-msgstr "Ovaj email link za potvrdu je istekao ili je nevažeći. Molim vas <a href=\"%(email_url)s\">tražite novu potvrdu e maila</a>."
+msgstr ""
+"Ovaj email link za potvrdu je istekao ili je nevažeći. Molim vas <a "
+"href=\"%(email_url)s\">tražite novu potvrdu e maila</a>."
 
 #: templates/account/login.html:19
 #, python-format
diff --git a/allauth/locale/sr_Latn/LC_MESSAGES/django.po b/allauth/locale/sr_Latn/LC_MESSAGES/django.po
index 0c347855..a001ebe5 100644
--- a/allauth/locale/sr_Latn/LC_MESSAGES/django.po
+++ b/allauth/locale/sr_Latn/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 22:07+0200\n"
 "Last-Translator: Nikola Vulovic <nivukiki@gmail.com>\n"
 "Language-Team: NONE\n"
@@ -93,19 +93,19 @@ msgstr ""
 msgid "The username and/or password you specified are not correct."
 msgstr "Korisničko ime i/ili lozinka koju ste naveli nisu tačni."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "Zaboravili ste lozinku?"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "token secret"
 msgid "Use authenticator app or code"
 msgstr "token tajna"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -301,13 +301,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Lozinka"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/sv/LC_MESSAGES/django.po b/allauth/locale/sv/LC_MESSAGES/django.po
index b2506286..14dfaa4c 100644
--- a/allauth/locale/sv/LC_MESSAGES/django.po
+++ b/allauth/locale/sv/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: django-allauth\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 22:08+0200\n"
 "Last-Translator: Jannis \n"
 "Language-Team: Swedish (http://www.transifex.com/projects/p/django-allauth/"
@@ -91,17 +91,17 @@ msgstr "Användarnamnet kan ej användas. Välj ett annat användarnamn."
 msgid "The username and/or password you specified are not correct."
 msgstr "Användarnamnet och/eller lösenordet är felaktigt."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "Glömt lösenordet?"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 msgid "Use authenticator app or code"
 msgstr ""
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -304,13 +304,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Lösenord"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/th/LC_MESSAGES/django.po b/allauth/locale/th/LC_MESSAGES/django.po
index 27b9df1f..3b367479 100644
--- a/allauth/locale/th/LC_MESSAGES/django.po
+++ b/allauth/locale/th/LC_MESSAGES/django.po
@@ -10,7 +10,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: django-allauth\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 22:14+0200\n"
 "Last-Translator: Nattaphoom Chaipreecha <attomos@gmail.com>\n"
 "Language-Team: Thai <LL@li.org>\n"
@@ -93,17 +93,17 @@ msgstr "ไม่สามารถใช้ชื่อผู้ใช้นี
 msgid "The username and/or password you specified are not correct."
 msgstr "ชื่อผู้ใช้และ/หรือรหัสผ่านที่ระบุมาไม่ถูกต้อง"
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "ลืมรหัสผ่าน?"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 msgid "Use authenticator app or code"
 msgstr ""
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -303,13 +303,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "รหัสผ่าน"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/tr/LC_MESSAGES/django.po b/allauth/locale/tr/LC_MESSAGES/django.po
index 65364186..7b08a9e7 100644
--- a/allauth/locale/tr/LC_MESSAGES/django.po
+++ b/allauth/locale/tr/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: django-allauth\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 22:13+0200\n"
 "Last-Translator: Jannis \n"
 "Language-Team: Turkish (http://www.transifex.com/projects/p/django-allauth/"
@@ -90,15 +90,15 @@ msgstr "Bu kullanıcı adı kullanılamaz. Lütfen başka bir kullanıcı adı d
 msgid "The username and/or password you specified are not correct."
 msgstr "Girdiğiniz kullanıcı adı ve/veya parola doğru değil."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 msgid "Use your password"
 msgstr "Şifrenizi kullanın"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 msgid "Use authenticator app or code"
 msgstr ""
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -292,13 +292,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Parola"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/uk/LC_MESSAGES/django.po b/allauth/locale/uk/LC_MESSAGES/django.po
index 3ed2c639..3d966020 100644
--- a/allauth/locale/uk/LC_MESSAGES/django.po
+++ b/allauth/locale/uk/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
 "PO-Revision-Date: 2024-04-20 22:11+0200\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -92,19 +92,19 @@ msgstr ""
 msgid "The username and/or password you specified are not correct."
 msgstr "Введене ім'я користувача і/або пароль є некоректними."
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 #, fuzzy
 #| msgid "Forgot Password?"
 msgid "Use your password"
 msgstr "Забули пароль?"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 #, fuzzy
 #| msgid "Authenticator secret"
 msgid "Use authenticator app or code"
 msgstr "Секрет аутентифікатора"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
 msgstr ""
@@ -300,13 +300,13 @@ msgstr ""
 msgid "Authenticator code"
 msgstr ""
 
-#: mfa/webauthn/forms.py:15
+#: mfa/webauthn/forms.py:16
 #, fuzzy
 #| msgid "Password"
 msgid "Passwordless"
 msgstr "Пароль"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
diff --git a/allauth/locale/zh_Hans/LC_MESSAGES/django.po b/allauth/locale/zh_Hans/LC_MESSAGES/django.po
index 2583fab2..a33af377 100644
--- a/allauth/locale/zh_Hans/LC_MESSAGES/django.po
+++ b/allauth/locale/zh_Hans/LC_MESSAGES/django.po
@@ -7,9 +7,9 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
-"PO-Revision-Date: 2024-05-15 05:21+0000\n"
-"Last-Translator: Unintended <Unintendedz@outlook.com>\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
+"PO-Revision-Date: 2024-07-06 07:09+0000\n"
+"Last-Translator: Zhuo Cheng <chengz21@mails.tsinghua.edu.cn>\n"
 "Language-Team: Chinese (Simplified) <https://hosted.weblate.org/projects/"
 "allauth/django-allauth/zh_Hans/>\n"
 "Language: zh_Hans\n"
@@ -17,7 +17,7 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 5.6-dev\n"
+"X-Generator: Weblate 5.7-dev\n"
 
 #: account/adapter.py:58
 msgid "This account is currently inactive."
@@ -25,7 +25,7 @@ msgstr "此账号当前未激活。"
 
 #: account/adapter.py:60
 msgid "You cannot remove your primary email address."
-msgstr "您不能删除您的主Email地址 (%(email)s)"
+msgstr "您不能删除您的主Email地址 (%(email)s)。"
 
 #: account/adapter.py:63
 msgid "This email address is already associated with this account."
@@ -37,38 +37,36 @@ msgstr "您提供的Email地址或密码不正确。"
 
 #: account/adapter.py:68
 msgid "A user is already registered with this email address."
-msgstr "此email地址已被其他用户注册."
+msgstr "此email地址已被其他用户注册。"
 
 #: account/adapter.py:69
 msgid "Please type your current password."
-msgstr "请输入您的当前密码."
+msgstr "请输入您的当前密码。"
 
 #: account/adapter.py:70 mfa/adapter.py:40
 msgid "Incorrect code."
-msgstr "错误代码."
+msgstr "错误代码。"
 
 #: account/adapter.py:71
 msgid "Incorrect password."
-msgstr "无效密码."
+msgstr "无效密码。"
 
 #: account/adapter.py:72
-#, fuzzy
-#| msgid "Invalid token."
 msgid "Invalid or expired key."
-msgstr "令牌无效。"
+msgstr "令牌无效或过期。"
 
 #: account/adapter.py:73
 msgid "The password reset token was invalid."
-msgstr "您重置密码的token是无效的."
+msgstr "您重置密码的token是无效的。"
 
 #: account/adapter.py:74
 #, python-format
 msgid "You cannot add more than %d email addresses."
-msgstr "您不能添加超过%d个Email地址."
+msgstr "您不能添加超过%d个Email地址。"
 
 #: account/adapter.py:76
 msgid "Too many failed login attempts. Try again later."
-msgstr "登录失败次数过多，请稍后重试."
+msgstr "登录失败次数过多，请稍后重试。"
 
 #: account/adapter.py:78
 msgid "The email address is not assigned to any user account"
@@ -81,26 +79,24 @@ msgstr "您的主Email地址必须被验证。"
 
 #: account/adapter.py:81
 msgid "Username can not be used. Please use other username."
-msgstr "此用户名不能使用，请改用其他用户名."
+msgstr "此用户名不能使用，请改用其他用户名。"
 
 #: account/adapter.py:84
 msgid "The username and/or password you specified are not correct."
-msgstr "您提供的用户名或密码不正确."
+msgstr "您提供的用户名或密码不正确。"
 
-#: account/adapter.py:736
+#: account/adapter.py:739
 msgid "Use your password"
-msgstr "使用你的密码"
+msgstr "使用您的密码"
 
-#: account/adapter.py:750
-#, fuzzy
-#| msgid "Use your authenticator app"
+#: account/adapter.py:748
 msgid "Use authenticator app or code"
-msgstr "使用你的认证app"
+msgstr "请使用认证APP或代码"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
-msgstr ""
+msgstr "请使用安全密钥或设备"
 
 #: account/admin.py:23
 msgid "Mark selected email addresses as verified"
@@ -112,7 +108,7 @@ msgstr "账户"
 
 #: account/forms.py:56 account/forms.py:481
 msgid "You must type the same password each time."
-msgstr "每次输入的密码必须相同."
+msgstr "每次输入的密码必须相同。"
 
 #: account/forms.py:91 account/forms.py:411 account/forms.py:562
 #: account/forms.py:676
@@ -149,7 +145,7 @@ msgstr "登录"
 
 #: account/forms.py:136
 msgid "Forgot your password?"
-msgstr "忘记密码了？"
+msgstr "忘记密码？"
 
 #: account/forms.py:298
 msgid "Email (again)"
@@ -165,7 +161,7 @@ msgstr "Email （选填项）"
 
 #: account/forms.py:369
 msgid "You must type the same email each time."
-msgstr "每次输入的Email必须相同"
+msgstr "每次输入的Email必须相同。"
 
 #: account/forms.py:417 account/forms.py:563
 msgid "Password (again)"
@@ -218,7 +214,7 @@ msgstr "已发送"
 
 #: account/models.py:155 socialaccount/models.py:69
 msgid "key"
-msgstr "key"
+msgstr "密钥"
 
 #: account/models.py:160
 msgid "email confirmation"
@@ -255,19 +251,17 @@ msgid ""
 msgstr "在验证您的Email地址之前，您不能激活两步验证。"
 
 #: mfa/adapter.py:141
-#, fuzzy
-#| msgid "secret key"
 msgid "Master key"
-msgstr "密钥"
+msgstr "主密钥"
 
 #: mfa/adapter.py:143
 msgid "Backup key"
-msgstr ""
+msgstr "备份密钥"
 
 #: mfa/adapter.py:144
 #, python-brace-format
 msgid "Key nr. {number}"
-msgstr ""
+msgstr "密钥 {number}"
 
 #: mfa/apps.py:9
 msgid "MFA"
@@ -279,35 +273,35 @@ msgstr "恢复代码"
 
 #: mfa/models.py:25
 msgid "TOTP Authenticator"
-msgstr "TOTP认证器"
+msgstr "TOTP 认证器"
 
 #: mfa/models.py:26
 msgid "WebAuthn"
-msgstr ""
+msgstr "WebAuthn"
 
 #: mfa/totp/forms.py:11
 msgid "Authenticator code"
 msgstr "认证器代码"
 
-#: mfa/webauthn/forms.py:15
-#, fuzzy
-#| msgid "Password"
+#: mfa/webauthn/forms.py:16
 msgid "Passwordless"
-msgstr "密码"
+msgstr "无密码"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
 "protection."
 msgstr ""
+"启用无密码操作允许您仅使用此密钥/设备登录，但会提出其他要求，例如生物特征或"
+"PIN保护。"
 
 #: socialaccount/adapter.py:36
 #, python-format
 msgid ""
 "An account already exists with this email address. Please sign in to that "
 "account first, then connect your %s account."
-msgstr "已有一个账号与此Email地址关联，请先登录该账号，然后连接你的 %s 账号。"
+msgstr "已有一个账号与此Email地址关联。请先登录该账号，然后连接您的 %s 账号。"
 
 #: socialaccount/adapter.py:40
 msgid "Invalid token."
@@ -319,11 +313,11 @@ msgstr "您的账号未设置密码。"
 
 #: socialaccount/adapter.py:42
 msgid "Your account has no verified email address."
-msgstr "您的账号下无任何验证过的Email地址。"
+msgstr "您的账号下无任何验证过的email地址。"
 
 #: socialaccount/adapter.py:44
 msgid "You cannot disconnect your last remaining third-party account."
-msgstr "您不能断开剩下的唯一的第三方账号"
+msgstr "您不能断开您剩下的唯一的第三方账号。"
 
 #: socialaccount/adapter.py:47
 #: templates/socialaccount/messages/account_connected_other.txt:2
@@ -364,7 +358,7 @@ msgstr "API密钥、客户端密钥或消费者密钥"
 
 #: socialaccount/models.py:69 templates/mfa/webauthn/authenticator_list.html:16
 msgid "Key"
-msgstr "键"
+msgstr "密钥"
 
 #: socialaccount/models.py:81
 msgid "social application"
@@ -380,7 +374,7 @@ msgstr "用户ID"
 
 #: socialaccount/models.py:119
 msgid "last login"
-msgstr "最后登录"
+msgstr "最后一次登录"
 
 #: socialaccount/models.py:120
 msgid "date joined"
@@ -404,7 +398,7 @@ msgstr "令牌"
 
 #: socialaccount/models.py:161
 msgid "\"oauth_token\" (OAuth1) or access token (OAuth2)"
-msgstr "\"oauth_token\" (OAuth1)或访问令牌 (OAuth2)"
+msgstr "\"oauth_token\" (OAuth1) 或访问令牌 (OAuth2)"
 
 #: socialaccount/models.py:165
 msgid "token secret"
@@ -412,7 +406,7 @@ msgstr "令牌密钥"
 
 #: socialaccount/models.py:166
 msgid "\"oauth_token_secret\" (OAuth1) or refresh token (OAuth2)"
-msgstr "\"oauth_token_secret\" (OAuth1)或刷新令牌 (OAuth2)"
+msgstr "\"oauth_token_secret\" (OAuth1) 或刷新令牌 (OAuth2)"
 
 #: socialaccount/models.py:169
 msgid "expires at"
@@ -428,7 +422,7 @@ msgstr "社交应用令牌"
 
 #: socialaccount/providers/douban/views.py:36
 msgid "Invalid profile data"
-msgstr "个人资料数据无效"
+msgstr "无效个人资料数据"
 
 #: socialaccount/providers/dummy/templates/dummy/authenticate_form.html:17
 msgid "Login"
@@ -443,7 +437,7 @@ msgstr "取消"
 #, python-format
 msgid ""
 "Invalid response while obtaining request token from \"%s\". Response was: %s."
-msgstr "在请求token时收到无效的响应\"%s\"，响应如下：%s."
+msgstr "在请求token时收到无效的响应\"%s\"。响应如下：%s。"
 
 #: socialaccount/providers/oauth/client.py:119
 #: socialaccount/providers/pocket/client.py:86
@@ -478,7 +472,7 @@ msgstr "账号未激活"
 
 #: templates/account/account_inactive.html:12
 msgid "This account is inactive."
-msgstr "此账号未激活"
+msgstr "此账号未激活。"
 
 #: templates/account/base_reauthenticate.html:5
 #: templates/account/base_reauthenticate.html:9
@@ -544,7 +538,7 @@ msgstr "首选Email"
 
 #: templates/account/email.html:44
 msgid "Make Primary"
-msgstr "设置首选Email"
+msgstr "设置为首选"
 
 #: templates/account/email.html:47 templates/account/email_change.html:37
 msgid "Re-send Verification"
@@ -687,7 +681,7 @@ msgstr "您的登录代码如下所示。请在打开的浏览器窗口中输入
 #: templates/account/email/login_code_message.txt:9
 #: templates/account/email/unknown_account_message.txt:6
 msgid "This mail can be safely ignored if you did not initiate this action."
-msgstr "如果您没有发起这个操作，可以安全地忽略这封邮件。"
+msgstr "如果您没有发起这个操作，您可以安全地忽略这封邮件。"
 
 #: templates/account/email/login_code_subject.txt:3
 msgid "Sign-In Code"
@@ -709,7 +703,8 @@ msgid ""
 "link below to reset your password."
 msgstr ""
 "您收到此电子邮件是因为您或其他人申请了重置您的用户账户密码。\n"
-"如果您没有申请重置密码，可以安全地忽略此邮件。点击下面的链接以重置您的密码。"
+"如果您没有申请重置密码，您可以安全地忽略此邮件。点击下面的链接以重置您的密"
+"码。"
 
 #: templates/account/email/password_reset_key_message.txt:9
 #, python-format
@@ -734,11 +729,11 @@ msgstr "密码重置"
 
 #: templates/account/email/password_set_message.txt:4
 msgid "Your password has been set."
-msgstr "您的密码已被设定。"
+msgstr "您的密码已被设置。"
 
 #: templates/account/email/password_set_subject.txt:3
 msgid "Password Set"
-msgstr "密码重置"
+msgstr "密码设置"
 
 #: templates/account/email/unknown_account_message.txt:4
 #, python-format
@@ -777,7 +772,7 @@ msgstr "您的电子邮件地址仍在等待验证。"
 
 #: templates/account/email_change.html:41
 msgid "Cancel Change"
-msgstr "放弃变更"
+msgstr "取消变更"
 
 #: templates/account/email_change.html:49
 msgid "Change to"
@@ -830,11 +825,11 @@ msgstr ""
 msgid ""
 "If you have not created an account yet, then please %(link)ssign "
 "up%(end_link)s first."
-msgstr "如果没有账号，请先 %(link)s注册%(end_link)s 。"
+msgstr "如果您还没有创建账号，请先 %(link)s注册%(end_link)s 。"
 
 #: templates/account/login.html:42
 msgid "Sign in with a passkey"
-msgstr ""
+msgstr "使用 passkey 登录"
 
 #: templates/account/login.html:47 templates/account/request_login_code.html:9
 msgid "Mail me a sign-in code"
@@ -848,36 +843,36 @@ msgstr "登出"
 
 #: templates/account/logout.html:11
 msgid "Are you sure you want to sign out?"
-msgstr "您确定要注销登录吗？"
+msgstr "您确定要登出吗？"
 
 #: templates/account/messages/cannot_delete_primary_email.txt:2
 #, python-format
 msgid "You cannot remove your primary email address (%(email)s)."
-msgstr "您不能删除您的主Email地址 (%(email)s) "
+msgstr "您不能删除您的主Email地址 (%(email)s) 。"
 
 #: templates/account/messages/email_confirmation_sent.txt:2
 #, python-format
 msgid "Confirmation email sent to %(email)s."
-msgstr "确认Email已发往 %(email)s."
+msgstr "确认邮件已发往 %(email)s。"
 
 #: templates/account/messages/email_confirmed.txt:2
 #, python-format
 msgid "You have confirmed %(email)s."
-msgstr "您已确认Email地址 %(email)s "
+msgstr "您已确认Email地址 %(email)s。"
 
 #: templates/account/messages/email_deleted.txt:2
 #, python-format
 msgid "Removed email address %(email)s."
-msgstr "已移除Email地址 %(email)s."
+msgstr "已移除Email地址 %(email)s。"
 
 #: templates/account/messages/logged_in.txt:4
 #, python-format
 msgid "Successfully signed in as %(name)s."
-msgstr "以 %(name)s..身份成功登录"
+msgstr "以 %(name)s 成功登录。"
 
 #: templates/account/messages/logged_out.txt:2
 msgid "You have signed out."
-msgstr "您已注销登录。"
+msgstr "您已登出。"
 
 #: templates/account/messages/login_code_sent.txt:2
 #, python-format
@@ -894,7 +889,7 @@ msgstr "密码设置成功。"
 
 #: templates/account/messages/primary_email_set.txt:2
 msgid "Primary email address set."
-msgstr "主Email地址已设置"
+msgstr "主Email地址已设置。"
 
 #: templates/account/password_change.html:4
 #: templates/account/password_change.html:8
@@ -910,15 +905,15 @@ msgstr "修改密码"
 
 #: templates/account/password_change.html:22
 msgid "Forgot Password?"
-msgstr "忘记密码了？"
+msgstr "忘记密码？"
 
 #: templates/account/password_reset.html:14
 msgid ""
 "Forgotten your password? Enter your email address below, and we'll send you "
 "an email allowing you to reset it."
 msgstr ""
-"忘记密码了吗？请在下面输入您的电子邮件地址，我们将发送一封电子邮件，让您可以"
-"重置密码。"
+"忘记您的密码了吗？请在下面输入您的电子邮件地址，我们将发送一封电子邮件，让您"
+"可以重置密码。"
 
 #: templates/account/password_reset.html:25
 msgid "Reset My Password"
@@ -938,7 +933,7 @@ msgstr ""
 
 #: templates/account/password_reset_from_key.html:10
 msgid "Bad Token"
-msgstr "Bad Token"
+msgstr "不正确的 token"
 
 #: templates/account/password_reset_from_key.html:18
 #, python-format
@@ -1004,11 +999,11 @@ msgstr "注意"
 #: templates/account/snippets/already_logged_in.html:7
 #, python-format
 msgid "You are already logged in as %(user_display)s."
-msgstr "您已以 %(user_display)s的身份登录"
+msgstr "您已以 %(user_display)s 的身份登录。"
 
 #: templates/account/snippets/warn_no_email.html:3
 msgid "Warning:"
-msgstr "警告: "
+msgstr "警告："
 
 #: templates/account/snippets/warn_no_email.html:3
 msgid ""
@@ -1023,7 +1018,7 @@ msgstr ""
 #: templates/account/verified_email_required.html:5
 #: templates/account/verified_email_required.html:9
 msgid "Verify Your Email Address"
-msgstr "验证您的Email地址。"
+msgstr "验证您的Email地址"
 
 #: templates/account/verification_sent.html:12
 msgid ""
@@ -1077,7 +1072,7 @@ msgstr "菜单："
 #: templates/allauth/layouts/base.html:43 templates/mfa/authenticate.html:10
 #: templates/mfa/index.html:5 templates/mfa/index.html:9
 msgid "Two-Factor Authentication"
-msgstr "双重身份认证"
+msgstr "双因素身份认证"
 
 #: templates/allauth/layouts/base.html:49
 #: templates/usersessions/usersession_list.html:6
@@ -1089,12 +1084,12 @@ msgstr "会话"
 msgid ""
 "Your account is protected by two-factor authentication. Please enter an "
 "authenticator code:"
-msgstr "您的帐户已受到双重身份认证的保护。请输入身份验证器代码："
+msgstr "您的帐户已受到双因素身份认证的保护。请输入身份验证器代码："
 
 #: templates/mfa/email/recovery_codes_generated_message.txt:4
 msgid ""
 "A new set of Two-Factor Authentication recovery codes has been generated."
-msgstr "已生成一组新的双重身份认证恢复代码。"
+msgstr "已生成一组新的双因素身份认证恢复代码。"
 
 #: templates/mfa/email/recovery_codes_generated_subject.txt:3
 msgid "New Recovery Codes Generated"
@@ -1119,24 +1114,20 @@ msgid "Authenticator App Deactivated"
 msgstr "身份验证器应用已停用"
 
 #: templates/mfa/email/webauthn_added_message.txt:4
-#, fuzzy
-#| msgid "A new set of recovery codes has been generated."
 msgid "A new security key has been added."
-msgstr "已生成新的恢复代码集。"
+msgstr "已添加新的安全密钥。"
 
 #: templates/mfa/email/webauthn_added_subject.txt:3
 msgid "Security Key Added"
-msgstr ""
+msgstr "已添加新的密钥"
 
 #: templates/mfa/email/webauthn_removed_message.txt:4
-#, fuzzy
-#| msgid "Your email has been confirmed."
 msgid "A security key has been removed."
-msgstr "您的Email已经被确认。"
+msgstr "一个安全密钥已被移除。"
 
 #: templates/mfa/email/webauthn_removed_subject.txt:3
 msgid "Security Key Removed"
-msgstr ""
+msgstr "密钥已被移除"
 
 #: templates/mfa/index.html:14 templates/mfa/totp/base.html:4
 msgid "Authenticator App"
@@ -1160,26 +1151,26 @@ msgstr "激活"
 
 #: templates/mfa/index.html:45 templates/mfa/webauthn/base.html:4
 msgid "Security Keys"
-msgstr ""
+msgstr "安全密钥"
 
 #: templates/mfa/index.html:50
 #, python-format
 msgid "You have added %(count)s security key/device."
 msgid_plural "You have added %(count)s security keys/devices."
-msgstr[0] ""
+msgstr[0] "您已添加%(count)s个安全密钥/设备。"
 
 #: templates/mfa/index.html:53
 msgid "No security keys have been added."
-msgstr ""
+msgstr "未添加安全密钥。"
 
 #: templates/mfa/index.html:60
 msgid "Manage"
-msgstr ""
+msgstr "管理"
 
 #: templates/mfa/index.html:65
 #: templates/mfa/webauthn/authenticator_list.html:68
 msgid "Add"
-msgstr ""
+msgstr "添加"
 
 #: templates/mfa/index.html:75 templates/mfa/recovery_codes/base.html:4
 #: templates/mfa/recovery_codes/generate.html:6
@@ -1217,11 +1208,11 @@ msgstr "已生成新的恢复代码集。"
 
 #: templates/mfa/messages/webauthn_added.txt:2
 msgid "Security key added."
-msgstr ""
+msgstr "已添加安全密钥。"
 
 #: templates/mfa/messages/webauthn_removed.txt:2
 msgid "Security key removed."
-msgstr ""
+msgstr "已移除安全密钥。"
 
 #: templates/mfa/reauthenticate.html:6
 msgid "Enter an authenticator code:"
@@ -1262,8 +1253,8 @@ msgid ""
 "below with your authenticator app. Then, input the verification code "
 "generated by the app below."
 msgstr ""
-"为了保护您的帐户，您需要使用身份验证器应用扫描下方的二维码。然后，在下方输入"
-"应用生成的验证代码。"
+"为使用双因素验证保护您的帐户，您需要使用身份验证器应用扫描下方的二维码。然"
+"后，在下方输入应用生成的验证代码。"
 
 #: templates/mfa/totp/activate_form.html:21
 msgid "Authenticator secret"
@@ -1288,79 +1279,73 @@ msgstr "您即将停用基于身份验证器应用的身份验证。确定要执
 
 #: templates/mfa/webauthn/add_form.html:7
 msgid "Add Security Key or Device"
-msgstr ""
+msgstr "添加安全密钥或设备"
 
 #: templates/mfa/webauthn/add_form.html:18
 msgid "Add key or device"
-msgstr ""
+msgstr "添加密钥或设备"
 
 #: templates/mfa/webauthn/authenticator_confirm_delete.html:6
 msgid "Remove Security Key or Device"
-msgstr ""
+msgstr "移除安全密钥或设备"
 
 #: templates/mfa/webauthn/authenticator_confirm_delete.html:8
-#, fuzzy
-#| msgid "Are you sure you want to sign out?"
 msgid "Are you sure you want to remove this security key?"
-msgstr "您确定要注销登录吗？"
+msgstr "您确定要移除此安全密钥吗？"
 
 #: templates/mfa/webauthn/authenticator_list.html:8
 msgid "Security Keys and Devices"
-msgstr ""
+msgstr "安全密钥和设备"
 
 #: templates/mfa/webauthn/authenticator_list.html:11
 msgid "You have not added any security keys/devices."
-msgstr ""
+msgstr "您尚未添加任何安全密钥/设备。"
 
 #: templates/mfa/webauthn/authenticator_list.html:19
 msgid "Usage"
-msgstr ""
+msgstr "用途"
 
 #: templates/mfa/webauthn/authenticator_list.html:31
 msgid "Passkey"
-msgstr ""
+msgstr "Passkey"
 
 #: templates/mfa/webauthn/authenticator_list.html:35
-#, fuzzy
-#| msgid "secret key"
 msgid "Security key"
-msgstr "密钥"
+msgstr "安全密钥"
 
 #: templates/mfa/webauthn/authenticator_list.html:38
 msgid "This key does not specify whether is a passkey."
-msgstr ""
+msgstr "此密钥未指定是否为 passkey。"
 
 #: templates/mfa/webauthn/authenticator_list.html:39
-#, fuzzy
-#| msgid "Unverified"
 msgid "Unspecified"
-msgstr "未验证"
+msgstr "未指定"
 
 #: templates/mfa/webauthn/authenticator_list.html:44
 #, python-format
 msgid "Added on %(created_at)s"
-msgstr ""
+msgstr "添加于 %(created_at)s"
 
 #: templates/mfa/webauthn/authenticator_list.html:46
 #, python-format
 msgid "Last used %(last_used)s"
-msgstr ""
+msgstr "上次使用：%(last_used)s"
 
 #: templates/mfa/webauthn/authenticator_list.html:54
 msgid "Edit"
-msgstr ""
+msgstr "编辑"
 
 #: templates/mfa/webauthn/edit_form.html:7
 msgid "Edit Security Key or Device"
-msgstr ""
+msgstr "编辑安全密钥或设备"
 
 #: templates/mfa/webauthn/edit_form.html:18
 msgid "Save"
-msgstr ""
+msgstr "保存"
 
 #: templates/mfa/webauthn/snippets/scripts.html:2
 msgid "This functionality requires JavaScript."
-msgstr ""
+msgstr "此功能需要 JavaScript。"
 
 #: templates/socialaccount/authentication_error.html:5
 #: templates/socialaccount/authentication_error.html:9
diff --git a/allauth/locale/zh_Hant/LC_MESSAGES/django.po b/allauth/locale/zh_Hant/LC_MESSAGES/django.po
index f9efb962..5df4e1ff 100644
--- a/allauth/locale/zh_Hant/LC_MESSAGES/django.po
+++ b/allauth/locale/zh_Hant/LC_MESSAGES/django.po
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-06-30 09:26-0500\n"
-"PO-Revision-Date: 2024-06-02 17:11+0000\n"
+"POT-Creation-Date: 2024-07-06 06:50-0500\n"
+"PO-Revision-Date: 2024-07-03 05:09+0000\n"
 "Last-Translator: hugoalh <hugoalh@users.noreply.hosted.weblate.org>\n"
 "Language-Team: Chinese (Traditional) <https://hosted.weblate.org/projects/"
 "allauth/django-allauth/zh_Hant/>\n"
@@ -17,97 +17,90 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 5.6-dev\n"
+"X-Generator: Weblate 5.7-dev\n"
 
 #: account/adapter.py:58
 msgid "This account is currently inactive."
 msgstr "此帳號目前沒有啟用。"
 
 #: account/adapter.py:60
-#, fuzzy
-#| msgid "You cannot remove your primary email address (%(email)s)."
 msgid "You cannot remove your primary email address."
-msgstr "您不能移除您的主要的電子郵件地址 (%(email)s) 。"
+msgstr "你不能移除你的主要電子郵件地址。"
 
 #: account/adapter.py:63
 msgid "This email address is already associated with this account."
-msgstr "此電子郵件已與這個帳號連結了。"
+msgstr "此電子郵件地址已經與此帳號連結了。"
 
 #: account/adapter.py:66
 msgid "The email address and/or password you specified are not correct."
-msgstr "您提供的電子郵件地址或密碼不正確。"
+msgstr "你提供的電子郵件地址和／或密碼不正確。"
 
 #: account/adapter.py:68
 msgid "A user is already registered with this email address."
-msgstr "已經有人使用這一個電子郵件註冊了。"
+msgstr "已經有使用者使用此電子郵件地址註冊。"
 
 #: account/adapter.py:69
 msgid "Please type your current password."
-msgstr "請輸入您目前的密碼。"
+msgstr "請輸入你目前的密碼。"
 
 #: account/adapter.py:70 mfa/adapter.py:40
 msgid "Incorrect code."
-msgstr ""
+msgstr "不正確的代碼。"
 
 #: account/adapter.py:71
 msgid "Incorrect password."
 msgstr "不正確的密碼。"
 
 #: account/adapter.py:72
-#, fuzzy
-#| msgid "Invalid confirmation key."
 msgid "Invalid or expired key."
-msgstr "Ongeldige bevestigingssleutel."
+msgstr "無效或已過期的金鑰。"
 
 #: account/adapter.py:73
 msgid "The password reset token was invalid."
-msgstr ""
+msgstr "密碼重置令牌無效。"
 
 #: account/adapter.py:74
-#, fuzzy, python-format
-#| msgid "Your account has no verified email address."
+#, python-format
 msgid "You cannot add more than %d email addresses."
-msgstr "您的帳號下沒有驗證過的電子郵件地址。"
+msgstr "你不能新增超過 %d 個電子郵件地址。"
 
 #: account/adapter.py:76
 msgid "Too many failed login attempts. Try again later."
-msgstr "登錄失敗次數過多，請稍後再試。"
+msgstr "登入嘗試失敗次數過多。請稍後再試。"
 
 #: account/adapter.py:78
 msgid "The email address is not assigned to any user account"
-msgstr "還沒有其他帳號使用這個電子郵件地址"
+msgstr "此電子郵件地址未分配給任何使用者帳號"
 
 #: account/adapter.py:79
 #: templates/account/messages/unverified_primary_email.txt:2
 msgid "Your primary email address must be verified."
-msgstr "您的主要電子郵件位址必須被驗證過。"
+msgstr "你的主要電子郵件地址必須已驗證。"
 
 #: account/adapter.py:81
 msgid "Username can not be used. Please use other username."
-msgstr "無法使用此使用者名稱，請使用其他名稱。"
+msgstr "無法使用此使用者名稱。請使用其他使用者名稱。"
 
 #: account/adapter.py:84
 msgid "The username and/or password you specified are not correct."
-msgstr "您提供的使用者名稱或密碼不正確。"
+msgstr "你提供的使用者名稱和／或密碼不正確。"
 
-#: account/adapter.py:736
-#, fuzzy
-#| msgid "Forgot Password?"
+#: account/adapter.py:739
 msgid "Use your password"
-msgstr "忘記密碼了？"
+msgstr "使用你的密碼"
 
-#: account/adapter.py:750
+#: account/adapter.py:748
 msgid "Use authenticator app or code"
-msgstr ""
+msgstr "使用驗證器應用程式或代碼"
 
-#: account/adapter.py:758 templates/mfa/authenticate.html:36
+#: account/adapter.py:755 templates/mfa/authenticate.html:36
 #: templates/mfa/webauthn/reauthenticate.html:15
 msgid "Use security key or device"
-msgstr ""
+msgstr "使用安全金鑰或裝置"
 
 #: account/admin.py:23
 msgid "Mark selected email addresses as verified"
-msgstr "將選擇的電子郵件地址標記為已驗證"
+msgstr "標記已選取的電子郵件地址為已驗證"
 
 #: account/apps.py:11
 msgid "Accounts"
@@ -134,7 +127,7 @@ msgstr "電子郵件地址"
 #: account/forms.py:106 account/forms.py:307 account/forms.py:494
 #: account/forms.py:575
 msgid "Email"
-msgstr "E-mail"
+msgstr "電子郵件"
 
 #: account/forms.py:109 account/forms.py:112 account/forms.py:259
 #: account/forms.py:262
@@ -151,26 +144,20 @@ msgid "Login"
 msgstr "登入"
 
 #: account/forms.py:136
-#, fuzzy
-#| msgid "Forgot Password?"
 msgid "Forgot your password?"
-msgstr "忘記密碼了？"
+msgstr "忘記你的密碼？"
 
 #: account/forms.py:298
-#, fuzzy
-#| msgid "Email (optional)"
 msgid "Email (again)"
-msgstr "E-mail (可不填)"
+msgstr "電子郵件（再次）"
 
 #: account/forms.py:302
-#, fuzzy
-#| msgid "email confirmation"
 msgid "Email address confirmation"
-msgstr "電子郵件確認"
+msgstr "電子郵件地址確認"
 
 #: account/forms.py:310
 msgid "Email (optional)"
-msgstr "E-mail (可不填)"
+msgstr "電子郵件（選擇性）"
 
 #: account/forms.py:369
 msgid "You must type the same email each time."
@@ -178,7 +165,7 @@ msgstr "你必須每次輸入相同的電子郵件。"
 
 #: account/forms.py:417 account/forms.py:563
 msgid "Password (again)"
-msgstr "密碼 (再一次)"
+msgstr "密碼（再次）"
 
 #: account/forms.py:543
 msgid "Current Password"
@@ -190,12 +177,12 @@ msgstr "新密碼"
 
 #: account/forms.py:546 account/forms.py:630
 msgid "New Password (again)"
-msgstr "新密碼 (再一次)"
+msgstr "新密碼（再次）"
 
 #: account/forms.py:716 account/forms.py:718 mfa/base/forms.py:15
 #: mfa/base/forms.py:17 mfa/totp/forms.py:13
 msgid "Code"
-msgstr ""
+msgstr "代碼"
 
 #: account/models.py:24
 msgid "user"
@@ -211,7 +198,7 @@ msgstr "已驗證"
 
 #: account/models.py:33
 msgid "primary"
-msgstr "主要的"
+msgstr "主要"
 
 #: account/models.py:39
 msgid "email addresses"
@@ -219,15 +206,15 @@ msgstr "電子郵件地址"
 
 #: account/models.py:153
 msgid "created"
-msgstr "以建立"
+msgstr "已建立"
 
 #: account/models.py:154
 msgid "sent"
-msgstr "已送出"
+msgstr "已傳送"
 
 #: account/models.py:155 socialaccount/models.py:69
 msgid "key"
-msgstr "key"
+msgstr "金鑰"
 
 #: account/models.py:160
 msgid "email confirmation"
@@ -239,39 +226,37 @@ msgstr "電子郵件確認"
 
 #: headless/apps.py:7
 msgid "Headless"
-msgstr ""
+msgstr "無頭"
 
 #: mfa/adapter.py:32
 msgid ""
 "You cannot add an email address to an account protected by two-factor "
 "authentication."
-msgstr ""
+msgstr "你不能將電子郵件地址新增至受雙重驗證保護的帳號。"
 
 #: mfa/adapter.py:35
 msgid "You cannot deactivate two-factor authentication."
-msgstr ""
+msgstr "你不能停用雙重驗證。"
 
 #: mfa/adapter.py:38
 msgid ""
 "You cannot generate recovery codes without having two-factor authentication "
 "enabled."
-msgstr ""
+msgstr "你不能在未啟用雙重驗證下產生復原代碼。"
 
 #: mfa/adapter.py:42
 msgid ""
 "You cannot activate two-factor authentication until you have verified your "
 "email address."
-msgstr ""
+msgstr "你不能在驗證你的電子郵件地址之前啟用雙重驗證。"
 
 #: mfa/adapter.py:141
-#, fuzzy
-#| msgid "secret key"
 msgid "Master key"
-msgstr "secret key"
+msgstr "主要金鑰"
 
 #: mfa/adapter.py:143
 msgid "Backup key"
-msgstr ""
+msgstr "備份金鑰"
 
 #: mfa/adapter.py:144
 #, python-brace-format
@@ -280,36 +265,36 @@ msgstr ""
 
 #: mfa/apps.py:9
 msgid "MFA"
-msgstr ""
+msgstr "多重要素驗證"
 
 #: mfa/models.py:24
 msgid "Recovery codes"
-msgstr ""
+msgstr "恢復代碼"
 
 #: mfa/models.py:25
 msgid "TOTP Authenticator"
-msgstr ""
+msgstr "TOTP 驗證器"
 
 #: mfa/models.py:26
 msgid "WebAuthn"
-msgstr ""
+msgstr "WebAuthn"
 
 #: mfa/totp/forms.py:11
 msgid "Authenticator code"
-msgstr ""
+msgstr "驗證器代碼"
 
-#: mfa/webauthn/forms.py:15
-#, fuzzy
-#| msgid "Password"
+#: mfa/webauthn/forms.py:16
 msgid "Passwordless"
-msgstr "密碼"
+msgstr "無密碼"
 
-#: mfa/webauthn/forms.py:18
+#: mfa/webauthn/forms.py:19
 msgid ""
 "Enabling passwordless operation allows you to sign in using just this key/"
 "device, but imposes additional requirements such as biometrics or PIN "
 "protection."
 msgstr ""
+"啟用無密碼操作可以讓你僅使用此金鑰／裝置登入，但會提出其他要求，例如生物辨識"
+"或 PIN 保護。"
 
 #: socialaccount/adapter.py:36
 #, fuzzy, python-format
@@ -323,10 +308,8 @@ msgstr ""
 "已經有一個帳號與此電子郵件連結了，請先登入該帳號，然後連接你的 %s 帳號。"
 
 #: socialaccount/adapter.py:40
-#, fuzzy
-#| msgid "Invalid confirmation key."
 msgid "Invalid token."
-msgstr "Ongeldige bevestigingssleutel."
+msgstr "無效的令牌。"
 
 #: socialaccount/adapter.py:41
 msgid "Your account has no password set up."
@@ -371,19 +354,19 @@ msgstr "client id"
 
 #: socialaccount/models.py:60
 msgid "App ID, or consumer key"
-msgstr "App ID, or consumer key"
+msgstr "應用程式 ID 或消費者金鑰"
 
 #: socialaccount/models.py:63
 msgid "secret key"
-msgstr "secret key"
+msgstr "秘密金鑰"
 
 #: socialaccount/models.py:66
 msgid "API secret, client secret, or consumer secret"
-msgstr "API secret, client secret, or consumer secret"
+msgstr "API 秘密、客戶秘密或消費者秘密"
 
 #: socialaccount/models.py:69 templates/mfa/webauthn/authenticator_list.html:16
 msgid "Key"
-msgstr "Key"
+msgstr "金鑰"
 
 #: socialaccount/models.py:81
 msgid "social application"
@@ -395,7 +378,7 @@ msgstr "社群應用程式"
 
 #: socialaccount/models.py:117
 msgid "uid"
-msgstr "uid"
+msgstr "使用者 ID"
 
 #: socialaccount/models.py:119
 msgid "last login"
diff --git a/allauth/mfa/base/views.py b/allauth/mfa/base/views.py
index 4bd2ccd4..cf62bb9a 100644
--- a/allauth/mfa/base/views.py
+++ b/allauth/mfa/base/views.py
@@ -84,7 +84,9 @@ class AuthenticateView(TemplateView):
             ret.update(
                 {
                     "webauthn_form": self.webauthn_form,
-                    "js_data": {"credentials": self.webauthn_form.authentication_data},
+                    "js_data": {
+                        "request_options": self.webauthn_form.authentication_data
+                    },
                 }
             )
         return ret
diff --git a/allauth/mfa/stages.py b/allauth/mfa/stages.py
index 2979b0a3..3a5f3ac6 100644
--- a/allauth/mfa/stages.py
+++ b/allauth/mfa/stages.py
@@ -9,6 +9,7 @@ from allauth.mfa.webauthn.internal.flows import did_use_passwordless_login
 
 
 class AuthenticateStage(LoginStage):
+    # NOTE: Duplicated in `allauth.headless.constants.Flow.MFA_AUTHENTICATE`.
     key = "mfa_authenticate"
 
     def handle(self):
diff --git a/allauth/mfa/static/mfa/js/webauthn.js b/allauth/mfa/static/mfa/js/webauthn.js
index c976950c..bd5eeae6 100644
--- a/allauth/mfa/static/mfa/js/webauthn.js
+++ b/allauth/mfa/static/mfa/js/webauthn.js
@@ -26,7 +26,7 @@
     addBtn.addEventListener('click', async function () {
       const passwordless = passwordlessCb ? passwordlessCb.checked : false
       try {
-        const credential = await createCredentials(o.data.credentials, passwordless)
+        const credential = await createCredentials(o.data.creation_options, passwordless)
         credentialInput.value = JSON.stringify(credential)
         form.submit()
       } catch (e) {
@@ -52,7 +52,7 @@
           throw new Error('Unable to fetch passkey data from server.')
         }
         const data = await response.json()
-        const credential = await webauthnJSON.get(data.credentials)
+        const credential = await webauthnJSON.get(data.request_options)
         credentialInput.value = JSON.stringify(credential)
         form.submit()
       } catch (e) {
@@ -68,7 +68,7 @@
     authenticateBtn.addEventListener('click', async function (e) {
       e.preventDefault()
       try {
-        const credential = await webauthnJSON.get(o.data.credentials)
+        const credential = await webauthnJSON.get(o.data.request_options)
         credentialInput.value = JSON.stringify(credential)
         form.submit()
       } catch (e) {
diff --git a/allauth/mfa/webauthn/forms.py b/allauth/mfa/webauthn/forms.py
index 9c5fdc84..e74e2fb5 100644
--- a/allauth/mfa/webauthn/forms.py
+++ b/allauth/mfa/webauthn/forms.py
@@ -1,11 +1,12 @@
 from django import forms
 from django.utils.translation import gettext_lazy as _
 
+from allauth.core import context
 from allauth.mfa import app_settings
 from allauth.mfa.adapter import get_adapter
 from allauth.mfa.base.internal.flows import check_rate_limit
 from allauth.mfa.models import Authenticator
-from allauth.mfa.webauthn.internal import auth
+from allauth.mfa.webauthn.internal import auth, flows
 
 
 class AddWebAuthnForm(forms.Form):
@@ -22,8 +23,9 @@ class AddWebAuthnForm(forms.Form):
 
     def __init__(self, *args, **kwargs):
         self.user = kwargs.pop("user")
-        self.registration_data = auth.begin_registration(self.user)
         initial = kwargs.setdefault("initial", {})
+        passwordless = bool(initial.get("passwordless"))
+        self.registration_data = auth.begin_registration(self.user, passwordless)
         initial.setdefault(
             "name",
             get_adapter().generate_authenticator_name(
@@ -32,6 +34,20 @@ class AddWebAuthnForm(forms.Form):
         )
         super().__init__(*args, **kwargs)
 
+    def clean_name(self):
+        """
+        We don't want to make `name` a required field, as the WebAuthn
+        ceremony happens before posting the resulting credential, and we don't
+        want to reject a valid credential because of a missing name -- it might
+        be resident already. So, gracefully plug in a name.
+        """
+        name = self.cleaned_data["name"]
+        if not name:
+            name = get_adapter().generate_authenticator_name(
+                self.user, Authenticator.Type.WEBAUTHN
+            )
+        return name
+
     def clean(self):
         cleaned_data = super().clean()
         credential = cleaned_data.get("credential")
@@ -90,7 +106,7 @@ class EditWebAuthnForm(forms.Form):
         super().__init__(*args, **kwargs)
 
     def save(self) -> Authenticator:
-        auth = self.instance.wrap()
-        auth.name = self.cleaned_data["name"]
-        self.instance.save()
+        flows.rename_authenticator(
+            context.request, self.instance, self.cleaned_data["name"]
+        )
         return self.instance
diff --git a/allauth/mfa/webauthn/internal/auth.py b/allauth/mfa/webauthn/internal/auth.py
index ca0384e8..e87c0502 100644
--- a/allauth/mfa/webauthn/internal/auth.py
+++ b/allauth/mfa/webauthn/internal/auth.py
@@ -13,6 +13,7 @@ from fido2.webauthn import (
     PublicKeyCredentialRpEntity,
     PublicKeyCredentialUserEntity,
     RegistrationResponse,
+    ResidentKeyRequirement,
     UserVerificationRequirement,
 )
 
@@ -79,13 +80,22 @@ def parse_registration_response(response: Any) -> RegistrationResponse:
         raise get_adapter().validation_error("incorrect_code")
 
 
-def begin_registration(user) -> Dict:
+def begin_registration(user, passwordless: bool) -> Dict:
     server = get_server()
     credentials = get_credentials(user)
     registration_data, state = server.register_begin(
         user=build_user_payload(user),
         credentials=credentials,
-        user_verification=UserVerificationRequirement.DISCOURAGED,
+        resident_key_requirement=(
+            ResidentKeyRequirement.REQUIRED
+            if passwordless
+            else ResidentKeyRequirement.DISCOURAGED
+        ),
+        user_verification=(
+            UserVerificationRequirement.REQUIRED
+            if passwordless
+            else UserVerificationRequirement.DISCOURAGED
+        ),
         challenge=generate_challenge(),
         extensions=EXTENSIONS,
     )
diff --git a/allauth/mfa/webauthn/internal/flows.py b/allauth/mfa/webauthn/internal/flows.py
index 71bb784d..aa5b5377 100644
--- a/allauth/mfa/webauthn/internal/flows.py
+++ b/allauth/mfa/webauthn/internal/flows.py
@@ -1,4 +1,4 @@
-from typing import Optional, Tuple
+from typing import Iterable, Optional, Tuple
 
 from django.contrib import messages
 from django.http import HttpRequest
@@ -22,9 +22,16 @@ from allauth.mfa.recovery_codes.internal.flows import (
 from allauth.mfa.webauthn.internal import auth
 
 
+def begin_registration(request: HttpRequest, user, passwordless: bool) -> dict:
+    raise_if_reauthentication_required(request)
+    creation_options = auth.begin_registration(user, passwordless)
+    return creation_options
+
+
 def add_authenticator(
     request, name: str, credential: dict
 ) -> Tuple[Authenticator, Optional[Authenticator]]:
+    raise_if_reauthentication_required(request)
     authenticator = auth.WebAuthn.add(
         request.user,
         name,
@@ -43,6 +50,12 @@ def add_authenticator(
     return authenticator, rc_authenticator
 
 
+def remove_authenticators(request, authenticators: Iterable[Authenticator]) -> None:
+    raise_if_reauthentication_required(request)
+    for authenticator in authenticators:
+        remove_authenticator(request, authenticator)
+
+
 def remove_authenticator(request, authenticator: Authenticator):
     raise_if_reauthentication_required(request)
     delete_and_cleanup(request, authenticator)
@@ -63,3 +76,14 @@ def did_use_passwordless_login(request: HttpRequest) -> bool:
         == ("mfa", "webauthn", True)
         for record in records
     )
+
+
+def reauthenticate(request: HttpRequest, authenticator: Authenticator):
+    post_authentication(request, authenticator, reauthenticated=True)
+
+
+def rename_authenticator(request, authenticator: Authenticator, name: str):
+    raise_if_reauthentication_required(request)
+    wrapper = authenticator.wrap()
+    wrapper.name = name
+    authenticator.save()
diff --git a/allauth/mfa/webauthn/views.py b/allauth/mfa/webauthn/views.py
index cdd82027..f252803e 100644
--- a/allauth/mfa/webauthn/views.py
+++ b/allauth/mfa/webauthn/views.py
@@ -12,7 +12,6 @@ from allauth.account.decorators import reauthentication_required
 from allauth.account.mixins import NextRedirectMixin
 from allauth.account.models import Login
 from allauth.account.views import BaseReauthenticateView
-from allauth.mfa.base.internal.flows import post_authentication
 from allauth.mfa.models import Authenticator
 from allauth.mfa.webauthn.forms import (
     AddWebAuthnForm,
@@ -30,7 +29,7 @@ class AddWebAuthnView(FormView):
 
     def get_context_data(self, **kwargs):
         ret = super().get_context_data()
-        ret["js_data"] = {"credentials": ret["form"].registration_data}
+        ret["js_data"] = {"creation_options": ret["form"].registration_data}
         return ret
 
     def get_form_kwargs(self):
@@ -101,7 +100,7 @@ class LoginWebAuthnView(FormView):
     def get(self, request, *args, **kwargs):
         if get_account_adapter().is_ajax(request):
             form = self.get_form()
-            data = {"credentials": form.authentication_data}
+            data = {"request_options": form.authentication_data}
             return JsonResponse(data)
         return HttpResponseRedirect(reverse("account_login"))
 
@@ -141,12 +140,12 @@ class ReauthenticateWebAuthnView(BaseReauthenticateView):
 
     def form_valid(self, form):
         authenticator = form.cleaned_data["credential"]
-        post_authentication(self.request, authenticator, reauthenticated=True)
+        flows.reauthenticate(self.request, authenticator)
         return super().form_valid(form)
 
     def get_context_data(self, **kwargs):
         ret = super().get_context_data()
-        ret["js_data"] = {"credentials": ret["form"].authentication_data}
+        ret["js_data"] = {"request_options": ret["form"].authentication_data}
         return ret
 
 
diff --git a/allauth/socialaccount/internal/flows/signup.py b/allauth/socialaccount/internal/flows/signup.py
index a5666303..c57b73e0 100644
--- a/allauth/socialaccount/internal/flows/signup.py
+++ b/allauth/socialaccount/internal/flows/signup.py
@@ -5,6 +5,7 @@ from django.urls import reverse
 from allauth import app_settings as allauth_settings
 from allauth.account import app_settings as account_settings
 from allauth.account.adapter import get_adapter as get_account_adapter
+from allauth.account.internal.flows.signup import prevent_enumeration
 from allauth.account.utils import (
     assess_unique_email,
     complete_signup,
@@ -75,9 +76,7 @@ def process_auto_signup(request, sociallogin):
             # address. Instead, we're going to send the user an email that
             # the account already exists, and on the outside make it appear
             # as if an email verification mail was sent.
-            account_adapter = get_account_adapter(request)
-            account_adapter.send_account_already_exists_mail(email)
-            resp = account_adapter.respond_email_verification_sent(request, None)
+            resp = prevent_enumeration(request, email)
             return False, resp
     elif app_settings.EMAIL_REQUIRED:
         # Nope, email is required and we don't have it yet...
diff --git a/allauth/socialaccount/providers/amazon_cognito/views.py b/allauth/socialaccount/providers/amazon_cognito/views.py
index 4871138a..424413c2 100644
--- a/allauth/socialaccount/providers/amazon_cognito/views.py
+++ b/allauth/socialaccount/providers/amazon_cognito/views.py
@@ -1,5 +1,6 @@
 from allauth.socialaccount import app_settings
 from allauth.socialaccount.adapter import get_adapter
+from allauth.socialaccount.models import SocialToken
 from allauth.socialaccount.providers.oauth2.views import (
     OAuth2Adapter,
     OAuth2CallbackView,
@@ -39,9 +40,9 @@ class AmazonCognitoOAuth2Adapter(OAuth2Adapter):
     def profile_url(self):
         return "{}/oauth2/userInfo".format(self.domain)
 
-    def complete_login(self, request, app, access_token, **kwargs):
+    def complete_login(self, request, app, token: SocialToken, **kwargs):
         headers = {
-            "Authorization": "Bearer {}".format(access_token),
+            "Authorization": "Bearer {}".format(token.token),
         }
         extra_data = (
             get_adapter().get_requests_session().get(self.profile_url, headers=headers)
diff --git a/allauth/socialaccount/providers/atlassian/views.py b/allauth/socialaccount/providers/atlassian/views.py
index 3acb92e2..51ea5e89 100644
--- a/allauth/socialaccount/providers/atlassian/views.py
+++ b/allauth/socialaccount/providers/atlassian/views.py
@@ -1,4 +1,5 @@
 from allauth.socialaccount.adapter import get_adapter
+from allauth.socialaccount.models import SocialToken
 from allauth.socialaccount.providers.oauth2.views import (
     OAuth2Adapter,
     OAuth2CallbackView,
@@ -12,9 +13,9 @@ class AtlassianOAuth2Adapter(OAuth2Adapter):
     authorize_url = "https://auth.atlassian.com/authorize"
     profile_url = "https://api.atlassian.com/me"
 
-    def complete_login(self, request, app, access_token, **kwargs):
+    def complete_login(self, request, app, token: SocialToken, **kwargs):
         headers = {
-            "Authorization": f"Bearer {access_token}",
+            "Authorization": f"Bearer {token.token}",
             "Accept": "application/json",
         }
         response = (
diff --git a/allauth/socialaccount/providers/facebook/provider.py b/allauth/socialaccount/providers/facebook/provider.py
index 643da5f8..ed091ced 100644
--- a/allauth/socialaccount/providers/facebook/provider.py
+++ b/allauth/socialaccount/providers/facebook/provider.py
@@ -1,4 +1,3 @@
-import json
 import requests
 import string
 from urllib.parse import quote
@@ -9,7 +8,6 @@ from django.template.loader import render_to_string
 from django.urls import reverse
 from django.utils.crypto import get_random_string
 from django.utils.html import escapejs
-from django.utils.safestring import mark_safe
 
 from allauth.account.models import EmailAddress
 from allauth.socialaccount.adapter import get_adapter
@@ -176,7 +174,7 @@ class FacebookProvider(OAuth2Provider):
             "errorUrl": abs_uri("socialaccount_login_error"),
             "csrfToken": get_token(request),
         }
-        ctx = {"fb_data": mark_safe(json.dumps(fb_data))}
+        ctx = {"fb_data": fb_data}
         return render_to_string("facebook/fbconnect.html", ctx, request=request)
 
     def get_nonce(self, request, or_create=False, pop=False):
diff --git a/allauth/socialaccount/providers/facebook/static/facebook/js/fbconnect.js b/allauth/socialaccount/providers/facebook/static/facebook/js/fbconnect.js
index 7f61478a..3d41e95e 100644
--- a/allauth/socialaccount/providers/facebook/static/facebook/js/fbconnect.js
+++ b/allauth/socialaccount/providers/facebook/static/facebook/js/fbconnect.js
@@ -29,7 +29,7 @@
   }
 
   var allauth = window.allauth = window.allauth || {}
-  var fbSettings = JSON.parse(document.getElementById('allauth-facebook-settings').innerHTML)
+  const fbSettings = JSON.parse(document.getElementById('allauth-facebook-settings').textContent)
   var fbInitialized = false
 
   allauth.facebook = {
diff --git a/allauth/socialaccount/providers/facebook/templates/facebook/fbconnect.html b/allauth/socialaccount/providers/facebook/templates/facebook/fbconnect.html
index 95b8bc2b..703716d2 100644
--- a/allauth/socialaccount/providers/facebook/templates/facebook/fbconnect.html
+++ b/allauth/socialaccount/providers/facebook/templates/facebook/fbconnect.html
@@ -1,4 +1,4 @@
 {% load static %}
 <div id="fb-root"></div>
-<script id="allauth-facebook-settings" type="application/json">{{ fb_data }}</script>
+{{ fb_data|json_script:"allauth-facebook-settings" }}
 <script type="text/javascript" src="{% static 'facebook/js/fbconnect.js' %}"></script>
diff --git a/allauth/socialaccount/providers/jupyterhub/views.py b/allauth/socialaccount/providers/jupyterhub/views.py
index ce61875c..818df56c 100644
--- a/allauth/socialaccount/providers/jupyterhub/views.py
+++ b/allauth/socialaccount/providers/jupyterhub/views.py
@@ -1,5 +1,6 @@
 from allauth.socialaccount import app_settings
 from allauth.socialaccount.adapter import get_adapter
+from allauth.socialaccount.models import SocialToken
 from allauth.socialaccount.providers.oauth2.views import (
     OAuth2Adapter,
     OAuth2CallbackView,
@@ -17,8 +18,8 @@ class JupyterHubOAuth2Adapter(OAuth2Adapter):
     authorize_url = "{0}/hub/api/oauth2/authorize".format(provider_base_url)
     profile_url = "{0}/hub/api/user".format(provider_base_url)
 
-    def complete_login(self, request, app, access_token, **kwargs):
-        headers = {"Authorization": "Bearer {0}".format(access_token)}
+    def complete_login(self, request, app, token: SocialToken, **kwargs):
+        headers = {"Authorization": "Bearer {0}".format(token.token)}
 
         extra_data = (
             get_adapter().get_requests_session().get(self.profile_url, headers=headers)
diff --git a/allauth/socialaccount/providers/lemonldap/views.py b/allauth/socialaccount/providers/lemonldap/views.py
index 93ad5efc..c6f37ad1 100644
--- a/allauth/socialaccount/providers/lemonldap/views.py
+++ b/allauth/socialaccount/providers/lemonldap/views.py
@@ -1,5 +1,6 @@
 from allauth.socialaccount import app_settings
 from allauth.socialaccount.adapter import get_adapter
+from allauth.socialaccount.models import SocialToken
 from allauth.socialaccount.providers.oauth2.views import (
     OAuth2Adapter,
     OAuth2CallbackView,
@@ -17,11 +18,11 @@ class LemonLDAPOAuth2Adapter(OAuth2Adapter):
     authorize_url = "{0}/oauth2/authorize".format(provider_base_url)
     profile_url = "{0}/oauth2/userinfo".format(provider_base_url)
 
-    def complete_login(self, request, app, token, response):
+    def complete_login(self, request, app, token: SocialToken, **kwargs):
         response = (
             get_adapter()
             .get_requests_session()
-            .post(self.profile_url, headers={"Authorization": "Bearer " + str(token)})
+            .post(self.profile_url, headers={"Authorization": "Bearer " + token.token})
         )
         response.raise_for_status()
         extra_data = response.json()
diff --git a/allauth/socialaccount/providers/nextcloud/views.py b/allauth/socialaccount/providers/nextcloud/views.py
index 6f790702..88435bb8 100644
--- a/allauth/socialaccount/providers/nextcloud/views.py
+++ b/allauth/socialaccount/providers/nextcloud/views.py
@@ -3,6 +3,7 @@ import xml.etree.ElementTree as ET
 from allauth.core import context
 from allauth.socialaccount import app_settings
 from allauth.socialaccount.adapter import get_adapter
+from allauth.socialaccount.models import SocialToken
 from allauth.socialaccount.providers.oauth2.views import (
     OAuth2Adapter,
     OAuth2CallbackView,
@@ -34,12 +35,12 @@ class NextCloudOAuth2Adapter(OAuth2Adapter):
     def profile_url(self):
         return self._build_server_url("/ocs/v1.php/cloud/users/")
 
-    def complete_login(self, request, app, token, **kwargs):
+    def complete_login(self, request, app, token: SocialToken, **kwargs):
         extra_data = self.get_user_info(token, kwargs["response"]["user_id"])
         return self.get_provider().sociallogin_from_response(request, extra_data)
 
-    def get_user_info(self, token, user_id):
-        headers = {"Authorization": "Bearer {0}".format(token)}
+    def get_user_info(self, token: SocialToken, user_id):
+        headers = {"Authorization": "Bearer {0}".format(token.token)}
         resp = (
             get_adapter()
             .get_requests_session()
diff --git a/allauth/socialaccount/providers/oauth2/views.py b/allauth/socialaccount/providers/oauth2/views.py
index c374054b..22c99fca 100644
--- a/allauth/socialaccount/providers/oauth2/views.py
+++ b/allauth/socialaccount/providers/oauth2/views.py
@@ -48,7 +48,7 @@ class OAuth2Adapter:
             self.request, provider=self.provider_id
         )
 
-    def complete_login(self, request, app, access_token, **kwargs):
+    def complete_login(self, request, app, token: SocialToken, **kwargs):
         """
         Returns a SocialLogin instance
         """
diff --git a/allauth/socialaccount/providers/openid_connect/views.py b/allauth/socialaccount/providers/openid_connect/views.py
index 09e9c589..abdd54ea 100644
--- a/allauth/socialaccount/providers/openid_connect/views.py
+++ b/allauth/socialaccount/providers/openid_connect/views.py
@@ -1,6 +1,7 @@
 from django.urls import reverse
 
 from allauth.socialaccount.adapter import get_adapter
+from allauth.socialaccount.models import SocialToken
 from allauth.socialaccount.providers.oauth2.views import (
     OAuth2Adapter,
     OAuth2CallbackView,
@@ -44,11 +45,11 @@ class OpenIDConnectOAuth2Adapter(OAuth2Adapter):
     def profile_url(self):
         return self.openid_config["userinfo_endpoint"]
 
-    def complete_login(self, request, app, token, response):
+    def complete_login(self, request, app, token: SocialToken, **kwargs):
         response = (
             get_adapter()
             .get_requests_session()
-            .get(self.profile_url, headers={"Authorization": "Bearer " + str(token)})
+            .get(self.profile_url, headers={"Authorization": "Bearer " + token.token})
         )
         response.raise_for_status()
         extra_data = response.json()
diff --git a/allauth/templates/allauth/elements/field.html b/allauth/templates/allauth/elements/field.html
index 4ca444be..216d2ade 100644
--- a/allauth/templates/allauth/elements/field.html
+++ b/allauth/templates/allauth/elements/field.html
@@ -1,11 +1,11 @@
 {% load allauth %}
 {{ attrs.errors }}
 <p>
-    <label for="{{ attrs.id }}">
-        {% slot label %}
-        {% endslot %}
-    </label>
     {% if attrs.type == "textarea" %}
+        <label for="{{ attrs.id }}">
+            {% slot label %}
+            {% endslot %}
+        </label>
         <textarea {% if attrs.required %}required{% endif %}
                   {% if attrs.rows %}rows="{{ attrs.rows }}"{% endif %}
                   {% if attrs.disabled %}disabled{% endif %}
@@ -15,6 +15,12 @@
                   {% if attrs.id %}id="{{ attrs.id }}"{% endif %}
                   {% if attrs.placeholder %}placeholder="{{ attrs.placeholder }}"{% endif %}>{% slot value %}{% endslot %}</textarea>
     {% else %}
+        {% if attrs.type != "checkbox" and attrs.type != "radio" %}
+            <label for="{{ attrs.id }}">
+                {% slot label %}
+                {% endslot %}
+            </label>
+        {% endif %}
         <input {% if attrs.required %}required{% endif %}
                {% if attrs.disabled %}disabled{% endif %}
                {% if attrs.readonly %}readonly{% endif %}
@@ -25,6 +31,12 @@
                {% if attrs.autocomplete %}autocomplete="{{ attrs.autocomplete }}"{% endif %}
                value="{{ attrs.value|default_if_none:'' }}"
                type="{{ attrs.type }}">
+        {% if attrs.type == "checkbox" or attrs.type == "radio" %}
+            <label for="{{ attrs.id }}">
+                {% slot label %}
+                {% endslot %}
+            </label>
+        {% endif %}
     {% endif %}
     {% if slots.help_text %}
         <span>
diff --git a/docs/headless/openapi-specification/description.md b/docs/headless/openapi-specification/description.md
index b79d23bd..801375e4 100644
--- a/docs/headless/openapi-specification/description.md
+++ b/docs/headless/openapi-specification/description.md
@@ -175,14 +175,15 @@ be initiated by means of these flows:
 - Login or signup using the third-party provider redirect flow (`provider_redirect`).
 - Login or signup by handing over a third-party provider retrieved elsewhere (`provider_token`).
 - Login using a special code (`login_by_code`).
+- Login using a passkey (`mfa_login_webauthn`).
 
 Depending on the state of the account, and the configuration of django-allauth, the flows above
 can either lead to becoming directly authenticated, or, to followup flows:
 - Provider signup (`provider_signup`).
 - Email verification (`verify_email`).
-- Two-factor authentication required (`mfa_authenticate`).
+- Two-factor authentication required (TOTP, recovery codes, or WebAuthn) (`mfa_authenticate`).
 
 While authenticated, re-authentication may be required to safeguard the account when sensitive actions
 are performed. The re-authentication flows are the following:
 - Re-authenticate using password (`reauthenticate`).
-- Re-authenticate using two-factor authentication (`mfa_reauthenticate`).
+- Re-authenticate using a 2FA authenticator (TOTP, recovery codes, or WebAuthn) (`mfa_reauthenticate`).
diff --git a/docs/headless/openapi-specification/openapi.yaml b/docs/headless/openapi-specification/openapi.yaml
index 5a979b5e..cb88e8b5 100644
--- a/docs/headless/openapi-specification/openapi.yaml
+++ b/docs/headless/openapi-specification/openapi.yaml
@@ -53,12 +53,14 @@ x-tagGroups:
       - "Authentication: Providers"
       - "Authentication: 2FA"
       - "Authentication: Login By Code"
+      - "Authentication: WebAuthn"
   - name: Account
     tags:
       - "Account: Email"
       - "Account: Password"
       - "Account: Providers"
       - "Account: 2FA"
+      - "Account: WebAuthn"
   - name: Sessions
     tags:
       - "Sessions"
@@ -506,6 +508,124 @@ paths:
                 invalid_code:
                   $ref: "#/components/examples/InvalidAuthenticatorCode"
   ######################################################################
+  # Authentication: WebAuthn
+  ######################################################################
+  /_allauth/{client}/v1/auth/webauthn/authenticate:
+    get:
+      tags:
+        - "Authentication: WebAuthn"
+      summary: Get WebAuthn credential request options for 2FA
+      parameters:
+        - $ref: "#/components/parameters/Client"
+        - $ref: "#/components/parameters/SessionToken"
+      description: |
+        Returns the WebAuthn credential request options, that can be
+        processed using `parseRequestOptionsFromJSON()` on the frontend.
+      responses:
+        "200":
+          $ref: "#/components/responses/WebAuthnRequestOptionsResponse"
+    post:
+      tags:
+        - "Authentication: WebAuthn"
+      summary: Perform 2FA using WebAuthn
+      parameters:
+        - $ref: "#/components/parameters/Client"
+        - $ref: "#/components/parameters/SessionToken"
+      description: |
+        Perform Two-Factor Authentication using a WebAuthn credential.
+      requestBody:
+        $ref: "#/components/requestBodies/AuthenticateWebAuthn"
+      responses:
+        "200":
+          $ref: "#/components/responses/Authenticated"
+        "400":
+          description: An input error occurred.
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/ErrorResponse"
+  /_allauth/{client}/v1/auth/webauthn/reauthenticate:
+    get:
+      tags:
+        - "Authentication: WebAuthn"
+      summary: Get WebAuthn credential request options for reauthentication
+      parameters:
+        - $ref: "#/components/parameters/Client"
+        - $ref: "#/components/parameters/SessionToken"
+      description: |
+        Returns the WebAuthn credential request options, that can be
+        processed using `parseRequestOptionsFromJSON()` on the frontend.
+      responses:
+        "200":
+          $ref: "#/components/responses/WebAuthnRequestOptionsResponse"
+    post:
+      tags:
+        - "Authentication: WebAuthn"
+      summary: Reauthenticate using WebAuthn
+      parameters:
+        - $ref: "#/components/parameters/Client"
+        - $ref: "#/components/parameters/SessionToken"
+      description: |
+        Reauthenticate the user using a WebAuthn credential.
+      requestBody:
+        $ref: "#/components/requestBodies/ReauthenticateWebAuthn"
+      responses:
+        "200":
+          $ref: "#/components/responses/Authenticated"
+        "400":
+          description: An input error occurred.
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/ErrorResponse"
+  /_allauth/{client}/v1/auth/webauthn/login:
+    get:
+      tags:
+        - "Authentication: WebAuthn"
+      summary: Get WebAuthn credential request options for login
+      parameters:
+        - $ref: "#/components/parameters/Client"
+        - $ref: "#/components/parameters/SessionToken"
+      description: |
+        Returns the WebAuthn credential request options, that can be
+        processed using `parseRequestOptionsFromJSON()` on the frontend.
+      responses:
+        "200":
+          $ref: "#/components/responses/WebAuthnRequestOptionsResponse"
+    post:
+      tags:
+        - "Authentication: WebAuthn"
+      summary: Login using WebAuthn
+      parameters:
+        - $ref: "#/components/parameters/Client"
+        - $ref: "#/components/parameters/SessionToken"
+      description: |
+        Login using a WebAuthn credential (Passkey). Both 200 and 401 can be
+        expected after a successful request.  The 401 can, for example, occur
+        when the credential passed was valid, but the email attached to the
+        account still requires verification.
+      requestBody:
+        $ref: "#/components/requestBodies/LoginWebAuthn"
+      responses:
+        "200":
+          $ref: "#/components/responses/Authenticated"
+        "400":
+          description: An input error occurred.
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/ErrorResponse"
+        "401":
+          description: |
+            Not authenticated.
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/AuthenticationResponse"
+              examples:
+                pending_email:
+                  $ref: "#/components/examples/UnauthenticatedPendingEmailVerification"
+  ######################################################################
   # Authentication: Login by Code
   ######################################################################
   /_allauth/{client}/v1/auth/code/request:
@@ -851,6 +971,75 @@ paths:
         "401":
           $ref: "#/components/responses/ReauthenticationRequired"
   ######################################################################
+  # Account: WebAuthn
+  ######################################################################
+  /_allauth/{client}/v1/account/webauthn:
+    get:
+      tags:
+        - "Account: WebAuthn"
+      summary: |
+        Get WebAuthn credential creation options
+      parameters:
+        - $ref: "#/components/parameters/Client"
+        - $ref: "#/components/parameters/SessionToken"
+      description: |
+        Returns the WebAuthn credential creation options, that can be
+        processed using `parseCreationOptionsFromJSON()` on the frontend.
+      responses:
+        "200":
+          $ref: "#/components/responses/WebAuthnCreationOptionsResponse"
+        "401":
+          $ref: "#/components/responses/ReauthenticationRequired"
+    put:
+      tags:
+        - "Account: WebAuthn"
+      summary: |
+        Rename a WebAuthn credential
+      description: |
+        You can alter the name of a WebAuthn credential by PUT'ting the ID and
+        name of the authenticator representing that credential. You can obtain
+        the credentials via the "List authenticators" endpoint.
+      parameters:
+        - $ref: "#/components/parameters/Client"
+        - $ref: "#/components/parameters/SessionToken"
+      requestBody:
+        $ref: "#/components/requestBodies/UpdateWebAuthn"
+      responses:
+        "200":
+          $ref: "#/components/responses/WebAuthnAuthenticator"
+        "401":
+          $ref: "#/components/responses/ReauthenticationRequired"
+    delete:
+      tags:
+        - "Account: WebAuthn"
+      summary: |
+        Delete a WebAuthn credential
+      parameters:
+        - $ref: "#/components/parameters/Client"
+        - $ref: "#/components/parameters/SessionToken"
+      requestBody:
+        $ref: "#/components/requestBodies/DeleteWebAuthn"
+      responses:
+        "200":
+          $ref: "#/components/responses/StatusOK"
+        "401":
+          $ref: "#/components/responses/ReauthenticationRequired"
+    post:
+      tags:
+        - "Account: WebAuthn"
+      summary: |
+        Add a WebAuthn credential
+      parameters:
+        - $ref: "#/components/parameters/Client"
+        - $ref: "#/components/parameters/SessionToken"
+      requestBody:
+        $ref: "#/components/requestBodies/AddWebAuthnAuthenticator"
+      responses:
+        "200":
+          $ref: "#/components/responses/AddWebAuthnAuthenticator"
+        "401":
+          $ref: "#/components/responses/ReauthenticationRequired"
+  ######################################################################
   # Sessions
   ######################################################################
   /_allauth/{client}/v1/auth/session:
@@ -1234,6 +1423,42 @@ components:
         application/json:
           schema:
             $ref: "#/components/schemas/Login"
+    LoginWebAuthn:
+      description: Login using WebAuthn.
+      required: true
+      content:
+        application/json:
+          schema:
+            type: object
+            properties:
+              credential:
+                $ref: "#/components/schemas/WebAuthnCredential"
+            required:
+              - credential
+    ReauthenticateWebAuthn:
+      description: Reauthenticate using WebAuthn.
+      required: true
+      content:
+        application/json:
+          schema:
+            type: object
+            properties:
+              credential:
+                $ref: "#/components/schemas/WebAuthnCredential"
+            required:
+              - credential
+    AuthenticateWebAuthn:
+      description: Authenticate using WebAuthn.
+      required: true
+      content:
+        application/json:
+          schema:
+            type: object
+            properties:
+              credential:
+                $ref: "#/components/schemas/WebAuthnCredential"
+            required:
+              - credential
     MFAAuthenticate:
       required: true
       content:
@@ -1382,6 +1607,45 @@ components:
         application/json:
           schema:
             $ref: "#/components/schemas/VerifyEmail"
+    UpdateWebAuthn:
+      content:
+        application/json:
+          schema:
+            type: object
+            properties:
+              id:
+                $ref: "#/components/schemas/AuthenticatorID"
+              name:
+                type: string
+                example: "Master key"
+    AddWebAuthnAuthenticator:
+      content:
+        application/json:
+          schema:
+            type: object
+            properties:
+              name:
+                type: string
+                example: "Master key"
+              credential:
+                $ref: "#/components/schemas/WebAuthnCredential"
+            required:
+              - credential
+    DeleteWebAuthn:
+      content:
+        application/json:
+          schema:
+            type: object
+            properties:
+              authenticators:
+                description: |
+                  The IDs of the authenticator that are to be deleted.
+                type: array
+                items:
+                  $ref: "#/components/schemas/AuthenticatorID"
+            required:
+              - authenticators
+
   ######################################################################
   # Components: Schemas
   ######################################################################
@@ -1815,6 +2079,11 @@ components:
       type: integer
       enum:
         - 200
+    AuthenticatorID:
+      type: integer
+      description: |
+        Authenticator ID.
+      example: 123
     SocialAccountConfiguration:
       type: object
       description: |
@@ -2054,6 +2323,28 @@ components:
                 - totp
           required:
             - type
+    WebAuthnAuthenticator:
+      allOf:
+        - $ref: "#/components/schemas/BaseAuthenticator"
+        - type: object
+          properties:
+            type:
+              type: string
+              enum:
+                - webauthn
+            id:
+              $ref: "#/components/schemas/AuthenticatorID"
+            name:
+              type: string
+              example: "Master key"
+            is_passwordless:
+              type: boolean
+              description: |
+                Whether or not this authenticator represents a passkey. Absent if it is not specified.
+          required:
+            - type
+            - id
+            - name
     RecoveryCodesAuthenticator:
       allOf:
         - $ref: "#/components/schemas/BaseAuthenticator"
@@ -2098,6 +2389,7 @@ components:
         oneOf:
           - $ref: "#/components/schemas/TOTPAuthenticator"
           - $ref: "#/components/schemas/RecoveryCodesAuthenticator"
+          - $ref: "#/components/schemas/WebAuthnAuthenticator"
     ProviderList:
       type: array
       items:
@@ -2175,6 +2467,25 @@ components:
         - status
         - data
         - meta
+    WebAuthnCredentialRequestOptions:
+      type: object
+      properties:
+        request_options:
+          type: object
+          example: {"status": 200, "data": {"request_options": {"publicKey": {"challenge": "aOecJJtLA2e-Dj2WU-zbRoJewbQqSUPxoA9EzsUL72o", "rpId": "localhost", "allowCredentials": [], "userVerification": "preferred"}}}}
+      required:
+        - request_options
+    WebAuthnCredentialCreationOptions:
+      type: object
+      properties:
+        creation_options:
+          type: object
+          example: {"status": 200, "data": {"request_options": {"publicKey": {"challenge": "aOecJJtLA2e-Dj2WU-zbRoJewbQqSUPxoA9EzsUL72o", "rpId": "localhost", "allowCredentials": [], "userVerification": "preferred"}}}}
+      required:
+        - creation_options
+    WebAuthnCredential:
+      type: object
+      example: {"credential":{"type":"public-key","id":"-J4JNfPfnLyRSMK4R...","rawId":"-J4JNfPfnLyRSMK4R...","authenticatorAttachment":"cross-platform","response":{"clientDataJSON":"eyJjaGFsbGVuZ2UiOi...","authenticatorData":"SZYN5YgO...","signature":"MEUCIE-7sqILygPqGbrRZ4j2nqeqUU...","userHandle":"Mg..."},"clientExtensionResults":{}}}
   ######################################################################
   # Components: Parameters
   ######################################################################
@@ -2524,3 +2835,67 @@ components:
         application/json:
           schema:
             $ref: "#/components/schemas/AuthenticatedResponse"
+    WebAuthnRequestOptionsResponse:
+      description: WebAuthn credential request options.
+      content:
+        application/json:
+          schema:
+            type: object
+            properties:
+              status:
+                $ref: "#/components/schemas/StatusOK"
+              data:
+                $ref: "#/components/schemas/WebAuthnCredentialRequestOptions"
+            required:
+              - status
+              - data
+    WebAuthnCreationOptionsResponse:
+      description: WebAuthn credential creation options.
+      content:
+        application/json:
+          schema:
+            type: object
+            properties:
+              status:
+                $ref: "#/components/schemas/StatusOK"
+              data:
+                $ref: "#/components/schemas/WebAuthnCredentialCreationOptions"
+            required:
+              - status
+              - data
+    WebAuthnAuthenticator:
+      description: A WebAuthn authenticator.
+      content:
+        application/json:
+          schema:
+            type: object
+            properties:
+              status:
+                $ref: "#/components/schemas/StatusOK"
+              data:
+                $ref: "#/components/schemas/WebAuthnAuthenticator"
+            required:
+              - status
+              - data
+    AddWebAuthnAuthenticator:
+      description: A WebAuthn authenticator.
+      content:
+        application/json:
+          schema:
+            type: object
+            properties:
+              status:
+                $ref: "#/components/schemas/StatusOK"
+              data:
+                $ref: "#/components/schemas/WebAuthnAuthenticator"
+              meta:
+                type: object
+                properties:
+                  recovery_codes_generated:
+                    type: boolean
+                    description: |
+                      Whether or not recovery codes where generated automatically.
+            required:
+              - status
+              - data
+              - meta
diff --git a/docs/installation/requirements.rst b/docs/installation/requirements.rst
index 5ec5823a..06f3e4de 100644
--- a/docs/installation/requirements.rst
+++ b/docs/installation/requirements.rst
@@ -5,7 +5,7 @@ This package has the following external dependencies:
 
 - Python 3.8, 3.9, or 3.10, 3.11, 3.12.
 
-- Django (4.0+)
+- Django (4.2+)
 
 - Depending on which social providers you use:
 
diff --git a/examples/react-spa/backend/backend/settings.py b/examples/react-spa/backend/backend/settings.py
index 3f0af674..f15dd92b 100644
--- a/examples/react-spa/backend/backend/settings.py
+++ b/examples/react-spa/backend/backend/settings.py
@@ -140,6 +140,9 @@ HEADLESS_FRONTEND_URLS = {
     "socialaccount_login_error": "/account/provider/callback",
 }
 
+MFA_SUPPORTED_TYPES = ["totp", "recovery_codes", "webauthn"]
+MFA_PASSKEY_LOGIN_ENABLED = True
+
 try:
     from .local_settings import *  # noqa
 except ImportError:
diff --git a/examples/react-spa/backend/requirements.txt b/examples/react-spa/backend/requirements.txt
index 438d86eb..2bedd334 100644
--- a/examples/react-spa/backend/requirements.txt
+++ b/examples/react-spa/backend/requirements.txt
@@ -1,2 +1,2 @@
-django-allauth[mfa,socialaccount]>=0.63.3
+django-allauth[mfa,socialaccount]>=0.63.6
 qrcode >= 7.0.0
diff --git a/examples/react-spa/frontend/package-lock.json b/examples/react-spa/frontend/package-lock.json
index 91d12881..13dad2c4 100644
--- a/examples/react-spa/frontend/package-lock.json
+++ b/examples/react-spa/frontend/package-lock.json
@@ -8,6 +8,7 @@
       "name": "frontend",
       "version": "0.1.0",
       "dependencies": {
+        "@github/webauthn-json": "^2.1.1",
         "@testing-library/jest-dom": "^5.17.0",
         "@testing-library/react": "^13.4.0",
         "@testing-library/user-event": "^13.5.0",
@@ -16,7 +17,6 @@
         "react-router-dom": "^6.21.3",
         "react-scripts": "5.0.1",
         "standard": "^17.1.0",
-        "standardjs": "^1.0.0-alpha",
         "web-vitals": "^2.1.4"
       }
     },
@@ -2379,6 +2379,14 @@
         "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
       }
     },
+    "node_modules/@github/webauthn-json": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/@github/webauthn-json/-/webauthn-json-2.1.1.tgz",
+      "integrity": "sha512-XrftRn4z75SnaJOmZQbt7Mk+IIjqVHw+glDGOxuHwXkZBZh/MBoRS7MHjSZMDaLhT4RjN2VqiEU7EOYleuJWSQ==",
+      "bin": {
+        "webauthn-json": "dist/bin/main.js"
+      }
+    },
     "node_modules/@humanwhocodes/config-array": {
       "version": "0.11.14",
       "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
@@ -16379,11 +16387,6 @@
         "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
       }
     },
-    "node_modules/standardjs": {
-      "version": "1.0.0-alpha",
-      "resolved": "https://registry.npmjs.org/standardjs/-/standardjs-1.0.0-alpha.tgz",
-      "integrity": "sha512-ZInCIyftuVwXrBb1qSOwSFlI7YPe13pF3GdEZ4qD8yH/V5YBA4TlPDM34x32F4/VzRHpkoZIUQqrMTq0DNLbZA=="
-    },
     "node_modules/static-eval": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz",
diff --git a/examples/react-spa/frontend/package.json b/examples/react-spa/frontend/package.json
index 2b48715e..fdf6da93 100644
--- a/examples/react-spa/frontend/package.json
+++ b/examples/react-spa/frontend/package.json
@@ -3,6 +3,7 @@
   "version": "0.1.0",
   "private": true,
   "dependencies": {
+    "@github/webauthn-json": "^2.1.1",
     "@testing-library/jest-dom": "^5.17.0",
     "@testing-library/react": "^13.4.0",
     "@testing-library/user-event": "^13.5.0",
diff --git a/examples/react-spa/frontend/src/Router.js b/examples/react-spa/frontend/src/Router.js
index 17ec6cbc..24bbe164 100644
--- a/examples/react-spa/frontend/src/Router.js
+++ b/examples/react-spa/frontend/src/Router.js
@@ -23,10 +23,16 @@ import MFAOverview, { loader as mfaOverviewLoader } from './mfa/MFAOverview'
 import ActivateTOTP, { loader as activateTOTPLoader } from './mfa/ActivateTOTP'
 import DeactivateTOTP from './mfa/DeactivateTOTP'
 import RecoveryCodes, { loader as recoveryCodesLoader } from './mfa/RecoveryCodes'
+import AddWebAuthn from './mfa/AddWebAuthn'
+import ReauthenticateWebAuthn from './mfa/ReauthenticateWebAuthn'
+import ListWebAuthn, { loader as listWebAuthnLoader } from './mfa/ListWebAuthn'
 import GenerateRecoveryCodes, { loader as generateRecoveryCodesLoader } from './mfa/GenerateRecoveryCodes'
 import ResetPassword, { loader as resetPasswordLoader } from './account/ResetPassword'
-import MFAAuthenticate from './mfa/MFAAuthenticate'
-import MFAReauthenticate from './mfa/MFAReauthenticate'
+import AuthenticateTOTP from './mfa/AuthenticateTOTP'
+import AuthenticateRecoveryCodes from './mfa/AuthenticateRecoveryCodes'
+import AuthenticateWebAuthn from './mfa/AuthenticateWebAuthn'
+import ReauthenticateRecoveryCodes from './mfa/ReauthenticateRecoveryCodes'
+import ReauthenticateTOTP from './mfa/ReauthenticateTOTP'
 import Reauthenticate from './account/Reauthenticate'
 import Sessions from './usersessions/Sessions'
 import Root from './Root'
@@ -113,12 +119,28 @@ function createRouter () {
           element: <AuthenticatedRoute><Reauthenticate /></AuthenticatedRoute>
         },
         {
-          path: '/account/2fa/reauthenticate',
-          element: <AuthenticatedRoute><MFAReauthenticate /></AuthenticatedRoute>
+          path: '/account/reauthenticate/totp',
+          element: <AuthenticatedRoute><ReauthenticateTOTP /></AuthenticatedRoute>
         },
         {
-          path: '/account/2fa/authenticate',
-          element: <AnonymousRoute><MFAAuthenticate /></AnonymousRoute>
+          path: '/account/reauthenticate/recovery-codes',
+          element: <AuthenticatedRoute><ReauthenticateRecoveryCodes /></AuthenticatedRoute>
+        },
+        {
+          path: '/account/reauthenticate/webauthn',
+          element: <AuthenticatedRoute><ReauthenticateWebAuthn /></AuthenticatedRoute>
+        },
+        {
+          path: '/account/authenticate/totp',
+          element: <AnonymousRoute><AuthenticateTOTP /></AnonymousRoute>
+        },
+        {
+          path: '/account/authenticate/recovery-codes',
+          element: <AnonymousRoute><AuthenticateRecoveryCodes /></AnonymousRoute>
+        },
+        {
+          path: '/account/authenticate/webauthn',
+          element: <AnonymousRoute><AuthenticateWebAuthn /></AnonymousRoute>
         },
         {
           path: '/account/2fa/totp/activate',
@@ -139,6 +161,15 @@ function createRouter () {
           element: <AuthenticatedRoute><GenerateRecoveryCodes /></AuthenticatedRoute>,
           loader: generateRecoveryCodesLoader
         },
+        {
+          path: '/account/2fa/webauthn',
+          element: <AuthenticatedRoute><ListWebAuthn /></AuthenticatedRoute>,
+          loader: listWebAuthnLoader
+        },
+        {
+          path: '/account/2fa/webauthn/add',
+          element: <AuthenticatedRoute><AddWebAuthn /></AuthenticatedRoute>
+        },
         {
           path: '/account/sessions',
           element: <AuthenticatedRoute><Sessions /></AuthenticatedRoute>
diff --git a/examples/react-spa/frontend/src/account/AuthenticateFlow.js b/examples/react-spa/frontend/src/account/AuthenticateFlow.js
new file mode 100644
index 00000000..09887e7b
--- /dev/null
+++ b/examples/react-spa/frontend/src/account/AuthenticateFlow.js
@@ -0,0 +1,61 @@
+import { Link, useLocation } from 'react-router-dom'
+import { pathForFlow } from '../auth'
+import { Flows, AuthenticatorType } from '../lib/allauth'
+
+const flowLabels = {}
+flowLabels[Flows.REAUTHENTICATE] = 'Use your password'
+flowLabels[`${Flows.MFA_REAUTHENTICATE}:${AuthenticatorType.TOTP}`] = 'Use your authenticator app'
+flowLabels[`${Flows.MFA_REAUTHENTICATE}:${AuthenticatorType.RECOVERY_CODES}`] = 'Use a recovery code'
+flowLabels[`${Flows.MFA_REAUTHENTICATE}:${AuthenticatorType.WEBAUTHN}`] = 'Use security key'
+
+function flowsToMethods (flows) {
+  const methods = []
+  flows.forEach(flow => {
+    if (flow.id === Flows.MFA_REAUTHENTICATE) {
+      flow.types.forEach(typ => {
+        methods.push({
+          label: flowLabels[`${flow.id}:${typ}`] || flow.id,
+          id: flow.id,
+          path: pathForFlow(flow, typ)
+        })
+      })
+    } else {
+      methods.push({
+        label: flowLabels[flow.id] || flow.id,
+        id: flow.id,
+        path: pathForFlow(flow)
+      })
+    }
+  })
+  return methods
+}
+
+export default function ReauthenticateFlow (props) {
+  const location = useLocation()
+  const methods = flowsToMethods(location.state.reauth.data.flows)
+
+  return (
+    <div>
+      <h1>Confirm Access</h1>
+      <p>
+        Please reauthenticate to safeguard your account.
+      </p>
+      {props.children}
+
+      {methods.length > 1
+        ? <><h2>Alternative Options</h2>
+          <ul>
+            {methods.filter(method => method.id !== props.method).map(method => {
+              return (
+                <li key={method.id}>
+                  <Link replace state={location.state} to={method.path + location.search}>{method.label}</Link>
+                </li>
+              )
+            })}
+          </ul>
+        </>
+        : null}
+    </div>
+
+  )
+}
diff --git a/examples/react-spa/frontend/src/account/Login.js b/examples/react-spa/frontend/src/account/Login.js
index b7bdf9b8..98529b90 100644
--- a/examples/react-spa/frontend/src/account/Login.js
+++ b/examples/react-spa/frontend/src/account/Login.js
@@ -5,6 +5,7 @@ import { Link } from 'react-router-dom'
 import { useConfig } from '../auth'
 import ProviderList from '../socialaccount/ProviderList'
 import Button from '../components/Button'
+import WebAuthnLoginButton from '../mfa/WebAuthnLoginButton'
 
 export default function Login () {
   const [email, setEmail] = useState('')
@@ -42,7 +43,8 @@ export default function Login () {
       </div>
       <Button disabled={response.fetching} onClick={() => submit()}>Login</Button>
 
-      <Link className='btn btn-secondary' to='/account/login/code'>Mail me a sign-in code.</Link>
+      <Link className='btn btn-secondary' to='/account/login/code'>Mail me a sign-in code</Link>
+      <WebAuthnLoginButton>Sign in with a passkey</WebAuthnLoginButton>
       {hasProviders
         ? <>
           <h2>Or use a third-party</h2>
diff --git a/examples/react-spa/frontend/src/account/ReauthenticateFlow.js b/examples/react-spa/frontend/src/account/ReauthenticateFlow.js
index 7d84f6c7..1b2ca307 100644
--- a/examples/react-spa/frontend/src/account/ReauthenticateFlow.js
+++ b/examples/react-spa/frontend/src/account/ReauthenticateFlow.js
@@ -1,30 +1,55 @@
 import { Link, useLocation } from 'react-router-dom'
 import { pathForFlow } from '../auth'
-import { Flows } from '../lib/allauth'
+import { Flows, AuthenticatorType } from '../lib/allauth'
 
 const flowLabels = {}
 flowLabels[Flows.REAUTHENTICATE] = 'Use your password'
-flowLabels[Flows.MFA_REAUTHENTICATE] = 'Use your authenticator app'
+flowLabels[`${Flows.MFA_REAUTHENTICATE}:${AuthenticatorType.TOTP}`] = 'Use your authenticator app'
+flowLabels[`${Flows.MFA_REAUTHENTICATE}:${AuthenticatorType.RECOVERY_CODES}`] = 'Use a recovery code'
+flowLabels[`${Flows.MFA_REAUTHENTICATE}:${AuthenticatorType.WEBAUTHN}`] = 'Use security key'
+
+function flowsToMethods (flows) {
+  const methods = []
+  flows.forEach(flow => {
+    if (flow.id === Flows.MFA_REAUTHENTICATE) {
+      flow.types.forEach(typ => {
+        const id = `${flow.id}:${typ}`
+        methods.push({
+          label: flowLabels[id],
+          id,
+          path: pathForFlow(flow, typ)
+        })
+      })
+    } else {
+      methods.push({
+        label: flowLabels[flow.id] || flow.id,
+        id: flow.id,
+        path: pathForFlow(flow)
+      })
+    }
+  })
+  return methods
+}
 
 export default function ReauthenticateFlow (props) {
   const location = useLocation()
-  const flows = location.state.reauth.data.flows
+  const methods = flowsToMethods(location.state.reauth.data.flows)
 
   return (
     <div>
-      <h1> Confirm Access</h1>
+      <h1>Confirm Access</h1>
       <p>
         Please reauthenticate to safeguard your account.
       </p>
       {props.children}
 
-      {flows.length > 1
+      {methods.length > 1
         ? <><h2>Alternative Options</h2>
           <ul>
-            {flows.filter(flow => flow.id !== props.flow).map(flow => {
+            {methods.filter(method => method.id !== props.method).map(method => {
               return (
-                <li key={flow.id}>
-                  <Link replace state={location.state} to={pathForFlow(flow.id) + location.search}>{flowLabels[flow.id] || flow.id}</Link>
+                <li key={method.id}>
+                  <Link replace state={location.state} to={method.path + location.search}>{method.label}</Link>
                 </li>
               )
             })}
diff --git a/examples/react-spa/frontend/src/auth/hooks.js b/examples/react-spa/frontend/src/auth/hooks.js
index 71bd5f30..0290c3fe 100644
--- a/examples/react-spa/frontend/src/auth/hooks.js
+++ b/examples/react-spa/frontend/src/auth/hooks.js
@@ -14,6 +14,11 @@ export function useUser () {
   return authInfo(auth).user
 }
 
+export function useAuthInfo () {
+  const auth = useContext(AuthContext)?.auth
+  return authInfo(auth)
+}
+
 function authInfo (auth) {
   const isAuthenticated = auth.status === 200 || (auth.status === 401 && auth.meta.is_authenticated)
   const requiresReauthentication = isAuthenticated && auth.status === 401
diff --git a/examples/react-spa/frontend/src/auth/routing.js b/examples/react-spa/frontend/src/auth/routing.js
index 403d7348..f8dea5bd 100644
--- a/examples/react-spa/frontend/src/auth/routing.js
+++ b/examples/react-spa/frontend/src/auth/routing.js
@@ -3,7 +3,7 @@ import {
   useLocation
 } from 'react-router-dom'
 import { useAuthChange, AuthChangeEvent, useAuthStatus } from './hooks'
-import { Flows } from '../lib/allauth'
+import { Flows, AuthenticatorType } from '../lib/allauth'
 
 export const URLs = Object.freeze({
   LOGIN_URL: '/account/login',
@@ -17,14 +17,23 @@ flow2path[Flows.LOGIN_BY_CODE] = '/account/login/code/confirm'
 flow2path[Flows.SIGNUP] = '/account/signup'
 flow2path[Flows.VERIFY_EMAIL] = '/account/verify-email'
 flow2path[Flows.PROVIDER_SIGNUP] = '/account/provider/signup'
-flow2path[Flows.MFA_AUTHENTICATE] = '/account/2fa/authenticate'
 flow2path[Flows.REAUTHENTICATE] = '/account/reauthenticate'
-flow2path[Flows.MFA_REAUTHENTICATE] = '/account/2fa/reauthenticate'
+flow2path[`${Flows.MFA_AUTHENTICATE}:${AuthenticatorType.TOTP}`] = '/account/authenticate/totp'
+flow2path[`${Flows.MFA_AUTHENTICATE}:${AuthenticatorType.RECOVERY_CODES}`] = '/account/authenticate/recovery-codes'
+flow2path[`${Flows.MFA_AUTHENTICATE}:${AuthenticatorType.WEBAUTHN}`] = '/account/authenticate/webauthn'
+flow2path[`${Flows.MFA_REAUTHENTICATE}:${AuthenticatorType.TOTP}`] = '/account/reauthenticate/totp'
+flow2path[`${Flows.MFA_REAUTHENTICATE}:${AuthenticatorType.RECOVERY_CODES}`] = '/account/reauthenticate/recovery-codes'
+flow2path[`${Flows.MFA_REAUTHENTICATE}:${AuthenticatorType.WEBAUTHN}`] = '/account/reauthenticate/webauthn'
 
-export function pathForFlow (flowId) {
-  const path = flow2path[flowId]
+export function pathForFlow (flow, typ) {
+  let key = flow.id
+  if (typeof flow.types !== 'undefined') {
+    typ = typ ?? flow.types[0]
+    key = `${key}:${typ}`
+  }
+  const path = flow2path[key] ?? flow2path[flow.id]
   if (!path) {
-    throw new Error(`Unknown path for flow: ${flowId}`)
+    throw new Error(`Unknown path for flow: ${flow.id}`)
   }
   return path
 }
@@ -32,7 +41,7 @@ export function pathForFlow (flowId) {
 export function pathForPendingFlow (auth) {
   const flow = auth.data.flows.find(flow => flow.is_pending)
   if (flow) {
-    return pathForFlow(flow.id)
+    return pathForFlow(flow)
   }
   return null
 }
@@ -80,7 +89,7 @@ export function AuthChangeRedirector ({ children }) {
     }
     case AuthChangeEvent.REAUTHENTICATION_REQUIRED: {
       const next = `next=${encodeURIComponent(location.pathname + location.search)}`
-      const path = pathForFlow(auth.data.flows[0].id)
+      const path = pathForFlow(auth.data.flows[0])
       return <Navigate to={`${path}?${next}`} state={{ reauth: auth }} />
     }
     case AuthChangeEvent.FLOW_UPDATED:
diff --git a/examples/react-spa/frontend/src/lib/allauth.js b/examples/react-spa/frontend/src/lib/allauth.js
index 3bbfac55..647135e6 100644
--- a/examples/react-spa/frontend/src/lib/allauth.js
+++ b/examples/react-spa/frontend/src/lib/allauth.js
@@ -64,12 +64,19 @@ export const URLs = Object.freeze({
   PROVIDER_TOKEN: BASE_URL + '/auth/provider/token',
 
   // Auth: Sessions
-  SESSIONS: BASE_URL + '/auth/sessions'
+  SESSIONS: BASE_URL + '/auth/sessions',
+
+  // Auth: WebAuthn
+  REAUTHENTICATE_WEBAUTHN: BASE_URL + '/auth/webauthn/reauthenticate',
+  AUTHENTICATE_WEBAUTHN: BASE_URL + '/auth/webauthn/authenticate',
+  LOGIN_WEBAUTHN: BASE_URL + '/auth/webauthn/login',
+  WEBAUTHN_AUTHENTICATOR: BASE_URL + '/account/authenticators/webauthn'
 })
 
 export const AuthenticatorType = Object.freeze({
   TOTP: 'totp',
-  RECOVERY_CODES: 'recovery_codes'
+  RECOVERY_CODES: 'recovery_codes',
+  WEBAUTHN: 'webauthn'
 })
 
 function postForm (action, data) {
@@ -88,7 +95,7 @@ function postForm (action, data) {
   f.submit()
 }
 
-const tokenStorage = sessionStorage
+const tokenStorage = window.sessionStorage
 
 async function request (method, path, data, headers) {
   const options = {
@@ -277,3 +284,50 @@ export function redirectToProvider (providerId, callbackURL, process = AuthProce
     csrfmiddlewaretoken: getCSRFToken()
   })
 }
+
+export async function getWebAuthnCreateOptions (passwordless) {
+  let url = URLs.WEBAUTHN_AUTHENTICATOR
+  if (passwordless) {
+    url += '?passwordless'
+  }
+  return await request('GET', url)
+}
+
+export async function addWebAuthnCredential (name, credential) {
+  return await request('POST', URLs.WEBAUTHN_AUTHENTICATOR, {
+    name,
+    credential
+  })
+}
+
+export async function deleteWebAuthnCredential (ids) {
+  return await request('DELETE', URLs.WEBAUTHN_AUTHENTICATOR, { authenticators: ids })
+}
+
+export async function updateWebAuthnCredential (id, data) {
+  return await request('PUT', URLs.WEBAUTHN_AUTHENTICATOR, { id, ...data })
+}
+
+export async function getWebAuthnRequestOptionsForReauthentication () {
+  return await request('GET', URLs.REAUTHENTICATE_WEBAUTHN)
+}
+
+export async function reauthenticateUsingWebAuthn (credential) {
+  return await request('POST', URLs.REAUTHENTICATE_WEBAUTHN, { credential })
+}
+
+export async function authenticateUsingWebAuthn (credential) {
+  return await request('POST', URLs.AUTHENTICATE_WEBAUTHN, { credential })
+}
+
+export async function loginUsingWebAuthn (credential) {
+  return await request('POST', URLs.LOGIN_WEBAUTHN, { credential })
+}
+
+export async function getWebAuthnRequestOptionsForLogin () {
+  return await request('GET', URLs.LOGIN_WEBAUTHN)
+}
+
+export async function getWebAuthnRequestOptionsForAuthentication () {
+  return await request('GET', URLs.AUTHENTICATE_WEBAUTHN)
+}
diff --git a/examples/react-spa/frontend/src/mfa/AddWebAuthn.js b/examples/react-spa/frontend/src/mfa/AddWebAuthn.js
new file mode 100644
index 00000000..98a7cb3f
--- /dev/null
+++ b/examples/react-spa/frontend/src/mfa/AddWebAuthn.js
@@ -0,0 +1,55 @@
+import { useState } from 'react'
+import { Navigate } from 'react-router-dom'
+import FormErrors from '../components/FormErrors'
+import Button from '../components/Button'
+import * as allauth from '../lib/allauth'
+import {
+  create,
+  parseCreationOptionsFromJSON
+} from '@github/webauthn-json/browser-ponyfill'
+
+export default function AddWebAuthn (props) {
+  const [passwordless, setPasswordless] = useState(false)
+  const [name, setName] = useState('')
+  const [response, setResponse] = useState({ fetching: false, content: null })
+
+  async function submit () {
+    setResponse({ ...response, fetching: true })
+    try {
+      const optResp = await allauth.getWebAuthnCreateOptions(passwordless)
+      const jsonOptions = optResp.data.creation_options
+      const options = parseCreationOptionsFromJSON(jsonOptions)
+      const credential = await create(options)
+      const addResp = await allauth.addWebAuthnCredential(name, credential)
+      setResponse((r) => { return { ...r, content: addResp } })
+    } catch (e) {
+      console.error(e)
+      window.alert(e)
+    }
+    setResponse((r) => { return { ...r, fetching: false } })
+  }
+
+  if (response.content?.status === 200) {
+    return <Navigate to={response.content.meta.recovery_codes_generated ? '/account/2fa/recovery-codes' : '/account/2fa/webauthn'} />
+  }
+  return (
+    <section>
+      <h1>Add Security Key</h1>
+
+      <div>
+        <label>
+          Name:
+          <input value={name} onChange={(e) => setName(e.target.value)} />
+          <FormErrors param='name' errors={response.content?.errors} />
+        </label>
+      </div>
+      <div>
+        <label>
+          Passwordless
+          <input type='checkbox' onChange={(e) => setPasswordless(e.target.checked)} checked={passwordless} />
+        </label>
+      </div>
+      <Button disabled={response.fetching} onClick={() => submit()}>Add key</Button>
+    </section>
+  )
+}
diff --git a/examples/react-spa/frontend/src/mfa/MFAAuthenticate.js b/examples/react-spa/frontend/src/mfa/AuthenticateCode.js
similarity index 52%
rename from examples/react-spa/frontend/src/mfa/MFAAuthenticate.js
rename to examples/react-spa/frontend/src/mfa/AuthenticateCode.js
index 1d104c73..63708756 100644
--- a/examples/react-spa/frontend/src/mfa/MFAAuthenticate.js
+++ b/examples/react-spa/frontend/src/mfa/AuthenticateCode.js
@@ -2,10 +2,18 @@ import { useState } from 'react'
 import FormErrors from '../components/FormErrors'
 import * as allauth from '../lib/allauth'
 import Button from '../components/Button'
+import { useAuthInfo } from '../auth/hooks'
+import { Navigate } from 'react-router-dom'
+import AuthenticateFlow from './AuthenticateFlow'
 
-export default function MFAAuthenticate () {
+export default function AuthenticateCode (props) {
   const [code, setCode] = useState('')
   const [response, setResponse] = useState({ fetching: false, content: null })
+  const authInfo = useAuthInfo()
+
+  if (authInfo?.pendingFlow?.id !== allauth.Flows.MFA_AUTHENTICATE) {
+    return <Navigate to='/' />
+  }
 
   function submit () {
     setResponse({ ...response, fetching: true })
@@ -19,20 +27,14 @@ export default function MFAAuthenticate () {
     })
   }
   return (
-    <section>
-      <h1>Two-Factor Authentication</h1>
-      <p>
-        Your account is protected by two-factor authentication. Please enter an authenticator code:
-      </p>
-      <div>
-        <label>
-          Authenticator code:
-          <input type='text' value={code} onChange={(e) => setCode(e.target.value)} />
-        </label>
-        <FormErrors param='code' errors={response.content?.errors} />
-      </div>
+    <AuthenticateFlow authenticatorType={props.authenticatorType}>
+      {props.children}
+      <label>
+        Code:
+        <input type='text' value={code} onChange={(e) => setCode(e.target.value)} />
+      </label>
+      <FormErrors param='code' errors={response.content?.errors} />
       <Button onClick={() => submit()}>Sign In</Button>
-
-    </section>
+    </AuthenticateFlow>
   )
 }
diff --git a/examples/react-spa/frontend/src/mfa/AuthenticateFlow.js b/examples/react-spa/frontend/src/mfa/AuthenticateFlow.js
new file mode 100644
index 00000000..218ea6b1
--- /dev/null
+++ b/examples/react-spa/frontend/src/mfa/AuthenticateFlow.js
@@ -0,0 +1,43 @@
+import { Link, Navigate } from 'react-router-dom'
+import { pathForFlow } from '../auth'
+import { Flows, AuthenticatorType } from '../lib/allauth'
+import { useAuthInfo } from '../auth/hooks'
+
+const labels = {}
+labels[AuthenticatorType.TOTP] = 'Use your authenticator app'
+labels[AuthenticatorType.RECOVERY_CODES] = 'Use a recovery code'
+labels[AuthenticatorType.WEBAUTHN] = 'Use security key'
+
+export default function AuthenticateFlow (props) {
+  const authInfo = useAuthInfo()
+
+  if (authInfo?.pendingFlow?.id !== Flows.MFA_AUTHENTICATE) {
+    return <Navigate to='/' />
+  }
+  const flow = authInfo.pendingFlow
+
+  return (
+    <section>
+      <h1>Two-Factor Authentication</h1>
+      <p>
+        Your account is protected by two-factor authentication.
+      </p>
+      {props.children}
+
+      {flow.types.length > 1
+        ? <><h2>Alternative Options</h2>
+          <ul>
+            {flow.types.map(typ => {
+              return (
+                <li key={typ}>
+                  <Link replace to={pathForFlow(flow, typ)}>{labels[typ]}</Link>
+                </li>
+              )
+            })}
+          </ul>
+        </>
+        : null}
+    </section>
+
+  )
+}
diff --git a/examples/react-spa/frontend/src/mfa/AuthenticateRecoveryCodes.js b/examples/react-spa/frontend/src/mfa/AuthenticateRecoveryCodes.js
new file mode 100644
index 00000000..d2bc61ca
--- /dev/null
+++ b/examples/react-spa/frontend/src/mfa/AuthenticateRecoveryCodes.js
@@ -0,0 +1,10 @@
+import AuthenticateCode from './AuthenticateCode'
+import { AuthenticatorType } from '../lib/allauth'
+
+export default function AuthenticateRecoveryCodes (props) {
+  return (
+    <AuthenticateCode authenticatorType={AuthenticatorType.RECOVERY_CODES}>
+      <p>Please enter a recovery code:</p>
+    </AuthenticateCode>
+  )
+}
diff --git a/examples/react-spa/frontend/src/mfa/AuthenticateTOTP.js b/examples/react-spa/frontend/src/mfa/AuthenticateTOTP.js
new file mode 100644
index 00000000..0055f285
--- /dev/null
+++ b/examples/react-spa/frontend/src/mfa/AuthenticateTOTP.js
@@ -0,0 +1,10 @@
+import AuthenticateCode from './AuthenticateCode'
+import { AuthenticatorType } from '../lib/allauth'
+
+export default function AuthenticateRecoveryCodes (props) {
+  return (
+    <AuthenticateCode authenticatorType={AuthenticatorType.TOTP}>
+      <p>Please enter an authenticator code:</p>
+    </AuthenticateCode>
+  )
+}
diff --git a/examples/react-spa/frontend/src/mfa/AuthenticateWebAuthn.js b/examples/react-spa/frontend/src/mfa/AuthenticateWebAuthn.js
new file mode 100644
index 00000000..3c3e35c5
--- /dev/null
+++ b/examples/react-spa/frontend/src/mfa/AuthenticateWebAuthn.js
@@ -0,0 +1,34 @@
+import { useState } from 'react'
+import { AuthenticatorType, getWebAuthnRequestOptionsForAuthentication, authenticateUsingWebAuthn } from '../lib/allauth'
+import Button from '../components/Button'
+import {
+  parseRequestOptionsFromJSON,
+  get
+} from '@github/webauthn-json/browser-ponyfill'
+import AuthenticateFlow from './AuthenticateFlow'
+
+export default function AuthenticateWebAuthn (props) {
+  const [response, setResponse] = useState({ fetching: false, content: null })
+
+  async function submit () {
+    setResponse({ ...response, fetching: true })
+    try {
+      const optResp = await getWebAuthnRequestOptionsForAuthentication()
+      const jsonOptions = optResp.data.request_options
+      const options = parseRequestOptionsFromJSON(jsonOptions)
+      const credential = await get(options)
+      const reauthResp = await authenticateUsingWebAuthn(credential)
+      setResponse((r) => { return { ...r, content: reauthResp } })
+    } catch (e) {
+      console.error(e)
+      window.alert(e)
+    }
+    setResponse((r) => { return { ...r, fetching: false } })
+  }
+
+  return (
+    <AuthenticateFlow authenticatorType={AuthenticatorType.WEBAUTHN}>
+      <Button disabled={response.fetching} onClick={() => submit()}>Use security key</Button>
+    </AuthenticateFlow>
+  )
+}
diff --git a/examples/react-spa/frontend/src/mfa/ListWebAuthn.js b/examples/react-spa/frontend/src/mfa/ListWebAuthn.js
new file mode 100644
index 00000000..f90eeb2a
--- /dev/null
+++ b/examples/react-spa/frontend/src/mfa/ListWebAuthn.js
@@ -0,0 +1,117 @@
+import { useState } from 'react'
+import { Link, useLoaderData, Navigate } from 'react-router-dom'
+import Button from '../components/Button'
+import * as allauth from '../lib/allauth'
+
+export async function loader ({ params }) {
+  const resp = await allauth.getAuthenticators()
+  return { authenticators: resp.data }
+}
+
+function Authenticator (props) {
+  const [name, setName] = useState(props.authenticator.name)
+  const { authenticator } = props
+  function onSubmit (e) {
+    e.preventDefault()
+    props.onSave(name)
+  }
+  return (
+    <tr>
+      {props.editting
+        ? <td>
+          <form onSubmit={onSubmit}>
+            <input onChange={(e) => setName(e.target.value)} value={name} type='text' />
+            <Button type='button' onClick={() => props.onCancel()}>        Cancel</Button>
+          </form>
+          </td>
+        : <td>{authenticator.name} <Button onClick={() => props.onEdit()}>        Edit</Button>
+          </td>}
+      <td>{typeof authenticator.is_passwordless === 'undefined'
+        ? 'Type unspecified'
+        : (authenticator.is_passwordless ? 'Passkey' : 'Security key')}
+      </td>
+      <td>{new Date(authenticator.created_at * 1000).toLocaleString()}</td>
+      <td>{authenticator.last_used_at ? new Date(authenticator.last_used_at * 1000).toLocaleString() : 'Unused'}</td>
+      <td>
+        <Button onClick={() => props.onDelete()}>Delete</Button>
+      </td>
+    </tr>
+  )
+}
+
+export default function ListWebAuthn (props) {
+  const { authenticators } = useLoaderData()
+  const [editId, setEditId] = useState(null)
+  const [keys, setKeys] = useState(() => authenticators.filter(authenticator => authenticator.type === allauth.AuthenticatorType.WEBAUTHN))
+  const [response, setResponse] = useState({ fetching: false, content: null })
+
+  async function optimisticSetKeys (newKeys, op) {
+    setResponse({ ...response, fetching: true })
+    const oldKeys = keys
+    setEditId(null)
+    setKeys(newKeys)
+    try {
+      const ok = await op()
+      if (!ok) {
+        setKeys(oldKeys)
+      }
+    } catch (e) {
+      setKeys(oldKeys)
+      console.error(e)
+      window.alert(e)
+    }
+    setResponse((r) => { return { ...r, fetching: false } })
+  }
+
+  async function deleteKey (key) {
+    const newKeys = keys.filter((k) => k.id !== key.id)
+    await optimisticSetKeys(newKeys, async () => {
+      const resp = await allauth.deleteWebAuthnCredential([key.id])
+      return (resp.status === 200)
+    })
+  }
+
+  async function onSave (key, name) {
+    const newKeys = keys.filter((k) => k.id !== key.id)
+    newKeys.push({ ...key, name })
+    await optimisticSetKeys(newKeys, async () => {
+      const resp = await allauth.updateWebAuthnCredential(key.id, { name })
+      return (resp.status === 200)
+    })
+  }
+
+  if (!keys.length && !response.fetching) {
+    return <Navigate to='/account/2fa' />
+  }
+
+  return (
+    <section>
+      <h1>Security Keys</h1>
+
+      <table className='table table-striped'>
+        <thead>
+          <tr>
+            <th>Name</th>
+            <th>Type</th>
+            <th>Created At</th>
+            <th>Last Used At</th>
+            <th>Actions</th>
+          </tr>
+        </thead>
+        <tbody>
+          {keys.map(key => {
+            return (
+              <Authenticator
+                key={key.id}
+                editting={key.id === editId} authenticator={key} onCancel={() => setEditId(null)}
+                onSave={(name) => onSave(key, name)}
+                onEdit={() => setEditId(key.id)} onDelete={() => deleteKey(key)}
+              />
+            )
+          })}
+        </tbody>
+      </table>
+      <Link to='/account/2fa/webauthn/add'>Add</Link>
+    </section>
+  )
+}
diff --git a/examples/react-spa/frontend/src/mfa/MFAOverview.js b/examples/react-spa/frontend/src/mfa/MFAOverview.js
index a101748a..4d3eb375 100644
--- a/examples/react-spa/frontend/src/mfa/MFAOverview.js
+++ b/examples/react-spa/frontend/src/mfa/MFAOverview.js
@@ -10,6 +10,7 @@ export async function loader ({ params }) {
 export default function MFAOverview (props) {
   const { authenticators } = useLoaderData()
   const totp = authenticators.find(authenticator => authenticator.type === allauth.AuthenticatorType.TOTP)
+  const webauthn = authenticators.filter(authenticator => authenticator.type === allauth.AuthenticatorType.WEBAUTHN)
   const recoveryCodes = authenticators.find(authenticator => authenticator.type === allauth.AuthenticatorType.RECOVERY_CODES)
   return (
     <section>
@@ -27,6 +28,16 @@ export default function MFAOverview (props) {
           <Link to='/account/2fa/totp/activate'>Activate</Link>
         </>}
 
+      <h2>Security Keys and Devices</h2>
+
+      {webauthn.length
+        ? <><p>You have added {webauthn.length} security keys/devices.</p>
+          <Link to='/account/2fa/webauthn'>Manage</Link>
+        </>
+        : <><p>No security keys have been added.</p>
+          <Link to='/account/2fa/webauthn/add'>Add</Link>
+        </>}
+
       <h2>Recovery Codes</h2>
 
       {!recoveryCodes
diff --git a/examples/react-spa/frontend/src/mfa/MFAReauthenticate.js b/examples/react-spa/frontend/src/mfa/ReauthenticateCode.js
similarity index 83%
rename from examples/react-spa/frontend/src/mfa/MFAReauthenticate.js
rename to examples/react-spa/frontend/src/mfa/ReauthenticateCode.js
index 726f20b8..507f2d2c 100644
--- a/examples/react-spa/frontend/src/mfa/MFAReauthenticate.js
+++ b/examples/react-spa/frontend/src/mfa/ReauthenticateCode.js
@@ -1,10 +1,10 @@
 import { useState } from 'react'
 import FormErrors from '../components/FormErrors'
-import { mfaReauthenticate, Flows } from '../lib/allauth'
+import { mfaReauthenticate } from '../lib/allauth'
 import ReauthenticateFlow from '../account/ReauthenticateFlow'
 import Button from '../components/Button'
 
-export default function MFAReauthenticate () {
+export default function ReauthenticateCode (props) {
   const [code, setCode] = useState('')
   const [response, setResponse] = useState({ fetching: false, content: null })
 
@@ -20,8 +20,8 @@ export default function MFAReauthenticate () {
     })
   }
   return (
-    <ReauthenticateFlow flow={Flows.MFA_REAUTHENTICATE}>
-      <p>Enter an authenticator code:</p>
+    <ReauthenticateFlow>
+      {props.children}
 
       <FormErrors errors={response.content?.errors} />
 
diff --git a/examples/react-spa/frontend/src/mfa/ReauthenticateRecoveryCodes.js b/examples/react-spa/frontend/src/mfa/ReauthenticateRecoveryCodes.js
new file mode 100644
index 00000000..33975cad
--- /dev/null
+++ b/examples/react-spa/frontend/src/mfa/ReauthenticateRecoveryCodes.js
@@ -0,0 +1,9 @@
+import ReauthenticateCode from './ReauthenticateCode'
+
+export default function ReauthenticateRecoveryCodes (props) {
+  return (
+    <ReauthenticateCode>
+      <p>Please enter a recovery code:</p>
+    </ReauthenticateCode>
+  )
+}
diff --git a/examples/react-spa/frontend/src/mfa/ReauthenticateTOTP.js b/examples/react-spa/frontend/src/mfa/ReauthenticateTOTP.js
new file mode 100644
index 00000000..3ae82bee
--- /dev/null
+++ b/examples/react-spa/frontend/src/mfa/ReauthenticateTOTP.js
@@ -0,0 +1,9 @@
+import ReauthenticateCode from './ReauthenticateCode'
+
+export default function ReauthenticateTOTP (props) {
+  return (
+    <ReauthenticateCode>
+      <p>Please enter an authenticator code:</p>
+    </ReauthenticateCode>
+  )
+}
diff --git a/examples/react-spa/frontend/src/mfa/ReauthenticateWebAuthn.js b/examples/react-spa/frontend/src/mfa/ReauthenticateWebAuthn.js
new file mode 100644
index 00000000..d0a6970c
--- /dev/null
+++ b/examples/react-spa/frontend/src/mfa/ReauthenticateWebAuthn.js
@@ -0,0 +1,35 @@
+import { useState } from 'react'
+import { Flows, getWebAuthnRequestOptionsForReauthentication, reauthenticateUsingWebAuthn } from '../lib/allauth'
+import ReauthenticateFlow from '../account/ReauthenticateFlow'
+import Button from '../components/Button'
+import {
+  parseRequestOptionsFromJSON,
+  get
+} from '@github/webauthn-json/browser-ponyfill'
+
+export default function ReauthenticateWebAuthn () {
+  const [response, setResponse] = useState({ fetching: false, content: null })
+
+  async function submit () {
+    setResponse({ ...response, fetching: true })
+    try {
+      const optResp = await getWebAuthnRequestOptionsForReauthentication()
+      const jsonOptions = optResp.data.request_options
+      const options = parseRequestOptionsFromJSON(jsonOptions)
+      const credential = await get(options)
+      const reauthResp = await reauthenticateUsingWebAuthn(credential)
+      setResponse((r) => { return { ...r, content: reauthResp } })
+    } catch (e) {
+      console.error(e)
+      window.alert(e)
+    }
+    setResponse((r) => { return { ...r, fetching: false } })
+  }
+
+  return (
+    <ReauthenticateFlow flow={Flows.MFA_REAUTHENTICATE}>
+
+      <Button disabled={response.fetching} onClick={() => submit()}>Use security key</Button>
+    </ReauthenticateFlow>
+  )
+}
diff --git a/examples/react-spa/frontend/src/mfa/WebAuthnLoginButton.js b/examples/react-spa/frontend/src/mfa/WebAuthnLoginButton.js
new file mode 100644
index 00000000..21311e97
--- /dev/null
+++ b/examples/react-spa/frontend/src/mfa/WebAuthnLoginButton.js
@@ -0,0 +1,29 @@
+import Button from '../components/Button'
+import { useState } from 'react'
+import { getWebAuthnRequestOptionsForLogin, loginUsingWebAuthn } from '../lib/allauth'
+import {
+  parseRequestOptionsFromJSON,
+  get
+} from '@github/webauthn-json/browser-ponyfill'
+
+export default function WebAuthnLoginButton (props) {
+  const [response, setResponse] = useState({ fetching: false, content: null })
+
+  async function submit (e) {
+    e.preventDefault()
+    setResponse({ ...response, fetching: true })
+    try {
+      const optResp = await getWebAuthnRequestOptionsForLogin()
+      const jsonOptions = optResp.data.request_options
+      const options = parseRequestOptionsFromJSON(jsonOptions)
+      const credential = await get(options)
+      const loginResp = await loginUsingWebAuthn(credential)
+      setResponse((r) => { return { ...r, content: loginResp } })
+    } catch (e) {
+      console.error(e)
+      window.alert(e)
+    }
+    setResponse((r) => { return { ...r, fetching: false } })
+  }
+  return <Button onClick={submit}>{props.children}</Button>
+}
diff --git a/examples/regular-django/requirements.txt b/examples/regular-django/requirements.txt
index afb5fdf0..a97b675e 100644
--- a/examples/regular-django/requirements.txt
+++ b/examples/regular-django/requirements.txt
@@ -1,1 +1,1 @@
-django-allauth[mfa,saml,socialaccount,steam]>=0.63.3
+django-allauth[mfa,saml,socialaccount,steam]>=0.63.6
diff --git a/setup.cfg b/setup.cfg
index db1ca64e..b6e5553a 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -24,8 +24,6 @@ classifiers =
     Programming Language :: Python :: 3.11
     Programming Language :: Python :: 3.12
     Framework :: Django
-    Framework :: Django :: 4.0
-    Framework :: Django :: 4.1
     Framework :: Django :: 4.2
     Framework :: Django :: 5.0
 project_urls =
@@ -45,7 +43,7 @@ tests_require =
     pytest >= 7.4
     pytest-django >= 4.5.2
 install_requires =
-    Django >= 4.0
+    Django >= 4.2
 
 [options.extras_require]
 mfa =
diff --git a/tox.ini b/tox.ini
index 8c1e3ba1..f0f81ce5 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,6 @@
 [tox]
 envlist =
-    prj{regular,headless_only}-py{38,39,310,311,312}-django{40,41,42}
+    prj{regular,headless_only}-py{38,39,310,311,312}-django{42}
     prj{regular,headless_only}-py{310,311,312}-django{50}
     prj{regular,headless_only}-py{310,311,312}-djangomain
     docs
@@ -23,8 +23,6 @@ deps =
     Pillow>=9.0
     pytest>=7.4
     pytest-django>=4.5.2
-    django40: Django==4.0.*
-    django41: Django==4.1.*
     django42: Django==4.2.*
     django50: Django==5.0.*
     djangomain: git+https://github.com/django/django.git@main#egg=django
@@ -118,8 +116,6 @@ PYTHON_VER =
     3.12: py312
 DJANGO =
     main: djangomain
-    4.0: django40
-    4.1: django41
     4.2: django42
     5.0: django50
 PRJ =


Test patch:
diff --git a/allauth/account/tests/test_login.py b/allauth/account/tests/test_login.py
index 8c7c3966..f0b02c51 100644
--- a/allauth/account/tests/test_login.py
+++ b/allauth/account/tests/test_login.py
@@ -1,7 +1,6 @@
 import json
 from unittest.mock import ANY, patch
 
-import django
 from django.conf import settings
 from django.contrib.auth import get_user_model
 from django.core import mail
@@ -140,27 +139,15 @@ class LoginTests(TestCase):
                     "password": ("doe" if is_valid_attempt else "wrong"),
                 },
             )
-            if django.VERSION >= (4, 1):
-                self.assertFormError(
-                    resp.context["form"],
-                    None,
-                    (
-                        "Too many failed login attempts. Try again later."
-                        if is_locked
-                        else "The username and/or password you specified are not correct."
-                    ),
-                )
-            else:
-                self.assertFormError(
-                    resp,
-                    "form",
-                    None,
-                    (
-                        "Too many failed login attempts. Try again later."
-                        if is_locked
-                        else "The username and/or password you specified are not correct."
-                    ),
-                )
+            self.assertFormError(
+                resp.context["form"],
+                None,
+                (
+                    "Too many failed login attempts. Try again later."
+                    if is_locked
+                    else "The username and/or password you specified are not correct."
+                ),
+            )
 
     @override_settings(
         ACCOUNT_AUTHENTICATION_METHOD=app_settings.AuthenticationMethod.EMAIL,
@@ -188,37 +175,19 @@ class LoginTests(TestCase):
         resp = self.client.post(
             reverse("account_login"), {"login": user.email, "password": "bad"}
         )
-        if django.VERSION >= (4, 1):
-            self.assertFormError(
-                resp.context["form"],
-                None,
-                "The email address and/or password you specified are not correct.",
-            )
-        else:
-            self.assertFormError(
-                resp,
-                "form",
-                None,
-                "The email address and/or password you specified are not correct.",
-            )
-
+        self.assertFormError(
+            resp.context["form"],
+            None,
+            "The email address and/or password you specified are not correct.",
+        )
         resp = self.client.post(
             reverse("account_login"), {"login": user.email, "password": "bad"}
         )
-        if django.VERSION >= (4, 1):
-            self.assertFormError(
-                resp.context["form"],
-                None,
-                "Too many failed login attempts. Try again later.",
-            )
-        else:
-            self.assertFormError(
-                resp,
-                "form",
-                None,
-                "Too many failed login attempts. Try again later.",
-            )
-
+        self.assertFormError(
+            resp.context["form"],
+            None,
+            "Too many failed login attempts. Try again later.",
+        )
         self.client.post(reverse("account_reset_password"), data={"email": user.email})
 
         body = mail.outbox[0].body
diff --git a/allauth/account/tests/test_signup.py b/allauth/account/tests/test_signup.py
index 4f4f21fc..f64d6334 100644
--- a/allauth/account/tests/test_signup.py
+++ b/allauth/account/tests/test_signup.py
@@ -1,4 +1,3 @@
-import django
 from django import forms
 from django.contrib.auth import get_user_model
 from django.contrib.auth.models import AnonymousUser
@@ -215,19 +214,11 @@ class SignupTests(TestCase):
                 "password2": "janedoe",
             },
         )
-        if django.VERSION >= (4, 1):
-            self.assertFormError(
-                resp.context["form"],
-                "password2",
-                "You must type the same password each time.",
-            )
-        else:
-            self.assertFormError(
-                resp,
-                "form",
-                "password2",
-                "You must type the same password each time.",
-            )
+        self.assertFormError(
+            resp.context["form"],
+            "password2",
+            "You must type the same password each time.",
+        )
 
     @override_settings(
         ACCOUNT_USERNAME_REQUIRED=True, ACCOUNT_SIGNUP_EMAIL_ENTER_TWICE=True
@@ -273,21 +264,12 @@ class SignupTests(TestCase):
                 "password2": "johndoe",
             },
         )
-        if django.VERSION >= (4, 1):
-            self.assertFormError(resp.context["form"], None, [])
-            self.assertFormError(
-                resp.context["form"],
-                "password1",
-                ["This password is too short. It must contain at least 9 characters."],
-            )
-        else:
-            self.assertFormError(resp, "form", None, [])
-            self.assertFormError(
-                resp,
-                "form",
-                "password1",
-                ["This password is too short. It must contain at least 9 characters."],
-            )
+        self.assertFormError(resp.context["form"], None, [])
+        self.assertFormError(
+            resp.context["form"],
+            "password1",
+            ["This password is too short. It must contain at least 9 characters."],
+        )
 
 
 def test_prevent_enumeration_with_mandatory_verification(
@@ -310,6 +292,7 @@ def test_prevent_enumeration_with_mandatory_verification(
     assert resp.status_code == 302
     assert resp["location"] == reverse("account_email_verification_sent")
     assertTemplateUsed(resp, "account/email/account_already_exists_message.txt")
+    assertTemplateUsed(resp, "account/messages/email_confirmation_sent.txt")
     assert EmailAddress.objects.filter(email="john@example.org").count() == 1
 
 
diff --git a/allauth/headless/mfa/tests/test_recovery_codes.py b/allauth/headless/mfa/tests/test_recovery_codes.py
new file mode 100644
index 00000000..7362eedb
--- /dev/null
+++ b/allauth/headless/mfa/tests/test_recovery_codes.py
@@ -0,0 +1,60 @@
+from allauth.mfa.models import Authenticator
+
+
+def test_get_recovery_codes_requires_reauth(
+    auth_client, user_with_recovery_codes, headless_reverse
+):
+    rc = Authenticator.objects.get(
+        type=Authenticator.Type.RECOVERY_CODES, user=user_with_recovery_codes
+    )
+    resp = auth_client.get(headless_reverse("headless:mfa:manage_recovery_codes"))
+    assert resp.status_code == 401
+    data = resp.json()
+    assert data["meta"]["is_authenticated"]
+    resp = auth_client.post(
+        headless_reverse("headless:mfa:reauthenticate"),
+        data={"code": rc.wrap().get_unused_codes()[0]},
+        content_type="application/json",
+    )
+    assert resp.status_code == 200
+
+
+def test_get_recovery_codes(
+    auth_client,
+    user_with_recovery_codes,
+    headless_reverse,
+    reauthentication_bypass,
+):
+    with reauthentication_bypass():
+        resp = auth_client.get(headless_reverse("headless:mfa:manage_recovery_codes"))
+    assert resp.status_code == 200
+    data = resp.json()
+    assert data["data"]["type"] == "recovery_codes"
+    assert len(data["data"]["unused_codes"]) == 10
+
+    with reauthentication_bypass():
+        resp = auth_client.get(headless_reverse("headless:mfa:authenticators"))
+    data = resp.json()
+    assert len(data["data"]) == 2
+    rc = [autor for autor in data["data"] if autor["type"] == "recovery_codes"][0]
+    assert "unused_codes" not in rc
+
+
+def test_generate_recovery_codes(
+    auth_client,
+    user_with_totp,
+    headless_reverse,
+    reauthentication_bypass,
+):
+    with reauthentication_bypass():
+        resp = auth_client.get(headless_reverse("headless:mfa:manage_recovery_codes"))
+    assert resp.status_code == 404
+    with reauthentication_bypass():
+        resp = auth_client.post(
+            headless_reverse("headless:mfa:manage_recovery_codes"),
+            content_type="application/json",
+        )
+    assert resp.status_code == 200
+    data = resp.json()
+    assert data["data"]["type"] == "recovery_codes"
+    assert len(data["data"]["unused_codes"]) == 10
diff --git a/allauth/headless/mfa/tests/test_totp.py b/allauth/headless/mfa/tests/test_totp.py
new file mode 100644
index 00000000..b6fe4b65
--- /dev/null
+++ b/allauth/headless/mfa/tests/test_totp.py
@@ -0,0 +1,62 @@
+from allauth.mfa.models import Authenticator
+
+
+def test_get_totp_not_active(
+    auth_client,
+    user,
+    headless_reverse,
+):
+    resp = auth_client.get(headless_reverse("headless:mfa:manage_totp"))
+    assert resp.status_code == 404
+    data = resp.json()
+    assert len(data["meta"]["secret"]) == 32
+    assert len(data["meta"]["totp_url"]) == 145
+
+
+def test_get_totp(
+    auth_client,
+    user_with_totp,
+    headless_reverse,
+):
+    resp = auth_client.get(headless_reverse("headless:mfa:manage_totp"))
+    assert resp.status_code == 200
+    data = resp.json()
+    assert data["data"]["type"] == "totp"
+    assert isinstance(data["data"]["created_at"], float)
+
+
+def test_deactivate_totp(
+    auth_client,
+    user_with_totp,
+    headless_reverse,
+    reauthentication_bypass,
+):
+    with reauthentication_bypass():
+        resp = auth_client.delete(headless_reverse("headless:mfa:manage_totp"))
+    assert resp.status_code == 200
+    assert not Authenticator.objects.filter(user=user_with_totp).exists()
+
+
+def test_activate_totp(
+    auth_client,
+    user,
+    headless_reverse,
+    reauthentication_bypass,
+    settings,
+    totp_validation_bypass,
+):
+    with reauthentication_bypass():
+        with totp_validation_bypass():
+            resp = auth_client.post(
+                headless_reverse("headless:mfa:manage_totp"),
+                data={"code": "42"},
+                content_type="application/json",
+            )
+    assert resp.status_code == 200
+    assert Authenticator.objects.filter(
+        user=user, type=Authenticator.Type.TOTP
+    ).exists()
+    data = resp.json()
+    assert data["data"]["type"] == "totp"
+    assert isinstance(data["data"]["created_at"], float)
+    assert data["data"]["last_used_at"] is None
diff --git a/allauth/headless/mfa/tests/test_views.py b/allauth/headless/mfa/tests/test_views.py
index 2658e666..60a1f9cb 100644
--- a/allauth/headless/mfa/tests/test_views.py
+++ b/allauth/headless/mfa/tests/test_views.py
@@ -1,6 +1,5 @@
 from allauth.account.models import EmailAddress, get_emailconfirmation_model
 from allauth.headless.constants import Flow
-from allauth.mfa.models import Authenticator
 
 
 def test_auth_unverified_email_and_mfa(
@@ -50,10 +49,12 @@ def test_auth_unverified_email_and_mfa(
             }
         )
     flows.append({"id": "provider_token", "providers": ["dummy"]})
+    flows.append({"id": "mfa_login_webauthn"})
     flows.append(
         {
             "id": "mfa_authenticate",
             "is_pending": True,
+            "types": ["totp"],
         }
     )
 
@@ -82,123 +83,3 @@ def test_auth_unverified_email_and_mfa(
             content_type="application/json",
         )
     assert resp.status_code == 200
-
-
-def test_get_recovery_codes_requires_reauth(
-    auth_client, user_with_recovery_codes, headless_reverse
-):
-    rc = Authenticator.objects.get(
-        type=Authenticator.Type.RECOVERY_CODES, user=user_with_recovery_codes
-    )
-    resp = auth_client.get(headless_reverse("headless:mfa:manage_recovery_codes"))
-    assert resp.status_code == 401
-    data = resp.json()
-    assert data["meta"]["is_authenticated"]
-    resp = auth_client.post(
-        headless_reverse("headless:mfa:reauthenticate"),
-        data={"code": rc.wrap().get_unused_codes()[0]},
-        content_type="application/json",
-    )
-    assert resp.status_code == 200
-
-
-def test_get_recovery_codes(
-    auth_client,
-    user_with_recovery_codes,
-    headless_reverse,
-    reauthentication_bypass,
-):
-    with reauthentication_bypass():
-        resp = auth_client.get(headless_reverse("headless:mfa:manage_recovery_codes"))
-    assert resp.status_code == 200
-    data = resp.json()
-    assert data["data"]["type"] == "recovery_codes"
-    assert len(data["data"]["unused_codes"]) == 10
-
-    with reauthentication_bypass():
-        resp = auth_client.get(headless_reverse("headless:mfa:authenticators"))
-    data = resp.json()
-    assert len(data["data"]) == 2
-    rc = [autor for autor in data["data"] if autor["type"] == "recovery_codes"][0]
-    assert "unused_codes" not in rc
-
-
-def test_generate_recovery_codes(
-    auth_client,
-    user_with_totp,
-    headless_reverse,
-    reauthentication_bypass,
-):
-    with reauthentication_bypass():
-        resp = auth_client.get(headless_reverse("headless:mfa:manage_recovery_codes"))
-    assert resp.status_code == 404
-    with reauthentication_bypass():
-        resp = auth_client.post(
-            headless_reverse("headless:mfa:manage_recovery_codes"),
-            content_type="application/json",
-        )
-    assert resp.status_code == 200
-    data = resp.json()
-    assert data["data"]["type"] == "recovery_codes"
-    assert len(data["data"]["unused_codes"]) == 10
-
-
-def test_get_totp_not_active(
-    auth_client,
-    user,
-    headless_reverse,
-):
-    resp = auth_client.get(headless_reverse("headless:mfa:manage_totp"))
-    assert resp.status_code == 404
-    data = resp.json()
-    assert len(data["meta"]["secret"]) == 32
-    assert len(data["meta"]["totp_url"]) == 145
-
-
-def test_get_totp(
-    auth_client,
-    user_with_totp,
-    headless_reverse,
-):
-    resp = auth_client.get(headless_reverse("headless:mfa:manage_totp"))
-    assert resp.status_code == 200
-    data = resp.json()
-    assert data["data"]["type"] == "totp"
-    assert isinstance(data["data"]["created_at"], float)
-
-
-def test_deactivate_totp(
-    auth_client,
-    user_with_totp,
-    headless_reverse,
-    reauthentication_bypass,
-):
-    with reauthentication_bypass():
-        resp = auth_client.delete(headless_reverse("headless:mfa:manage_totp"))
-    assert resp.status_code == 200
-    assert not Authenticator.objects.filter(user=user_with_totp).exists()
-
-
-def test_activate_totp(
-    auth_client,
-    user,
-    headless_reverse,
-    reauthentication_bypass,
-    settings,
-    totp_validation_bypass,
-):
-    with reauthentication_bypass():
-        with totp_validation_bypass():
-            resp = auth_client.post(
-                headless_reverse("headless:mfa:manage_totp"),
-                data={"code": "42"},
-                content_type="application/json",
-            )
-    assert resp.status_code == 200
-    assert Authenticator.objects.filter(
-        user=user, type=Authenticator.Type.TOTP
-    ).exists()
-    data = resp.json()
-    assert data["data"]["type"] == "totp"
-    assert isinstance(data["data"]["created_at"], float)
-    assert data["data"]["last_used_at"] is None
diff --git a/allauth/headless/mfa/tests/test_webauthn.py b/allauth/headless/mfa/tests/test_webauthn.py
new file mode 100644
index 00000000..79c40120
--- /dev/null
+++ b/allauth/headless/mfa/tests/test_webauthn.py
@@ -0,0 +1,167 @@
+from unittest.mock import ANY
+
+from allauth.headless.constants import Flow
+from allauth.mfa.models import Authenticator
+
+
+def test_passkey_login(
+    client, passkey, webauthn_authentication_bypass, headless_reverse
+):
+    with webauthn_authentication_bypass(passkey) as credential:
+        resp = client.post(
+            headless_reverse("headless:mfa:login_webauthn"),
+            data={"credential": credential},
+            content_type="application/json",
+        )
+    data = resp.json()
+    assert data["data"]["user"]["id"] == passkey.user_id
+
+
+def test_passkey_login_get_options(client, headless_reverse, db):
+    resp = client.get(headless_reverse("headless:mfa:login_webauthn"))
+    data = resp.json()
+    assert data == {"status": 200, "data": {"request_options": {"publicKey": ANY}}}
+
+
+def test_reauthenticate(
+    auth_client,
+    passkey,
+    user_with_recovery_codes,
+    webauthn_authentication_bypass,
+    headless_reverse,
+):
+    # View recovery codes, confirm webauthn reauthentication is an option
+    resp = auth_client.get(headless_reverse("headless:mfa:manage_recovery_codes"))
+    assert resp.status_code == 401
+    assert Flow.MFA_REAUTHENTICATE in [
+        flow["id"] for flow in resp.json()["data"]["flows"]
+    ]
+
+    # Get request options
+    with webauthn_authentication_bypass(passkey):
+        resp = auth_client.get(headless_reverse("headless:mfa:reauthenticate_webauthn"))
+        data = resp.json()
+        assert data["status"] == 200
+        assert data["data"]["request_options"] == ANY
+
+    # Reauthenticate
+    with webauthn_authentication_bypass(passkey) as credential:
+        resp = auth_client.post(
+            headless_reverse("headless:mfa:reauthenticate_webauthn"),
+            data={"credential": credential},
+            content_type="application/json",
+        )
+        assert resp.status_code == 200
+    resp = auth_client.get(headless_reverse("headless:mfa:manage_recovery_codes"))
+    assert resp.status_code == 200
+
+
+def test_update_authenticator(
+    auth_client, headless_reverse, passkey, reauthentication_bypass
+):
+    data = {"id": passkey.pk, "name": "Renamed!"}
+    resp = auth_client.put(
+        headless_reverse("headless:mfa:manage_webauthn"),
+        data=data,
+        content_type="application/json",
+    )
+    # Reauthentication required
+    assert resp.status_code == 401
+    with reauthentication_bypass():
+        resp = auth_client.put(
+            headless_reverse("headless:mfa:manage_webauthn"),
+            data=data,
+            content_type="application/json",
+        )
+    assert resp.status_code == 200
+    passkey.refresh_from_db()
+    assert passkey.wrap().name == "Renamed!"
+
+
+def test_delete_authenticator(
+    auth_client, headless_reverse, passkey, reauthentication_bypass
+):
+    data = {"authenticators": [passkey.pk]}
+    resp = auth_client.delete(
+        headless_reverse("headless:mfa:manage_webauthn"),
+        data=data,
+        content_type="application/json",
+    )
+    # Reauthentication required
+    assert resp.status_code == 401
+    with reauthentication_bypass():
+        resp = auth_client.delete(
+            headless_reverse("headless:mfa:manage_webauthn"),
+            data=data,
+            content_type="application/json",
+        )
+    assert resp.status_code == 200
+    assert not Authenticator.objects.filter(pk=passkey.pk).exists()
+
+
+def test_add_authenticator(
+    user,
+    auth_client,
+    headless_reverse,
+    webauthn_registration_bypass,
+    reauthentication_bypass,
+):
+    resp = auth_client.get(headless_reverse("headless:mfa:manage_webauthn"))
+    # Reauthentication required
+    assert resp.status_code == 401
+
+    with reauthentication_bypass():
+        resp = auth_client.get(headless_reverse("headless:mfa:manage_webauthn"))
+        data = resp.json()
+        assert data["data"]["creation_options"] == ANY
+
+        with webauthn_registration_bypass(user, False) as credential:
+            resp = auth_client.post(
+                headless_reverse("headless:mfa:manage_webauthn"),
+                data={"credential": credential},
+                content_type="application/json",
+            )
+            assert resp.status_code == 200
+    assert (
+        Authenticator.objects.filter(
+            type=Authenticator.Type.WEBAUTHN, user=user
+        ).count()
+        == 1
+    )
+
+
+def test_2fa_login(
+    client,
+    user,
+    user_password,
+    passkey,
+    webauthn_authentication_bypass,
+    headless_reverse,
+):
+    resp = client.post(
+        headless_reverse("headless:account:login"),
+        data={
+            "username": user.username,
+            "password": user_password,
+        },
+        content_type="application/json",
+    )
+    assert resp.status_code == 401
+    data = resp.json()
+    pending_flows = [f for f in data["data"]["flows"] if f.get("is_pending")]
+    assert len(pending_flows) == 1
+    pending_flow = pending_flows[0]
+    assert pending_flow == {
+        "id": "mfa_authenticate",
+        "is_pending": True,
+        "types": ["webauthn"],
+    }
+    with webauthn_authentication_bypass(passkey) as credential:
+        resp = client.post(
+            headless_reverse("headless:mfa:authenticate_webauthn"),
+            data={"credential": credential},
+            content_type="application/json",
+        )
+    data = resp.json()
+    assert resp.status_code == 200
+    assert data["data"]["user"]["id"] == passkey.user_id
diff --git a/allauth/mfa/webauthn/tests/test_views.py b/allauth/mfa/webauthn/tests/test_views.py
index f4127516..47c32308 100644
--- a/allauth/mfa/webauthn/tests/test_views.py
+++ b/allauth/mfa/webauthn/tests/test_views.py
@@ -50,7 +50,7 @@ def test_get_passkey_login_challenge(client, db):
     assert resp["content-type"] == "application/json"
     data = resp.json()
     assert data == {
-        "credentials": {
+        "request_options": {
             "publicKey": {
                 "challenge": ANY,
                 "rpId": "testserver",
diff --git a/allauth/socialaccount/providers/facebook/tests.py b/allauth/socialaccount/providers/facebook/tests.py
index 2d511d70..5e9e190d 100644
--- a/allauth/socialaccount/providers/facebook/tests.py
+++ b/allauth/socialaccount/providers/facebook/tests.py
@@ -1,5 +1,3 @@
-import json
-
 from django.contrib.auth import get_user_model
 from django.test.client import RequestFactory
 from django.test.utils import override_settings
@@ -130,7 +128,7 @@ class FacebookTests(OAuth2TestsMixin, TestCase):
     )
     def test_login_by_token_reauthenticate(self):
         resp = self.client.get(reverse("account_login"))
-        nonce = json.loads(resp.context["fb_data"])["loginOptions"]["auth_nonce"]
+        nonce = resp.context["fb_data"]["loginOptions"]["auth_nonce"]
         with mocked_response(
             {"access_token": "app_token"},
             {
diff --git a/allauth/socialaccount/tests/test_signup.py b/allauth/socialaccount/tests/test_signup.py
index 8691be8f..80b63f62 100644
--- a/allauth/socialaccount/tests/test_signup.py
+++ b/allauth/socialaccount/tests/test_signup.py
@@ -1,6 +1,5 @@
 from unittest.mock import patch
 
-import django
 from django.contrib.auth import get_user_model
 from django.contrib.auth.models import AnonymousUser
 from django.urls import reverse
@@ -280,24 +279,13 @@ def test_unique_email_validation_signup(
     form = resp.context["form"]
     assert form["email"].value() == email
     resp = client.post(reverse("socialaccount_signup"), data={"email": email})
-
-    if django.VERSION >= (4, 1):
-        assertFormError(
-            resp.context["form"],
-            "email",
-            "An account already exists with this email address."
-            " Please sign in to that account first, then connect"
-            " your Unittest Server account.",
-        )
-    else:
-        assertFormError(
-            resp,
-            "form",
-            "email",
-            "An account already exists with this email address."
-            " Please sign in to that account first, then connect"
-            " your Unittest Server account.",
-        )
+    assertFormError(
+        resp.context["form"],
+        "email",
+        "An account already exists with this email address."
+        " Please sign in to that account first, then connect"
+        " your Unittest Server account.",
+    )
 
 
 def test_social_account_taken_at_signup(
diff --git a/tests/headless_only/settings.py b/tests/headless_only/settings.py
index cef72750..f75c98ba 100644
--- a/tests/headless_only/settings.py
+++ b/tests/headless_only/settings.py
@@ -263,3 +263,6 @@ HEADLESS_FRONTEND_URLS = {
     "account_reset_password_from_key": "/spa/password/reset/{key}/",
     "account_signup": "/spa/signup",
 }
+
+MFA_SUPPORTED_TYPES = ["totp", "webauthn", "recovery_codes"]
+MFA_PASSKEY_LOGIN_ENABLED = True