def test_change_primary_method(active_user_with_many_otp_methods):
    client = APIClient()
    first_step = login(active_user_with_many_otp_methods)
    first_primary_method = \
        active_user_with_many_otp_methods.mfa_methods.filter(
            is_primary=True,
        )[0]
    login_response = client.post(
        path='/auth/login/code/',
        data={
            'token': first_step.data.get('ephemeral_token'),
            'code': create_otp_code(first_primary_method.secret),
        },
        format='json',
    )
    client.credentials(HTTP_AUTHORIZATION=header_template.format(
        get_token_from_response(login_response)))
    response = client.post(
        path='/auth/mfa/change-primary-method/',
        data={
            'method': 'sms',
            'code': create_otp_code(first_primary_method.secret),
        },
        format='json',
    )
    new_primary_method = active_user_with_many_otp_methods.mfa_methods.filter(
        is_primary=True, )[0]
    assert response.status_code == 200
    assert first_primary_method != new_primary_method
    assert new_primary_method.name == 'sms'
def test_change_primary_method_to_inactive(active_user_with_email_otp):
    client = APIClient()
    first_step = login(active_user_with_email_otp)
    first_primary_method = \
        active_user_with_email_otp.mfa_methods.filter(
            is_primary=True,
        )[0]
    login_response = client.post(
        path='/auth/login/code/',
        data={
            'token': first_step.data.get('ephemeral_token'),
            'code': create_otp_code(first_primary_method.secret),
        },
        format='json',
    )
    client.credentials(HTTP_AUTHORIZATION=header_template.format(
        get_token_from_response(login_response)))
    response = client.post(
        path='/auth/mfa/change-primary-method/',
        data={
            'method': 'sms',
            'code': create_otp_code(first_primary_method.secret),
        },
        format='json',
    )
    error_code = 'missing_method'
    assert response.status_code == 400
    assert response.data.get('non_field_errors')[0].code == error_code
Example #3
0
def test_backup_codes_regeneration(active_user_with_backup_codes):
    client = APIClient()
    first_step = login(active_user_with_backup_codes)
    first_primary_method = active_user_with_backup_codes.mfa_methods.first()
    old_backup_codes = first_primary_method.backup_codes
    login_response = client.post(
        path='/auth/login/code/',
        data={
            'token': first_step.data.get('ephemeral_token'),
            'code': create_otp_code(first_primary_method.secret),
        },
        format='json',
    )
    client.credentials(HTTP_AUTHORIZATION=header_template.format(
        get_token_from_response(login_response)))
    response = client.post(
        path='/auth/email/codes/regenerate/',
        data={
            'code': create_otp_code(first_primary_method.secret),
        },
        format='json',
    )
    new_backup_codes = \
        active_user_with_backup_codes.mfa_methods.first().backup_codes
    assert response.status_code == 200
    assert old_backup_codes != new_backup_codes
def test_2fa_integration(user, logged_in_client, client):
    mfa_activate_url = reverse("v0:mfa-activate", args=["app"])
    # check that url present
    mfa_confirm = reverse("v0:mfa-activate-confirm", args=["app"])
    mfa_backup_codes = reverse("v0:mfa-regenerate-codes", args=["app"])
    mfa_deactivate = reverse("v0:mfa-deactivate", args=["app"])

    # Step 1: activate MFA
    response = logged_in_client.post(mfa_activate_url)
    assert response.status_code == 200
    qr_link = response.data.get("qr_link")
    assert qr_link
    assert MFAMethod.objects.filter(user=user).count() == 1
    secret = create_secret()
    # Step 2: confirm MFA (mocked step)
    assert not MFAMethod.objects.filter(
        user=user, is_active=True, is_primary=True).exists()
    MFAMethod.objects.filter(user=user).update(secret=secret)
    response = logged_in_client.post(mfa_confirm,
                                     {"code": create_otp_code(secret)})
    assert response.status_code == 200
    assert response.data["backup_codes"]
    assert MFAMethod.objects.filter(user=user, is_active=True,
                                    is_primary=True).exists()

    # Step 3: trying to login
    payload = {"email": user.email, "password": TEST_PASSWORD}
    response = client.post(GENERATE_CODE_URL, payload)
    assert response.status_code == 200

    ephemeral_token = response.data["ephemeral_token"]
    payload = {
        "ephemeral_token": ephemeral_token,
        "code": create_otp_code(secret)
    }

    # Step 4: login with otp data
    response = client.post(GENERATE_TOKEN_URL, payload)
    assert response.status_code == 200
    assert response.data["access"]
    client.defaults["HTTP_AUTHORIZATION"] = f"Bearer {response.data['access']}"

    # Step 5: request backup codes
    response = client.post(mfa_backup_codes, {"code": create_otp_code(secret)})
    assert response.status_code == 200
    assert response.data["backup_codes"]

    # Step 6: deactivate
    code = response.data["backup_codes"][0]
    response = client.post(mfa_deactivate, {"code": code})
    assert response.status_code == 204
    assert MFAMethod.objects.filter(name="app").exists()
    assert not MFAMethod.objects.get(name="app").is_active
