diff --git a/authentik/stages/authenticator_mobile/api/device.py b/authentik/stages/authenticator_mobile/api/device.py index 1f517ac78..c546a2720 100644 --- a/authentik/stages/authenticator_mobile/api/device.py +++ b/authentik/stages/authenticator_mobile/api/device.py @@ -1,6 +1,6 @@ """AuthenticatorMobileStage API Views""" from django_filters.rest_framework.backends import DjangoFilterBackend -from drf_spectacular.utils import extend_schema, inline_serializer, OpenApiResponse +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 @@ -75,7 +75,7 @@ class MobileDeviceViewSet( methods=["POST"], detail=True, permission_classes=[], - filter_backends = [], + filter_backends=[], authentication_classes=[MobileDeviceTokenAuthentication], ) def enrollment_callback(self, request: Request, pk: str) -> Response: @@ -115,11 +115,13 @@ class MobileDeviceViewSet( ), }, ) - @action(methods=["POST"], + @action( + methods=["POST"], detail=True, permission_classes=[], - filter_backends = [], - authentication_classes=[MobileDeviceTokenAuthentication],) + filter_backends=[], + authentication_classes=[MobileDeviceTokenAuthentication], + ) def enrollment_status(self, request: Request, pk: str) -> Response: """Check device enrollment status""" device: MobileDevice = self.get_object() diff --git a/authentik/stages/authenticator_mobile/models.py b/authentik/stages/authenticator_mobile/models.py index bacf94afe..f6e52cffd 100644 --- a/authentik/stages/authenticator_mobile/models.py +++ b/authentik/stages/authenticator_mobile/models.py @@ -1,39 +1,42 @@ """Mobile authenticator stage""" from typing import Optional from uuid import uuid4 + +from django.contrib.auth import get_user_model +from django.db import models +from django.http import HttpRequest +from django.utils.translation import gettext as __ +from django.utils.translation import gettext_lazy as _ +from django.views import View +from django_otp.models import Device +from firebase_admin import credentials, initialize_app +from firebase_admin.exceptions import FirebaseError from firebase_admin.messaging import ( - Message, - send, AndroidConfig, AndroidNotification, APNSConfig, APNSPayload, - Notification, Aps, + Message, + Notification, + send, ) -from firebase_admin.exceptions import FirebaseError -from structlog.stdlib import get_logger -from django.contrib.auth import get_user_model -from django.db import models -from django.utils.translation import gettext_lazy as _ -from django.views import View -from django_otp.models import Device from rest_framework.serializers import BaseSerializer, Serializer +from structlog.stdlib import get_logger from authentik.core.models import ExpiringModel from authentik.core.types import UserSettingSerializer from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage from authentik.lib.generators import generate_id from authentik.lib.models import SerializerModel - -from firebase_admin import initialize_app -from firebase_admin import credentials +from authentik.tenants.utils import DEFAULT_TENANT cred = credentials.Certificate("firebase.json") initialize_app(cred) LOGGER = get_logger() + def default_token_key(): """Default token key""" return generate_id(40) @@ -95,11 +98,23 @@ class MobileDevice(SerializerModel, Device): return MobileDeviceSerializer - def send_message(self, **context): + def send_message(self, request: Optional[HttpRequest], **context): + """Send mobile message""" + branding = DEFAULT_TENANT.branding_title + domain = "" + if request: + branding = request.tenant.branding_title + domain = request.get_host() message = Message( notification=Notification( - title="$GOOG up 1.43% on the day", - body="$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.", + title=__("%(brand)s authentication request" % {"brand": branding}), + body=__( + "%(user)s is attempting to log in to %(domain)s" + % { + "user": self.user.username, + "domain": domain, + } + ), ), android=AndroidConfig( priority="normal", diff --git a/authentik/stages/authenticator_validate/stage.py b/authentik/stages/authenticator_validate/stage.py index ef03274b2..4a195adce 100644 --- a/authentik/stages/authenticator_validate/stage.py +++ b/authentik/stages/authenticator_validate/stage.py @@ -142,7 +142,12 @@ class AuthenticatorValidationChallengeResponse(ChallengeResponse): def validate(self, attrs: dict): # Checking if the given data is from a valid device class is done above # Here we only check if the any data was sent at all - if "code" not in attrs and "webauthn" not in attrs and "duo" not in attrs and "mobile" not in attrs: + if ( + "code" not in attrs + and "webauthn" not in attrs + and "duo" not in attrs + and "mobile" not in attrs + ): raise ValidationError("Empty response") self.stage.executor.plan.context.setdefault(PLAN_CONTEXT_METHOD, "auth_mfa") self.stage.executor.plan.context.setdefault(PLAN_CONTEXT_METHOD_ARGS, {})