pythondjangotwo-factor-authenticationdjango-two-factor-auth

How to override Django Two Factor Authorization default login success response


I am using Django Two Factor Authorization in my login view. I am using a plugin manager to register the plugin TwoFactorPlugin which

My problem is that although in the view I return the view's HttpResponse, it always returns the LOGIN_REDIRECT_URL. If I remove the LOGIN_REDIRECT_URL, it will always redirect to accounts/profile/, which is the two factor's default login success url.

Is it possible to override this to return the HttpResponse of the view?

Is there another way to do this (or any suggestion really) ?

views.py:

from two_factor.views import LoginView as TwoFactorLoginView

@login_required
def login_success(request):
    success_message = f"User logged in successfully."
    return HttpResponse(success_message)


class TwoFactor_LoginView(TwoFactorLoginView):
    def form_valid(self, form):
        response = super().form_valid(form)
        # plugin_response is an HttpResponse return from the plugin "two_factor"
        plugin_response = PluginManager.perform_action(
            "two_factor",
            self.request,
            email=self.request.user.email,
            password=form.cleaned_data.get("password"),
            token=self.request.POST.get("token"),
        )
        # If the plugin action returns an HttpResponse, use it instead
        if isinstance(plugin_response, HttpResponse):
            return plugin_response
        return response

urls.py:

path("login/", TwoFactor_LoginView.as_view(), name="login"),
path("login_success/", login_success, name="login_success"),

settings.py:

LOGIN_URL = "login/"
LOGIN_REDIRECT_URL = "/login_success/"

Solution

  • I completely rewrote this code.

    In the django-two-factor-auth the custom LoginView class has a done method which basically redirects to LOGIN_REDIRECT_URL.

    So I overrode it by adding the done method in my class, exposing the response to be able to modify it to my liking:

    def done(self, form_list, **kwargs):
        """
        Login the user and redirect to the desired page.
        """
    
        # Check if remember cookie should be set after login
        current_step_data = self.storage.get_step_data(self.steps.current)
        remember = bool(current_step_data and current_step_data.get("token-remember") == "on")
    
        login(self.request, self.get_user())
    
        redirect_to = self.get_success_url()
    
        device = getattr(self.get_user(), "otp_device", None)
        response = redirect(redirect_to)
    
        if device:
            user_verified.send(
                sender=__name__, request=self.request, user=self.get_user(), device=device
            )
    
            # Set a remember cookie if activated
    
            if getattr(settings, "TWO_FACTOR_REMEMBER_COOKIE_AGE", None) and remember:
                # choose a unique cookie key to remember devices for multiple users in the same browser
                cookie_key = REMEMBER_COOKIE_PREFIX + str(uuid4())
                cookie_value = get_remember_device_cookie(
                    user=self.get_user(), otp_device_id=device.persistent_id
                )
                response.set_cookie(
                    cookie_key,
                    cookie_value,
                    max_age=settings.TWO_FACTOR_REMEMBER_COOKIE_AGE,
                    domain=getattr(settings, "TWO_FACTOR_REMEMBER_COOKIE_DOMAIN", None),
                    path=getattr(settings, "TWO_FACTOR_REMEMBER_COOKIE_PATH", "/"),
                    secure=getattr(settings, "TWO_FACTOR_REMEMBER_COOKIE_SECURE", False),
                    httponly=getattr(settings, "TWO_FACTOR_REMEMBER_COOKIE_HTTPONLY", True),
                    samesite=getattr(settings, "TWO_FACTOR_REMEMBER_COOKIE_SAMESITE", "Lax"),
                )
            return response
    
        # If the user does not have a device.
        elif OTPRequiredMixin.is_otp_view(self.request.GET.get("next")):
            if self.request.GET.get("next"):
                self.request.session["next"] = self.get_success_url()
            return redirect("two_factor:setup")
    
        return response
    

    Now all I had to do is add my own get_success_url to my TwoFactor_LoginView class:

    def get_success_url(self):
        return reverse("login_success")