diff --git a/authentik/stages/authenticator_mobile/api/device.py b/authentik/stages/authenticator_mobile/api/device.py index ad91bcaf4..bdea62a62 100644 --- a/authentik/stages/authenticator_mobile/api/device.py +++ b/authentik/stages/authenticator_mobile/api/device.py @@ -37,7 +37,8 @@ class MobileDeviceInfoSerializer(PassiveSerializer): ("android", "Android"), ) ) - version = CharField() + os_version = CharField() + model = CharField() app_version = CharField() diff --git a/authentik/stages/authenticator_mobile/migrations/0002_mobiletransaction.py b/authentik/stages/authenticator_mobile/migrations/0002_mobiletransaction.py new file mode 100644 index 000000000..d8ac76926 --- /dev/null +++ b/authentik/stages/authenticator_mobile/migrations/0002_mobiletransaction.py @@ -0,0 +1,36 @@ +# Generated by Django 4.2.4 on 2023-09-04 18:18 + +import authentik.core.models +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + dependencies = [ + ("authentik_stages_authenticator_mobile", "0001_initial"), + ] + + operations = [ + migrations.CreateModel( + name="MobileTransaction", + fields=[ + ( + "expires", + models.DateTimeField(default=authentik.core.models.default_token_duration), + ), + ("expiring", models.BooleanField(default=True)), + ("tx_id", models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ( + "device", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="authentik_stages_authenticator_mobile.mobiledevice", + ), + ), + ], + options={ + "abstract": False, + }, + ), + ] diff --git a/authentik/stages/authenticator_mobile/models.py b/authentik/stages/authenticator_mobile/models.py index 136b4978e..53b4143fb 100644 --- a/authentik/stages/authenticator_mobile/models.py +++ b/authentik/stages/authenticator_mobile/models.py @@ -98,6 +98,20 @@ class MobileDevice(SerializerModel, Device): return MobileDeviceSerializer + def __str__(self): + return str(self.name) or str(self.user) + + class Meta: + verbose_name = _("Mobile Device") + verbose_name_plural = _("Mobile Devices") + + +class MobileTransaction(ExpiringModel): + """A single push transaction""" + + tx_id = models.UUIDField(default=uuid4, primary_key=True) + device = models.ForeignKey(MobileDevice, on_delete=models.CASCADE) + def send_message(self, request: Optional[HttpRequest], **context): """Send mobile message""" branding = DEFAULT_TENANT.branding_title @@ -111,7 +125,7 @@ class MobileDevice(SerializerModel, Device): body=__( "%(user)s is attempting to log in to %(domain)s" % { - "user": self.user.username, + "user": self.device.user.username, "domain": domain, } ), @@ -127,12 +141,13 @@ class MobileDevice(SerializerModel, Device): badge=0, sound="default", content_available=True, - category="authentik_push_authentication", + category="cat_authentik_push_authorization", ), interruption_level="time-sensitive", + tx_id=str(self.tx_id), ), ), - token=self.firebase_token, + token=self.device.firebase_token, ) try: response = send(message) @@ -141,13 +156,6 @@ class MobileDevice(SerializerModel, Device): LOGGER.warning("failed to push", exc=exc) return True - def __str__(self): - return str(self.name) or str(self.user) - - class Meta: - verbose_name = _("Mobile Device") - verbose_name_plural = _("Mobile Devices") - class MobileDeviceToken(ExpiringModel): """Mobile device token""" diff --git a/authentik/stages/authenticator_validate/challenge.py b/authentik/stages/authenticator_validate/challenge.py index e05ca8770..acd32a226 100644 --- a/authentik/stages/authenticator_validate/challenge.py +++ b/authentik/stages/authenticator_validate/challenge.py @@ -26,7 +26,7 @@ from authentik.root.middleware import ClientIPMiddleware from authentik.stages.authenticator import match_token from authentik.stages.authenticator.models import Device from authentik.stages.authenticator_duo.models import AuthenticatorDuoStage, DuoDevice -from authentik.stages.authenticator_mobile.models import MobileDevice +from authentik.stages.authenticator_mobile.models import MobileDevice, MobileTransaction from authentik.stages.authenticator_sms.models import SMSDevice from authentik.stages.authenticator_validate.models import AuthenticatorValidateStage, DeviceClasses from authentik.stages.authenticator_webauthn.models import UserVerification, WebAuthnDevice @@ -193,7 +193,8 @@ def validate_challenge_mobile(device_pk: str, stage_view: StageView, user: User) ).name try: - response = device.send_message(stage_view.request, **push_context) + tx = MobileTransaction.objects.create(device=device) + response = tx.send_message(stage_view.request, **push_context) # {'result': 'allow', 'status': 'allow', 'status_msg': 'Success. Logging you in...'} if not response: LOGGER.debug("mobile push response", result=response) diff --git a/schema.yml b/schema.yml index 225a22614..839c9b379 100644 --- a/schema.yml +++ b/schema.yml @@ -35299,7 +35299,10 @@ components: properties: platform: $ref: '#/components/schemas/PlatformEnum' - version: + os_version: + type: string + minLength: 1 + model: type: string minLength: 1 app_version: @@ -35307,8 +35310,9 @@ components: minLength: 1 required: - app_version + - model + - os_version - platform - - version MobileDeviceRequest: type: object description: Serializer for Mobile authenticator devices