Example #5
0
 def test_2fa_login_with_2fa(self):
     """User can log in when 2fa is active and authorised call succeeds"""
     # Get ephemeral token
     response = self.client.post('/v1/auth/login/', {
         "email": '*****@*****.**',
         "password": "******"
     })
     self.assertEqual(response.status_code, 200)
     self.assertEqual("ephemeral_token" in response.data, True)
     # Create a code
     MFAMethod = apps.get_model('trench.MFAMethod')
     MFA = MFAMethod.objects.get(user=self.user2, name='email')
     code = create_otp_code(MFA.secret)
     # 2FA Login
     response = self.client.post(
         '/v1/auth/login/code/', {
             "ephemeral_token": response.data['ephemeral_token'],
             "code": code
         })
     self.assertEqual(response.status_code, 200)
     self.assertEqual("auth_token" in response.data, True)
     # Check authorised call
     self.client.credentials(HTTP_AUTHORIZATION="Token " +
                             response.data["auth_token"])
     response = self.client.get('/v1/auth/users/me/')
     self.assertEqual(response.status_code, 200)
     self.assertEqual(response.data["email"], '*****@*****.**')
def test_new_method_after_deactivation_user_doesnt_have_method(
        active_user_with_many_otp_methods):
    client = APIClient()
    first_step = login(active_user_with_many_otp_methods)
    first_primary_method = \
        active_user_with_many_otp_methods.mfa_methods.filter(
            is_primary=True,
        )[0]
    login_response = client.post(
        path='/auth/login/code/',
        data={
            'token': first_step.data.get('ephemeral_token'),
            'code': create_otp_code(first_primary_method.secret),
        },
        format='json',
    )
    client.credentials(HTTP_AUTHORIZATION=header_template.format(
        get_token_from_response(login_response)))
    response = client.post(
        path='/auth/email/deactivate/',
        data={'new_primary_method': 'test'},
        format='json',
    )

    assert response.status_code == 400
    error_code = 'method_not_registered_for_user'
    assert response.data.get('new_primary_method')[0].code == error_code
Example #7
0
def test_user_with_many_methods(active_user_with_many_otp_methods):
    client = APIClient()

    initial_active_methods_count = active_user_with_many_otp_methods.mfa_methods.filter(
        is_active=True).count()

    first_step = login(active_user_with_many_otp_methods)
    primary_method = active_user_with_many_otp_methods.mfa_methods.filter(
        is_primary=True, )
    # As user has several methods get first and get sure only 1 is primary
    assert len(primary_method) == 1

    secret = primary_method[0].secret
    second_step_response = client.post(
        path='/auth/login/code/',
        data={
            'token': first_step.data.get('ephemeral_token'),
            'code': create_otp_code(secret),
        },
        format='json',
    )
    # Log in the user in the second step and make sure it is correct
    assert second_step_response.status_code == 200

    client.credentials(HTTP_AUTHORIZATION=header_template.format(
        get_token_from_response(second_step_response)))
    active_methods_response = client.get(
        path='/auth/mfa/user-active-methods/', )

    # This user should have 3 methods, so we check that return has 3 methods
    assert len(active_methods_response.data) == initial_active_methods_count
def test_backup_codes_regeneration_disabled_method(
    active_user_with_many_otp_methods, ):
    client = APIClient()
    first_step = login(active_user_with_many_otp_methods)
    first_primary_method = active_user_with_many_otp_methods.mfa_methods.filter(
        is_primary=True, )[0]
    sms_method = active_user_with_many_otp_methods.mfa_methods.get(
        name='sms', )
    sms_method.is_active = False
    sms_method.save()
    login_response = client.post(
        path='/auth/login/code/',
        data={
            'token': first_step.data.get('ephemeral_token'),
            'code': create_otp_code(first_primary_method.secret),
        },
        format='json',
    )
    client.credentials(HTTP_AUTHORIZATION=header_template.format(
        get_token_from_response(login_response)))
    response = client.post(
        path='/auth/sms/codes/regenerate/',
        format='json',
    )
    error_msg = 'Method is disabled.'
    assert response.status_code == 400
    assert response.data.get('error') == error_msg
