from django.http import Http404 from django.utils.decorators import method_decorator from django.views.decorators.cache import never_cache from django.views.decorators.debug import sensitive_post_parameters from django.views.generic import FormView from . import settings from .methods import fido2 from .methods import recovery from .methods import totp class MFAFormView(FormView): @property def method(self): if self.kwargs['method'] in settings.METHODS: if self.kwargs['method'] == 'FIDO2': return fido2 elif self.kwargs['method'] == 'TOTP': return totp elif self.kwargs['method'] == 'recovery': return recovery raise Http404 @property def challenge(self): try: return self.request.session['mfa_challenge'] except KeyError as e: raise Http404 from e def begin(self): raise NotImplementedError # pragma: no cover def complete(self, code): raise NotImplementedError # pragma: no cover @method_decorator(sensitive_post_parameters()) @method_decorator(never_cache) def dispatch(self, *args, **kwargs): return super().dispatch(*args, **kwargs) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) if 'mfa_data' not in context: data, state = self.begin() self.request.session['mfa_challenge'] = (data, state) context['mfa_data'] = data return context def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['validate_code'] = self.complete return kwargs def form_invalid(self, form): # do not generate a new challenge return self.render_to_response(self.get_context_data( form=form, mfa_data=self.challenge[0] )) def form_valid(self, form): del self.request.session['mfa_challenge'] return super().form_valid(form)