We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Twilio SMS sending "U" instead of numerical MFA code
**Describe the bug**
When using the SMS authenticator stage with Twilio configured, users are being sent MFA text messages containing nothing other than the letter "U". I have confirmed in the Twilio console that the body of the message received from Authentik is indeed just the letter "U".

**To Reproduce**
Steps to reproduce the behavior:
1. Log in with a user that has an SMS device setup, or set up a new SMS device
2. See issue with received text message containing only the letter "U"

**Expected behavior**
Users should receive a text message with a numerical code for MFA.

**Version and Deployment (please complete the following information):**
 - authentik version: 2023.2.2
 - Deployment: docker-compose


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `authentik/stages/authenticator_sms/models.py`
Content:
```
1 """SMS Authenticator models"""
2 from hashlib import sha256
3 from typing import Optional
4 
5 from django.contrib.auth import get_user_model
6 from django.db import models
7 from django.utils.translation import gettext_lazy as _
8 from django.views import View
9 from django_otp.models import SideChannelDevice
10 from requests.exceptions import RequestException
11 from rest_framework.exceptions import ValidationError
12 from rest_framework.serializers import BaseSerializer
13 from structlog.stdlib import get_logger
14 from twilio.base.exceptions import TwilioRestException
15 from twilio.rest import Client
16 
17 from authentik.core.types import UserSettingSerializer
18 from authentik.events.models import Event, EventAction, NotificationWebhookMapping
19 from authentik.events.utils import sanitize_item
20 from authentik.flows.models import ConfigurableStage, Stage
21 from authentik.lib.models import SerializerModel
22 from authentik.lib.utils.errors import exception_to_string
23 from authentik.lib.utils.http import get_http_session
24 
25 LOGGER = get_logger()
26 
27 
28 class SMSProviders(models.TextChoices):
29     """Supported SMS Providers"""
30 
31     TWILIO = "twilio"
32     GENERIC = "generic"
33 
34 
35 class SMSAuthTypes(models.TextChoices):
36     """Supported SMS Auth Types"""
37 
38     BASIC = "basic"
39     BEARER = "bearer"
40 
41 
42 class AuthenticatorSMSStage(ConfigurableStage, Stage):
43     """Use SMS-based TOTP instead of authenticator-based."""
44 
45     provider = models.TextField(choices=SMSProviders.choices)
46 
47     from_number = models.TextField()
48 
49     account_sid = models.TextField()
50     auth = models.TextField()
51     auth_password = models.TextField(default="", blank=True)
52     auth_type = models.TextField(choices=SMSAuthTypes.choices, default=SMSAuthTypes.BASIC)
53 
54     verify_only = models.BooleanField(
55         default=False,
56         help_text=_(
57             "When enabled, the Phone number is only used during enrollment to verify the "
58             "users authenticity. Only a hash of the phone number is saved to ensure it is "
59             "not re-used in the future."
60         ),
61     )
62 
63     mapping = models.ForeignKey(
64         NotificationWebhookMapping,
65         null=True,
66         default=None,
67         on_delete=models.SET_NULL,
68         help_text=_("Optionally modify the payload being sent to custom providers."),
69     )
70 
71     def send(self, token: str, device: "SMSDevice"):
72         """Send message via selected provider"""
73         if self.provider == SMSProviders.TWILIO:
74             return self.send_twilio(token, device)
75         if self.provider == SMSProviders.GENERIC:
76             return self.send_generic(token, device)
77         raise ValueError(f"invalid provider {self.provider}")
78 
79     def get_message(self, token: str) -> str:
80         """Get SMS message"""
81         return _("Use this code to authenticate in authentik: %(token)s" % {"token": token})
82 
83     def send_twilio(self, token: str, device: "SMSDevice"):
84         """send sms via twilio provider"""
85         client = Client(self.account_sid, self.auth)
86 
87         try:
88             message = client.messages.create(
89                 to=device.phone_number, from_=self.from_number, body=self.get_message(token)
90             )
91             LOGGER.debug("Sent SMS", to=device, message=message.sid)
92         except TwilioRestException as exc:
93             LOGGER.warning("Error sending token by Twilio SMS", exc=exc, msg=exc.msg)
94             raise ValidationError(exc.msg)
95 
96     def send_generic(self, token: str, device: "SMSDevice"):
97         """Send SMS via outside API"""
98         payload = {
99             "From": self.from_number,
100             "To": device.phone_number,
101             "Body": token,
102             "Message": self.get_message(token),
103         }
104 
105         if self.mapping:
106             payload = sanitize_item(
107                 self.mapping.evaluate(
108                     user=device.user,
109                     request=None,
110                     device=device,
111                     token=token,
112                     stage=self,
113                 )
114             )
115 
116         if self.auth_type == SMSAuthTypes.BEARER:
117             response = get_http_session().post(
118                 f"{self.account_sid}",
119                 json=payload,
120                 headers={"Authorization": f"Bearer {self.auth}"},
121             )
122         elif self.auth_type == SMSAuthTypes.BASIC:
123             response = get_http_session().post(
124                 f"{self.account_sid}",
125                 json=payload,
126                 auth=(self.auth, self.auth_password),
127             )
128         else:
129             raise ValueError(f"Invalid Auth type '{self.auth_type}'")
130 
131         LOGGER.debug("Sent SMS", to=device.phone_number)
132         try:
133             response.raise_for_status()
134         except RequestException as exc:
135             LOGGER.warning(
136                 "Error sending token by generic SMS",
137                 exc=exc,
138                 status=response.status_code,
139                 body=response.text[:100],
140             )
141             Event.new(
142                 EventAction.CONFIGURATION_ERROR,
143                 message="Error sending SMS",
144                 exc=exception_to_string(exc),
145                 status_code=response.status_code,
146                 body=response.text,
147             ).set_user(device.user).save()
148             if response.status_code >= 400:
149                 raise ValidationError(response.text)
150             raise
151 
152     @property
153     def serializer(self) -> type[BaseSerializer]:
154         from authentik.stages.authenticator_sms.api import AuthenticatorSMSStageSerializer
155 
156         return AuthenticatorSMSStageSerializer
157 
158     @property
159     def type(self) -> type[View]:
160         from authentik.stages.authenticator_sms.stage import AuthenticatorSMSStageView
161 
162         return AuthenticatorSMSStageView
163 
164     @property
165     def component(self) -> str:
166         return "ak-stage-authenticator-sms-form"
167 
168     def ui_user_settings(self) -> Optional[UserSettingSerializer]:
169         return UserSettingSerializer(
170             data={
171                 "title": str(self._meta.verbose_name),
172                 "component": "ak-user-settings-authenticator-sms",
173             }
174         )
175 
176     def __str__(self) -> str:
177         return f"SMS Authenticator Setup Stage {self.name}"
178 
179     class Meta:
180         verbose_name = _("SMS Authenticator Setup Stage")
181         verbose_name_plural = _("SMS Authenticator Setup Stages")
182 
183 
184 def hash_phone_number(phone_number: str) -> str:
185     """Hash phone number with prefix"""
186     return "hash:" + sha256(phone_number.encode()).hexdigest()
187 
188 
189 class SMSDevice(SerializerModel, SideChannelDevice):
190     """SMS Device"""
191 
192     user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
193 
194     # Connect to the stage to when validating access we know the API Credentials
195     stage = models.ForeignKey(AuthenticatorSMSStage, on_delete=models.CASCADE)
196 
197     phone_number = models.TextField()
198 
199     last_t = models.DateTimeField(auto_now=True)
200 
201     def set_hashed_number(self):
202         """Set phone_number to hashed number"""
203         self.phone_number = hash_phone_number(self.phone_number)
204 
205     @property
206     def is_hashed(self) -> bool:
207         """Check if the phone number is hashed"""
208         return self.phone_number.startswith("hash:")
209 
210     @property
211     def serializer(self) -> type[BaseSerializer]:
212         from authentik.stages.authenticator_sms.api import SMSDeviceSerializer
213 
214         return SMSDeviceSerializer
215 
216     def verify_token(self, token):
217         valid = super().verify_token(token)
218         if valid:
219             self.save()
220         return valid
221 
222     def __str__(self):
223         return str(self.name) or str(self.user)
224 
225     class Meta:
226         verbose_name = _("SMS Device")
227         verbose_name_plural = _("SMS Devices")
228         unique_together = (("stage", "phone_number"),)
229
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/authentik/stages/authenticator_sms/models.py b/authentik/stages/authenticator_sms/models.py
--- a/authentik/stages/authenticator_sms/models.py
+++ b/authentik/stages/authenticator_sms/models.py
@@ -86,7 +86,7 @@
 
         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 @@
 
         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),
             )

{"golden_diff": "diff --git a/authentik/stages/authenticator_sms/models.py b/authentik/stages/authenticator_sms/models.py\n--- a/authentik/stages/authenticator_sms/models.py\n+++ b/authentik/stages/authenticator_sms/models.py\n@@ -86,7 +86,7 @@\n \n         try:\n             message = client.messages.create(\n-                to=device.phone_number, from_=self.from_number, body=self.get_message(token)\n+                to=device.phone_number, from_=self.from_number, body=str(self.get_message(token))\n             )\n             LOGGER.debug(\"Sent SMS\", to=device, message=message.sid)\n         except TwilioRestException as exc:\n@@ -115,13 +115,13 @@\n \n         if self.auth_type == SMSAuthTypes.BEARER:\n             response = get_http_session().post(\n-                f\"{self.account_sid}\",\n+                self.account_sid,\n                 json=payload,\n                 headers={\"Authorization\": f\"Bearer {self.auth}\"},\n             )\n         elif self.auth_type == SMSAuthTypes.BASIC:\n             response = get_http_session().post(\n-                f\"{self.account_sid}\",\n+                self.account_sid,\n                 json=payload,\n                 auth=(self.auth, self.auth_password),\n             )\n", "issue": "Twilio SMS sending \"U\" instead of numerical MFA code\n**Describe the bug**\r\nWhen using the SMS authenticator stage with Twilio configured, users are being sent MFA text messages containing nothing other than the letter \"U\". I have confirmed in the Twilio console that the body of the message received from Authentik is indeed just the letter \"U\".\r\n\r\n**To Reproduce**\r\nSteps to reproduce the behavior:\r\n1. Log in with a user that has an SMS device setup, or set up a new SMS device\r\n2. See issue with received text message containing only the letter \"U\"\r\n\r\n**Expected behavior**\r\nUsers should receive a text message with a numerical code for MFA.\r\n\r\n**Version and Deployment (please complete the following information):**\r\n - authentik version: 2023.2.2\r\n - Deployment: docker-compose\r\n\n", "before_files": [{"content": "\"\"\"SMS Authenticator models\"\"\"\nfrom hashlib import sha256\nfrom typing import Optional\n\nfrom django.contrib.auth import get_user_model\nfrom django.db import models\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views import View\nfrom django_otp.models import SideChannelDevice\nfrom requests.exceptions import RequestException\nfrom rest_framework.exceptions import ValidationError\nfrom rest_framework.serializers import BaseSerializer\nfrom structlog.stdlib import get_logger\nfrom twilio.base.exceptions import TwilioRestException\nfrom twilio.rest import Client\n\nfrom authentik.core.types import UserSettingSerializer\nfrom authentik.events.models import Event, EventAction, NotificationWebhookMapping\nfrom authentik.events.utils import sanitize_item\nfrom authentik.flows.models import ConfigurableStage, Stage\nfrom authentik.lib.models import SerializerModel\nfrom authentik.lib.utils.errors import exception_to_string\nfrom authentik.lib.utils.http import get_http_session\n\nLOGGER = get_logger()\n\n\nclass SMSProviders(models.TextChoices):\n    \"\"\"Supported SMS Providers\"\"\"\n\n    TWILIO = \"twilio\"\n    GENERIC = \"generic\"\n\n\nclass SMSAuthTypes(models.TextChoices):\n    \"\"\"Supported SMS Auth Types\"\"\"\n\n    BASIC = \"basic\"\n    BEARER = \"bearer\"\n\n\nclass AuthenticatorSMSStage(ConfigurableStage, Stage):\n    \"\"\"Use SMS-based TOTP instead of authenticator-based.\"\"\"\n\n    provider = models.TextField(choices=SMSProviders.choices)\n\n    from_number = models.TextField()\n\n    account_sid = models.TextField()\n    auth = models.TextField()\n    auth_password = models.TextField(default=\"\", blank=True)\n    auth_type = models.TextField(choices=SMSAuthTypes.choices, default=SMSAuthTypes.BASIC)\n\n    verify_only = models.BooleanField(\n        default=False,\n        help_text=_(\n            \"When enabled, the Phone number is only used during enrollment to verify the \"\n            \"users authenticity. Only a hash of the phone number is saved to ensure it is \"\n            \"not re-used in the future.\"\n        ),\n    )\n\n    mapping = models.ForeignKey(\n        NotificationWebhookMapping,\n        null=True,\n        default=None,\n        on_delete=models.SET_NULL,\n        help_text=_(\"Optionally modify the payload being sent to custom providers.\"),\n    )\n\n    def send(self, token: str, device: \"SMSDevice\"):\n        \"\"\"Send message via selected provider\"\"\"\n        if self.provider == SMSProviders.TWILIO:\n            return self.send_twilio(token, device)\n        if self.provider == SMSProviders.GENERIC:\n            return self.send_generic(token, device)\n        raise ValueError(f\"invalid provider {self.provider}\")\n\n    def get_message(self, token: str) -> str:\n        \"\"\"Get SMS message\"\"\"\n        return _(\"Use this code to authenticate in authentik: %(token)s\" % {\"token\": token})\n\n    def send_twilio(self, token: str, device: \"SMSDevice\"):\n        \"\"\"send sms via twilio provider\"\"\"\n        client = Client(self.account_sid, self.auth)\n\n        try:\n            message = client.messages.create(\n                to=device.phone_number, from_=self.from_number, body=self.get_message(token)\n            )\n            LOGGER.debug(\"Sent SMS\", to=device, message=message.sid)\n        except TwilioRestException as exc:\n            LOGGER.warning(\"Error sending token by Twilio SMS\", exc=exc, msg=exc.msg)\n            raise ValidationError(exc.msg)\n\n    def send_generic(self, token: str, device: \"SMSDevice\"):\n        \"\"\"Send SMS via outside API\"\"\"\n        payload = {\n            \"From\": self.from_number,\n            \"To\": device.phone_number,\n            \"Body\": token,\n            \"Message\": self.get_message(token),\n        }\n\n        if self.mapping:\n            payload = sanitize_item(\n                self.mapping.evaluate(\n                    user=device.user,\n                    request=None,\n                    device=device,\n                    token=token,\n                    stage=self,\n                )\n            )\n\n        if self.auth_type == SMSAuthTypes.BEARER:\n            response = get_http_session().post(\n                f\"{self.account_sid}\",\n                json=payload,\n                headers={\"Authorization\": f\"Bearer {self.auth}\"},\n            )\n        elif self.auth_type == SMSAuthTypes.BASIC:\n            response = get_http_session().post(\n                f\"{self.account_sid}\",\n                json=payload,\n                auth=(self.auth, self.auth_password),\n            )\n        else:\n            raise ValueError(f\"Invalid Auth type '{self.auth_type}'\")\n\n        LOGGER.debug(\"Sent SMS\", to=device.phone_number)\n        try:\n            response.raise_for_status()\n        except RequestException as exc:\n            LOGGER.warning(\n                \"Error sending token by generic SMS\",\n                exc=exc,\n                status=response.status_code,\n                body=response.text[:100],\n            )\n            Event.new(\n                EventAction.CONFIGURATION_ERROR,\n                message=\"Error sending SMS\",\n                exc=exception_to_string(exc),\n                status_code=response.status_code,\n                body=response.text,\n            ).set_user(device.user).save()\n            if response.status_code >= 400:\n                raise ValidationError(response.text)\n            raise\n\n    @property\n    def serializer(self) -> type[BaseSerializer]:\n        from authentik.stages.authenticator_sms.api import AuthenticatorSMSStageSerializer\n\n        return AuthenticatorSMSStageSerializer\n\n    @property\n    def type(self) -> type[View]:\n        from authentik.stages.authenticator_sms.stage import AuthenticatorSMSStageView\n\n        return AuthenticatorSMSStageView\n\n    @property\n    def component(self) -> str:\n        return \"ak-stage-authenticator-sms-form\"\n\n    def ui_user_settings(self) -> Optional[UserSettingSerializer]:\n        return UserSettingSerializer(\n            data={\n                \"title\": str(self._meta.verbose_name),\n                \"component\": \"ak-user-settings-authenticator-sms\",\n            }\n        )\n\n    def __str__(self) -> str:\n        return f\"SMS Authenticator Setup Stage {self.name}\"\n\n    class Meta:\n        verbose_name = _(\"SMS Authenticator Setup Stage\")\n        verbose_name_plural = _(\"SMS Authenticator Setup Stages\")\n\n\ndef hash_phone_number(phone_number: str) -> str:\n    \"\"\"Hash phone number with prefix\"\"\"\n    return \"hash:\" + sha256(phone_number.encode()).hexdigest()\n\n\nclass SMSDevice(SerializerModel, SideChannelDevice):\n    \"\"\"SMS Device\"\"\"\n\n    user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)\n\n    # Connect to the stage to when validating access we know the API Credentials\n    stage = models.ForeignKey(AuthenticatorSMSStage, on_delete=models.CASCADE)\n\n    phone_number = models.TextField()\n\n    last_t = models.DateTimeField(auto_now=True)\n\n    def set_hashed_number(self):\n        \"\"\"Set phone_number to hashed number\"\"\"\n        self.phone_number = hash_phone_number(self.phone_number)\n\n    @property\n    def is_hashed(self) -> bool:\n        \"\"\"Check if the phone number is hashed\"\"\"\n        return self.phone_number.startswith(\"hash:\")\n\n    @property\n    def serializer(self) -> type[BaseSerializer]:\n        from authentik.stages.authenticator_sms.api import SMSDeviceSerializer\n\n        return SMSDeviceSerializer\n\n    def verify_token(self, token):\n        valid = super().verify_token(token)\n        if valid:\n            self.save()\n        return valid\n\n    def __str__(self):\n        return str(self.name) or str(self.user)\n\n    class Meta:\n        verbose_name = _(\"SMS Device\")\n        verbose_name_plural = _(\"SMS Devices\")\n        unique_together = ((\"stage\", \"phone_number\"),)\n", "path": "authentik/stages/authenticator_sms/models.py"}], "after_files": [{"content": "\"\"\"SMS Authenticator models\"\"\"\nfrom hashlib import sha256\nfrom typing import Optional\n\nfrom django.contrib.auth import get_user_model\nfrom django.db import models\nfrom django.utils.translation import gettext_lazy as _\nfrom django.views import View\nfrom django_otp.models import SideChannelDevice\nfrom requests.exceptions import RequestException\nfrom rest_framework.exceptions import ValidationError\nfrom rest_framework.serializers import BaseSerializer\nfrom structlog.stdlib import get_logger\nfrom twilio.base.exceptions import TwilioRestException\nfrom twilio.rest import Client\n\nfrom authentik.core.types import UserSettingSerializer\nfrom authentik.events.models import Event, EventAction, NotificationWebhookMapping\nfrom authentik.events.utils import sanitize_item\nfrom authentik.flows.models import ConfigurableStage, Stage\nfrom authentik.lib.models import SerializerModel\nfrom authentik.lib.utils.errors import exception_to_string\nfrom authentik.lib.utils.http import get_http_session\n\nLOGGER = get_logger()\n\n\nclass SMSProviders(models.TextChoices):\n    \"\"\"Supported SMS Providers\"\"\"\n\n    TWILIO = \"twilio\"\n    GENERIC = \"generic\"\n\n\nclass SMSAuthTypes(models.TextChoices):\n    \"\"\"Supported SMS Auth Types\"\"\"\n\n    BASIC = \"basic\"\n    BEARER = \"bearer\"\n\n\nclass AuthenticatorSMSStage(ConfigurableStage, Stage):\n    \"\"\"Use SMS-based TOTP instead of authenticator-based.\"\"\"\n\n    provider = models.TextField(choices=SMSProviders.choices)\n\n    from_number = models.TextField()\n\n    account_sid = models.TextField()\n    auth = models.TextField()\n    auth_password = models.TextField(default=\"\", blank=True)\n    auth_type = models.TextField(choices=SMSAuthTypes.choices, default=SMSAuthTypes.BASIC)\n\n    verify_only = models.BooleanField(\n        default=False,\n        help_text=_(\n            \"When enabled, the Phone number is only used during enrollment to verify the \"\n            \"users authenticity. Only a hash of the phone number is saved to ensure it is \"\n            \"not re-used in the future.\"\n        ),\n    )\n\n    mapping = models.ForeignKey(\n        NotificationWebhookMapping,\n        null=True,\n        default=None,\n        on_delete=models.SET_NULL,\n        help_text=_(\"Optionally modify the payload being sent to custom providers.\"),\n    )\n\n    def send(self, token: str, device: \"SMSDevice\"):\n        \"\"\"Send message via selected provider\"\"\"\n        if self.provider == SMSProviders.TWILIO:\n            return self.send_twilio(token, device)\n        if self.provider == SMSProviders.GENERIC:\n            return self.send_generic(token, device)\n        raise ValueError(f\"invalid provider {self.provider}\")\n\n    def get_message(self, token: str) -> str:\n        \"\"\"Get SMS message\"\"\"\n        return _(\"Use this code to authenticate in authentik: %(token)s\" % {\"token\": token})\n\n    def send_twilio(self, token: str, device: \"SMSDevice\"):\n        \"\"\"send sms via twilio provider\"\"\"\n        client = Client(self.account_sid, self.auth)\n\n        try:\n            message = client.messages.create(\n                to=device.phone_number, from_=self.from_number, body=str(self.get_message(token))\n            )\n            LOGGER.debug(\"Sent SMS\", to=device, message=message.sid)\n        except TwilioRestException as exc:\n            LOGGER.warning(\"Error sending token by Twilio SMS\", exc=exc, msg=exc.msg)\n            raise ValidationError(exc.msg)\n\n    def send_generic(self, token: str, device: \"SMSDevice\"):\n        \"\"\"Send SMS via outside API\"\"\"\n        payload = {\n            \"From\": self.from_number,\n            \"To\": device.phone_number,\n            \"Body\": token,\n            \"Message\": self.get_message(token),\n        }\n\n        if self.mapping:\n            payload = sanitize_item(\n                self.mapping.evaluate(\n                    user=device.user,\n                    request=None,\n                    device=device,\n                    token=token,\n                    stage=self,\n                )\n            )\n\n        if self.auth_type == SMSAuthTypes.BEARER:\n            response = get_http_session().post(\n                self.account_sid,\n                json=payload,\n                headers={\"Authorization\": f\"Bearer {self.auth}\"},\n            )\n        elif self.auth_type == SMSAuthTypes.BASIC:\n            response = get_http_session().post(\n                self.account_sid,\n                json=payload,\n                auth=(self.auth, self.auth_password),\n            )\n        else:\n            raise ValueError(f\"Invalid Auth type '{self.auth_type}'\")\n\n        LOGGER.debug(\"Sent SMS\", to=device.phone_number)\n        try:\n            response.raise_for_status()\n        except RequestException as exc:\n            LOGGER.warning(\n                \"Error sending token by generic SMS\",\n                exc=exc,\n                status=response.status_code,\n                body=response.text[:100],\n            )\n            Event.new(\n                EventAction.CONFIGURATION_ERROR,\n                message=\"Error sending SMS\",\n                exc=exception_to_string(exc),\n                status_code=response.status_code,\n                body=response.text,\n            ).set_user(device.user).save()\n            if response.status_code >= 400:\n                raise ValidationError(response.text)\n            raise\n\n    @property\n    def serializer(self) -> type[BaseSerializer]:\n        from authentik.stages.authenticator_sms.api import AuthenticatorSMSStageSerializer\n\n        return AuthenticatorSMSStageSerializer\n\n    @property\n    def type(self) -> type[View]:\n        from authentik.stages.authenticator_sms.stage import AuthenticatorSMSStageView\n\n        return AuthenticatorSMSStageView\n\n    @property\n    def component(self) -> str:\n        return \"ak-stage-authenticator-sms-form\"\n\n    def ui_user_settings(self) -> Optional[UserSettingSerializer]:\n        return UserSettingSerializer(\n            data={\n                \"title\": str(self._meta.verbose_name),\n                \"component\": \"ak-user-settings-authenticator-sms\",\n            }\n        )\n\n    def __str__(self) -> str:\n        return f\"SMS Authenticator Setup Stage {self.name}\"\n\n    class Meta:\n        verbose_name = _(\"SMS Authenticator Setup Stage\")\n        verbose_name_plural = _(\"SMS Authenticator Setup Stages\")\n\n\ndef hash_phone_number(phone_number: str) -> str:\n    \"\"\"Hash phone number with prefix\"\"\"\n    return \"hash:\" + sha256(phone_number.encode()).hexdigest()\n\n\nclass SMSDevice(SerializerModel, SideChannelDevice):\n    \"\"\"SMS Device\"\"\"\n\n    user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)\n\n    # Connect to the stage to when validating access we know the API Credentials\n    stage = models.ForeignKey(AuthenticatorSMSStage, on_delete=models.CASCADE)\n\n    phone_number = models.TextField()\n\n    last_t = models.DateTimeField(auto_now=True)\n\n    def set_hashed_number(self):\n        \"\"\"Set phone_number to hashed number\"\"\"\n        self.phone_number = hash_phone_number(self.phone_number)\n\n    @property\n    def is_hashed(self) -> bool:\n        \"\"\"Check if the phone number is hashed\"\"\"\n        return self.phone_number.startswith(\"hash:\")\n\n    @property\n    def serializer(self) -> type[BaseSerializer]:\n        from authentik.stages.authenticator_sms.api import SMSDeviceSerializer\n\n        return SMSDeviceSerializer\n\n    def verify_token(self, token):\n        valid = super().verify_token(token)\n        if valid:\n            self.save()\n        return valid\n\n    def __str__(self):\n        return str(self.name) or str(self.user)\n\n    class Meta:\n        verbose_name = _(\"SMS Device\")\n        verbose_name_plural = _(\"SMS Devices\")\n        unique_together = ((\"stage\", \"phone_number\"),)\n", "path": "authentik/stages/authenticator_sms/models.py"}]}