stages: authenticator_endpoint_gdtc (#10477)
* rework Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add loading overlay for chrome Signed-off-by: Jens Langhammer <jens@goauthentik.io> * start docs Signed-off-by: Jens Langhammer <jens@goauthentik.io> * Apply suggestions from code review Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com> Signed-off-by: Jens L. <jens@beryju.org> * save data Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix web ui, prevent deletion Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix Signed-off-by: Jens Langhammer <jens@goauthentik.io> * text fixes Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io> Signed-off-by: Jens L. <jens@beryju.org> Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
This commit is contained in:
@ -51,6 +51,10 @@ from authentik.enterprise.providers.microsoft_entra.models import (
|
||||
MicrosoftEntraProviderUser,
|
||||
)
|
||||
from authentik.enterprise.providers.rac.models import ConnectionToken
|
||||
from authentik.enterprise.stages.authenticator_endpoint_gdtc.models import (
|
||||
EndpointDevice,
|
||||
EndpointDeviceConnection,
|
||||
)
|
||||
from authentik.events.logs import LogEvent, capture_logs
|
||||
from authentik.events.models import SystemTask
|
||||
from authentik.events.utils import cleanse_dict
|
||||
@ -119,6 +123,8 @@ def excluded_models() -> list[type[Model]]:
|
||||
GoogleWorkspaceProviderGroup,
|
||||
MicrosoftEntraProviderUser,
|
||||
MicrosoftEntraProviderGroup,
|
||||
EndpointDevice,
|
||||
EndpointDeviceConnection,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@ -6,7 +6,6 @@ from rest_framework.fields import (
|
||||
BooleanField,
|
||||
CharField,
|
||||
DateTimeField,
|
||||
IntegerField,
|
||||
SerializerMethodField,
|
||||
)
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
@ -15,6 +14,7 @@ from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ViewSet
|
||||
|
||||
from authentik.core.api.utils import MetaNameSerializer
|
||||
from authentik.enterprise.stages.authenticator_endpoint_gdtc.models import EndpointDevice
|
||||
from authentik.rbac.decorators import permission_required
|
||||
from authentik.stages.authenticator import device_classes, devices_for_user
|
||||
from authentik.stages.authenticator.models import Device
|
||||
@ -24,7 +24,7 @@ from authentik.stages.authenticator_webauthn.models import WebAuthnDevice
|
||||
class DeviceSerializer(MetaNameSerializer):
|
||||
"""Serializer for Duo authenticator devices"""
|
||||
|
||||
pk = IntegerField()
|
||||
pk = CharField()
|
||||
name = CharField()
|
||||
type = SerializerMethodField()
|
||||
confirmed = BooleanField()
|
||||
@ -41,6 +41,8 @@ class DeviceSerializer(MetaNameSerializer):
|
||||
"""Get extra description"""
|
||||
if isinstance(instance, WebAuthnDevice):
|
||||
return instance.device_type.description
|
||||
if isinstance(instance, EndpointDevice):
|
||||
return instance.data.get("deviceSignals", {}).get("deviceModel")
|
||||
return ""
|
||||
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import code
|
||||
import platform
|
||||
import sys
|
||||
import traceback
|
||||
from pprint import pprint
|
||||
|
||||
from django.apps import apps
|
||||
from django.core.management.base import BaseCommand
|
||||
@ -34,7 +35,9 @@ class Command(BaseCommand):
|
||||
|
||||
def get_namespace(self):
|
||||
"""Prepare namespace with all models"""
|
||||
namespace = {}
|
||||
namespace = {
|
||||
"pprint": pprint,
|
||||
}
|
||||
|
||||
# Gather Django models and constants from each app
|
||||
for app in apps.get_app_configs():
|
||||
|
||||
@ -29,7 +29,7 @@ class TestDevicesAPI(APITestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
body = loads(response.content.decode())
|
||||
self.assertEqual(len(body), 1)
|
||||
self.assertEqual(body[0]["pk"], self.device1.pk)
|
||||
self.assertEqual(body[0]["pk"], str(self.device1.pk))
|
||||
|
||||
def test_user_api_as_admin(self):
|
||||
"""Test user API"""
|
||||
@ -54,4 +54,6 @@ class TestDevicesAPI(APITestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
body = loads(response.content.decode())
|
||||
self.assertEqual(len(body), 2)
|
||||
self.assertEqual({body[0]["pk"], body[1]["pk"]}, {self.device1.pk, self.device2.pk})
|
||||
self.assertEqual(
|
||||
{body[0]["pk"], body[1]["pk"]}, {str(self.device1.pk), str(self.device2.pk)}
|
||||
)
|
||||
|
||||
@ -17,6 +17,7 @@ TENANT_APPS = [
|
||||
"authentik.enterprise.providers.google_workspace",
|
||||
"authentik.enterprise.providers.microsoft_entra",
|
||||
"authentik.enterprise.providers.rac",
|
||||
"authentik.enterprise.stages.authenticator_endpoint_gdtc",
|
||||
"authentik.enterprise.stages.source",
|
||||
]
|
||||
|
||||
|
||||
@ -0,0 +1,82 @@
|
||||
"""AuthenticatorEndpointGDTCStage API Views"""
|
||||
|
||||
from django_filters.rest_framework.backends import DjangoFilterBackend
|
||||
from rest_framework import mixins
|
||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
||||
from rest_framework.permissions import IsAdminUser
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
from rest_framework.viewsets import GenericViewSet, ModelViewSet
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.api.authorization import OwnerFilter, OwnerPermissions
|
||||
from authentik.core.api.used_by import UsedByMixin
|
||||
from authentik.enterprise.api import EnterpriseRequiredMixin
|
||||
from authentik.enterprise.stages.authenticator_endpoint_gdtc.models import (
|
||||
AuthenticatorEndpointGDTCStage,
|
||||
EndpointDevice,
|
||||
)
|
||||
from authentik.flows.api.stages import StageSerializer
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
|
||||
class AuthenticatorEndpointGDTCStageSerializer(EnterpriseRequiredMixin, StageSerializer):
|
||||
"""AuthenticatorEndpointGDTCStage Serializer"""
|
||||
|
||||
class Meta:
|
||||
model = AuthenticatorEndpointGDTCStage
|
||||
fields = StageSerializer.Meta.fields + [
|
||||
"configure_flow",
|
||||
"friendly_name",
|
||||
"credentials",
|
||||
]
|
||||
|
||||
|
||||
class AuthenticatorEndpointGDTCStageViewSet(UsedByMixin, ModelViewSet):
|
||||
"""AuthenticatorEndpointGDTCStage Viewset"""
|
||||
|
||||
queryset = AuthenticatorEndpointGDTCStage.objects.all()
|
||||
serializer_class = AuthenticatorEndpointGDTCStageSerializer
|
||||
filterset_fields = [
|
||||
"name",
|
||||
"configure_flow",
|
||||
]
|
||||
search_fields = ["name"]
|
||||
ordering = ["name"]
|
||||
|
||||
|
||||
class EndpointDeviceSerializer(ModelSerializer):
|
||||
"""Serializer for Endpoint authenticator devices"""
|
||||
|
||||
class Meta:
|
||||
model = EndpointDevice
|
||||
fields = ["pk", "name"]
|
||||
depth = 2
|
||||
|
||||
|
||||
class EndpointDeviceViewSet(
|
||||
mixins.RetrieveModelMixin,
|
||||
mixins.ListModelMixin,
|
||||
UsedByMixin,
|
||||
GenericViewSet,
|
||||
):
|
||||
"""Viewset for Endpoint authenticator devices"""
|
||||
|
||||
queryset = EndpointDevice.objects.all()
|
||||
serializer_class = EndpointDeviceSerializer
|
||||
search_fields = ["name"]
|
||||
filterset_fields = ["name"]
|
||||
ordering = ["name"]
|
||||
permission_classes = [OwnerPermissions]
|
||||
filter_backends = [OwnerFilter, DjangoFilterBackend, OrderingFilter, SearchFilter]
|
||||
|
||||
|
||||
class EndpointAdminDeviceViewSet(ModelViewSet):
|
||||
"""Viewset for Endpoint authenticator devices (for admins)"""
|
||||
|
||||
permission_classes = [IsAdminUser]
|
||||
queryset = EndpointDevice.objects.all()
|
||||
serializer_class = EndpointDeviceSerializer
|
||||
search_fields = ["name"]
|
||||
filterset_fields = ["name"]
|
||||
ordering = ["name"]
|
||||
@ -0,0 +1,13 @@
|
||||
"""authentik Endpoint app config"""
|
||||
|
||||
from authentik.enterprise.apps import EnterpriseConfig
|
||||
|
||||
|
||||
class AuthentikStageAuthenticatorEndpointConfig(EnterpriseConfig):
|
||||
"""authentik endpoint config"""
|
||||
|
||||
name = "authentik.enterprise.stages.authenticator_endpoint_gdtc"
|
||||
label = "authentik_stages_authenticator_endpoint_gdtc"
|
||||
verbose_name = "authentik Enterprise.Stages.Authenticator.Endpoint GDTC"
|
||||
default = True
|
||||
mountpoint = "endpoint/gdtc/"
|
||||
@ -0,0 +1,115 @@
|
||||
# Generated by Django 5.0.9 on 2024-10-22 11:40
|
||||
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("authentik_flows", "0027_auto_20231028_1424"),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="AuthenticatorEndpointGDTCStage",
|
||||
fields=[
|
||||
(
|
||||
"stage_ptr",
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to="authentik_flows.stage",
|
||||
),
|
||||
),
|
||||
("friendly_name", models.TextField(null=True)),
|
||||
("credentials", models.JSONField()),
|
||||
(
|
||||
"configure_flow",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
help_text="Flow used by an authenticated user to configure this Stage. If empty, user will not be able to configure this stage.",
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
to="authentik_flows.flow",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Endpoint Authenticator Google Device Trust Connector Stage",
|
||||
"verbose_name_plural": "Endpoint Authenticator Google Device Trust Connector Stages",
|
||||
},
|
||||
bases=("authentik_flows.stage", models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="EndpointDevice",
|
||||
fields=[
|
||||
("created", models.DateTimeField(auto_now_add=True)),
|
||||
("last_updated", models.DateTimeField(auto_now=True)),
|
||||
(
|
||||
"name",
|
||||
models.CharField(
|
||||
help_text="The human-readable name of this device.", max_length=64
|
||||
),
|
||||
),
|
||||
(
|
||||
"confirmed",
|
||||
models.BooleanField(default=True, help_text="Is this device ready for use?"),
|
||||
),
|
||||
("last_used", models.DateTimeField(null=True)),
|
||||
("uuid", models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||
(
|
||||
"host_identifier",
|
||||
models.TextField(
|
||||
help_text="A unique identifier for the endpoint device, usually the device serial number",
|
||||
unique=True,
|
||||
),
|
||||
),
|
||||
("data", models.JSONField()),
|
||||
(
|
||||
"user",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Endpoint Device",
|
||||
"verbose_name_plural": "Endpoint Devices",
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="EndpointDeviceConnection",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
|
||||
),
|
||||
),
|
||||
("attributes", models.JSONField()),
|
||||
(
|
||||
"device",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="authentik_stages_authenticator_endpoint_gdtc.endpointdevice",
|
||||
),
|
||||
),
|
||||
(
|
||||
"stage",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="authentik_stages_authenticator_endpoint_gdtc.authenticatorendpointgdtcstage",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,101 @@
|
||||
"""Endpoint stage"""
|
||||
|
||||
from uuid import uuid4
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from google.oauth2.service_account import Credentials
|
||||
from rest_framework.serializers import BaseSerializer, Serializer
|
||||
|
||||
from authentik.core.types import UserSettingSerializer
|
||||
from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
|
||||
from authentik.flows.stage import StageView
|
||||
from authentik.lib.models import SerializerModel
|
||||
from authentik.stages.authenticator.models import Device
|
||||
|
||||
|
||||
class AuthenticatorEndpointGDTCStage(ConfigurableStage, FriendlyNamedStage, Stage):
|
||||
"""Setup Google Chrome Device-trust connection"""
|
||||
|
||||
credentials = models.JSONField()
|
||||
|
||||
def google_credentials(self):
|
||||
return {
|
||||
"credentials": Credentials.from_service_account_info(
|
||||
self.credentials, scopes=["https://www.googleapis.com/auth/verifiedaccess"]
|
||||
),
|
||||
}
|
||||
|
||||
@property
|
||||
def serializer(self) -> type[BaseSerializer]:
|
||||
from authentik.enterprise.stages.authenticator_endpoint_gdtc.api import (
|
||||
AuthenticatorEndpointGDTCStageSerializer,
|
||||
)
|
||||
|
||||
return AuthenticatorEndpointGDTCStageSerializer
|
||||
|
||||
@property
|
||||
def view(self) -> type[StageView]:
|
||||
from authentik.enterprise.stages.authenticator_endpoint_gdtc.stage import (
|
||||
AuthenticatorEndpointStageView,
|
||||
)
|
||||
|
||||
return AuthenticatorEndpointStageView
|
||||
|
||||
@property
|
||||
def component(self) -> str:
|
||||
return "ak-stage-authenticator-endpoint-gdtc-form"
|
||||
|
||||
def ui_user_settings(self) -> UserSettingSerializer | None:
|
||||
return UserSettingSerializer(
|
||||
data={
|
||||
"title": self.friendly_name or str(self._meta.verbose_name),
|
||||
"component": "ak-user-settings-authenticator-endpoint",
|
||||
}
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Endpoint Authenticator Google Device Trust Connector Stage {self.name}"
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Endpoint Authenticator Google Device Trust Connector Stage")
|
||||
verbose_name_plural = _("Endpoint Authenticator Google Device Trust Connector Stages")
|
||||
|
||||
|
||||
class EndpointDevice(SerializerModel, Device):
|
||||
"""Endpoint Device for a single user"""
|
||||
|
||||
uuid = models.UUIDField(primary_key=True, default=uuid4)
|
||||
host_identifier = models.TextField(
|
||||
unique=True,
|
||||
help_text="A unique identifier for the endpoint device, usually the device serial number",
|
||||
)
|
||||
|
||||
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
|
||||
data = models.JSONField()
|
||||
|
||||
@property
|
||||
def serializer(self) -> Serializer:
|
||||
from authentik.enterprise.stages.authenticator_endpoint_gdtc.api import (
|
||||
EndpointDeviceSerializer,
|
||||
)
|
||||
|
||||
return EndpointDeviceSerializer
|
||||
|
||||
def __str__(self):
|
||||
return str(self.name) or str(self.user_id)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Endpoint Device")
|
||||
verbose_name_plural = _("Endpoint Devices")
|
||||
|
||||
|
||||
class EndpointDeviceConnection(models.Model):
|
||||
device = models.ForeignKey(EndpointDevice, on_delete=models.CASCADE)
|
||||
stage = models.ForeignKey(AuthenticatorEndpointGDTCStage, on_delete=models.CASCADE)
|
||||
|
||||
attributes = models.JSONField()
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Endpoint device connection {self.device_id} to {self.stage_id}"
|
||||
@ -0,0 +1,32 @@
|
||||
from django.http import HttpResponse
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from authentik.flows.challenge import (
|
||||
Challenge,
|
||||
ChallengeResponse,
|
||||
FrameChallenge,
|
||||
FrameChallengeResponse,
|
||||
)
|
||||
from authentik.flows.stage import ChallengeStageView
|
||||
|
||||
|
||||
class AuthenticatorEndpointStageView(ChallengeStageView):
|
||||
"""Endpoint stage"""
|
||||
|
||||
response_class = FrameChallengeResponse
|
||||
|
||||
def get_challenge(self, *args, **kwargs) -> Challenge:
|
||||
return FrameChallenge(
|
||||
data={
|
||||
"component": "xak-flow-frame",
|
||||
"url": self.request.build_absolute_uri(
|
||||
reverse("authentik_stages_authenticator_endpoint_gdtc:chrome")
|
||||
),
|
||||
"loading_overlay": True,
|
||||
"loading_text": _("Verifying your browser..."),
|
||||
}
|
||||
)
|
||||
|
||||
def challenge_valid(self, response: ChallengeResponse) -> HttpResponse:
|
||||
return self.executor.stage_ok()
|
||||
@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<script>
|
||||
window.parent.postMessage({
|
||||
message: "submit",
|
||||
source: "goauthentik.io",
|
||||
context: "flow-executor"
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
@ -0,0 +1,26 @@
|
||||
"""API URLs"""
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from authentik.enterprise.stages.authenticator_endpoint_gdtc.api import (
|
||||
AuthenticatorEndpointGDTCStageViewSet,
|
||||
EndpointAdminDeviceViewSet,
|
||||
EndpointDeviceViewSet,
|
||||
)
|
||||
from authentik.enterprise.stages.authenticator_endpoint_gdtc.views.dtc import (
|
||||
GoogleChromeDeviceTrustConnector,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path("chrome/", GoogleChromeDeviceTrustConnector.as_view(), name="chrome"),
|
||||
]
|
||||
|
||||
api_urlpatterns = [
|
||||
("authenticators/endpoint", EndpointDeviceViewSet),
|
||||
(
|
||||
"authenticators/admin/endpoint",
|
||||
EndpointAdminDeviceViewSet,
|
||||
"admin-endpointdevice",
|
||||
),
|
||||
("stages/authenticator/endpoint_gdtc", AuthenticatorEndpointGDTCStageViewSet),
|
||||
]
|
||||
@ -0,0 +1,84 @@
|
||||
from json import dumps, loads
|
||||
from typing import Any
|
||||
|
||||
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.urls import reverse
|
||||
from django.views import View
|
||||
from googleapiclient.discovery import build
|
||||
|
||||
from authentik.enterprise.stages.authenticator_endpoint_gdtc.models import (
|
||||
AuthenticatorEndpointGDTCStage,
|
||||
EndpointDevice,
|
||||
EndpointDeviceConnection,
|
||||
)
|
||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
||||
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||
from authentik.stages.password.stage import PLAN_CONTEXT_METHOD, PLAN_CONTEXT_METHOD_ARGS
|
||||
|
||||
# Header we get from chrome that initiates verified access
|
||||
HEADER_DEVICE_TRUST = "X-Device-Trust"
|
||||
# Header we send to the client with the challenge
|
||||
HEADER_ACCESS_CHALLENGE = "X-Verified-Access-Challenge"
|
||||
# Header we get back from the client that we verify with google
|
||||
HEADER_ACCESS_CHALLENGE_RESPONSE = "X-Verified-Access-Challenge-Response"
|
||||
# Header value for x-device-trust that initiates the flow
|
||||
DEVICE_TRUST_VERIFIED_ACCESS = "VerifiedAccess"
|
||||
|
||||
|
||||
class GoogleChromeDeviceTrustConnector(View):
|
||||
"""Google Chrome Device-trust connector based endpoint authenticator"""
|
||||
|
||||
def get_flow_plan(self) -> FlowPlan:
|
||||
flow_plan: FlowPlan = self.request.session[SESSION_KEY_PLAN]
|
||||
return flow_plan
|
||||
|
||||
def setup(self, request: HttpRequest, *args: Any, **kwargs: Any) -> None:
|
||||
super().setup(request, *args, **kwargs)
|
||||
stage: AuthenticatorEndpointGDTCStage = self.get_flow_plan().bindings[0].stage
|
||||
self.google_client = build(
|
||||
"verifiedaccess",
|
||||
"v2",
|
||||
cache_discovery=False,
|
||||
**stage.google_credentials(),
|
||||
)
|
||||
|
||||
def get(self, request: HttpRequest) -> HttpResponse:
|
||||
x_device_trust = request.headers.get(HEADER_DEVICE_TRUST)
|
||||
x_access_challenge_response = request.headers.get(HEADER_ACCESS_CHALLENGE_RESPONSE)
|
||||
if x_device_trust == "VerifiedAccess" and x_access_challenge_response is None:
|
||||
challenge = self.google_client.challenge().generate().execute()
|
||||
res = HttpResponseRedirect(
|
||||
self.request.build_absolute_uri(
|
||||
reverse("authentik_stages_authenticator_endpoint_gdtc:chrome")
|
||||
)
|
||||
)
|
||||
res[HEADER_ACCESS_CHALLENGE] = dumps(challenge)
|
||||
return res
|
||||
if x_access_challenge_response:
|
||||
response = (
|
||||
self.google_client.challenge()
|
||||
.verify(body=loads(x_access_challenge_response))
|
||||
.execute()
|
||||
)
|
||||
# Remove deprecated string representation of deviceSignals
|
||||
response.pop("deviceSignal", None)
|
||||
flow_plan: FlowPlan = self.get_flow_plan()
|
||||
device, _ = EndpointDevice.objects.update_or_create(
|
||||
host_identifier=response["deviceSignals"]["serialNumber"],
|
||||
user=flow_plan.context.get(PLAN_CONTEXT_PENDING_USER),
|
||||
defaults={"name": response["deviceSignals"]["hostname"], "data": response},
|
||||
)
|
||||
EndpointDeviceConnection.objects.update_or_create(
|
||||
device=device,
|
||||
stage=flow_plan.bindings[0].stage,
|
||||
defaults={
|
||||
"attributes": response,
|
||||
},
|
||||
)
|
||||
flow_plan.context.setdefault(PLAN_CONTEXT_METHOD, "trusted_endpoint")
|
||||
flow_plan.context.setdefault(PLAN_CONTEXT_METHOD_ARGS, {})
|
||||
flow_plan.context[PLAN_CONTEXT_METHOD_ARGS].setdefault("endpoints", [])
|
||||
flow_plan.context[PLAN_CONTEXT_METHOD_ARGS]["endpoints"].append(response)
|
||||
request.session[SESSION_KEY_PLAN] = flow_plan
|
||||
return TemplateResponse(request, "stages/authenticator_endpoint/google_chrome_dtc.html")
|
||||
@ -8,7 +8,7 @@ from uuid import UUID
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.db import models
|
||||
from django.http import JsonResponse
|
||||
from rest_framework.fields import CharField, ChoiceField, DictField
|
||||
from rest_framework.fields import BooleanField, CharField, ChoiceField, DictField
|
||||
from rest_framework.request import Request
|
||||
|
||||
from authentik.core.api.utils import PassiveSerializer
|
||||
@ -160,6 +160,20 @@ class AutoSubmitChallengeResponse(ChallengeResponse):
|
||||
component = CharField(default="ak-stage-autosubmit")
|
||||
|
||||
|
||||
class FrameChallenge(Challenge):
|
||||
"""Challenge type to render a frame"""
|
||||
|
||||
component = CharField(default="xak-flow-frame")
|
||||
url = CharField()
|
||||
loading_overlay = BooleanField(default=False)
|
||||
loading_text = CharField()
|
||||
|
||||
|
||||
class FrameChallengeResponse(ChallengeResponse):
|
||||
|
||||
component = CharField(default="xak-flow-frame")
|
||||
|
||||
|
||||
class DataclassEncoder(DjangoJSONEncoder):
|
||||
"""Convert any dataclass to json"""
|
||||
|
||||
|
||||
@ -38,6 +38,7 @@ LANGUAGE_COOKIE_NAME = "authentik_language"
|
||||
SESSION_COOKIE_NAME = "authentik_session"
|
||||
SESSION_COOKIE_DOMAIN = CONFIG.get("cookie_domain", None)
|
||||
APPEND_SLASH = False
|
||||
X_FRAME_OPTIONS = "SAMEORIGIN"
|
||||
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
"django.contrib.auth.backends.ModelBackend",
|
||||
|
||||
@ -68,7 +68,7 @@ class AuthenticatorAttachment(models.TextChoices):
|
||||
|
||||
|
||||
class AuthenticatorWebAuthnStage(ConfigurableStage, FriendlyNamedStage, Stage):
|
||||
"""WebAuthn stage"""
|
||||
"""Stage to enroll WebAuthn-based authenticators."""
|
||||
|
||||
user_verification = models.TextField(
|
||||
choices=UserVerification.choices,
|
||||
|
||||
@ -3361,6 +3361,46 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"model",
|
||||
"identifiers"
|
||||
],
|
||||
"properties": {
|
||||
"model": {
|
||||
"const": "authentik_stages_authenticator_endpoint_gdtc.authenticatorendpointgdtcstage"
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"state": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"absent",
|
||||
"present",
|
||||
"created",
|
||||
"must_created"
|
||||
],
|
||||
"default": "present"
|
||||
},
|
||||
"conditions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"permissions": {
|
||||
"$ref": "#/$defs/model_authentik_stages_authenticator_endpoint_gdtc.authenticatorendpointgdtcstage_permissions"
|
||||
},
|
||||
"attrs": {
|
||||
"$ref": "#/$defs/model_authentik_stages_authenticator_endpoint_gdtc.authenticatorendpointgdtcstage"
|
||||
},
|
||||
"identifiers": {
|
||||
"$ref": "#/$defs/model_authentik_stages_authenticator_endpoint_gdtc.authenticatorendpointgdtcstage"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -4304,6 +4344,7 @@
|
||||
"authentik.enterprise.providers.google_workspace",
|
||||
"authentik.enterprise.providers.microsoft_entra",
|
||||
"authentik.enterprise.providers.rac",
|
||||
"authentik.enterprise.stages.authenticator_endpoint_gdtc",
|
||||
"authentik.enterprise.stages.source",
|
||||
"authentik.events"
|
||||
],
|
||||
@ -4400,6 +4441,7 @@
|
||||
"authentik_providers_rac.racprovider",
|
||||
"authentik_providers_rac.endpoint",
|
||||
"authentik_providers_rac.racpropertymapping",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.authenticatorendpointgdtcstage",
|
||||
"authentik_stages_source.sourcestage",
|
||||
"authentik_events.event",
|
||||
"authentik_events.notificationtransport",
|
||||
@ -6451,6 +6493,18 @@
|
||||
"authentik_stages_authenticator_duo.delete_duodevice",
|
||||
"authentik_stages_authenticator_duo.view_authenticatorduostage",
|
||||
"authentik_stages_authenticator_duo.view_duodevice",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.add_authenticatorendpointgdtcstage",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.add_endpointdevice",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.add_endpointdeviceconnection",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.change_authenticatorendpointgdtcstage",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.change_endpointdevice",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.change_endpointdeviceconnection",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.delete_authenticatorendpointgdtcstage",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.delete_endpointdevice",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.delete_endpointdeviceconnection",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.view_authenticatorendpointgdtcstage",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.view_endpointdevice",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.view_endpointdeviceconnection",
|
||||
"authentik_stages_authenticator_sms.add_authenticatorsmsstage",
|
||||
"authentik_stages_authenticator_sms.add_smsdevice",
|
||||
"authentik_stages_authenticator_sms.change_authenticatorsmsstage",
|
||||
@ -12107,6 +12161,18 @@
|
||||
"authentik_stages_authenticator_duo.delete_duodevice",
|
||||
"authentik_stages_authenticator_duo.view_authenticatorduostage",
|
||||
"authentik_stages_authenticator_duo.view_duodevice",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.add_authenticatorendpointgdtcstage",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.add_endpointdevice",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.add_endpointdeviceconnection",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.change_authenticatorendpointgdtcstage",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.change_endpointdevice",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.change_endpointdeviceconnection",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.delete_authenticatorendpointgdtcstage",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.delete_endpointdevice",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.delete_endpointdeviceconnection",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.view_authenticatorendpointgdtcstage",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.view_endpointdevice",
|
||||
"authentik_stages_authenticator_endpoint_gdtc.view_endpointdeviceconnection",
|
||||
"authentik_stages_authenticator_sms.add_authenticatorsmsstage",
|
||||
"authentik_stages_authenticator_sms.add_smsdevice",
|
||||
"authentik_stages_authenticator_sms.change_authenticatorsmsstage",
|
||||
@ -12997,6 +13063,144 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"model_authentik_stages_authenticator_endpoint_gdtc.authenticatorendpointgdtcstage": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"title": "Name"
|
||||
},
|
||||
"flow_set": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"title": "Name"
|
||||
},
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"maxLength": 50,
|
||||
"minLength": 1,
|
||||
"pattern": "^[-a-zA-Z0-9_]+$",
|
||||
"title": "Slug",
|
||||
"description": "Visible in the URL."
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"title": "Title",
|
||||
"description": "Shown as the Title in Flow pages."
|
||||
},
|
||||
"designation": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"authentication",
|
||||
"authorization",
|
||||
"invalidation",
|
||||
"enrollment",
|
||||
"unenrollment",
|
||||
"recovery",
|
||||
"stage_configuration"
|
||||
],
|
||||
"title": "Designation",
|
||||
"description": "Decides what this Flow is used for. For example, the Authentication flow is redirect to when an un-authenticated user visits authentik."
|
||||
},
|
||||
"policy_engine_mode": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"all",
|
||||
"any"
|
||||
],
|
||||
"title": "Policy engine mode"
|
||||
},
|
||||
"compatibility_mode": {
|
||||
"type": "boolean",
|
||||
"title": "Compatibility mode",
|
||||
"description": "Enable compatibility mode, increases compatibility with password managers on mobile devices."
|
||||
},
|
||||
"layout": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"stacked",
|
||||
"content_left",
|
||||
"content_right",
|
||||
"sidebar_left",
|
||||
"sidebar_right"
|
||||
],
|
||||
"title": "Layout"
|
||||
},
|
||||
"denied_action": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"message_continue",
|
||||
"message",
|
||||
"continue"
|
||||
],
|
||||
"title": "Denied action",
|
||||
"description": "Configure what should happen when a flow denies access to a user."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"slug",
|
||||
"title",
|
||||
"designation"
|
||||
]
|
||||
},
|
||||
"title": "Flow set"
|
||||
},
|
||||
"configure_flow": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Configure flow",
|
||||
"description": "Flow used by an authenticated user to configure this Stage. If empty, user will not be able to configure this stage."
|
||||
},
|
||||
"friendly_name": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"minLength": 1,
|
||||
"title": "Friendly name"
|
||||
},
|
||||
"credentials": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
"title": "Credentials"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
},
|
||||
"model_authentik_stages_authenticator_endpoint_gdtc.authenticatorendpointgdtcstage_permissions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"permission"
|
||||
],
|
||||
"properties": {
|
||||
"permission": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"add_authenticatorendpointgdtcstage",
|
||||
"change_authenticatorendpointgdtcstage",
|
||||
"delete_authenticatorendpointgdtcstage",
|
||||
"view_authenticatorendpointgdtcstage"
|
||||
]
|
||||
},
|
||||
"user": {
|
||||
"type": "integer"
|
||||
},
|
||||
"role": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"model_authentik_stages_source.sourcestage": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
844
schema.yml
844
schema.yml
@ -636,6 +636,238 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/authenticators/admin/endpoint/:
|
||||
get:
|
||||
operationId: authenticators_admin_endpoint_list
|
||||
description: Viewset for Endpoint authenticator devices (for admins)
|
||||
parameters:
|
||||
- in: query
|
||||
name: name
|
||||
schema:
|
||||
type: string
|
||||
- name: ordering
|
||||
required: false
|
||||
in: query
|
||||
description: Which field to use when ordering the results.
|
||||
schema:
|
||||
type: string
|
||||
- name: page
|
||||
required: false
|
||||
in: query
|
||||
description: A page number within the paginated result set.
|
||||
schema:
|
||||
type: integer
|
||||
- name: page_size
|
||||
required: false
|
||||
in: query
|
||||
description: Number of results to return per page.
|
||||
schema:
|
||||
type: integer
|
||||
- name: search
|
||||
required: false
|
||||
in: query
|
||||
description: A search term.
|
||||
schema:
|
||||
type: string
|
||||
tags:
|
||||
- authenticators
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PaginatedEndpointDeviceList'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
post:
|
||||
operationId: authenticators_admin_endpoint_create
|
||||
description: Viewset for Endpoint authenticator devices (for admins)
|
||||
tags:
|
||||
- authenticators
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/EndpointDeviceRequest'
|
||||
required: true
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'201':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/EndpointDevice'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/authenticators/admin/endpoint/{uuid}/:
|
||||
get:
|
||||
operationId: authenticators_admin_endpoint_retrieve
|
||||
description: Viewset for Endpoint authenticator devices (for admins)
|
||||
parameters:
|
||||
- in: path
|
||||
name: uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Endpoint Device.
|
||||
required: true
|
||||
tags:
|
||||
- authenticators
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/EndpointDevice'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
put:
|
||||
operationId: authenticators_admin_endpoint_update
|
||||
description: Viewset for Endpoint authenticator devices (for admins)
|
||||
parameters:
|
||||
- in: path
|
||||
name: uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Endpoint Device.
|
||||
required: true
|
||||
tags:
|
||||
- authenticators
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/EndpointDeviceRequest'
|
||||
required: true
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/EndpointDevice'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
patch:
|
||||
operationId: authenticators_admin_endpoint_partial_update
|
||||
description: Viewset for Endpoint authenticator devices (for admins)
|
||||
parameters:
|
||||
- in: path
|
||||
name: uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Endpoint Device.
|
||||
required: true
|
||||
tags:
|
||||
- authenticators
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PatchedEndpointDeviceRequest'
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/EndpointDevice'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
delete:
|
||||
operationId: authenticators_admin_endpoint_destroy
|
||||
description: Viewset for Endpoint authenticator devices (for admins)
|
||||
parameters:
|
||||
- in: path
|
||||
name: uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Endpoint Device.
|
||||
required: true
|
||||
tags:
|
||||
- authenticators
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'204':
|
||||
description: No response body
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/authenticators/admin/sms/:
|
||||
get:
|
||||
operationId: authenticators_admin_sms_list
|
||||
@ -1809,6 +2041,134 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/authenticators/endpoint/:
|
||||
get:
|
||||
operationId: authenticators_endpoint_list
|
||||
description: Viewset for Endpoint authenticator devices
|
||||
parameters:
|
||||
- in: query
|
||||
name: name
|
||||
schema:
|
||||
type: string
|
||||
- name: ordering
|
||||
required: false
|
||||
in: query
|
||||
description: Which field to use when ordering the results.
|
||||
schema:
|
||||
type: string
|
||||
- name: page
|
||||
required: false
|
||||
in: query
|
||||
description: A page number within the paginated result set.
|
||||
schema:
|
||||
type: integer
|
||||
- name: page_size
|
||||
required: false
|
||||
in: query
|
||||
description: Number of results to return per page.
|
||||
schema:
|
||||
type: integer
|
||||
- name: search
|
||||
required: false
|
||||
in: query
|
||||
description: A search term.
|
||||
schema:
|
||||
type: string
|
||||
tags:
|
||||
- authenticators
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PaginatedEndpointDeviceList'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/authenticators/endpoint/{uuid}/:
|
||||
get:
|
||||
operationId: authenticators_endpoint_retrieve
|
||||
description: Viewset for Endpoint authenticator devices
|
||||
parameters:
|
||||
- in: path
|
||||
name: uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Endpoint Device.
|
||||
required: true
|
||||
tags:
|
||||
- authenticators
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/EndpointDevice'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/authenticators/endpoint/{uuid}/used_by/:
|
||||
get:
|
||||
operationId: authenticators_endpoint_used_by_list
|
||||
description: Get a list of all objects that use this object
|
||||
parameters:
|
||||
- in: path
|
||||
name: uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Endpoint Device.
|
||||
required: true
|
||||
tags:
|
||||
- authenticators
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/UsedBy'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/authenticators/sms/:
|
||||
get:
|
||||
operationId: authenticators_sms_list
|
||||
@ -22725,6 +23085,7 @@ paths:
|
||||
- authentik_sources_scim.scimsourcepropertymapping
|
||||
- authentik_stages_authenticator_duo.authenticatorduostage
|
||||
- authentik_stages_authenticator_duo.duodevice
|
||||
- authentik_stages_authenticator_endpoint_gdtc.authenticatorendpointgdtcstage
|
||||
- authentik_stages_authenticator_sms.authenticatorsmsstage
|
||||
- authentik_stages_authenticator_sms.smsdevice
|
||||
- authentik_stages_authenticator_static.authenticatorstaticstage
|
||||
@ -22959,6 +23320,7 @@ paths:
|
||||
- authentik_sources_scim.scimsourcepropertymapping
|
||||
- authentik_stages_authenticator_duo.authenticatorduostage
|
||||
- authentik_stages_authenticator_duo.duodevice
|
||||
- authentik_stages_authenticator_endpoint_gdtc.authenticatorendpointgdtcstage
|
||||
- authentik_stages_authenticator_sms.authenticatorsmsstage
|
||||
- authentik_stages_authenticator_sms.smsdevice
|
||||
- authentik_stages_authenticator_static.authenticatorstaticstage
|
||||
@ -29031,6 +29393,285 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/stages/authenticator/endpoint_gdtc/:
|
||||
get:
|
||||
operationId: stages_authenticator_endpoint_gdtc_list
|
||||
description: AuthenticatorEndpointGDTCStage Viewset
|
||||
parameters:
|
||||
- in: query
|
||||
name: configure_flow
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
- in: query
|
||||
name: name
|
||||
schema:
|
||||
type: string
|
||||
- name: ordering
|
||||
required: false
|
||||
in: query
|
||||
description: Which field to use when ordering the results.
|
||||
schema:
|
||||
type: string
|
||||
- name: page
|
||||
required: false
|
||||
in: query
|
||||
description: A page number within the paginated result set.
|
||||
schema:
|
||||
type: integer
|
||||
- name: page_size
|
||||
required: false
|
||||
in: query
|
||||
description: Number of results to return per page.
|
||||
schema:
|
||||
type: integer
|
||||
- name: search
|
||||
required: false
|
||||
in: query
|
||||
description: A search term.
|
||||
schema:
|
||||
type: string
|
||||
tags:
|
||||
- stages
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PaginatedAuthenticatorEndpointGDTCStageList'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
post:
|
||||
operationId: stages_authenticator_endpoint_gdtc_create
|
||||
description: AuthenticatorEndpointGDTCStage Viewset
|
||||
tags:
|
||||
- stages
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/AuthenticatorEndpointGDTCStageRequest'
|
||||
required: true
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'201':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/AuthenticatorEndpointGDTCStage'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/stages/authenticator/endpoint_gdtc/{stage_uuid}/:
|
||||
get:
|
||||
operationId: stages_authenticator_endpoint_gdtc_retrieve
|
||||
description: AuthenticatorEndpointGDTCStage Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: stage_uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Endpoint Authenticator Google
|
||||
Device Trust Connector Stage.
|
||||
required: true
|
||||
tags:
|
||||
- stages
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/AuthenticatorEndpointGDTCStage'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
put:
|
||||
operationId: stages_authenticator_endpoint_gdtc_update
|
||||
description: AuthenticatorEndpointGDTCStage Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: stage_uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Endpoint Authenticator Google
|
||||
Device Trust Connector Stage.
|
||||
required: true
|
||||
tags:
|
||||
- stages
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/AuthenticatorEndpointGDTCStageRequest'
|
||||
required: true
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/AuthenticatorEndpointGDTCStage'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
patch:
|
||||
operationId: stages_authenticator_endpoint_gdtc_partial_update
|
||||
description: AuthenticatorEndpointGDTCStage Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: stage_uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Endpoint Authenticator Google
|
||||
Device Trust Connector Stage.
|
||||
required: true
|
||||
tags:
|
||||
- stages
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PatchedAuthenticatorEndpointGDTCStageRequest'
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/AuthenticatorEndpointGDTCStage'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
delete:
|
||||
operationId: stages_authenticator_endpoint_gdtc_destroy
|
||||
description: AuthenticatorEndpointGDTCStage Viewset
|
||||
parameters:
|
||||
- in: path
|
||||
name: stage_uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Endpoint Authenticator Google
|
||||
Device Trust Connector Stage.
|
||||
required: true
|
||||
tags:
|
||||
- stages
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'204':
|
||||
description: No response body
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/stages/authenticator/endpoint_gdtc/{stage_uuid}/used_by/:
|
||||
get:
|
||||
operationId: stages_authenticator_endpoint_gdtc_used_by_list
|
||||
description: Get a list of all objects that use this object
|
||||
parameters:
|
||||
- in: path
|
||||
name: stage_uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
description: A UUID string identifying this Endpoint Authenticator Google
|
||||
Device Trust Connector Stage.
|
||||
required: true
|
||||
tags:
|
||||
- stages
|
||||
security:
|
||||
- authentik: []
|
||||
responses:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/UsedBy'
|
||||
description: ''
|
||||
'400':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ValidationError'
|
||||
description: ''
|
||||
'403':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/GenericError'
|
||||
description: ''
|
||||
/stages/authenticator/sms/:
|
||||
get:
|
||||
operationId: stages_authenticator_sms_list
|
||||
@ -35915,6 +36556,7 @@ components:
|
||||
- authentik.enterprise.providers.google_workspace
|
||||
- authentik.enterprise.providers.microsoft_entra
|
||||
- authentik.enterprise.providers.rac
|
||||
- authentik.enterprise.stages.authenticator_endpoint_gdtc
|
||||
- authentik.enterprise.stages.source
|
||||
- authentik.events
|
||||
type: string
|
||||
@ -36380,6 +37022,80 @@ components:
|
||||
- client_id
|
||||
- client_secret
|
||||
- name
|
||||
AuthenticatorEndpointGDTCStage:
|
||||
type: object
|
||||
description: AuthenticatorEndpointGDTCStage Serializer
|
||||
properties:
|
||||
pk:
|
||||
type: string
|
||||
format: uuid
|
||||
readOnly: true
|
||||
title: Stage uuid
|
||||
name:
|
||||
type: string
|
||||
component:
|
||||
type: string
|
||||
description: Get object type so that we know how to edit the object
|
||||
readOnly: true
|
||||
verbose_name:
|
||||
type: string
|
||||
description: Return object's verbose_name
|
||||
readOnly: true
|
||||
verbose_name_plural:
|
||||
type: string
|
||||
description: Return object's plural verbose_name
|
||||
readOnly: true
|
||||
meta_model_name:
|
||||
type: string
|
||||
description: Return internal model name
|
||||
readOnly: true
|
||||
flow_set:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/FlowSet'
|
||||
configure_flow:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
description: Flow used by an authenticated user to configure this Stage.
|
||||
If empty, user will not be able to configure this stage.
|
||||
friendly_name:
|
||||
type: string
|
||||
nullable: true
|
||||
credentials: {}
|
||||
required:
|
||||
- component
|
||||
- credentials
|
||||
- meta_model_name
|
||||
- name
|
||||
- pk
|
||||
- verbose_name
|
||||
- verbose_name_plural
|
||||
AuthenticatorEndpointGDTCStageRequest:
|
||||
type: object
|
||||
description: AuthenticatorEndpointGDTCStage Serializer
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
minLength: 1
|
||||
flow_set:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/FlowSetRequest'
|
||||
configure_flow:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
description: Flow used by an authenticated user to configure this Stage.
|
||||
If empty, user will not be able to configure this stage.
|
||||
friendly_name:
|
||||
type: string
|
||||
nullable: true
|
||||
minLength: 1
|
||||
credentials: {}
|
||||
required:
|
||||
- credentials
|
||||
- name
|
||||
AuthenticatorSMSChallenge:
|
||||
type: object
|
||||
description: SMS Setup challenge
|
||||
@ -37629,6 +38345,7 @@ components:
|
||||
- $ref: '#/components/schemas/DummyChallenge'
|
||||
- $ref: '#/components/schemas/EmailChallenge'
|
||||
- $ref: '#/components/schemas/FlowErrorChallenge'
|
||||
- $ref: '#/components/schemas/FrameChallenge'
|
||||
- $ref: '#/components/schemas/IdentificationChallenge'
|
||||
- $ref: '#/components/schemas/OAuthDeviceCodeChallenge'
|
||||
- $ref: '#/components/schemas/OAuthDeviceCodeFinishChallenge'
|
||||
@ -37656,6 +38373,7 @@ components:
|
||||
ak-stage-dummy: '#/components/schemas/DummyChallenge'
|
||||
ak-stage-email: '#/components/schemas/EmailChallenge'
|
||||
ak-stage-flow-error: '#/components/schemas/FlowErrorChallenge'
|
||||
xak-flow-frame: '#/components/schemas/FrameChallenge'
|
||||
ak-stage-identification: '#/components/schemas/IdentificationChallenge'
|
||||
ak-provider-oauth2-device-code: '#/components/schemas/OAuthDeviceCodeChallenge'
|
||||
ak-provider-oauth2-device-code-finish: '#/components/schemas/OAuthDeviceCodeFinishChallenge'
|
||||
@ -38316,7 +39034,7 @@ components:
|
||||
description: Return internal model name
|
||||
readOnly: true
|
||||
pk:
|
||||
type: integer
|
||||
type: string
|
||||
name:
|
||||
type: string
|
||||
type:
|
||||
@ -38929,6 +39647,35 @@ components:
|
||||
- protocol
|
||||
- provider
|
||||
- provider_obj
|
||||
EndpointDevice:
|
||||
type: object
|
||||
description: Serializer for Endpoint authenticator devices
|
||||
properties:
|
||||
pk:
|
||||
type: string
|
||||
format: uuid
|
||||
title: Uuid
|
||||
name:
|
||||
type: string
|
||||
description: The human-readable name of this device.
|
||||
maxLength: 64
|
||||
required:
|
||||
- name
|
||||
EndpointDeviceRequest:
|
||||
type: object
|
||||
description: Serializer for Endpoint authenticator devices
|
||||
properties:
|
||||
pk:
|
||||
type: string
|
||||
format: uuid
|
||||
title: Uuid
|
||||
name:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: The human-readable name of this device.
|
||||
maxLength: 64
|
||||
required:
|
||||
- name
|
||||
EndpointRequest:
|
||||
type: object
|
||||
description: Endpoint Serializer
|
||||
@ -39525,6 +40272,7 @@ components:
|
||||
- $ref: '#/components/schemas/ConsentChallengeResponseRequest'
|
||||
- $ref: '#/components/schemas/DummyChallengeResponseRequest'
|
||||
- $ref: '#/components/schemas/EmailChallengeResponseRequest'
|
||||
- $ref: '#/components/schemas/FrameChallengeResponseRequest'
|
||||
- $ref: '#/components/schemas/IdentificationChallengeResponseRequest'
|
||||
- $ref: '#/components/schemas/OAuthDeviceCodeChallengeResponseRequest'
|
||||
- $ref: '#/components/schemas/OAuthDeviceCodeFinishChallengeResponseRequest'
|
||||
@ -39547,6 +40295,7 @@ components:
|
||||
ak-stage-consent: '#/components/schemas/ConsentChallengeResponseRequest'
|
||||
ak-stage-dummy: '#/components/schemas/DummyChallengeResponseRequest'
|
||||
ak-stage-email: '#/components/schemas/EmailChallengeResponseRequest'
|
||||
xak-flow-frame: '#/components/schemas/FrameChallengeResponseRequest'
|
||||
ak-stage-identification: '#/components/schemas/IdentificationChallengeResponseRequest'
|
||||
ak-provider-oauth2-device-code: '#/components/schemas/OAuthDeviceCodeChallengeResponseRequest'
|
||||
ak-provider-oauth2-device-code-finish: '#/components/schemas/OAuthDeviceCodeFinishChallengeResponseRequest'
|
||||
@ -39903,6 +40652,39 @@ components:
|
||||
required:
|
||||
- href
|
||||
- name
|
||||
FrameChallenge:
|
||||
type: object
|
||||
description: Challenge type to render a frame
|
||||
properties:
|
||||
flow_info:
|
||||
$ref: '#/components/schemas/ContextualFlowInfo'
|
||||
component:
|
||||
type: string
|
||||
default: xak-flow-frame
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ErrorDetail'
|
||||
url:
|
||||
type: string
|
||||
loading_overlay:
|
||||
type: boolean
|
||||
default: false
|
||||
loading_text:
|
||||
type: string
|
||||
required:
|
||||
- loading_text
|
||||
- url
|
||||
FrameChallengeResponseRequest:
|
||||
type: object
|
||||
description: Base class for all challenge responses
|
||||
properties:
|
||||
component:
|
||||
type: string
|
||||
minLength: 1
|
||||
default: xak-flow-frame
|
||||
GenericError:
|
||||
type: object
|
||||
description: Generic API Error
|
||||
@ -42142,6 +42924,7 @@ components:
|
||||
- authentik_providers_rac.racprovider
|
||||
- authentik_providers_rac.endpoint
|
||||
- authentik_providers_rac.racpropertymapping
|
||||
- authentik_stages_authenticator_endpoint_gdtc.authenticatorendpointgdtcstage
|
||||
- authentik_stages_source.sourcestage
|
||||
- authentik_events.event
|
||||
- authentik_events.notificationtransport
|
||||
@ -43235,6 +44018,18 @@ components:
|
||||
required:
|
||||
- pagination
|
||||
- results
|
||||
PaginatedAuthenticatorEndpointGDTCStageList:
|
||||
type: object
|
||||
properties:
|
||||
pagination:
|
||||
$ref: '#/components/schemas/Pagination'
|
||||
results:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/AuthenticatorEndpointGDTCStage'
|
||||
required:
|
||||
- pagination
|
||||
- results
|
||||
PaginatedAuthenticatorSMSStageList:
|
||||
type: object
|
||||
properties:
|
||||
@ -43451,6 +44246,18 @@ components:
|
||||
required:
|
||||
- pagination
|
||||
- results
|
||||
PaginatedEndpointDeviceList:
|
||||
type: object
|
||||
properties:
|
||||
pagination:
|
||||
$ref: '#/components/schemas/Pagination'
|
||||
results:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/EndpointDevice'
|
||||
required:
|
||||
- pagination
|
||||
- results
|
||||
PaginatedEndpointList:
|
||||
type: object
|
||||
properties:
|
||||
@ -45088,6 +45895,28 @@ components:
|
||||
admin_secret_key:
|
||||
type: string
|
||||
writeOnly: true
|
||||
PatchedAuthenticatorEndpointGDTCStageRequest:
|
||||
type: object
|
||||
description: AuthenticatorEndpointGDTCStage Serializer
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
minLength: 1
|
||||
flow_set:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/FlowSetRequest'
|
||||
configure_flow:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
description: Flow used by an authenticated user to configure this Stage.
|
||||
If empty, user will not be able to configure this stage.
|
||||
friendly_name:
|
||||
type: string
|
||||
nullable: true
|
||||
minLength: 1
|
||||
credentials: {}
|
||||
PatchedAuthenticatorSMSStageRequest:
|
||||
type: object
|
||||
description: AuthenticatorSMSStage Serializer
|
||||
@ -45569,6 +46398,19 @@ components:
|
||||
activate_user_on_success:
|
||||
type: boolean
|
||||
description: Activate users upon completion of stage.
|
||||
PatchedEndpointDeviceRequest:
|
||||
type: object
|
||||
description: Serializer for Endpoint authenticator devices
|
||||
properties:
|
||||
pk:
|
||||
type: string
|
||||
format: uuid
|
||||
title: Uuid
|
||||
name:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: The human-readable name of this device.
|
||||
maxLength: 64
|
||||
PatchedEndpointRequest:
|
||||
type: object
|
||||
description: Endpoint Serializer
|
||||
|
||||
@ -2,6 +2,7 @@ import "@goauthentik/admin/rbac/ObjectPermissionModal";
|
||||
import "@goauthentik/admin/stages/StageWizard";
|
||||
import "@goauthentik/admin/stages/authenticator_duo/AuthenticatorDuoStageForm";
|
||||
import "@goauthentik/admin/stages/authenticator_duo/DuoDeviceImportForm";
|
||||
import "@goauthentik/admin/stages/authenticator_endpoint_gdtc/AuthenticatorEndpointGDTCStageForm";
|
||||
import "@goauthentik/admin/stages/authenticator_sms/AuthenticatorSMSStageForm";
|
||||
import "@goauthentik/admin/stages/authenticator_static/AuthenticatorStaticStageForm";
|
||||
import "@goauthentik/admin/stages/authenticator_totp/AuthenticatorTOTPStageForm";
|
||||
@ -25,8 +26,7 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import "@goauthentik/elements/forms/DeleteBulkForm";
|
||||
import "@goauthentik/elements/forms/ModalForm";
|
||||
import "@goauthentik/elements/forms/ProxyForm";
|
||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
||||
import { TableColumn } from "@goauthentik/elements/table/Table";
|
||||
import { PaginatedResponse, TableColumn } from "@goauthentik/elements/table/Table";
|
||||
import { TablePage } from "@goauthentik/elements/table/TablePage";
|
||||
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
|
||||
|
||||
|
||||
@ -0,0 +1,75 @@
|
||||
import { BaseStageForm } from "@goauthentik/admin/stages/BaseStageForm";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/elements/CodeMirror";
|
||||
import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
|
||||
import { AuthenticatorEndpointGDTCStage, StagesApi } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-stage-authenticator-endpoint-gdtc-form")
|
||||
export class AuthenticatorEndpointGDTCStageForm extends BaseStageForm<AuthenticatorEndpointGDTCStage> {
|
||||
loadInstance(pk: string): Promise<AuthenticatorEndpointGDTCStage> {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorEndpointGdtcRetrieve({
|
||||
stageUuid: pk,
|
||||
});
|
||||
}
|
||||
|
||||
async send(data: AuthenticatorEndpointGDTCStage): Promise<AuthenticatorEndpointGDTCStage> {
|
||||
if (this.instance) {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorEndpointGdtcPartialUpdate({
|
||||
stageUuid: this.instance.pk || "",
|
||||
patchedAuthenticatorEndpointGDTCStageRequest: data,
|
||||
});
|
||||
} else {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorEndpointGdtcCreate({
|
||||
authenticatorEndpointGDTCStageRequest: data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
return html` <span>
|
||||
${msg(
|
||||
"Stage used to verify users' browsers using Google Chrome Device Trust. This stage can be used in authentication/authorization flows.",
|
||||
)}
|
||||
</span>
|
||||
<ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
value="${first(this.instance?.name, "")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-group expanded>
|
||||
<span slot="header"> ${msg("Google Verified Access API")} </span>
|
||||
<div slot="body" class="pf-c-form">
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Credentials")}
|
||||
required
|
||||
name="credentials"
|
||||
>
|
||||
<ak-codemirror
|
||||
mode=${CodeMirrorMode.JavaScript}
|
||||
.value="${first(this.instance?.credentials, {})}"
|
||||
></ak-codemirror>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Google Cloud credentials file.")}
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
</div>
|
||||
</ak-form-group>`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ak-stage-authenticator-endpoint-gdtc-form": AuthenticatorEndpointGDTCStageForm;
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,12 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { SentryIgnoredError } from "@goauthentik/common/errors";
|
||||
import { deviceTypeName } from "@goauthentik/common/labels";
|
||||
import { getRelativeTime } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/elements/forms/DeleteBulkForm";
|
||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
||||
import { Table, TableColumn } from "@goauthentik/elements/table/Table";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
@ -54,20 +55,21 @@ export class UserDeviceTable extends Table<Device> {
|
||||
|
||||
async deleteWrapper(device: Device) {
|
||||
const api = new AuthenticatorsApi(DEFAULT_CONFIG);
|
||||
const id = { id: device.pk };
|
||||
switch (device.type) {
|
||||
case "authentik_stages_authenticator_duo.DuoDevice":
|
||||
return api.authenticatorsAdminDuoDestroy(id);
|
||||
return api.authenticatorsAdminDuoDestroy({ id: parseInt(device.pk, 10) });
|
||||
case "authentik_stages_authenticator_sms.SMSDevice":
|
||||
return api.authenticatorsAdminSmsDestroy(id);
|
||||
return api.authenticatorsAdminSmsDestroy({ id: parseInt(device.pk, 10) });
|
||||
case "authentik_stages_authenticator_totp.TOTPDevice":
|
||||
return api.authenticatorsAdminTotpDestroy(id);
|
||||
return api.authenticatorsAdminTotpDestroy({ id: parseInt(device.pk, 10) });
|
||||
case "authentik_stages_authenticator_static.StaticDevice":
|
||||
return api.authenticatorsAdminStaticDestroy(id);
|
||||
return api.authenticatorsAdminStaticDestroy({ id: parseInt(device.pk, 10) });
|
||||
case "authentik_stages_authenticator_webauthn.WebAuthnDevice":
|
||||
return api.authenticatorsAdminWebauthnDestroy(id);
|
||||
return api.authenticatorsAdminWebauthnDestroy({ id: parseInt(device.pk, 10) });
|
||||
default:
|
||||
break;
|
||||
throw new SentryIgnoredError(
|
||||
msg(str`Device type ${device.verboseName} cannot be deleted`),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@ import { themeImage } from "@goauthentik/elements/utils/images";
|
||||
import "@goauthentik/flow/sources/apple/AppleLoginInit";
|
||||
import "@goauthentik/flow/sources/plex/PlexLoginInit";
|
||||
import "@goauthentik/flow/stages/FlowErrorStage";
|
||||
import "@goauthentik/flow/stages/FlowFrameStage";
|
||||
import "@goauthentik/flow/stages/RedirectStage";
|
||||
import { StageHost, SubmitOptions } from "@goauthentik/flow/stages/base";
|
||||
|
||||
@ -170,6 +171,19 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||
this.addEventListener(EVENT_FLOW_INSPECTOR_TOGGLE, () => {
|
||||
this.inspectorOpen = !this.inspectorOpen;
|
||||
});
|
||||
window.addEventListener("message", (event) => {
|
||||
const msg: {
|
||||
source?: string;
|
||||
context?: string;
|
||||
message: string;
|
||||
} = event.data;
|
||||
if (msg.source !== "goauthentik.io" || msg.context !== "flow-executor") {
|
||||
return;
|
||||
}
|
||||
if (msg.message === "submit") {
|
||||
this.submit({} as FlowChallengeResponseRequest);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async getTheme(): Promise<UiThemeEnum> {
|
||||
@ -429,6 +443,11 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||
</ak-stage-redirect>`;
|
||||
case "xak-flow-shell":
|
||||
return html`${unsafeHTML((this.challenge as ShellChallenge).body)}`;
|
||||
case "xak-flow-frame":
|
||||
return html`<xak-flow-frame
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
></xak-flow-frame>`;
|
||||
default:
|
||||
return html`Invalid native challenge element`;
|
||||
}
|
||||
|
||||
@ -114,7 +114,7 @@ export class InputPassword extends AKElement {
|
||||
this.input.type = "password";
|
||||
this.input.name = this.name;
|
||||
this.input.placeholder = this.placeholder;
|
||||
this.input.autofocus = true;
|
||||
this.input.autofocus = this.grabFocus;
|
||||
this.input.autocomplete = "current-password";
|
||||
this.input.classList.add("pf-c-form-control");
|
||||
this.input.required = true;
|
||||
|
||||
54
web/src/flow/stages/FlowFrameStage.ts
Normal file
54
web/src/flow/stages/FlowFrameStage.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import "@goauthentik/elements/EmptyState";
|
||||
import "@goauthentik/flow/FormStatic";
|
||||
import { BaseStage } from "@goauthentik/flow/stages/base";
|
||||
|
||||
import { CSSResult, TemplateResult, css, html, nothing } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
import { FrameChallenge, FrameChallengeResponseRequest } from "@goauthentik/api";
|
||||
|
||||
@customElement("xak-flow-frame")
|
||||
export class FlowFrameStage extends BaseStage<FrameChallenge, FrameChallengeResponseRequest> {
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFLogin, PFForm, PFFormControl, PFTitle, css``];
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
if (!this.challenge) {
|
||||
return html`<ak-empty-state loading> </ak-empty-state>`;
|
||||
}
|
||||
return html` <header class="pf-c-login__main-header">
|
||||
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
|
||||
</header>
|
||||
<div class="pf-c-login__main-body">
|
||||
${this.challenge.loadingOverlay
|
||||
? html`<ak-empty-state
|
||||
loading
|
||||
header=${this.challenge.loadingText ?? undefined}
|
||||
>
|
||||
</ak-empty-state>`
|
||||
: nothing}
|
||||
<iframe
|
||||
style=${this.challenge.loadingOverlay
|
||||
? "width:0;height:0;position:absolute;"
|
||||
: ""}
|
||||
src=${this.challenge.url}
|
||||
></iframe>
|
||||
</div>
|
||||
<footer class="pf-c-login__main-footer">
|
||||
<ul class="pf-c-login__main-footer-links"></ul>
|
||||
</footer>`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"xak-flow-frame": FlowFrameStage;
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,9 @@
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { SentryIgnoredError } from "@goauthentik/common/errors";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
@ -10,11 +11,11 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
import { AuthenticatorsApi, Device } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-user-mfa-form")
|
||||
export class MFADeviceForm extends ModelForm<Device, number> {
|
||||
export class MFADeviceForm extends ModelForm<Device, string> {
|
||||
@property()
|
||||
deviceType!: string;
|
||||
|
||||
async loadInstance(pk: number): Promise<Device> {
|
||||
async loadInstance(pk: string): Promise<Device> {
|
||||
const devices = await new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsAllList();
|
||||
return devices.filter((device) => {
|
||||
return device.pk === pk && device.type === this.deviceType;
|
||||
@ -29,36 +30,38 @@ export class MFADeviceForm extends ModelForm<Device, number> {
|
||||
switch (this.instance?.type) {
|
||||
case "authentik_stages_authenticator_duo.DuoDevice":
|
||||
await new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsDuoUpdate({
|
||||
id: this.instance?.pk,
|
||||
id: parseInt(this.instance?.pk, 10),
|
||||
duoDeviceRequest: device,
|
||||
});
|
||||
break;
|
||||
case "authentik_stages_authenticator_sms.SMSDevice":
|
||||
await new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsSmsUpdate({
|
||||
id: this.instance?.pk,
|
||||
id: parseInt(this.instance?.pk, 10),
|
||||
sMSDeviceRequest: device,
|
||||
});
|
||||
break;
|
||||
case "authentik_stages_authenticator_totp.TOTPDevice":
|
||||
await new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsTotpUpdate({
|
||||
id: this.instance?.pk,
|
||||
id: parseInt(this.instance?.pk, 10),
|
||||
tOTPDeviceRequest: device,
|
||||
});
|
||||
break;
|
||||
case "authentik_stages_authenticator_static.StaticDevice":
|
||||
await new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsStaticUpdate({
|
||||
id: this.instance?.pk,
|
||||
id: parseInt(this.instance?.pk, 10),
|
||||
staticDeviceRequest: device,
|
||||
});
|
||||
break;
|
||||
case "authentik_stages_authenticator_webauthn.WebAuthnDevice":
|
||||
await new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorsWebauthnUpdate({
|
||||
id: this.instance?.pk,
|
||||
id: parseInt(this.instance?.pk, 10),
|
||||
webAuthnDeviceRequest: device,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
throw new SentryIgnoredError(
|
||||
msg(str`Device type ${device.verboseName} cannot be edited`),
|
||||
);
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { AndNext, DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { SentryIgnoredError } from "@goauthentik/common/errors";
|
||||
import { deviceTypeName } from "@goauthentik/common/labels";
|
||||
import { getRelativeTime } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/elements/buttons/Dropdown";
|
||||
@ -10,7 +11,7 @@ import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/tab
|
||||
import "@goauthentik/user/user-settings/mfa/MFADeviceForm";
|
||||
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { ifDefined } from "lit/directives/if-defined.js";
|
||||
@ -89,7 +90,7 @@ export class MFADevicesPage extends Table<Device> {
|
||||
|
||||
async deleteWrapper(device: Device) {
|
||||
const api = new AuthenticatorsApi(DEFAULT_CONFIG);
|
||||
const id = { id: device.pk };
|
||||
const id = { id: parseInt(device.pk, 10) };
|
||||
switch (device.type) {
|
||||
case "authentik_stages_authenticator_duo.DuoDevice":
|
||||
return api.authenticatorsDuoDestroy(id);
|
||||
@ -102,7 +103,9 @@ export class MFADevicesPage extends Table<Device> {
|
||||
case "authentik_stages_authenticator_webauthn.WebAuthnDevice":
|
||||
return api.authenticatorsWebauthnDestroy(id);
|
||||
default:
|
||||
break;
|
||||
throw new SentryIgnoredError(
|
||||
msg(str`Device type ${device.verboseName} cannot be deleted`),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,78 @@
|
||||
---
|
||||
title: Endpoint Authenticator Google Device Trust Connector Stage
|
||||
---
|
||||
|
||||
<span class="badge badge--primary">Enterprise</span>
|
||||
<span class="badge badge--version">authentik 2024.10+</span>
|
||||
|
||||
---
|
||||
|
||||
With this stage, authentik can validate users' Chrome browsers and ensure that users' devices are compliant and up-to-date.
|
||||
|
||||
:::info
|
||||
This stage only works with Google Chrome, as it relies on the [Chrome Verified Access API](https://developers.google.com/chrome/verified-access).
|
||||
:::
|
||||
|
||||
## Configuration
|
||||
|
||||
The main steps to set up your Google workspace are as follows:
|
||||
|
||||
1. [Create your Google Cloud Project](#create-a-google-cloud-project)
|
||||
2. [Create a service account](#create-a-service-account)
|
||||
3. [Set credentials for the service account](#set-credentials-for-the-service-account)
|
||||
4. [Define access and scope in the Admin Console](#set-credentials-for-the-service-account)
|
||||
|
||||
For detailed instructions, refer to Google documentation.
|
||||
|
||||
### Create a Google cloud project
|
||||
|
||||
1. Open the Google Cloud Console (https://cloud.google.com/cloud-console).
|
||||
2. In upper left, click the drop-down box to open the **Select a project** modal box, and then select **New Project**.
|
||||
3. Create a new project and give it a name like "authentik GWS".
|
||||
4. Use the search bar at the top of your new project page to search for "API Library".
|
||||
5. On the **API Library** page, use the search bar again to find "Chrome Verified Access API".
|
||||
6. On the **Chrome Verified Access API** page, click **Enable**.
|
||||
|
||||
### Create a service account
|
||||
|
||||
1. After the new Chrome Verified Access API is enabled (it might take a few minutes), return to the Google Cloud console home page (click on **Google Cloud** in upper left).
|
||||
2. Use the search bar to find and navigate to the **IAM** page.
|
||||
3. On the **IAM** page, click **Service Accounts** in the left navigation pane.
|
||||
4. At the top of the **Service Accounts** page, click **Create Service Account**.
|
||||
|
||||
- Under **Service account details** page, define the **Name** and **Description** for the new service account, and then click **Create and Continue**.
|
||||
- Under **Grant this service account access to project** you do not need to define a role, so click **Continue**.
|
||||
- Under **Grant users access to project** you do not need to define a role, so click **Done** to complete the creation of the service account.
|
||||
|
||||
### Set credentials for the service account
|
||||
|
||||
1. On the **Service accounts** page, click the account that you just created.
|
||||
2. Click the **Keys** tab at top of the page, the click **Add Key -> Create new key**.
|
||||
3. In the Create modal box, select JSON as the key type, and then click **Create**.
|
||||
A pop-up displays with the private key, and the key is saved to your computer as a JSON file.
|
||||
Later, when you create the stage in authentik, you will add this key in the **Credentials** field.
|
||||
4. On the service account page, click the **Details** tab, and expand the **Advanced settings** area.
|
||||
5. Log in to the Admin Console, and then navigate to **Chrome browser -> Connectors**.
|
||||
6. Click on **New Provider Configuration**.
|
||||
7. Under Okta, click "Set up".
|
||||
8. Enter a name.
|
||||
9. Enter the URL: https://authentik.company/endpoint/gdtc/chrome/
|
||||
10. Under Service accounts, enter the full name of the service account created above, for example `authentik-gdtc-docs@authentik-enterprise-dev.iam.gserviceaccount.com`.
|
||||
|
||||
### Create the stage
|
||||
|
||||
1. Log in as an admin to authentik, and go to the Admin interface.
|
||||
|
||||
2. In the Admin interface, navigate to **Flows -> Stages**.
|
||||
|
||||
3. Click **Create**, and select **Endpoint Authenticator Google Device Trust Connector Stage**, and in the **New stage** modal box, define the following fields:
|
||||
|
||||
- **Name**: define a descriptive name, such as "chrome-device-trust".
|
||||
|
||||
- **Google Verified Access API**
|
||||
|
||||
- **Credentials**: paste the contents of the JSON file (the key) that you downloaded earlier.
|
||||
|
||||
4. Click **Finish**.
|
||||
|
||||
After creating the stage, it can be used in any flow. Compared to other Authenticator stages, this stage does not require enrollment. Instead of adding an [Authenticator Validation Stage](../authenticator_validate/index.md), this stage only verifies the users' browser.
|
||||
@ -282,6 +282,7 @@ export default {
|
||||
},
|
||||
items: [
|
||||
"add-secure-apps/flows-stages/stages/authenticator_duo/index",
|
||||
"add-secure-apps/flows-stages/stages/authenticator_endpoint_gdtc/index",
|
||||
"add-secure-apps/flows-stages/stages/authenticator_sms/index",
|
||||
"add-secure-apps/flows-stages/stages/authenticator_static/index",
|
||||
"add-secure-apps/flows-stages/stages/authenticator_totp/index",
|
||||
|
||||
Reference in New Issue
Block a user