示例#1
0
    def validate_user(self, username, password, client, request, *args,
                      **kwargs):
        """
        Overrides the OAuth2Validator 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 deux.oauth2.exceptions.InvalidLoginError: If invalid MFA
            code or backup code are submitted. Also if both types of code are
            submitted simultaneously.
        :raises deux.oauth2.exceptions.ChallengeRequiredMessage: If the user
            has MFA enabled but only supplies the correct username and
            password. This exception will prompt the OAuth2 system to send a
            response asking the user to supply an MFA code.
        """

        user = authenticate(username=username, password=password)
        if not (user and user.is_active):
            raise InvalidLoginError(
                force_text(strings.INVALID_CREDENTIALS_ERROR))

        mfa = None
        if hasattr(user, "multi_factor_auth"):
            mfa = user.multi_factor_auth

        if mfa and mfa.enabled:
            mfa_code = request.extra_credentials.get("mfa_code")
            backup_code = request.extra_credentials.get("backup_code")

            if mfa_code and backup_code:
                raise InvalidLoginError(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 InvalidLoginError(
                        force_text(strings.INVALID_MFA_CODE_ERROR))
            elif backup_code:
                if not mfa.check_and_use_backup_code(backup_code):
                    raise InvalidLoginError(
                        force_text(strings.INVALID_BACKUP_CODE_ERROR))
            else:
                challenge = MultiFactorChallenge(mfa, mfa.challenge_type)
                challenge.generate_challenge()
                raise ChallengeRequiredMessage(mfa.challenge_type)
        request.user = user
        return True
示例#2
0
    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."

        multi_factor_model_name = mfa_settings.MFA_MODEL._meta.model_name
        mfa = getattr(user,
                      "{}_multi_factor_auth".format(multi_factor_model_name),
                      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