diff --git a/authentik/stages/authenticator_sms/models.py b/authentik/stages/authenticator_sms/models.py index bec675222..159847650 100644 --- a/authentik/stages/authenticator_sms/models.py +++ b/authentik/stages/authenticator_sms/models.py @@ -86,7 +86,7 @@ class AuthenticatorSMSStage(ConfigurableStage, Stage): try: message = client.messages.create( - to=device.phone_number, from_=self.from_number, body=self.get_message(token) + to=device.phone_number, from_=self.from_number, body=str(self.get_message(token)) ) LOGGER.debug("Sent SMS", to=device, message=message.sid) except TwilioRestException as exc: @@ -115,13 +115,13 @@ class AuthenticatorSMSStage(ConfigurableStage, Stage): if self.auth_type == SMSAuthTypes.BEARER: response = get_http_session().post( - f"{self.account_sid}", + self.account_sid, json=payload, headers={"Authorization": f"Bearer {self.auth}"}, ) elif self.auth_type == SMSAuthTypes.BASIC: response = get_http_session().post( - f"{self.account_sid}", + self.account_sid, json=payload, auth=(self.auth, self.auth_password), ) diff --git a/authentik/stages/authenticator_sms/tests.py b/authentik/stages/authenticator_sms/tests.py index 79bfc4bf6..4b37df0e5 100644 --- a/authentik/stages/authenticator_sms/tests.py +++ b/authentik/stages/authenticator_sms/tests.py @@ -1,17 +1,21 @@ """Test SMS API""" from unittest.mock import MagicMock, patch +from urllib.parse import parse_qsl from django.urls import reverse +from requests_mock import Mocker from authentik.core.tests.utils import create_test_admin_user, create_test_flow from authentik.flows.models import FlowStageBinding from authentik.flows.tests import FlowTestCase +from authentik.lib.generators import generate_id from authentik.stages.authenticator_sms.models import ( AuthenticatorSMSStage, SMSDevice, SMSProviders, hash_phone_number, ) +from authentik.stages.authenticator_sms.stage import SESSION_KEY_SMS_DEVICE class AuthenticatorSMSStageTests(FlowTestCase): @@ -80,6 +84,61 @@ class AuthenticatorSMSStageTests(FlowTestCase): phone_number_required=False, ) + def test_stage_submit_twilio(self): + """test stage (submit) (twilio)""" + self.stage.account_sid = generate_id() + self.stage.auth = generate_id() + self.stage.from_number = generate_id() + self.stage.save() + self.client.get( + reverse("authentik_flows:configure", kwargs={"stage_uuid": self.stage.stage_uuid}), + ) + response = self.client.get( + reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}), + ) + self.assertStageResponse( + response, + self.flow, + self.user, + component="ak-stage-authenticator-sms", + phone_number_required=True, + ) + number = generate_id() + + with Mocker() as mocker: + mocker.post( + ( + "https://api.twilio.com/2010-04-01/Accounts/" + f"{self.stage.account_sid}/Messages.json" + ), + json={}, + ) + response = self.client.post( + reverse("authentik_api:flow-executor", kwargs={"flow_slug": self.flow.slug}), + data={"component": "ak-stage-authenticator-sms", "phone_number": number}, + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(mocker.call_count, 1) + self.assertEqual(mocker.request_history[0].method, "POST") + request_body = dict(parse_qsl(mocker.request_history[0].body)) + device: SMSDevice = self.client.session[SESSION_KEY_SMS_DEVICE] + self.assertEqual( + request_body, + { + "To": number, + "From": self.stage.from_number, + "Body": f"Use this code to authenticate in authentik: {device.token}", + }, + ) + self.assertStageResponse( + response, + self.flow, + self.user, + component="ak-stage-authenticator-sms", + response_errors={}, + phone_number_required=False, + ) + def test_stage_context_data(self): """test stage context data""" self.client.get(