jazzband / django-two-factor-auth

Complete Two-Factor Authentication for Django providing the easiest integration into most Django projects.
MIT License
1.67k stars 443 forks source link

Django recaptcha2 field bypass otp check #287

Open lucawen opened 5 years ago

lucawen commented 5 years ago

Im trying to implement django recaptcha2 with django two factor auth and when is with recaptcha field, its not requisiting code for login.. When i remove the field for recaptcha, its work normal.

I tried with django-nocaptcha-recaptcha and have the same behavior.

Maybe a validation problem in the views ?

Expected Behavior

After insert values in form, and click on recaptcha button, if have devices, show the token form

Current Behavior

When using the recaptcha field, when a user have a device to check, its logging normal, without requesting the code.

Possible Solution

Maybe the view validation of current form its not accepting another fields validation or only the first field validation

Steps to Reproduce (for bugs)

  1. install recaptcha2. Check for this lib documentation for how to install, its simple: https://github.com/kbytesys/django-recaptcha2

  2. Create custom forms for add recaptcha:

from django.contrib.auth.forms import AuthenticationForm

from snowpenguin.django.recaptcha2.fields import ReCaptchaField
from snowpenguin.django.recaptcha2.widgets import ReCaptchaWidget
from two_factor.forms import AuthenticationTokenForm, BackupTokenForm

class WizardFormRecaptcha(object):
    def __init__(self, skip_captcha_errors=False, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if skip_captcha_errors:
            del self.fields['recaptcha2']

class AuthenticationRecaptchaForm(WizardFormRecaptcha, AuthenticationForm):
    recaptcha2 = ReCaptchaField(widget=ReCaptchaWidget())

class AuthenticationTokenRecaptchaForm(
        WizardFormRecaptcha, AuthenticationTokenForm):
    recaptcha2 = ReCaptchaField(widget=ReCaptchaWidget())

class BackupTokenRecaptchaForm(WizardFormRecaptcha, BackupTokenForm):
    recaptcha2 = ReCaptchaField(widget=ReCaptchaWidget())
  1. In the view for LoginView from two_factor.views.core, update form_list for use the new forms:
form_list = (
        ('auth', AuthenticationRecaptchaForm),
        ('token', AuthenticationTokenRecaptchaForm),
        ('backup', BackupTokenRecaptchaForm),
    )
  1. Add and override somethings in the view, for the recaptcha do not validate again if already validated
should_skip_captcha = False

def render_done(self, *args, **kwargs):
    # django-forms runs form.is_valid() for all steps after the final step
    # meaning the same captcha code is sent to google multiple times.
    # Google rejects the code the second time as it's already seen it and
    # thinks the second is a "replay attack" - so prevent formtools from
    # validating the captcha twice.
    self.should_skip_captcha = True
    return super().render_done(*args, **kwargs)

def get_form_kwargs(self, step):
    kwargs = super().get_form_kwargs(step=step)
    if self.should_skip_captcha:
        kwargs['skip_captcha_errors'] = True
    return kwargs
  1. now test login with and without recaptcha field in form, (just comment the field line in respective form and add 'pass' for keep form ok)

Context

Add recaptcha for login in two factor auth template, validate in the backend not just the frontend.

Your Environment

moggers87 commented 5 years ago

Does it work if you make only the username/password form have a recaptcha field?

lucawen commented 5 years ago

No... the recaptcha return as required to input..

moggers87 commented 5 years ago

I'm not sure what you mean by that. As I read it, you'll have a recaptcha on every step which is simply unnecessary IMO.

lucawen commented 5 years ago

still not working with recaptcha only on login form..

lucawen commented 5 years ago

ooh.. now i understand the last question... Yes, only with normal user/pass form with recaptcha works

slogan621 commented 5 years ago

I'm having this same problem. Works everywhere but with forms that inherit django.contrib.auth.forms.AuthenticationForm. Django 1.8.3.