def test_second_method_activation(active_user_with_email_otp):
    client = APIClient()
    first_step = login(active_user_with_email_otp)
    secret = active_user_with_email_otp.mfa_methods.first().secret
    response = client.post(
        path='/auth/login/code/',
        data={
            'token': first_step.data.get('ephemeral_token'),
            'code': create_otp_code(secret),
        },
        format='json',
    )
    assert response.status_code == 200
    client.credentials(HTTP_AUTHORIZATION=header_template.format(
        get_token_from_response(response)))

    # This user should have 1 methods, so we check that it has 1 methods.
    assert len(active_user_with_email_otp.mfa_methods.all()) == 1
    try:
        response = client.post(
            path='/auth/sms/activate/',
            data={
                'phone_number': '555-555-555',
            },
            format='json',
        )
    except (TwilioRestException, TwilioException):
        # twilio rises this exception in test, but the new mfa_method is
        # created anyway.
        pass

    # Now we check that the user has a new method after the activation.
    assert len(active_user_with_email_otp.mfa_methods.all()) == 2
def test_request_codes(active_user_with_email_otp):
    client = APIClient()
    first_step = login(active_user_with_email_otp)
    first_primary_method = active_user_with_email_otp.mfa_methods.first()
    login_response = client.post(
        path='/auth/login/code/',
        data={
            'token': first_step.data.get('ephemeral_token'),
            'code': create_otp_code(first_primary_method.secret),
        },
        format='json',
    )
    client.credentials(HTTP_AUTHORIZATION=header_template.format(
        get_token_from_response(login_response)))

    response = client.post(
        path='/auth/code/request/',
        data={
            'method': 'email',
        },
        format='json',
    )
    expected_msg = 'Email message with MFA code had been sent.'
    assert response.status_code == 200
    assert response.data.get('message') == expected_msg
def test_confirm_activation_otp(active_user):
    client = APIClient()
    login_response = login(active_user)
    client.credentials(HTTP_AUTHORIZATION=header_template.format(
        get_token_from_response(login_response)))
    client.post(
        path='/auth/email/activate/',
        format='json',
    )
    # Until here only make user create a second step confirmation
    active_user_method = active_user.mfa_methods.first()
    active_user_method.is_primary = True
    active_user_method.is_active = True
    active_user_method.save()
    # We manually activate the method
    first_step = login(active_user)
    secret = active_user.mfa_methods.first().secret
    code = create_otp_code(secret)
    response = client.post(
        path='/auth/email/activate/confirm/',
        data={
            'token': first_step.data.get('ephemeral_token'),
            'code': code,
        },
        format='json',
    )
    # Confirm the response is OK and user gets 5 backup codes
    assert response.status_code == 200
    assert len(response.json().get('backup_codes')) == 5
def test_second_method_activation_already_active(active_user_with_email_otp):
    client = APIClient()
    first_step = login(active_user_with_email_otp)
    secret = active_user_with_email_otp.mfa_methods.first().secret
    response = client.post(
        path='/auth/login/code/',
        data={
            'token': first_step.data.get('ephemeral_token'),
            'code': create_otp_code(secret),
        },
        format='json',
    )
    assert response.status_code == 200
    client.credentials(HTTP_AUTHORIZATION=header_template.format(
        get_token_from_response(response)))

    # This user should have 1 methods, so we check that it has 1 methods.
    assert len(active_user_with_email_otp.mfa_methods.all()) == 1
    response = client.post(
        path='/auth/email/activate/',
        format='json',
    )
    error_msg = 'MFA method already active.'
    assert response.status_code == 400
    assert response.data.get('error') == error_msg
Example #13
0
def test_deactivation_otp(active_user_with_email_otp):
    client = APIClient()
    first_step = login(active_user_with_email_otp)
    secret = active_user_with_email_otp.mfa_methods.first().secret
    login_response = client.post(
        path='/auth/login/code/',
        data={
            'token': first_step.data.get('ephemeral_token'),
            'code': create_otp_code(secret),
        },
        format='json',
    )
    client.credentials(HTTP_AUTHORIZATION=header_template.format(
        get_token_from_response(login_response)))
    response = client.post(
        path='/auth/email/deactivate/',
        data={
            'code': create_otp_code(secret),
        },
        format='json',
    )
    assert response.status_code == 204
    assert not active_user_with_email_otp.mfa_methods.first().is_active
def test_wrong_second_step_verification_with_ephemeral_token(
        active_user_with_email_otp):
    client = APIClient()
    first_step = login(active_user_with_email_otp)
    secret = active_user_with_email_otp.mfa_methods.first().secret
    response = client.post(
        path='/auth/login/code/',
        data={
            'token': first_step.data.get('ephemeral_token') + 'wrong',
            'code': create_otp_code(secret),
        },
        format='json',
    )
    assert response.status_code == 400
