Source code for deux.authtoken.serializers

from __future__ import absolute_import, unicode_literals

from django.utils.encoding import force_text
from rest_framework import serializers
from rest_framework.authtoken.serializers import AuthTokenSerializer

from deux import strings
from deux.services import MultiFactorChallenge, verify_mfa_code


[docs]class MFAAuthTokenSerializer(AuthTokenSerializer): """ class::MFAAuthTokenSerializer() This extends the ``AuthTokenSerializer`` to support multifactor authentication. """ #: Serializer field for MFA code field. mfa_code = serializers.CharField(required=False) #: Serializer field for Backup code. backup_code = serializers.CharField(required=False)
[docs] def validate(self, attrs): """ Extends the AuthTokenSerializer validate method to implement multi factor authentication. If MFA is disabled, authentication requires just a username and password. If MFA is enabled, authentication requires a username, password, and either a MFA code or a backup code. If the request only provides the username and password, the server will generate an appropriate challenge and respond with `mfa_required = True`. Upon using a backup code to authenticate, MFA will be disabled. :param attrs: Dictionary of data inputted by the user. :raises serializers.ValidationError: If invalid MFA code or backup code are submitted. Also if both types of code are submitted simultaneously. """ attrs = super(MFAAuthTokenSerializer, self).validate(attrs) # User must exist if super method didn't throw error. user = attrs["user"] assert user is not None, "User should exist after super call." mfa = getattr(user, "multi_factor_auth", None) if mfa and mfa.enabled: mfa_code = attrs.get("mfa_code") backup_code = attrs.get("backup_code") if mfa_code and backup_code: raise serializers.ValidationError( force_text(strings.BOTH_CODES_ERROR)) elif mfa_code: bin_key = mfa.get_bin_key(mfa.challenge_type) if not verify_mfa_code(bin_key, mfa_code): raise serializers.ValidationError( force_text(strings.INVALID_MFA_CODE_ERROR)) elif backup_code: if not mfa.check_and_use_backup_code(backup_code): raise serializers.ValidationError( force_text(strings.INVALID_BACKUP_CODE_ERROR)) else: challenge = MultiFactorChallenge(mfa, mfa.challenge_type) challenge.generate_challenge() attrs["mfa_required"] = True attrs["mfa_type"] = mfa.challenge_type return attrs