Source code for deux.oauth2.validators

from __future__ import absolute_import, unicode_literals

from oauth2_provider.oauth2_validators import OAuth2Validator

from django.contrib.auth import authenticate
from django.utils.encoding import force_text

from deux import strings
from deux.oauth2.exceptions import (
    ChallengeRequiredMessage,
    InvalidLoginError,
)
from deux.services import MultiFactorChallenge, verify_mfa_code


[docs]class MFAOAuth2Validator(OAuth2Validator): """ class::MFAOAuth2Validator() OAuth2 validator class for MFA that validates requests to authenticate with username and password by also verifying that they supply the correct MFA code or backup code if multifactor authentication is enabled. """
[docs] 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