def test_ephemeral_token_verification_simple_jwt(active_user_with_email_otp):
    client = APIClient()
    first_step = login(active_user_with_email_otp)
    secret = active_user_with_email_otp.mfa_methods.first().secret
    response = client.post(
        path='/simplejwt-auth/login/code/',
        data={
            'token': first_step.data.get('ephemeral_token'),
            'code': create_otp_code(secret),
        },
        format='json',
    )
    assert response.status_code == 200
    assert get_username_from_jwt(response, 'access') == getattr(
        active_user_with_email_otp,
        User.USERNAME_FIELD,
    )
Example #16
0
def test_add_user_mfa(active_user):
    client = APIClient()
    login_request = login(active_user)
    client.credentials(HTTP_AUTHORIZATION=header_template.format(
        get_token_from_response(login_request)))
    secret = create_secret()
    response = client.post(
        path='/auth/email/activate/',
        data={
            'secret': secret,
            'code': create_otp_code(secret),
            'user': getattr(
                active_user,
                active_user.USERNAME_FIELD,
            )
        },
        format='json',
    )
    assert response.status_code == 200
Example #17
0
 def test_2fa_activate(self):
     """User can activate 2fa"""
     # Login
     response = self.client.post('/v1/auth/login/', {
         "email": '*****@*****.**',
         "password": "******"
     })
     self.client.credentials(HTTP_AUTHORIZATION="Token " +
                             response.data["auth_token"])
     # Activate a method
     response = self.client.post('/v1/auth/email/activate/', {})
     self.assertEqual(response.status_code, 200)
     # Create a code
     MFAMethod = apps.get_model('trench.MFAMethod')
     MFA = MFAMethod.objects.get(user=self.user, name='email')
     code = create_otp_code(MFA.secret)
     # Confirm the method
     response = self.client.post('/v1/auth/email/activate/confirm/',
                                 {"code": code})
     self.assertEqual(response.status_code, 200)
def test_request_code_non_existing_method(active_user_with_email_otp):
    client = APIClient()
    first_step = login(active_user_with_email_otp)
    first_primary_method = active_user_with_email_otp.mfa_methods.first()
    login_response = client.post(
        path='/auth/login/code/',
        data={
            'token': first_step.data.get('ephemeral_token'),
            'code': create_otp_code(first_primary_method.secret),
        },
        format='json',
    )
    client.credentials(HTTP_AUTHORIZATION=header_template.format(
        get_token_from_response(login_response)))

    response = client.post(
        path='/auth/code/request/',
        data={
            'method': 'test',
        },
        format='json',
    )
    assert response.status_code == 400
Example #19
0
 def test_2fa_disable_2fa(self):
     """User can disable a 2fa method"""
     # Get ephemeral token
     response = self.client.post('/v1/auth/login/', {
         "email": '*****@*****.**',
         "password": "******"
     })
     # Create a code
     MFAMethod = apps.get_model('trench.MFAMethod')
     MFA = MFAMethod.objects.get(user=self.user2, name='email')
     code = create_otp_code(MFA.secret)
     # 2FA Login
     response = self.client.post(
         '/v1/auth/login/code/', {
             "ephemeral_token": response.data['ephemeral_token'],
             "code": code
         })
     self.client.credentials(HTTP_AUTHORIZATION="Token " +
                             response.data["auth_token"])
     # Test disable
     response = self.client.post('/v1/auth/email/deactivate/',
                                 {"code": code})
     self.assertEqual(response.status_code, 204)
def test_deactivation_otp_already_disabled_method(
    active_user_with_email_and_inactive_other_methods_otp, ):
    client = APIClient()
    first_step = login(active_user_with_email_and_inactive_other_methods_otp)
    secret = active_user_with_email_and_inactive_other_methods_otp.mfa_methods.first(
    ).secret  # noqa
    login_response = client.post(
        path='/auth/login/code/',
        data={
            'token': first_step.data.get('ephemeral_token'),
            'code': create_otp_code(secret),
        },
        format='json',
    )
    client.credentials(HTTP_AUTHORIZATION=header_template.format(
        get_token_from_response(login_response)))
    response = client.post(
        path='/auth/sms/deactivate/',
        format='json',
    )
    msg_error = 'Method already disabled.'
    assert response.status_code == 400
    assert response.data.get('error') == msg_error
Example #21
0
def test_validate_code(active_user_with_email_otp):
    email_method = active_user_with_email_otp.mfa_methods.get()
    valid_code = create_otp_code(email_method.secret)

    assert validate_code("123456", email_method) is False
    assert validate_code(valid_code, email_method) is True
Example #22
0
 def create_code(self):
     return create_otp_code(self.obj.secret)