implement more
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
parent
ad5ccf8062
commit
c55f88d4df
|
@ -4,7 +4,7 @@ from django_filters.rest_framework.backends import DjangoFilterBackend
|
|||
from drf_spectacular.utils import OpenApiResponse, extend_schema, inline_serializer
|
||||
from rest_framework import mixins
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import CharField, ChoiceField
|
||||
from rest_framework.fields import CharField, ChoiceField, UUIDField
|
||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
||||
from rest_framework.permissions import IsAdminUser
|
||||
from rest_framework.request import Request
|
||||
|
@ -70,7 +70,7 @@ class MobileDeviceSetPushKeySerializer(PassiveSerializer):
|
|||
class MobileDeviceResponseSerializer(PassiveSerializer):
|
||||
"""Response from push sent to phone"""
|
||||
|
||||
tx_id = CharField(required=True)
|
||||
tx_id = UUIDField(required=True)
|
||||
status = ChoiceField(
|
||||
TransactionStates.choices,
|
||||
required=True,
|
||||
|
@ -205,11 +205,12 @@ class MobileDeviceViewSet(
|
|||
def receive_response(self, request: Request, pk: str) -> Response:
|
||||
"""Get response from notification on phone"""
|
||||
data = MobileDeviceResponseSerializer(data=request.data)
|
||||
data.is_valid()
|
||||
data.is_valid(raise_exception=True)
|
||||
transaction = MobileTransaction.objects.filter(tx_id=data.validated_data["tx_id"]).first()
|
||||
if not transaction:
|
||||
raise Http404
|
||||
transaction.status = data.validated_data["status"]
|
||||
transaction.save()
|
||||
return Response(status=204)
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
"""Mobile authenticator stage"""
|
||||
from time import sleep
|
||||
from typing import Optional
|
||||
from uuid import uuid4
|
||||
|
||||
|
@ -128,9 +129,6 @@ class MobileTransaction(ExpiringModel):
|
|||
branding = request.tenant.branding_title
|
||||
domain = request.get_host()
|
||||
message = Message(
|
||||
data={
|
||||
"tx_id": str(self.tx_id),
|
||||
},
|
||||
notification=Notification(
|
||||
title=__("%(brand)s authentication request" % {"brand": branding}),
|
||||
body=__(
|
||||
|
@ -155,6 +153,7 @@ class MobileTransaction(ExpiringModel):
|
|||
category="cat_authentik_push_authorization",
|
||||
),
|
||||
interruption_level="time-sensitive",
|
||||
tx_id=str(self.tx_id),
|
||||
),
|
||||
),
|
||||
token=self.device.firebase_token,
|
||||
|
@ -166,6 +165,20 @@ class MobileTransaction(ExpiringModel):
|
|||
LOGGER.warning("failed to push", exc=exc)
|
||||
return True
|
||||
|
||||
def wait_for_response(self, max_checks=30) -> TransactionStates:
|
||||
"""Wait for a change in status"""
|
||||
checks = 0
|
||||
while True:
|
||||
self.refresh_from_db()
|
||||
if self.status in [TransactionStates.accept, TransactionStates.deny]:
|
||||
self.delete()
|
||||
return self.status
|
||||
checks += 1
|
||||
if checks > max_checks:
|
||||
self.delete()
|
||||
raise TimeoutError()
|
||||
sleep(1)
|
||||
|
||||
|
||||
class MobileDeviceToken(ExpiringModel):
|
||||
"""Mobile device token"""
|
||||
|
|
|
@ -26,7 +26,11 @@ 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, MobileTransaction
|
||||
from authentik.stages.authenticator_mobile.models import (
|
||||
MobileDevice,
|
||||
MobileTransaction,
|
||||
TransactionStates,
|
||||
)
|
||||
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
|
||||
|
@ -194,20 +198,22 @@ def validate_challenge_mobile(device_pk: str, stage_view: StageView, user: User)
|
|||
|
||||
try:
|
||||
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)
|
||||
tx.send_message(stage_view.request, **push_context)
|
||||
status = tx.wait_for_response()
|
||||
if status == TransactionStates.deny:
|
||||
LOGGER.debug("mobile push response", result=status)
|
||||
login_failed.send(
|
||||
sender=__name__,
|
||||
credentials={"username": user.username},
|
||||
request=stage_view.request,
|
||||
stage=stage_view.executor.current_stage,
|
||||
device_class=DeviceClasses.MOBILE.value,
|
||||
mobile_response=response,
|
||||
mobile_response=status,
|
||||
)
|
||||
raise ValidationError("Mobile denied access", code="denied")
|
||||
return device
|
||||
except TimeoutError:
|
||||
raise ValidationError("Mobile push notification timed out.")
|
||||
except RuntimeError as exc:
|
||||
Event.new(
|
||||
EventAction.CONFIGURATION_ERROR,
|
||||
|
|
|
@ -35340,7 +35340,7 @@ components:
|
|||
properties:
|
||||
tx_id:
|
||||
type: string
|
||||
minLength: 1
|
||||
format: uuid
|
||||
status:
|
||||
$ref: '#/components/schemas/MobileDeviceResponseStatusEnum'
|
||||
required:
|
||||
|
|
Reference in New Issue