def test_get_totp_digits(self): # test that the default is 6 if TWO_FACTOR_TOTP_DIGITS is not set self.assertEqual(totp_digits(), 6) for no_digits in (6, 8): with self.settings(TWO_FACTOR_TOTP_DIGITS=no_digits): self.assertEqual(totp_digits(), no_digits)
def get(self, request, *args, **kwargs): # Get the data from the session try: key = self.request.session[self.session_key_name] except KeyError: raise Http404() # Get data for qrcode image_factory_string = getattr(settings, 'TWO_FACTOR_QR_FACTORY', self.default_qr_factory) image_factory = import_string(image_factory_string) content_type = self.image_content_types[image_factory.kind] try: username = self.request.user.get_username() except AttributeError: username = self.request.user.username otpauth_url = get_otpauth_url(accountname=username, issuer=get_current_site( self.request).name, secret=key, digits=totp_digits()) # Make and return QR code img = qrcode.make(otpauth_url, image_factory=image_factory) resp = HttpResponse(content_type=content_type) img.save(resp) return resp
def get(self, request, *args, **kwargs): # Get the data from the session try: key = self.request.session[self.session_key_name] del self.request.session[self.session_key_name] except KeyError: raise Http404() # Get data for qrcode image_factory_string = getattr(settings, 'TWO_FACTOR_QR_FACTORY', self.default_qr_factory) image_factory = import_string(image_factory_string) content_type = self.image_content_types[image_factory.kind] try: username = self.request.user.get_username() except AttributeError: username = self.request.user.username otpauth_url = get_otpauth_url(accountname=username, issuer=get_current_site(self.request).name, secret=key, digits=totp_digits()) # Make and return QR code img = qrcode.make(otpauth_url, image_factory=image_factory) resp = HttpResponse(content_type=content_type) img.save(resp) return resp
class HQDeviceValidationForm(DeviceValidationForm): token = forms.IntegerField(required=False, label=_("Token"), min_value=1, max_value=int('9' * totp_digits())) def __init__(self, **kwargs): super(HQDeviceValidationForm, self).__init__(**kwargs) self.helper = FormHelper() self.helper.form_class = 'form form-horizontal' self.helper.label_class = 'col-sm-3 col-md-4 col-lg-2' self.helper.field_class = 'col-sm-9 col-md-8 col-lg-6' # Next button is defined first so the enter key triggers it self.helper.layout = crispy.Layout( crispy.Fieldset('', 'token'), hqcrispy.FormActions( twbscrispy.StrictButton( _('Next'), css_class='btn-primary', type='submit', ), twbscrispy.StrictButton( _('Back'), css_class='btn-default', type='submit', value='method', name='wizard_goto_step', ), )) def clean_token(self): token = self.cleaned_data['token'] if not token or not self.device.verify_token(token): raise forms.ValidationError(self.error_messages['invalid_token']) return token
class CustomDeviceValidationForm(DeviceValidationForm): token = forms.IntegerField( label=_("Token"), min_value=1, max_value=int('9' * totp_digits()), widget=forms.NumberInput(attrs={ 'class': 'auth-token-input', }))
class CustomAuthenticationTokenForm(AuthenticationTokenForm): otp_token = forms.IntegerField( label=_("Token"), min_value=1, max_value=int('9' * totp_digits()), widget=forms.NumberInput(attrs={ 'class': 'auth-token-input', }))
class AuthenticationTokenForm(TwoFactorAuthenticationTokenForm): """ We add this form to update the widget of otp_token. The default widget is an input element whose type is a number, which doesn't stylistically match our theme. """ otp_token = forms.IntegerField(label=_("Token"), min_value=1, max_value=int('9' * totp_digits()), widget=forms.TextInput)
def generate_challenge(self): # local import to avoid circular import from two_factor.utils import totp_digits """ Sends the current TOTP token to `self.number` using `self.method`. """ no_digits = totp_digits() token = str(totp(self.bin_key, digits=no_digits)).zfill(no_digits) send_email(device=self, token=token)
def __init__(self, key, user, metadata=None, **kwargs): super(TOTPDeviceForm, self).__init__(**kwargs) self.key = key self.tolerance = 1 self.t0 = 0 self.step = 30 self.drift = 0 self.digits = totp_digits() self.user = user self.metadata = metadata or {}
def generate_challenge_token(cls): """ Generates a token challenge delivered to a user either via email or phone which is then verified. """ # local import to avoid circular import from two_factor.utils import totp_digits no_digits = totp_digits() token = str(totp(cls.bin_key, digits=no_digits)).zfill(no_digits) return token
class DeviceValidationForm(BaseDeviceValidationForm): token = forms.IntegerField( label="Token", min_value=1, max_value=int("9" * totp_digits()), widget=forms.TextInput(attrs={ "placeholder": "Enter generated token", "class": "form__text" }), required=False, )
def verify_token(self, token): # local import to avoid circular import from two_factor.utils import totp_digits try: token = int(token) except ValueError: return False for drift in range(-5, 1): if totp(self.bin_key, drift=drift, digits=totp_digits()) == token: return True return False
def generate_challenge(self): # local import to avoid circular import from two_factor.utils import totp_digits """ Sends the current TOTP token to `self.number` using `self.method`. """ no_digits = totp_digits() token = str(totp(self.bin_key, digits=no_digits)).zfill(no_digits) if self.method == 'call': make_call(device=self, token=token) else: send_sms(device=self, token=token)
def verify_generated_token(cls, token): """ Verified generated tokens """ # local import to avoid circular import from two_factor.utils import totp_digits try: token = int(token) except ValueError: return False for drift in range(-100, 100): # bigger range for emails if totp(cls.bin_key, drift=drift, digits=totp_digits()) == token: return True return False
def get_context_data(self, form, **kwargs): context = super().get_context_data(form, **kwargs) if self.steps.current == 'generator': try: username = self.request.user.get_username() except AttributeError: username = self.request.user.username otpauth_url = get_otpauth_url(accountname=username, issuer=self.get_issuer(), secret=context['secret_key'], digits=totp_digits()) context.update({ 'otpauth_url': otpauth_url, }) return context
def generate_challenge(self): # local import to avoid circular import from two_factor.utils import totp_digits """ Sends the current TOTP token to `self.number` using `self.method`. """ no_digits = totp_digits() token = str(totp(self.bin_key, digits=no_digits)).zfill(no_digits) if self.method == 'call': make_call(device=self, token=token) else: key = "{}_{}".format(THROTTLE_CACHE_KEY, self.number.as_e164) r = cache.get(key) if r: raise ValidationError( 'Please wait %s seconds before trying to request a new verification code' % round(r - time.time())) send_sms(device=self, token=token) delay = getattr(settings, 'TWO_FACTOR_SMS_THROTTLE', 60) cache.set(key, time.time() + delay, delay)
def __init__(self, key, user, metadata=None, **kwargs): super().__init__(self, key, user, **kwargs) self.key = key self.tolerance = 1 self.t0 = 0 self.step = 30 self.drift = 0 self.digits = totp_digits() self.user = user self.metadata = metadata or {} self.helper = FormHelper() self.fields['token'].label = False extra_attrs = { "class": "login100-form validate-form textinput textInput form-control", "title": "", "placeholder": "Enter token from authenticator app...", } self.fields["token"].widget.attrs.update(extra_attrs)
class HQTOTPDeviceForm(TOTPDeviceForm): token = forms.IntegerField(required=False, label=_("Token"), min_value=1, max_value=int('9' * totp_digits())) def __init__(self, **kwargs): super(HQTOTPDeviceForm, self).__init__(**kwargs) self.helper = FormHelper() self.helper.form_class = 'form form-horizontal' self.helper.label_class = 'col-sm-3 col-md-4 col-lg-2' self.helper.field_class = 'col-sm-9 col-md-8 col-lg-6' # Next button is defined first so the enter key triggers it self.helper.layout = crispy.Layout( crispy.Fieldset('', 'token'), hqcrispy.FormActions( twbscrispy.StrictButton( _('Next'), css_class='btn-primary', type='submit', ), twbscrispy.StrictButton( _('Back'), css_class='btn-default', type='submit', value='method', name='wizard_goto_step', ), )) def save(self): couch_user = CouchUser.from_django_user(self.user) if couch_user.two_factor_auth_disabled_until: couch_user.two_factor_auth_disabled_until = None couch_user.save() return super(HQTOTPDeviceForm, self).save()
class TwoFactorDeviceValidationForm(DeviceValidationForm): token = forms.IntegerField(label="Token", min_value=1, max_value=int('9' * totp_digits()), widget=widgets.MetroNumberInput)
class TwoFactorTOTPDeviceForm(TOTPDeviceForm): token = forms.IntegerField(label="Token", min_value=0, max_value=int('9' * totp_digits()), widget=widgets.MetroNumberInput)
class TwoFactorAuthTokenForm(AuthenticationTokenForm): otp_token = forms.IntegerField(widget=widgets.MetroNumberInput, label="Token", min_value=1, max_value=int('9' * totp_digits())) otp_token.widget.attrs.update({'autofocus': 'autofocus'})