pythondjangoauthenticationgoogle-2fadjango-two-factor-auth

Django - Two Factor Authentication Custom Login


After a quick search and reading documentation I implemented Django - Two Factor Authentication in one of my Django project [Reference Link].

It works great I am using Google Authenticator for token based login. The problem arises when I want to extend login methodology of the library. I want to enforce my every user to use 2-Factor-Auth as compulsion. I am not using any signup measures so there has to be a check at the time of Login for a user.

The problem is to design a custom login mechanism but I am unable to incorporate this library with the custom login.

PS: I have a custom user model and currently I am using default Login that came with Django Two Factor Authentication.

I did not though the code was necessary so I did not posted it but I can share it if needed.


Solution

  • After a through look at the library code I was able to manipulate the check in the two_factor library.

    So, looking at two_factor folder it is easily understandable that is nothing but a Django app similar to others.

    I navigated to the library files in my virtual environment venv\Lib\site-packages\two_factor\views\core.py. As mentioned in the documentation there is no enforcement for users till now to setup 2fa.

    In LoginView(...) there is a function done. IT checks for the device availability for 2fa just add a else clause for redirection.

    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:
                signals.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'),
                                        )
            else:
                return redirect('two_factor:setup')
    
            return response
    

    So what happens is the check for device can only be successful if the user has setup the 2fa but it will never be true for unverified user.

    I totally understand there will be a more efficient and elegant method to do the above task but with my little knowledge and time limitation I have to do this. I will post an update if I encounter and I also welcome feedbacks for my solution.