fix stuff
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
parent
4813bd033e
commit
130ec2128d
|
@ -130,7 +130,10 @@ SPECTACULAR_SETTINGS = {
|
|||
"CONTACT": {
|
||||
"email": "hello@goauthentik.io",
|
||||
},
|
||||
"AUTHENTICATION_WHITELIST": ["authentik.api.authentication.TokenAuthentication"],
|
||||
"AUTHENTICATION_WHITELIST": [
|
||||
"authentik.stages.authenticator_mobile.api.auth.MobileDeviceTokenAuthentication",
|
||||
"authentik.api.authentication.TokenAuthentication",
|
||||
],
|
||||
"LICENSE": {
|
||||
"name": "MIT",
|
||||
"url": "https://github.com/goauthentik/authentik/blob/main/LICENSE",
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
"""Mobile device token authentication"""
|
||||
from typing import Any
|
||||
|
||||
from drf_spectacular.extensions import OpenApiAuthenticationExtension
|
||||
from rest_framework.authentication import BaseAuthentication, get_authorization_header
|
||||
from rest_framework.request import Request
|
||||
|
||||
from authentik.api.authentication import validate_auth
|
||||
from authentik.core.models import User
|
||||
from authentik.stages.authenticator_mobile.models import MobileDeviceToken
|
||||
|
||||
|
||||
class MobileDeviceTokenAuthentication(BaseAuthentication):
|
||||
"""Mobile device token authentication"""
|
||||
|
||||
def authenticate(self, request: Request) -> tuple[User, Any] | None:
|
||||
"""Token-based authentication using HTTP Bearer authentication"""
|
||||
auth = get_authorization_header(request)
|
||||
raw_token = validate_auth(auth)
|
||||
device_token: MobileDeviceToken = MobileDeviceToken.objects.filter(token=raw_token).first()
|
||||
if not device_token:
|
||||
return None
|
||||
|
||||
return (device_token.user, None)
|
||||
|
||||
|
||||
class TokenSchema(OpenApiAuthenticationExtension):
|
||||
"""Auth schema"""
|
||||
|
||||
target_class = MobileDeviceTokenAuthentication
|
||||
name = "mobile_device_token"
|
||||
|
||||
def get_security_definition(self, auto_schema):
|
||||
"""Auth schema"""
|
||||
return {
|
||||
"type": "apiKey",
|
||||
"in": "header",
|
||||
"name": "Authorization",
|
||||
"scheme": "bearer",
|
||||
}
|
|
@ -1,60 +1,14 @@
|
|||
"""AuthenticatorMobileStage API Views"""
|
||||
from django_filters.rest_framework.backends import DjangoFilterBackend
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import extend_schema, inline_serializer
|
||||
from rest_framework import mixins
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import CharField
|
||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
||||
from rest_framework.permissions import IsAdminUser
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
from rest_framework.viewsets import GenericViewSet, ModelViewSet
|
||||
|
||||
from authentik.api.authorization import OwnerFilter, OwnerPermissions
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.flows.api.stages import StageSerializer
|
||||
from authentik.stages.authenticator_mobile.models import AuthenticatorMobileStage, MobileDevice
|
||||
|
||||
|
||||
class AuthenticatorMobileStageSerializer(StageSerializer):
|
||||
"""AuthenticatorMobileStage Serializer"""
|
||||
|
||||
class Meta:
|
||||
model = AuthenticatorMobileStage
|
||||
fields = StageSerializer.Meta.fields + [
|
||||
"configure_flow",
|
||||
"friendly_name",
|
||||
]
|
||||
|
||||
|
||||
class AuthenticatorMobileStageViewSet(UsedByMixin, ModelViewSet):
|
||||
"""AuthenticatorMobileStage Viewset"""
|
||||
|
||||
queryset = AuthenticatorMobileStage.objects.all()
|
||||
serializer_class = AuthenticatorMobileStageSerializer
|
||||
filterset_fields = [
|
||||
"name",
|
||||
"configure_flow",
|
||||
]
|
||||
search_fields = ["name"]
|
||||
ordering = ["name"]
|
||||
|
||||
@extend_schema(
|
||||
request=OpenApiTypes.NONE,
|
||||
responses={
|
||||
200: inline_serializer(
|
||||
"MobileDeviceEnrollmentCallbackSerializer",
|
||||
{
|
||||
"device_token": CharField(required=True),
|
||||
},
|
||||
),
|
||||
},
|
||||
)
|
||||
@action(methods=["POST"], detail=True, permission_classes=[])
|
||||
def enrollment_callback(self, request: Request, pk: str) -> Response:
|
||||
"""Enrollment callback"""
|
||||
from authentik.stages.authenticator_mobile.models import MobileDevice
|
||||
|
||||
|
||||
class MobileDeviceSerializer(ModelSerializer):
|
|
@ -0,0 +1,63 @@
|
|||
"""AuthenticatorMobileStage API Views"""
|
||||
from drf_spectacular.utils import extend_schema, inline_serializer
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import CharField
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ModelViewSet
|
||||
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.flows.api.stages import StageSerializer
|
||||
from authentik.stages.authenticator_mobile.api.auth import MobileDeviceTokenAuthentication
|
||||
from authentik.stages.authenticator_mobile.models import AuthenticatorMobileStage
|
||||
|
||||
|
||||
class AuthenticatorMobileStageSerializer(StageSerializer):
|
||||
"""AuthenticatorMobileStage Serializer"""
|
||||
|
||||
class Meta:
|
||||
model = AuthenticatorMobileStage
|
||||
fields = StageSerializer.Meta.fields + [
|
||||
"configure_flow",
|
||||
"friendly_name",
|
||||
]
|
||||
|
||||
|
||||
class AuthenticatorMobileStageViewSet(UsedByMixin, ModelViewSet):
|
||||
"""AuthenticatorMobileStage Viewset"""
|
||||
|
||||
queryset = AuthenticatorMobileStage.objects.all()
|
||||
serializer_class = AuthenticatorMobileStageSerializer
|
||||
filterset_fields = [
|
||||
"name",
|
||||
"configure_flow",
|
||||
]
|
||||
search_fields = ["name"]
|
||||
ordering = ["name"]
|
||||
|
||||
@extend_schema(
|
||||
responses={
|
||||
200: inline_serializer(
|
||||
"MobileDeviceEnrollmentCallbackSerializer",
|
||||
{
|
||||
"device_token": CharField(required=True),
|
||||
},
|
||||
),
|
||||
},
|
||||
request=inline_serializer(
|
||||
"MobileDeviceEnrollmentSerializer",
|
||||
{
|
||||
"device_token": CharField(required=True),
|
||||
},
|
||||
),
|
||||
)
|
||||
@action(
|
||||
methods=["POST"],
|
||||
detail=True,
|
||||
permission_classes=[],
|
||||
authentication_classes=[MobileDeviceTokenAuthentication],
|
||||
)
|
||||
def enrollment_callback(self, request: Request, pk: str) -> Response:
|
||||
"""Enrollment callback"""
|
||||
print(request.data)
|
||||
return Response(status=204)
|
|
@ -25,7 +25,9 @@ class AuthenticatorMobileStage(ConfigurableStage, FriendlyNamedStage, Stage):
|
|||
|
||||
@property
|
||||
def serializer(self) -> type[BaseSerializer]:
|
||||
from authentik.stages.authenticator_mobile.api import AuthenticatorMobileStageSerializer
|
||||
from authentik.stages.authenticator_mobile.api.stage import (
|
||||
AuthenticatorMobileStageSerializer,
|
||||
)
|
||||
|
||||
return AuthenticatorMobileStageSerializer
|
||||
|
||||
|
@ -67,7 +69,7 @@ class MobileDevice(SerializerModel, Device):
|
|||
|
||||
@property
|
||||
def serializer(self) -> Serializer:
|
||||
from authentik.stages.authenticator_mobile.api import MobileDeviceSerializer
|
||||
from authentik.stages.authenticator_mobile.api.device import MobileDeviceSerializer
|
||||
|
||||
return MobileDeviceSerializer
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ class AuthenticatorMobileStageView(ChallengeStageView):
|
|||
payload = AuthenticatorMobilePayloadChallenge(
|
||||
data={
|
||||
# TODO: use cloud gateway?
|
||||
"u": self.request.get_host(),
|
||||
"u": self.request.build_absolute_uri("/"),
|
||||
"s": str(stage.stage_uuid),
|
||||
"t": self.executor.plan.context[FLOW_PLAN_MOBILE_ENROLL].token,
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
"""API URLs"""
|
||||
from authentik.stages.authenticator_mobile.api import (
|
||||
from authentik.stages.authenticator_mobile.api.device import (
|
||||
AdminMobileDeviceViewSet,
|
||||
AuthenticatorMobileStageViewSet,
|
||||
MobileDeviceViewSet,
|
||||
)
|
||||
from authentik.stages.authenticator_mobile.api.stage import AuthenticatorMobileStageViewSet
|
||||
|
||||
api_urlpatterns = [
|
||||
("authenticators/mobile", MobileDeviceViewSet),
|
||||
|
|
21
schema.yml
21
schema.yml
|
@ -22611,8 +22611,14 @@ paths:
|
|||
required: true
|
||||
tags:
|
||||
- stages
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/MobileDeviceEnrollmentRequest'
|
||||
required: true
|
||||
security:
|
||||
- authentik: []
|
||||
- mobile_device_token: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
|
@ -34141,6 +34147,14 @@ components:
|
|||
type: string
|
||||
required:
|
||||
- device_token
|
||||
MobileDeviceEnrollmentRequest:
|
||||
type: object
|
||||
properties:
|
||||
device_token:
|
||||
type: string
|
||||
minLength: 1
|
||||
required:
|
||||
- device_token
|
||||
MobileDeviceRequest:
|
||||
type: object
|
||||
description: Serializer for Mobile authenticator devices
|
||||
|
@ -43692,5 +43706,10 @@ components:
|
|||
in: header
|
||||
name: Authorization
|
||||
scheme: bearer
|
||||
mobile_device_token:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: Authorization
|
||||
scheme: bearer
|
||||
servers:
|
||||
- url: /api/v3/
|
||||
|
|
Reference in New Issue