flows: make use of oneOf OpenAPI to annotate all challenge types
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
3b41c662ed
commit
6f6ae7831e
|
@ -2,6 +2,9 @@
|
|||
from importlib import import_module
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.db.utils import ProgrammingError
|
||||
|
||||
from authentik.lib.utils.reflection import all_subclasses
|
||||
|
||||
|
||||
class AuthentikFlowsConfig(AppConfig):
|
||||
|
@ -14,3 +17,10 @@ class AuthentikFlowsConfig(AppConfig):
|
|||
|
||||
def ready(self):
|
||||
import_module("authentik.flows.signals")
|
||||
try:
|
||||
from authentik.flows.models import Stage
|
||||
|
||||
for stage in all_subclasses(Stage):
|
||||
_ = stage().type
|
||||
except ProgrammingError:
|
||||
pass
|
||||
|
|
|
@ -35,9 +35,9 @@ class Challenge(PassiveSerializer):
|
|||
type = ChoiceField(
|
||||
choices=[(x.value, x.name) for x in ChallengeTypes],
|
||||
)
|
||||
component = CharField(required=False)
|
||||
title = CharField(required=False)
|
||||
background = CharField(required=False)
|
||||
component = CharField(default="")
|
||||
|
||||
response_errors = DictField(
|
||||
child=ErrorDetailSerializer(many=True), allow_empty=True, required=False
|
||||
|
@ -48,12 +48,14 @@ class RedirectChallenge(Challenge):
|
|||
"""Challenge type to redirect the client"""
|
||||
|
||||
to = CharField()
|
||||
component = CharField(default="xak-flow-redirect")
|
||||
|
||||
|
||||
class ShellChallenge(Challenge):
|
||||
"""Legacy challenge type to render HTML as-is"""
|
||||
"""challenge type to render HTML as-is"""
|
||||
|
||||
body = CharField()
|
||||
component = CharField(default="xak-flow-shell")
|
||||
|
||||
|
||||
class WithUserInfoChallenge(Challenge):
|
||||
|
@ -67,6 +69,7 @@ class AccessDeniedChallenge(Challenge):
|
|||
"""Challenge when a flow's active stage calls `stage_invalid()`."""
|
||||
|
||||
error_message = CharField(required=False)
|
||||
component = CharField(default="ak-stage-access-denied")
|
||||
|
||||
|
||||
class PermissionSerializer(PassiveSerializer):
|
||||
|
@ -80,6 +83,7 @@ class ChallengeResponse(PassiveSerializer):
|
|||
"""Base class for all challenge responses"""
|
||||
|
||||
stage: Optional["StageView"]
|
||||
component = CharField(default="")
|
||||
|
||||
def __init__(self, instance=None, data=None, **kwargs):
|
||||
self.stage = kwargs.pop("stage", None)
|
||||
|
|
|
@ -11,7 +11,12 @@ from django.utils.decorators import method_decorator
|
|||
from django.views.decorators.clickjacking import xframe_options_sameorigin
|
||||
from django.views.generic import View
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema
|
||||
from drf_spectacular.utils import (
|
||||
OpenApiParameter,
|
||||
OpenApiResponse,
|
||||
PolymorphicProxySerializer,
|
||||
extend_schema,
|
||||
)
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.views import APIView
|
||||
from sentry_sdk import capture_exception
|
||||
|
@ -22,10 +27,12 @@ from authentik.events.models import cleanse_dict
|
|||
from authentik.flows.challenge import (
|
||||
AccessDeniedChallenge,
|
||||
Challenge,
|
||||
ChallengeResponse,
|
||||
ChallengeTypes,
|
||||
HttpChallengeResponse,
|
||||
RedirectChallenge,
|
||||
ShellChallenge,
|
||||
WithUserInfoChallenge,
|
||||
)
|
||||
from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException
|
||||
from authentik.flows.models import ConfigurableStage, Flow, FlowDesignation, Stage
|
||||
|
@ -35,7 +42,7 @@ from authentik.flows.planner import (
|
|||
FlowPlan,
|
||||
FlowPlanner,
|
||||
)
|
||||
from authentik.lib.utils.reflection import class_to_path
|
||||
from authentik.lib.utils.reflection import all_subclasses, class_to_path
|
||||
from authentik.lib.utils.urls import is_url_absolute, redirect_with_qs
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
@ -46,6 +53,43 @@ SESSION_KEY_APPLICATION_PRE = "authentik_flows_application_pre"
|
|||
SESSION_KEY_GET = "authentik_flows_get"
|
||||
|
||||
|
||||
def challenge_types():
|
||||
"""This is a workaround for PolymorphicProxySerializer not accepting a callable for
|
||||
`serializers`. This function returns a class which is an iterator, which returns the
|
||||
subclasses of Challenge, and Challenge itself."""
|
||||
|
||||
class Inner(dict):
|
||||
"""dummy class with custom callback on .items()"""
|
||||
|
||||
def items(self):
|
||||
mapping = {}
|
||||
classes = all_subclasses(Challenge)
|
||||
classes.remove(WithUserInfoChallenge)
|
||||
for cls in classes:
|
||||
mapping[cls().fields["component"].default] = cls
|
||||
return mapping.items()
|
||||
|
||||
return Inner()
|
||||
|
||||
|
||||
def challenge_response_types():
|
||||
"""This is a workaround for PolymorphicProxySerializer not accepting a callable for
|
||||
`serializers`. This function returns a class which is an iterator, which returns the
|
||||
subclasses of Challenge, and Challenge itself."""
|
||||
|
||||
class Inner(dict):
|
||||
"""dummy class with custom callback on .items()"""
|
||||
|
||||
def items(self):
|
||||
mapping = {}
|
||||
classes = all_subclasses(ChallengeResponse)
|
||||
for cls in classes:
|
||||
mapping[cls(stage=None).fields["component"].default] = cls
|
||||
return mapping.items()
|
||||
|
||||
return Inner()
|
||||
|
||||
|
||||
@method_decorator(xframe_options_sameorigin, name="dispatch")
|
||||
class FlowExecutorView(APIView):
|
||||
"""Stage 1 Flow executor, passing requests to Stage Views"""
|
||||
|
@ -126,7 +170,11 @@ class FlowExecutorView(APIView):
|
|||
|
||||
@extend_schema(
|
||||
responses={
|
||||
200: Challenge(),
|
||||
200: PolymorphicProxySerializer(
|
||||
component_name="Challenge",
|
||||
serializers=challenge_types(),
|
||||
resource_type_field_name="component",
|
||||
),
|
||||
404: OpenApiResponse(
|
||||
description="No Token found"
|
||||
), # This error can be raised by the email stage
|
||||
|
@ -159,8 +207,18 @@ class FlowExecutorView(APIView):
|
|||
return to_stage_response(request, FlowErrorResponse(request, exc))
|
||||
|
||||
@extend_schema(
|
||||
responses={200: Challenge()},
|
||||
request=OpenApiTypes.OBJECT,
|
||||
responses={
|
||||
200: PolymorphicProxySerializer(
|
||||
component_name="Challenge",
|
||||
serializers=challenge_types(),
|
||||
resource_type_field_name="component",
|
||||
),
|
||||
},
|
||||
request=PolymorphicProxySerializer(
|
||||
component_name="ChallengeResponse",
|
||||
serializers=challenge_response_types(),
|
||||
resource_type_field_name="component",
|
||||
),
|
||||
parameters=[
|
||||
OpenApiParameter(
|
||||
name="query",
|
||||
|
|
|
@ -34,6 +34,7 @@ class AutosubmitChallenge(Challenge):
|
|||
|
||||
url = CharField()
|
||||
attrs = DictField(child=CharField())
|
||||
component = CharField(default="ak-stage-autosubmit")
|
||||
|
||||
|
||||
# This View doesn't have a URL on purpose, as its called by the FlowExecutor
|
||||
|
|
|
@ -17,6 +17,7 @@ class PlexAuthenticationChallenge(Challenge):
|
|||
|
||||
client_id = CharField()
|
||||
slug = CharField()
|
||||
component = CharField(default="ak-flow-sources-plex")
|
||||
|
||||
|
||||
class PlexSource(Source):
|
||||
|
|
|
@ -25,6 +25,7 @@ class AuthenticatorDuoChallenge(WithUserInfoChallenge):
|
|||
activation_barcode = CharField()
|
||||
activation_code = CharField()
|
||||
stage_uuid = CharField()
|
||||
component = CharField(default="ak-stage-authenticator-duo")
|
||||
|
||||
|
||||
class AuthenticatorDuoStageView(ChallengeStageView):
|
||||
|
@ -42,7 +43,6 @@ class AuthenticatorDuoStageView(ChallengeStageView):
|
|||
return AuthenticatorDuoChallenge(
|
||||
data={
|
||||
"type": ChallengeTypes.NATIVE.value,
|
||||
"component": "ak-stage-authenticator-duo",
|
||||
"activation_barcode": enroll["activation_barcode"],
|
||||
"activation_code": enroll["activation_code"],
|
||||
"stage_uuid": stage.stage_uuid,
|
||||
|
|
|
@ -22,6 +22,7 @@ class AuthenticatorStaticChallenge(WithUserInfoChallenge):
|
|||
"""Static authenticator challenge"""
|
||||
|
||||
codes = ListField(child=CharField())
|
||||
component = CharField(default="ak-stage-authenticator-static")
|
||||
|
||||
|
||||
class AuthenticatorStaticStageView(ChallengeStageView):
|
||||
|
@ -32,7 +33,6 @@ class AuthenticatorStaticStageView(ChallengeStageView):
|
|||
return AuthenticatorStaticChallenge(
|
||||
data={
|
||||
"type": ChallengeTypes.NATIVE.value,
|
||||
"component": "ak-stage-authenticator-static",
|
||||
"codes": [token.token for token in tokens],
|
||||
}
|
||||
)
|
||||
|
|
|
@ -25,6 +25,7 @@ class AuthenticatorTOTPChallenge(WithUserInfoChallenge):
|
|||
"""TOTP Setup challenge"""
|
||||
|
||||
config_url = CharField()
|
||||
component = CharField(default="ak-stage-authenticator-totp")
|
||||
|
||||
|
||||
class AuthenticatorTOTPChallengeResponse(ChallengeResponse):
|
||||
|
@ -33,6 +34,7 @@ class AuthenticatorTOTPChallengeResponse(ChallengeResponse):
|
|||
device: TOTPDevice
|
||||
|
||||
code = IntegerField()
|
||||
component = CharField(default="ak-stage-authenticator-totp")
|
||||
|
||||
def validate_code(self, code: int) -> int:
|
||||
"""Validate totp code"""
|
||||
|
@ -52,7 +54,6 @@ class AuthenticatorTOTPStageView(ChallengeStageView):
|
|||
return AuthenticatorTOTPChallenge(
|
||||
data={
|
||||
"type": ChallengeTypes.NATIVE.value,
|
||||
"component": "ak-stage-authenticator-totp",
|
||||
"config_url": device.config_url,
|
||||
}
|
||||
)
|
||||
|
|
|
@ -30,18 +30,20 @@ LOGGER = get_logger()
|
|||
PER_DEVICE_CLASSES = [DeviceClasses.WEBAUTHN]
|
||||
|
||||
|
||||
class AuthenticatorChallenge(WithUserInfoChallenge):
|
||||
class AuthenticatorValidationChallenge(WithUserInfoChallenge):
|
||||
"""Authenticator challenge"""
|
||||
|
||||
device_challenges = ListField(child=DeviceChallenge())
|
||||
component = CharField(default="ak-stage-authenticator-validate")
|
||||
|
||||
|
||||
class AuthenticatorChallengeResponse(ChallengeResponse):
|
||||
class AuthenticatorValidationChallengeResponse(ChallengeResponse):
|
||||
"""Challenge used for Code-based and WebAuthn authenticators"""
|
||||
|
||||
code = CharField(required=False)
|
||||
webauthn = JSONField(required=False)
|
||||
duo = IntegerField(required=False)
|
||||
component = CharField(default="ak-stage-authenticator-validate")
|
||||
|
||||
def _challenge_allowed(self, classes: list):
|
||||
device_challenges: list[dict] = self.stage.request.session.get(
|
||||
|
@ -83,7 +85,7 @@ class AuthenticatorChallengeResponse(ChallengeResponse):
|
|||
class AuthenticatorValidateStageView(ChallengeStageView):
|
||||
"""Authenticator Validation"""
|
||||
|
||||
response_class = AuthenticatorChallengeResponse
|
||||
response_class = AuthenticatorValidationChallengeResponse
|
||||
|
||||
def get_device_challenges(self) -> list[dict]:
|
||||
"""Get a list of all device challenges applicable for the current stage"""
|
||||
|
@ -144,19 +146,18 @@ class AuthenticatorValidateStageView(ChallengeStageView):
|
|||
return self.executor.stage_ok()
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def get_challenge(self) -> AuthenticatorChallenge:
|
||||
def get_challenge(self) -> AuthenticatorValidationChallenge:
|
||||
challenges = self.request.session["device_challenges"]
|
||||
return AuthenticatorChallenge(
|
||||
return AuthenticatorValidationChallenge(
|
||||
data={
|
||||
"type": ChallengeTypes.NATIVE.value,
|
||||
"component": "ak-stage-authenticator-validate",
|
||||
"device_challenges": challenges,
|
||||
}
|
||||
)
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def challenge_valid(
|
||||
self, challenge: AuthenticatorChallengeResponse
|
||||
self, challenge: AuthenticatorValidationChallengeResponse
|
||||
) -> HttpResponse:
|
||||
# All validation is done by the serializer
|
||||
return self.executor.stage_ok()
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.http.request import QueryDict
|
||||
from rest_framework.fields import JSONField
|
||||
from rest_framework.fields import CharField, JSONField
|
||||
from rest_framework.serializers import ValidationError
|
||||
from structlog.stdlib import get_logger
|
||||
from webauthn.webauthn import (
|
||||
|
@ -41,12 +41,14 @@ class AuthenticatorWebAuthnChallenge(WithUserInfoChallenge):
|
|||
"""WebAuthn Challenge"""
|
||||
|
||||
registration = JSONField()
|
||||
component = CharField(default="ak-stage-authenticator-webauthn")
|
||||
|
||||
|
||||
class AuthenticatorWebAuthnChallengeResponse(ChallengeResponse):
|
||||
"""WebAuthn Challenge response"""
|
||||
|
||||
response = JSONField()
|
||||
component = CharField(default="ak-stage-authenticator-webauthn")
|
||||
|
||||
request: HttpRequest
|
||||
user: User
|
||||
|
@ -134,7 +136,6 @@ class AuthenticatorWebAuthnStageView(ChallengeStageView):
|
|||
return AuthenticatorWebAuthnChallenge(
|
||||
data={
|
||||
"type": ChallengeTypes.NATIVE.value,
|
||||
"component": "ak-stage-authenticator-webauthn",
|
||||
"registration": registration_dict,
|
||||
}
|
||||
)
|
||||
|
|
|
@ -21,12 +21,14 @@ class CaptchaChallenge(WithUserInfoChallenge):
|
|||
"""Site public key"""
|
||||
|
||||
site_key = CharField()
|
||||
component = CharField(default="ak-stage-captcha")
|
||||
|
||||
|
||||
class CaptchaChallengeResponse(ChallengeResponse):
|
||||
"""Validate captcha token"""
|
||||
|
||||
token = CharField()
|
||||
component = CharField(default="ak-stage-captcha")
|
||||
|
||||
def validate_token(self, token: str) -> str:
|
||||
"""Validate captcha token"""
|
||||
|
@ -64,7 +66,6 @@ class CaptchaStageView(ChallengeStageView):
|
|||
return CaptchaChallenge(
|
||||
data={
|
||||
"type": ChallengeTypes.NATIVE.value,
|
||||
"component": "ak-stage-captcha",
|
||||
"site_key": self.executor.current_stage.public_key,
|
||||
}
|
||||
)
|
||||
|
|
|
@ -25,11 +25,14 @@ class ConsentChallenge(WithUserInfoChallenge):
|
|||
|
||||
header_text = CharField()
|
||||
permissions = PermissionSerializer(many=True)
|
||||
component = CharField(default="ak-stage-consent")
|
||||
|
||||
|
||||
class ConsentChallengeResponse(ChallengeResponse):
|
||||
"""Consent challenge response, any valid response request is valid"""
|
||||
|
||||
component = CharField(default="ak-stage-consent")
|
||||
|
||||
|
||||
class ConsentStageView(ChallengeStageView):
|
||||
"""Simple consent checker."""
|
||||
|
@ -40,7 +43,6 @@ class ConsentStageView(ChallengeStageView):
|
|||
challenge = ConsentChallenge(
|
||||
data={
|
||||
"type": ChallengeTypes.NATIVE.value,
|
||||
"component": "ak-stage-consent",
|
||||
}
|
||||
)
|
||||
if PLAN_CONTEXT_CONSENT_TITLE in self.executor.plan.context:
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
"""authentik multi-stage authentication engine"""
|
||||
from django.http.response import HttpResponse
|
||||
from rest_framework.fields import CharField
|
||||
|
||||
from authentik.flows.challenge import Challenge, ChallengeResponse, ChallengeTypes
|
||||
from authentik.flows.stage import ChallengeStageView
|
||||
|
@ -8,10 +9,14 @@ from authentik.flows.stage import ChallengeStageView
|
|||
class DummyChallenge(Challenge):
|
||||
"""Dummy challenge"""
|
||||
|
||||
component = CharField(default="ak-stage-dummy")
|
||||
|
||||
|
||||
class DummyChallengeResponse(ChallengeResponse):
|
||||
"""Dummy challenge response"""
|
||||
|
||||
component = CharField(default="ak-stage-dummy")
|
||||
|
||||
|
||||
class DummyStageView(ChallengeStageView):
|
||||
"""Dummy stage for testing with multiple stages"""
|
||||
|
@ -25,7 +30,6 @@ class DummyStageView(ChallengeStageView):
|
|||
return DummyChallenge(
|
||||
data={
|
||||
"type": ChallengeTypes.NATIVE.value,
|
||||
"component": "ak-stage-dummy",
|
||||
"title": self.executor.current_stage.name,
|
||||
}
|
||||
)
|
||||
|
|
|
@ -8,6 +8,7 @@ from django.urls import reverse
|
|||
from django.utils.http import urlencode
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext as _
|
||||
from rest_framework.fields import CharField
|
||||
from rest_framework.serializers import ValidationError
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
|
@ -28,11 +29,15 @@ PLAN_CONTEXT_EMAIL_SENT = "email_sent"
|
|||
class EmailChallenge(Challenge):
|
||||
"""Email challenge"""
|
||||
|
||||
component = CharField(default="ak-stage-email")
|
||||
|
||||
|
||||
class EmailChallengeResponse(ChallengeResponse):
|
||||
"""Email challenge resposen. No fields. This challenge is
|
||||
always declared invalid to give the user a chance to retry"""
|
||||
|
||||
component = CharField(default="ak-stage-email")
|
||||
|
||||
def validate(self, data):
|
||||
raise ValidationError("")
|
||||
|
||||
|
@ -97,7 +102,6 @@ class EmailStageView(ChallengeStageView):
|
|||
challenge = EmailChallenge(
|
||||
data={
|
||||
"type": ChallengeTypes.NATIVE.value,
|
||||
"component": "ak-stage-email",
|
||||
"title": "Email sent.",
|
||||
}
|
||||
)
|
||||
|
|
|
@ -36,11 +36,15 @@ class IdentificationChallenge(Challenge):
|
|||
primary_action = CharField()
|
||||
sources = UILoginButtonSerializer(many=True, required=False)
|
||||
|
||||
component = CharField(default="ak-stage-identification")
|
||||
|
||||
|
||||
class IdentificationChallengeResponse(ChallengeResponse):
|
||||
"""Identification challenge"""
|
||||
|
||||
uid_field = CharField()
|
||||
component = CharField(default="ak-stage-identification")
|
||||
|
||||
pre_user: Optional[User] = None
|
||||
|
||||
def validate_uid_field(self, value: str) -> str:
|
||||
|
@ -81,7 +85,6 @@ class IdentificationStageView(ChallengeStageView):
|
|||
challenge = IdentificationChallenge(
|
||||
data={
|
||||
"type": ChallengeTypes.NATIVE.value,
|
||||
"component": "ak-stage-identification",
|
||||
"primary_action": _("Log in"),
|
||||
"user_fields": current_stage.user_fields,
|
||||
}
|
||||
|
|
|
@ -63,12 +63,16 @@ class PasswordChallenge(WithUserInfoChallenge):
|
|||
|
||||
recovery_url = CharField(required=False)
|
||||
|
||||
component = CharField(default="ak-stage-password")
|
||||
|
||||
|
||||
class PasswordChallengeResponse(ChallengeResponse):
|
||||
"""Password challenge response"""
|
||||
|
||||
password = CharField()
|
||||
|
||||
component = CharField(default="ak-stage-password")
|
||||
|
||||
|
||||
class PasswordStageView(ChallengeStageView):
|
||||
"""Authentication stage which authenticates against django's AuthBackend"""
|
||||
|
@ -79,7 +83,6 @@ class PasswordStageView(ChallengeStageView):
|
|||
challenge = PasswordChallenge(
|
||||
data={
|
||||
"type": ChallengeTypes.NATIVE.value,
|
||||
"component": "ak-stage-password",
|
||||
}
|
||||
)
|
||||
recovery_flow = Flow.objects.filter(designation=FlowDesignation.RECOVERY)
|
||||
|
|
|
@ -26,7 +26,7 @@ LOGGER = get_logger()
|
|||
PLAN_CONTEXT_PROMPT = "prompt_data"
|
||||
|
||||
|
||||
class PromptSerializer(PassiveSerializer):
|
||||
class StagePromptSerializer(PassiveSerializer):
|
||||
"""Serializer for a single Prompt field"""
|
||||
|
||||
field_key = CharField()
|
||||
|
@ -40,17 +40,22 @@ class PromptSerializer(PassiveSerializer):
|
|||
class PromptChallenge(Challenge):
|
||||
"""Initial challenge being sent, define fields"""
|
||||
|
||||
fields = PromptSerializer(many=True)
|
||||
fields = StagePromptSerializer(many=True)
|
||||
component = CharField(default="ak-stage-prompt")
|
||||
|
||||
|
||||
class PromptResponseChallenge(ChallengeResponse):
|
||||
"""Validate response, fields are dynamically created based
|
||||
on the stage"""
|
||||
|
||||
def __init__(self, *args, stage: PromptStage, plan: FlowPlan, **kwargs):
|
||||
component = CharField(default="ak-stage-prompt")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.stage = stage
|
||||
self.plan = plan
|
||||
self.stage: PromptStage = kwargs.pop("stage", None)
|
||||
self.plan: FlowPlan = kwargs.pop("plan", None)
|
||||
if not self.stage:
|
||||
return
|
||||
# list() is called so we only load the fields once
|
||||
fields = list(self.stage.fields.all())
|
||||
for field in fields:
|
||||
|
@ -159,8 +164,7 @@ class PromptStageView(ChallengeStageView):
|
|||
challenge = PromptChallenge(
|
||||
data={
|
||||
"type": ChallengeTypes.NATIVE.value,
|
||||
"component": "ak-stage-prompt",
|
||||
"fields": [PromptSerializer(field).data for field in fields],
|
||||
"fields": [StagePromptSerializer(field).data for field in fields],
|
||||
},
|
||||
)
|
||||
return challenge
|
||||
|
|
730
schema.yml
730
schema.yml
|
@ -3550,16 +3550,13 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
$ref: '#/components/schemas/ChallengeResponseRequest'
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
$ref: '#/components/schemas/ChallengeResponseRequest'
|
||||
multipart/form-data:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
$ref: '#/components/schemas/ChallengeResponseRequest'
|
||||
security:
|
||||
- authentik: []
|
||||
- cookieAuth: []
|
||||
|
@ -14924,6 +14921,29 @@ paths:
|
|||
$ref: '#/components/schemas/GenericError'
|
||||
components:
|
||||
schemas:
|
||||
AccessDeniedChallenge:
|
||||
type: object
|
||||
description: Challenge when a flow's active stage calls `stage_invalid()`.
|
||||
properties:
|
||||
type:
|
||||
$ref: '#/components/schemas/ChallengeChoices'
|
||||
title:
|
||||
type: string
|
||||
background:
|
||||
type: string
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-access-denied
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ErrorDetail'
|
||||
error_message:
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
ActionEnum:
|
||||
enum:
|
||||
- login
|
||||
|
@ -15138,6 +15158,42 @@ components:
|
|||
If empty, user will not be able to configure this stage.
|
||||
required:
|
||||
- name
|
||||
AuthenticatorDuoChallenge:
|
||||
type: object
|
||||
description: Duo Challenge
|
||||
properties:
|
||||
type:
|
||||
$ref: '#/components/schemas/ChallengeChoices'
|
||||
title:
|
||||
type: string
|
||||
background:
|
||||
type: string
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-authenticator-duo
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ErrorDetail'
|
||||
pending_user:
|
||||
type: string
|
||||
pending_user_avatar:
|
||||
type: string
|
||||
activation_barcode:
|
||||
type: string
|
||||
activation_code:
|
||||
type: string
|
||||
stage_uuid:
|
||||
type: string
|
||||
required:
|
||||
- activation_barcode
|
||||
- activation_code
|
||||
- pending_user
|
||||
- pending_user_avatar
|
||||
- stage_uuid
|
||||
- type
|
||||
AuthenticatorDuoStage:
|
||||
type: object
|
||||
description: AuthenticatorDuoStage Serializer
|
||||
|
@ -15208,6 +15264,38 @@ components:
|
|||
- client_id
|
||||
- client_secret
|
||||
- name
|
||||
AuthenticatorStaticChallenge:
|
||||
type: object
|
||||
description: Static authenticator challenge
|
||||
properties:
|
||||
type:
|
||||
$ref: '#/components/schemas/ChallengeChoices'
|
||||
title:
|
||||
type: string
|
||||
background:
|
||||
type: string
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-authenticator-static
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ErrorDetail'
|
||||
pending_user:
|
||||
type: string
|
||||
pending_user_avatar:
|
||||
type: string
|
||||
codes:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
required:
|
||||
- codes
|
||||
- pending_user
|
||||
- pending_user_avatar
|
||||
- type
|
||||
AuthenticatorStaticStage:
|
||||
type: object
|
||||
description: AuthenticatorStaticStage Serializer
|
||||
|
@ -15270,6 +15358,47 @@ components:
|
|||
minimum: -2147483648
|
||||
required:
|
||||
- name
|
||||
AuthenticatorTOTPChallenge:
|
||||
type: object
|
||||
description: TOTP Setup challenge
|
||||
properties:
|
||||
type:
|
||||
$ref: '#/components/schemas/ChallengeChoices'
|
||||
title:
|
||||
type: string
|
||||
background:
|
||||
type: string
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-authenticator-totp
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ErrorDetail'
|
||||
pending_user:
|
||||
type: string
|
||||
pending_user_avatar:
|
||||
type: string
|
||||
config_url:
|
||||
type: string
|
||||
required:
|
||||
- config_url
|
||||
- pending_user
|
||||
- pending_user_avatar
|
||||
- type
|
||||
AuthenticatorTOTPChallengeResponseRequest:
|
||||
type: object
|
||||
description: TOTP Challenge response, device is set by get_response_instance
|
||||
properties:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-authenticator-totp
|
||||
code:
|
||||
type: integer
|
||||
required:
|
||||
- code
|
||||
AuthenticatorTOTPStage:
|
||||
type: object
|
||||
description: AuthenticatorTOTPStage Serializer
|
||||
|
@ -15406,6 +15535,124 @@ components:
|
|||
is not prompted again.
|
||||
required:
|
||||
- name
|
||||
AuthenticatorValidationChallenge:
|
||||
type: object
|
||||
description: Authenticator challenge
|
||||
properties:
|
||||
type:
|
||||
$ref: '#/components/schemas/ChallengeChoices'
|
||||
title:
|
||||
type: string
|
||||
background:
|
||||
type: string
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-authenticator-validate
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ErrorDetail'
|
||||
pending_user:
|
||||
type: string
|
||||
pending_user_avatar:
|
||||
type: string
|
||||
device_challenges:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/DeviceChallenge'
|
||||
required:
|
||||
- device_challenges
|
||||
- pending_user
|
||||
- pending_user_avatar
|
||||
- type
|
||||
AuthenticatorValidationChallengeResponseRequest:
|
||||
type: object
|
||||
description: Challenge used for Code-based and WebAuthn authenticators
|
||||
properties:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-authenticator-validate
|
||||
code:
|
||||
type: string
|
||||
webauthn:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
duo:
|
||||
type: integer
|
||||
AuthenticatorWebAuthnChallenge:
|
||||
type: object
|
||||
description: WebAuthn Challenge
|
||||
properties:
|
||||
type:
|
||||
$ref: '#/components/schemas/ChallengeChoices'
|
||||
title:
|
||||
type: string
|
||||
background:
|
||||
type: string
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-authenticator-webauthn
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ErrorDetail'
|
||||
pending_user:
|
||||
type: string
|
||||
pending_user_avatar:
|
||||
type: string
|
||||
registration:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
required:
|
||||
- pending_user
|
||||
- pending_user_avatar
|
||||
- registration
|
||||
- type
|
||||
AuthenticatorWebAuthnChallengeResponseRequest:
|
||||
type: object
|
||||
description: WebAuthn Challenge response
|
||||
properties:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-authenticator-webauthn
|
||||
response:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
required:
|
||||
- response
|
||||
AutosubmitChallenge:
|
||||
type: object
|
||||
description: Autosubmit challenge used to send and navigate a POST request
|
||||
properties:
|
||||
type:
|
||||
$ref: '#/components/schemas/ChallengeChoices'
|
||||
title:
|
||||
type: string
|
||||
background:
|
||||
type: string
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-autosubmit
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ErrorDetail'
|
||||
url:
|
||||
type: string
|
||||
attrs:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
required:
|
||||
- attrs
|
||||
- type
|
||||
- url
|
||||
BackendsEnum:
|
||||
enum:
|
||||
- django.contrib.auth.backends.ModelBackend
|
||||
|
@ -15430,6 +15677,47 @@ components:
|
|||
enum:
|
||||
- can_save_media
|
||||
type: string
|
||||
CaptchaChallenge:
|
||||
type: object
|
||||
description: Site public key
|
||||
properties:
|
||||
type:
|
||||
$ref: '#/components/schemas/ChallengeChoices'
|
||||
title:
|
||||
type: string
|
||||
background:
|
||||
type: string
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-captcha
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ErrorDetail'
|
||||
pending_user:
|
||||
type: string
|
||||
pending_user_avatar:
|
||||
type: string
|
||||
site_key:
|
||||
type: string
|
||||
required:
|
||||
- pending_user
|
||||
- pending_user_avatar
|
||||
- site_key
|
||||
- type
|
||||
CaptchaChallengeResponseRequest:
|
||||
type: object
|
||||
description: Validate captcha token
|
||||
properties:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-captcha
|
||||
token:
|
||||
type: string
|
||||
required:
|
||||
- token
|
||||
CaptchaStage:
|
||||
type: object
|
||||
description: CaptchaStage Serializer
|
||||
|
@ -15557,33 +15845,75 @@ components:
|
|||
- certificate_data
|
||||
- name
|
||||
Challenge:
|
||||
type: object
|
||||
description: |-
|
||||
Challenge that gets sent to the client based on which stage
|
||||
is currently active
|
||||
properties:
|
||||
type:
|
||||
$ref: '#/components/schemas/ChallengeChoices'
|
||||
component:
|
||||
type: string
|
||||
title:
|
||||
type: string
|
||||
background:
|
||||
type: string
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ErrorDetail'
|
||||
required:
|
||||
- type
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/AccessDeniedChallenge'
|
||||
- $ref: '#/components/schemas/AuthenticatorDuoChallenge'
|
||||
- $ref: '#/components/schemas/AuthenticatorStaticChallenge'
|
||||
- $ref: '#/components/schemas/AuthenticatorTOTPChallenge'
|
||||
- $ref: '#/components/schemas/AuthenticatorValidationChallenge'
|
||||
- $ref: '#/components/schemas/AuthenticatorWebAuthnChallenge'
|
||||
- $ref: '#/components/schemas/AutosubmitChallenge'
|
||||
- $ref: '#/components/schemas/CaptchaChallenge'
|
||||
- $ref: '#/components/schemas/ConsentChallenge'
|
||||
- $ref: '#/components/schemas/DummyChallenge'
|
||||
- $ref: '#/components/schemas/EmailChallenge'
|
||||
- $ref: '#/components/schemas/IdentificationChallenge'
|
||||
- $ref: '#/components/schemas/PasswordChallenge'
|
||||
- $ref: '#/components/schemas/PlexAuthenticationChallenge'
|
||||
- $ref: '#/components/schemas/PromptChallenge'
|
||||
- $ref: '#/components/schemas/RedirectChallenge'
|
||||
- $ref: '#/components/schemas/ShellChallenge'
|
||||
discriminator:
|
||||
propertyName: component
|
||||
mapping:
|
||||
ak-stage-access-denied: '#/components/schemas/AccessDeniedChallenge'
|
||||
ak-stage-authenticator-duo: '#/components/schemas/AuthenticatorDuoChallenge'
|
||||
ak-stage-authenticator-static: '#/components/schemas/AuthenticatorStaticChallenge'
|
||||
ak-stage-authenticator-totp: '#/components/schemas/AuthenticatorTOTPChallenge'
|
||||
ak-stage-authenticator-validate: '#/components/schemas/AuthenticatorValidationChallenge'
|
||||
ak-stage-authenticator-webauthn: '#/components/schemas/AuthenticatorWebAuthnChallenge'
|
||||
ak-stage-autosubmit: '#/components/schemas/AutosubmitChallenge'
|
||||
ak-stage-captcha: '#/components/schemas/CaptchaChallenge'
|
||||
ak-stage-consent: '#/components/schemas/ConsentChallenge'
|
||||
ak-stage-dummy: '#/components/schemas/DummyChallenge'
|
||||
ak-stage-email: '#/components/schemas/EmailChallenge'
|
||||
ak-stage-identification: '#/components/schemas/IdentificationChallenge'
|
||||
ak-stage-password: '#/components/schemas/PasswordChallenge'
|
||||
ak-flow-sources-plex: '#/components/schemas/PlexAuthenticationChallenge'
|
||||
ak-stage-prompt: '#/components/schemas/PromptChallenge'
|
||||
xak-flow-redirect: '#/components/schemas/RedirectChallenge'
|
||||
xak-flow-shell: '#/components/schemas/ShellChallenge'
|
||||
ChallengeChoices:
|
||||
enum:
|
||||
- native
|
||||
- shell
|
||||
- redirect
|
||||
type: string
|
||||
ChallengeResponseRequest:
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/AuthenticatorTOTPChallengeResponseRequest'
|
||||
- $ref: '#/components/schemas/AuthenticatorValidationChallengeResponseRequest'
|
||||
- $ref: '#/components/schemas/AuthenticatorWebAuthnChallengeResponseRequest'
|
||||
- $ref: '#/components/schemas/CaptchaChallengeResponseRequest'
|
||||
- $ref: '#/components/schemas/ConsentChallengeResponseRequest'
|
||||
- $ref: '#/components/schemas/DummyChallengeResponseRequest'
|
||||
- $ref: '#/components/schemas/EmailChallengeResponseRequest'
|
||||
- $ref: '#/components/schemas/IdentificationChallengeResponseRequest'
|
||||
- $ref: '#/components/schemas/PasswordChallengeResponseRequest'
|
||||
- $ref: '#/components/schemas/PromptResponseChallengeRequest'
|
||||
discriminator:
|
||||
propertyName: component
|
||||
mapping:
|
||||
ak-stage-authenticator-totp: '#/components/schemas/AuthenticatorTOTPChallengeResponseRequest'
|
||||
ak-stage-authenticator-validate: '#/components/schemas/AuthenticatorValidationChallengeResponseRequest'
|
||||
ak-stage-authenticator-webauthn: '#/components/schemas/AuthenticatorWebAuthnChallengeResponseRequest'
|
||||
ak-stage-captcha: '#/components/schemas/CaptchaChallengeResponseRequest'
|
||||
ak-stage-consent: '#/components/schemas/ConsentChallengeResponseRequest'
|
||||
ak-stage-dummy: '#/components/schemas/DummyChallengeResponseRequest'
|
||||
ak-stage-email: '#/components/schemas/EmailChallengeResponseRequest'
|
||||
ak-stage-identification: '#/components/schemas/IdentificationChallengeResponseRequest'
|
||||
ak-stage-password: '#/components/schemas/PasswordChallengeResponseRequest'
|
||||
ak-stage-prompt: '#/components/schemas/PromptResponseChallengeRequest'
|
||||
ClientTypeEnum:
|
||||
enum:
|
||||
- confidential
|
||||
|
@ -15625,6 +15955,48 @@ components:
|
|||
- error_reporting_environment
|
||||
- error_reporting_send_pii
|
||||
- ui_footer_links
|
||||
ConsentChallenge:
|
||||
type: object
|
||||
description: Challenge info for consent screens
|
||||
properties:
|
||||
type:
|
||||
$ref: '#/components/schemas/ChallengeChoices'
|
||||
title:
|
||||
type: string
|
||||
background:
|
||||
type: string
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-consent
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ErrorDetail'
|
||||
pending_user:
|
||||
type: string
|
||||
pending_user_avatar:
|
||||
type: string
|
||||
header_text:
|
||||
type: string
|
||||
permissions:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Permission'
|
||||
required:
|
||||
- header_text
|
||||
- pending_user
|
||||
- pending_user_avatar
|
||||
- permissions
|
||||
- type
|
||||
ConsentChallengeResponseRequest:
|
||||
type: object
|
||||
description: Consent challenge response, any valid response request is valid
|
||||
properties:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-consent
|
||||
ConsentStage:
|
||||
type: object
|
||||
description: ConsentStage Serializer
|
||||
|
@ -15740,6 +16112,21 @@ components:
|
|||
$ref: '#/components/schemas/FlowRequest'
|
||||
required:
|
||||
- name
|
||||
DeviceChallenge:
|
||||
type: object
|
||||
description: Single device challenge
|
||||
properties:
|
||||
device_class:
|
||||
type: string
|
||||
device_uid:
|
||||
type: string
|
||||
challenge:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
required:
|
||||
- challenge
|
||||
- device_class
|
||||
- device_uid
|
||||
DeviceClassesEnum:
|
||||
enum:
|
||||
- static
|
||||
|
@ -15837,6 +16224,34 @@ components:
|
|||
required:
|
||||
- name
|
||||
- url
|
||||
DummyChallenge:
|
||||
type: object
|
||||
description: Dummy challenge
|
||||
properties:
|
||||
type:
|
||||
$ref: '#/components/schemas/ChallengeChoices'
|
||||
title:
|
||||
type: string
|
||||
background:
|
||||
type: string
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-dummy
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ErrorDetail'
|
||||
required:
|
||||
- type
|
||||
DummyChallengeResponseRequest:
|
||||
type: object
|
||||
description: Dummy challenge response
|
||||
properties:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-dummy
|
||||
DummyPolicy:
|
||||
type: object
|
||||
description: Dummy Policy Serializer
|
||||
|
@ -15944,6 +16359,36 @@ components:
|
|||
$ref: '#/components/schemas/FlowRequest'
|
||||
required:
|
||||
- name
|
||||
EmailChallenge:
|
||||
type: object
|
||||
description: Email challenge
|
||||
properties:
|
||||
type:
|
||||
$ref: '#/components/schemas/ChallengeChoices'
|
||||
title:
|
||||
type: string
|
||||
background:
|
||||
type: string
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-email
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ErrorDetail'
|
||||
required:
|
||||
- type
|
||||
EmailChallengeResponseRequest:
|
||||
type: object
|
||||
description: |-
|
||||
Email challenge resposen. No fields. This challenge is
|
||||
always declared invalid to give the user a chance to retry
|
||||
properties:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-email
|
||||
EmailStage:
|
||||
type: object
|
||||
description: EmailStage Serializer
|
||||
|
@ -16640,6 +17085,57 @@ components:
|
|||
minimum: -2147483648
|
||||
required:
|
||||
- ip
|
||||
IdentificationChallenge:
|
||||
type: object
|
||||
description: Identification challenges with all UI elements
|
||||
properties:
|
||||
type:
|
||||
$ref: '#/components/schemas/ChallengeChoices'
|
||||
title:
|
||||
type: string
|
||||
background:
|
||||
type: string
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-identification
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ErrorDetail'
|
||||
user_fields:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
application_pre:
|
||||
type: string
|
||||
enroll_url:
|
||||
type: string
|
||||
recovery_url:
|
||||
type: string
|
||||
primary_action:
|
||||
type: string
|
||||
sources:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/UILoginButton'
|
||||
required:
|
||||
- primary_action
|
||||
- type
|
||||
- user_fields
|
||||
IdentificationChallengeResponseRequest:
|
||||
type: object
|
||||
description: Identification challenge
|
||||
properties:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-identification
|
||||
uid_field:
|
||||
type: string
|
||||
required:
|
||||
- uid_field
|
||||
IdentificationStage:
|
||||
type: object
|
||||
description: IdentificationStage Serializer
|
||||
|
@ -20375,6 +20871,46 @@ components:
|
|||
required:
|
||||
- pagination
|
||||
- results
|
||||
PasswordChallenge:
|
||||
type: object
|
||||
description: Password challenge UI fields
|
||||
properties:
|
||||
type:
|
||||
$ref: '#/components/schemas/ChallengeChoices'
|
||||
title:
|
||||
type: string
|
||||
background:
|
||||
type: string
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-password
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ErrorDetail'
|
||||
pending_user:
|
||||
type: string
|
||||
pending_user_avatar:
|
||||
type: string
|
||||
recovery_url:
|
||||
type: string
|
||||
required:
|
||||
- pending_user
|
||||
- pending_user_avatar
|
||||
- type
|
||||
PasswordChallengeResponseRequest:
|
||||
type: object
|
||||
description: Password challenge response
|
||||
properties:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-password
|
||||
password:
|
||||
type: string
|
||||
required:
|
||||
- password
|
||||
PasswordExpiryPolicy:
|
||||
type: object
|
||||
description: Password Expiry Policy Serializer
|
||||
|
@ -22038,6 +22574,44 @@ components:
|
|||
name:
|
||||
type: string
|
||||
maxLength: 200
|
||||
Permission:
|
||||
type: object
|
||||
description: Permission used for consent
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
id:
|
||||
type: string
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
PlexAuthenticationChallenge:
|
||||
type: object
|
||||
description: Challenge shown to the user in identification stage
|
||||
properties:
|
||||
type:
|
||||
$ref: '#/components/schemas/ChallengeChoices'
|
||||
title:
|
||||
type: string
|
||||
background:
|
||||
type: string
|
||||
component:
|
||||
type: string
|
||||
default: ak-flow-sources-plex
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ErrorDetail'
|
||||
client_id:
|
||||
type: string
|
||||
slug:
|
||||
type: string
|
||||
required:
|
||||
- client_id
|
||||
- slug
|
||||
- type
|
||||
PlexSource:
|
||||
type: object
|
||||
description: Plex Source Serializer
|
||||
|
@ -22359,6 +22933,32 @@ components:
|
|||
- label
|
||||
- pk
|
||||
- type
|
||||
PromptChallenge:
|
||||
type: object
|
||||
description: Initial challenge being sent, define fields
|
||||
properties:
|
||||
type:
|
||||
$ref: '#/components/schemas/ChallengeChoices'
|
||||
title:
|
||||
type: string
|
||||
background:
|
||||
type: string
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-prompt
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ErrorDetail'
|
||||
fields:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/StagePrompt'
|
||||
required:
|
||||
- fields
|
||||
- type
|
||||
PromptRequest:
|
||||
type: object
|
||||
description: Prompt Serializer
|
||||
|
@ -22388,6 +22988,15 @@ components:
|
|||
- field_key
|
||||
- label
|
||||
- type
|
||||
PromptResponseChallengeRequest:
|
||||
type: object
|
||||
description: |-
|
||||
Validate response, fields are dynamically created based
|
||||
on the stage
|
||||
properties:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-prompt
|
||||
PromptStage:
|
||||
type: object
|
||||
description: PromptStage Serializer
|
||||
|
@ -22789,12 +23398,13 @@ components:
|
|||
properties:
|
||||
type:
|
||||
$ref: '#/components/schemas/ChallengeChoices'
|
||||
component:
|
||||
type: string
|
||||
title:
|
||||
type: string
|
||||
background:
|
||||
type: string
|
||||
component:
|
||||
type: string
|
||||
default: xak-flow-redirect
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
|
@ -23462,6 +24072,30 @@ components:
|
|||
- warning
|
||||
- alert
|
||||
type: string
|
||||
ShellChallenge:
|
||||
type: object
|
||||
description: challenge type to render HTML as-is
|
||||
properties:
|
||||
type:
|
||||
$ref: '#/components/schemas/ChallengeChoices'
|
||||
title:
|
||||
type: string
|
||||
background:
|
||||
type: string
|
||||
component:
|
||||
type: string
|
||||
default: xak-flow-shell
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ErrorDetail'
|
||||
body:
|
||||
type: string
|
||||
required:
|
||||
- body
|
||||
- type
|
||||
SignatureAlgorithmEnum:
|
||||
enum:
|
||||
- http://www.w3.org/2000/09/xmldsig#rsa-sha1
|
||||
|
@ -23591,6 +24225,29 @@ components:
|
|||
- pk
|
||||
- verbose_name
|
||||
- verbose_name_plural
|
||||
StagePrompt:
|
||||
type: object
|
||||
description: Serializer for a single Prompt field
|
||||
properties:
|
||||
field_key:
|
||||
type: string
|
||||
label:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
required:
|
||||
type: boolean
|
||||
placeholder:
|
||||
type: string
|
||||
order:
|
||||
type: integer
|
||||
required:
|
||||
- field_key
|
||||
- label
|
||||
- order
|
||||
- placeholder
|
||||
- required
|
||||
- type
|
||||
StageRequest:
|
||||
type: object
|
||||
description: Stage Serializer
|
||||
|
@ -23818,6 +24475,21 @@ components:
|
|||
- description
|
||||
- model_name
|
||||
- name
|
||||
UILoginButton:
|
||||
type: object
|
||||
description: Serializer for Login buttons of sources
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
challenge:
|
||||
type: object
|
||||
additionalProperties: {}
|
||||
icon_url:
|
||||
type: string
|
||||
nullable: true
|
||||
required:
|
||||
- challenge
|
||||
- name
|
||||
User:
|
||||
type: object
|
||||
description: User Serializer
|
||||
|
|
|
@ -8,23 +8,3 @@ export interface Error {
|
|||
export interface ErrorDict {
|
||||
[key: string]: Error[];
|
||||
}
|
||||
|
||||
export interface Challenge {
|
||||
type: ChallengeChoices;
|
||||
component?: string;
|
||||
title?: string;
|
||||
response_errors?: ErrorDict;
|
||||
}
|
||||
|
||||
export interface WithUserInfoChallenge extends Challenge {
|
||||
pending_user: string;
|
||||
pending_user_avatar: string;
|
||||
}
|
||||
|
||||
export interface ShellChallenge extends Challenge {
|
||||
body: string;
|
||||
}
|
||||
|
||||
export interface RedirectChallenge extends Challenge {
|
||||
to: string;
|
||||
}
|
||||
|
|
|
@ -25,29 +25,16 @@ import "./stages/identification/IdentificationStage";
|
|||
import "./stages/password/PasswordStage";
|
||||
import "./stages/prompt/PromptStage";
|
||||
import "./sources/plex/PlexLoginInit";
|
||||
import { ShellChallenge, RedirectChallenge } from "../api/Flows";
|
||||
import { IdentificationChallenge } from "./stages/identification/IdentificationStage";
|
||||
import { PasswordChallenge } from "./stages/password/PasswordStage";
|
||||
import { ConsentChallenge } from "./stages/consent/ConsentStage";
|
||||
import { EmailChallenge } from "./stages/email/EmailStage";
|
||||
import { AutosubmitChallenge } from "./stages/autosubmit/AutosubmitStage";
|
||||
import { PromptChallenge } from "./stages/prompt/PromptStage";
|
||||
import { AuthenticatorTOTPChallenge } from "./stages/authenticator_totp/AuthenticatorTOTPStage";
|
||||
import { AuthenticatorStaticChallenge } from "./stages/authenticator_static/AuthenticatorStaticStage";
|
||||
import { AuthenticatorValidateStageChallenge } from "./stages/authenticator_validate/AuthenticatorValidateStage";
|
||||
import { WebAuthnAuthenticatorRegisterChallenge } from "./stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage";
|
||||
import { CaptchaChallenge } from "./stages/captcha/CaptchaStage";
|
||||
import { StageHost } from "./stages/base";
|
||||
import { Challenge, ChallengeChoices, Config, FlowsApi } from "authentik-api";
|
||||
import { Challenge, ChallengeChoices, Config, FlowsApi, RedirectChallenge, ShellChallenge } from "authentik-api";
|
||||
import { config, DEFAULT_CONFIG } from "../api/Config";
|
||||
import { ifDefined } from "lit-html/directives/if-defined";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { AccessDeniedChallenge } from "./access_denied/FlowAccessDenied";
|
||||
import { PFSize } from "../elements/Spinner";
|
||||
import { TITLE_DEFAULT } from "../constants";
|
||||
import { configureSentry } from "../api/Sentry";
|
||||
import { PlexAuthenticationChallenge } from "./sources/plex/PlexLoginInit";
|
||||
import { AuthenticatorDuoChallenge } from "./stages/authenticator_duo/AuthenticatorDuoStage";
|
||||
import { ChallengeResponseRequest } from "authentik-api/dist/models/ChallengeResponseRequest";
|
||||
|
||||
|
||||
@customElement("ak-flow-executor")
|
||||
export class FlowExecutor extends LitElement implements StageHost {
|
||||
|
@ -112,18 +99,18 @@ export class FlowExecutor extends LitElement implements StageHost {
|
|||
});
|
||||
}
|
||||
|
||||
submit<T>(formData?: T): Promise<void> {
|
||||
submit(payload: ChallengeResponseRequest): Promise<void> {
|
||||
payload.component = this.challenge.component;
|
||||
this.loading = true;
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsExecutorSolveRaw({
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsExecutorSolve({
|
||||
flowSlug: this.flowSlug,
|
||||
requestBody: formData || {},
|
||||
query: window.location.search.substring(1),
|
||||
}).then((challengeRaw) => {
|
||||
return challengeRaw.raw.json();
|
||||
challengeResponseRequest: payload,
|
||||
}).then((data) => {
|
||||
this.challenge = data;
|
||||
this.postUpdate();
|
||||
}).catch((e: Response) => {
|
||||
console.debug(e);
|
||||
this.errorMessage(e.statusText);
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
|
@ -135,19 +122,18 @@ export class FlowExecutor extends LitElement implements StageHost {
|
|||
this.config = config;
|
||||
});
|
||||
this.loading = true;
|
||||
new FlowsApi(DEFAULT_CONFIG).flowsExecutorGetRaw({
|
||||
new FlowsApi(DEFAULT_CONFIG).flowsExecutorGet({
|
||||
flowSlug: this.flowSlug,
|
||||
query: window.location.search.substring(1),
|
||||
}).then((challengeRaw) => {
|
||||
return challengeRaw.raw.json();
|
||||
}).then((challenge) => {
|
||||
this.challenge = challenge as Challenge;
|
||||
this.challenge = challenge;
|
||||
// Only set background on first update, flow won't change throughout execution
|
||||
if (this.challenge?.background) {
|
||||
this.setBackground(this.challenge.background);
|
||||
}
|
||||
this.postUpdate();
|
||||
}).catch((e: Response) => {
|
||||
console.debug(e);
|
||||
// Catch JSON or Update errors
|
||||
this.errorMessage(e.statusText);
|
||||
}).finally(() => {
|
||||
|
@ -202,35 +188,35 @@ export class FlowExecutor extends LitElement implements StageHost {
|
|||
case ChallengeChoices.Native:
|
||||
switch (this.challenge.component) {
|
||||
case "ak-stage-access-denied":
|
||||
return html`<ak-stage-access-denied .host=${this} .challenge=${this.challenge as AccessDeniedChallenge}></ak-stage-access-denied>`;
|
||||
return html`<ak-stage-access-denied .host=${this} .challenge=${this.challenge}></ak-stage-access-denied>`;
|
||||
case "ak-stage-identification":
|
||||
return html`<ak-stage-identification .host=${this} .challenge=${this.challenge as IdentificationChallenge}></ak-stage-identification>`;
|
||||
return html`<ak-stage-identification .host=${this} .challenge=${this.challenge}></ak-stage-identification>`;
|
||||
case "ak-stage-password":
|
||||
return html`<ak-stage-password .host=${this} .challenge=${this.challenge as PasswordChallenge}></ak-stage-password>`;
|
||||
return html`<ak-stage-password .host=${this} .challenge=${this.challenge}></ak-stage-password>`;
|
||||
case "ak-stage-captcha":
|
||||
return html`<ak-stage-captcha .host=${this} .challenge=${this.challenge as CaptchaChallenge}></ak-stage-captcha>`;
|
||||
return html`<ak-stage-captcha .host=${this} .challenge=${this.challenge}></ak-stage-captcha>`;
|
||||
case "ak-stage-consent":
|
||||
return html`<ak-stage-consent .host=${this} .challenge=${this.challenge as ConsentChallenge}></ak-stage-consent>`;
|
||||
return html`<ak-stage-consent .host=${this} .challenge=${this.challenge}></ak-stage-consent>`;
|
||||
case "ak-stage-dummy":
|
||||
return html`<ak-stage-dummy .host=${this} .challenge=${this.challenge as Challenge}></ak-stage-dummy>`;
|
||||
return html`<ak-stage-dummy .host=${this} .challenge=${this.challenge}></ak-stage-dummy>`;
|
||||
case "ak-stage-email":
|
||||
return html`<ak-stage-email .host=${this} .challenge=${this.challenge as EmailChallenge}></ak-stage-email>`;
|
||||
return html`<ak-stage-email .host=${this} .challenge=${this.challenge}></ak-stage-email>`;
|
||||
case "ak-stage-autosubmit":
|
||||
return html`<ak-stage-autosubmit .host=${this} .challenge=${this.challenge as AutosubmitChallenge}></ak-stage-autosubmit>`;
|
||||
return html`<ak-stage-autosubmit .host=${this} .challenge=${this.challenge}></ak-stage-autosubmit>`;
|
||||
case "ak-stage-prompt":
|
||||
return html`<ak-stage-prompt .host=${this} .challenge=${this.challenge as PromptChallenge}></ak-stage-prompt>`;
|
||||
return html`<ak-stage-prompt .host=${this} .challenge=${this.challenge}></ak-stage-prompt>`;
|
||||
case "ak-stage-authenticator-totp":
|
||||
return html`<ak-stage-authenticator-totp .host=${this} .challenge=${this.challenge as AuthenticatorTOTPChallenge}></ak-stage-authenticator-totp>`;
|
||||
return html`<ak-stage-authenticator-totp .host=${this} .challenge=${this.challenge}></ak-stage-authenticator-totp>`;
|
||||
case "ak-stage-authenticator-duo":
|
||||
return html`<ak-stage-authenticator-duo .host=${this} .challenge=${this.challenge as AuthenticatorDuoChallenge}></ak-stage-authenticator-duo>`;
|
||||
return html`<ak-stage-authenticator-duo .host=${this} .challenge=${this.challenge}></ak-stage-authenticator-duo>`;
|
||||
case "ak-stage-authenticator-static":
|
||||
return html`<ak-stage-authenticator-static .host=${this} .challenge=${this.challenge as AuthenticatorStaticChallenge}></ak-stage-authenticator-static>`;
|
||||
return html`<ak-stage-authenticator-static .host=${this} .challenge=${this.challenge}></ak-stage-authenticator-static>`;
|
||||
case "ak-stage-authenticator-webauthn":
|
||||
return html`<ak-stage-authenticator-webauthn .host=${this} .challenge=${this.challenge as WebAuthnAuthenticatorRegisterChallenge}></ak-stage-authenticator-webauthn>`;
|
||||
return html`<ak-stage-authenticator-webauthn .host=${this} .challenge=${this.challenge}></ak-stage-authenticator-webauthn>`;
|
||||
case "ak-stage-authenticator-validate":
|
||||
return html`<ak-stage-authenticator-validate .host=${this} .challenge=${this.challenge as AuthenticatorValidateStageChallenge}></ak-stage-authenticator-validate>`;
|
||||
return html`<ak-stage-authenticator-validate .host=${this} .challenge=${this.challenge}></ak-stage-authenticator-validate>`;
|
||||
case "ak-flow-sources-plex":
|
||||
return html`<ak-flow-sources-plex .host=${this} .challenge=${this.challenge as PlexAuthenticationChallenge}></ak-flow-sources-plex>`;
|
||||
return html`<ak-flow-sources-plex .host=${this} .challenge=${this.challenge}></ak-flow-sources-plex>`;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -288,8 +274,7 @@ export class FlowExecutor extends LitElement implements StageHost {
|
|||
</li>`;
|
||||
}))}
|
||||
${this.config?.brandingTitle != "authentik" ? html`
|
||||
<li><a href="https://goauthentik.io">${t`Powered by authentik`}</a></li>
|
||||
` : html``}
|
||||
<li><a href="https://goauthentik.io">${t`Powered by authentik`}</a></li>` : html``}
|
||||
</ul>
|
||||
</footer>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Challenge } from "authentik-api";
|
||||
import { AccessDeniedChallenge } from "authentik-api";
|
||||
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { BaseStage } from "../stages/base";
|
||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
|
@ -12,10 +12,6 @@ import { t } from "@lingui/macro";
|
|||
|
||||
import "../../elements/EmptyState";
|
||||
|
||||
export interface AccessDeniedChallenge extends Challenge {
|
||||
error_message?: string;
|
||||
}
|
||||
|
||||
@customElement("ak-stage-access-denied")
|
||||
export class FlowAccessDenied extends BaseStage {
|
||||
|
||||
|
@ -45,9 +41,9 @@ export class FlowAccessDenied extends BaseStage {
|
|||
<i class="pf-icon pf-icon-error-circle-o"></i>
|
||||
${t`Request has been denied.`}
|
||||
</p>
|
||||
${this.challenge?.error_message &&
|
||||
${this.challenge?.errorMessage &&
|
||||
html`<hr>
|
||||
<p>${this.challenge.error_message}</p>`}
|
||||
<p>${this.challenge.errorMessage}</p>`}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { t } from "@lingui/macro";
|
||||
import { Challenge } from "authentik-api";
|
||||
import { PlexAuthenticationChallenge } from "authentik-api";
|
||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
|
@ -16,12 +16,6 @@ import { SourcesApi } from "authentik-api";
|
|||
import { showMessage } from "../../../elements/messages/MessageContainer";
|
||||
import { MessageLevel } from "../../../elements/messages/Message";
|
||||
|
||||
export interface PlexAuthenticationChallenge extends Challenge {
|
||||
|
||||
client_id: string;
|
||||
slug: string;
|
||||
|
||||
}
|
||||
|
||||
@customElement("ak-flow-sources-plex")
|
||||
export class PlexLoginInit extends BaseStage {
|
||||
|
@ -34,9 +28,9 @@ export class PlexLoginInit extends BaseStage {
|
|||
}
|
||||
|
||||
async firstUpdated(): Promise<void> {
|
||||
const authInfo = await PlexAPIClient.getPin(this.challenge?.client_id || "");
|
||||
const authInfo = await PlexAPIClient.getPin(this.challenge?.clientId || "");
|
||||
const authWindow = popupCenterScreen(authInfo.authUrl, "plex auth", 550, 700);
|
||||
PlexAPIClient.pinPoll(this.challenge?.client_id || "", authInfo.pin.id).then(token => {
|
||||
PlexAPIClient.pinPoll(this.challenge?.clientId || "", authInfo.pin.id).then(token => {
|
||||
authWindow?.close();
|
||||
new SourcesApi(DEFAULT_CONFIG).sourcesPlexRedeemTokenCreate({
|
||||
plexTokenRedeemRequest: {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { t } from "@lingui/macro";
|
||||
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { WithUserInfoChallenge } from "../../../api/Flows";
|
||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
|
@ -13,15 +12,9 @@ import "../../../elements/forms/FormElement";
|
|||
import "../../../elements/EmptyState";
|
||||
import "../../FormStatic";
|
||||
import { FlowURLManager } from "../../../api/legacy";
|
||||
import { StagesApi } from "authentik-api";
|
||||
import { AuthenticatorDuoChallenge, StagesApi } from "authentik-api";
|
||||
import { DEFAULT_CONFIG } from "../../../api/Config";
|
||||
|
||||
export interface AuthenticatorDuoChallenge extends WithUserInfoChallenge {
|
||||
activation_barcode: string;
|
||||
activation_code: string;
|
||||
stage_uuid: string;
|
||||
}
|
||||
|
||||
@customElement("ak-stage-authenticator-duo")
|
||||
export class AuthenticatorDuoStage extends BaseStage {
|
||||
|
||||
|
@ -42,10 +35,11 @@ export class AuthenticatorDuoStage extends BaseStage {
|
|||
|
||||
checkEnrollStatus(): Promise<void> {
|
||||
return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorDuoEnrollmentStatusCreate({
|
||||
stageUuid: this.challenge?.stage_uuid || "",
|
||||
}).then(r => {
|
||||
stageUuid: this.challenge?.stageUuid || "",
|
||||
}).then(() => {
|
||||
this.host?.submit({});
|
||||
}).catch(e => {
|
||||
}).catch(() => {
|
||||
console.debug("authentik/flows/duo: Waiting for auth status");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -65,17 +59,17 @@ export class AuthenticatorDuoStage extends BaseStage {
|
|||
<form class="pf-c-form" @submit=${(e: Event) => { this.submitForm(e); }}>
|
||||
<ak-form-static
|
||||
class="pf-c-form__group"
|
||||
userAvatar="${this.challenge.pending_user_avatar}"
|
||||
user=${this.challenge.pending_user}>
|
||||
userAvatar="${this.challenge.pendingUserAvatar}"
|
||||
user=${this.challenge.pendingUser}>
|
||||
<div slot="link">
|
||||
<a href="${FlowURLManager.cancel()}">${t`Not you?`}</a>
|
||||
</div>
|
||||
</ak-form-static>
|
||||
<img src=${this.challenge.activation_barcode} />
|
||||
<img src=${this.challenge.activationBarcode} />
|
||||
<p>
|
||||
${t`Alternatively, if your current device has Duo installed, click on this link:`}
|
||||
</p>
|
||||
<a href=${this.challenge.activation_code}>${t`Duo activation`}</a>
|
||||
<a href=${this.challenge.activationCode}>${t`Duo activation`}</a>
|
||||
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
<button type="button" class="pf-c-button pf-m-primary pf-m-block" @click=${() => {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { t } from "@lingui/macro";
|
||||
import { css, CSSResult, customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { WithUserInfoChallenge } from "../../../api/Flows";
|
||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
|
@ -13,6 +12,7 @@ import "../../../elements/forms/FormElement";
|
|||
import "../../../elements/EmptyState";
|
||||
import "../../FormStatic";
|
||||
import { FlowURLManager } from "../../../api/legacy";
|
||||
import { AuthenticatorStaticChallenge } from "authentik-api";
|
||||
|
||||
export const STATIC_TOKEN_STYLE = css`
|
||||
/* Static OTP Tokens */
|
||||
|
@ -29,9 +29,6 @@ export const STATIC_TOKEN_STYLE = css`
|
|||
}
|
||||
`;
|
||||
|
||||
export interface AuthenticatorStaticChallenge extends WithUserInfoChallenge {
|
||||
codes: number[];
|
||||
}
|
||||
|
||||
@customElement("ak-stage-authenticator-static")
|
||||
export class AuthenticatorStaticStage extends BaseStage {
|
||||
|
@ -59,8 +56,8 @@ export class AuthenticatorStaticStage extends BaseStage {
|
|||
<form class="pf-c-form" @submit=${(e: Event) => { this.submitForm(e); }}>
|
||||
<ak-form-static
|
||||
class="pf-c-form__group"
|
||||
userAvatar="${this.challenge.pending_user_avatar}"
|
||||
user=${this.challenge.pending_user}>
|
||||
userAvatar="${this.challenge.pendingUserAvatar}"
|
||||
user=${this.challenge.pendingUser}>
|
||||
<div slot="link">
|
||||
<a href="${FlowURLManager.cancel()}">${t`Not you?`}</a>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { t } from "@lingui/macro";
|
||||
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { WithUserInfoChallenge } from "../../../api/Flows";
|
||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
|
@ -16,10 +15,8 @@ import "../../../elements/EmptyState";
|
|||
import "../../FormStatic";
|
||||
import { MessageLevel } from "../../../elements/messages/Message";
|
||||
import { FlowURLManager } from "../../../api/legacy";
|
||||
import { AuthenticatorTOTPChallenge } from "authentik-api";
|
||||
|
||||
export interface AuthenticatorTOTPChallenge extends WithUserInfoChallenge {
|
||||
config_url: string;
|
||||
}
|
||||
|
||||
@customElement("ak-stage-authenticator-totp")
|
||||
export class AuthenticatorTOTPStage extends BaseStage {
|
||||
|
@ -47,20 +44,20 @@ export class AuthenticatorTOTPStage extends BaseStage {
|
|||
<form class="pf-c-form" @submit=${(e: Event) => { this.submitForm(e); }}>
|
||||
<ak-form-static
|
||||
class="pf-c-form__group"
|
||||
userAvatar="${this.challenge.pending_user_avatar}"
|
||||
user=${this.challenge.pending_user}>
|
||||
userAvatar="${this.challenge.pendingUserAvatar}"
|
||||
user=${this.challenge.pendingUser}>
|
||||
<div slot="link">
|
||||
<a href="${FlowURLManager.cancel()}">${t`Not you?`}</a>
|
||||
</div>
|
||||
</ak-form-static>
|
||||
<input type="hidden" name="otp_uri" value=${this.challenge.config_url} />
|
||||
<input type="hidden" name="otp_uri" value=${this.challenge.configUrl} />
|
||||
<ak-form-element>
|
||||
<!-- @ts-ignore -->
|
||||
<qr-code data="${this.challenge.config_url}"></qr-code>
|
||||
<qr-code data="${this.challenge.configUrl}"></qr-code>
|
||||
<button type="button" class="pf-c-button pf-m-secondary pf-m-progress pf-m-in-progress" @click=${(e: Event) => {
|
||||
e.preventDefault();
|
||||
if (!this.challenge?.config_url) return;
|
||||
navigator.clipboard.writeText(this.challenge?.config_url).then(() => {
|
||||
if (!this.challenge?.configUrl) return;
|
||||
navigator.clipboard.writeText(this.challenge?.configUrl).then(() => {
|
||||
showMessage({
|
||||
level: MessageLevel.success,
|
||||
message: t`Successfully copied TOTP Config.`
|
||||
|
@ -75,7 +72,7 @@ export class AuthenticatorTOTPStage extends BaseStage {
|
|||
label="${t`Code`}"
|
||||
?required="${true}"
|
||||
class="pf-c-form__group"
|
||||
.errors=${(this.challenge?.response_errors || {})["code"]}>
|
||||
.errors=${(this.challenge?.responseErrors || {})["code"]}>
|
||||
<!-- @ts-ignore -->
|
||||
<input type="text"
|
||||
name="code"
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { t } from "@lingui/macro";
|
||||
import { css, CSSResult, customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { WithUserInfoChallenge } from "../../../api/Flows";
|
||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
|
@ -13,6 +12,9 @@ import "./AuthenticatorValidateStageWebAuthn";
|
|||
import "./AuthenticatorValidateStageCode";
|
||||
import "./AuthenticatorValidateStageDuo";
|
||||
import { PasswordManagerPrefill } from "../identification/IdentificationStage";
|
||||
import { DeviceChallenge } from "authentik-api";
|
||||
import { AuthenticatorValidationChallenge } from "authentik-api/dist/models/AuthenticatorValidationChallenge";
|
||||
import { ChallengeResponseRequest } from "authentik-api/dist/models/ChallengeResponseRequest";
|
||||
|
||||
export enum DeviceClasses {
|
||||
STATIC = "static",
|
||||
|
@ -21,33 +23,17 @@ export enum DeviceClasses {
|
|||
DUO = "duo",
|
||||
}
|
||||
|
||||
export interface DeviceChallenge {
|
||||
device_class: DeviceClasses;
|
||||
device_uid: string;
|
||||
challenge: unknown;
|
||||
}
|
||||
|
||||
export interface AuthenticatorValidateStageChallenge extends WithUserInfoChallenge {
|
||||
device_challenges: DeviceChallenge[];
|
||||
}
|
||||
|
||||
export interface AuthenticatorValidateStageChallengeResponse {
|
||||
code?: string;
|
||||
webauthn?: string;
|
||||
duo?: number;
|
||||
}
|
||||
|
||||
@customElement("ak-stage-authenticator-validate")
|
||||
export class AuthenticatorValidateStage extends BaseStage implements StageHost {
|
||||
|
||||
@property({ attribute: false })
|
||||
challenge?: AuthenticatorValidateStageChallenge;
|
||||
challenge?: AuthenticatorValidationChallenge;
|
||||
|
||||
@property({attribute: false})
|
||||
selectedDeviceChallenge?: DeviceChallenge;
|
||||
|
||||
submit<T>(formData?: T): Promise<void> {
|
||||
return this.host?.submit<T>(formData) || Promise.resolve();
|
||||
submit(payload: ChallengeResponseRequest): Promise<void> {
|
||||
return this.host?.submit(payload) || Promise.resolve();
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
|
@ -79,7 +65,7 @@ export class AuthenticatorValidateStage extends BaseStage implements StageHost {
|
|||
}
|
||||
|
||||
renderDevicePickerSingle(deviceChallenge: DeviceChallenge): TemplateResult {
|
||||
switch (deviceChallenge.device_class) {
|
||||
switch (deviceChallenge.deviceClass) {
|
||||
case DeviceClasses.DUO:
|
||||
return html`<i class="fas fa-mobile-alt"></i>
|
||||
<div class="right">
|
||||
|
@ -124,7 +110,7 @@ export class AuthenticatorValidateStage extends BaseStage implements StageHost {
|
|||
renderDevicePicker(): TemplateResult {
|
||||
return html`
|
||||
<ul>
|
||||
${this.challenge?.device_challenges.map((challenges) => {
|
||||
${this.challenge?.deviceChallenges.map((challenges) => {
|
||||
return html`<li>
|
||||
<button class="pf-c-button authenticator-button" type="button" @click=${() => {
|
||||
this.selectedDeviceChallenge = challenges;
|
||||
|
@ -140,30 +126,31 @@ export class AuthenticatorValidateStage extends BaseStage implements StageHost {
|
|||
if (!this.selectedDeviceChallenge) {
|
||||
return html``;
|
||||
}
|
||||
switch (this.selectedDeviceChallenge?.device_class) {
|
||||
switch (this.selectedDeviceChallenge?.deviceClass) {
|
||||
case DeviceClasses.STATIC:
|
||||
case DeviceClasses.TOTP:
|
||||
return html`<ak-stage-authenticator-validate-code
|
||||
.host=${this}
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
.deviceChallenge=${this.selectedDeviceChallenge}
|
||||
.showBackButton=${(this.challenge?.device_challenges.length || []) > 1}>
|
||||
.showBackButton=${(this.challenge?.deviceChallenges.length || []) > 1}>
|
||||
</ak-stage-authenticator-validate-code>`;
|
||||
case DeviceClasses.WEBAUTHN:
|
||||
return html`<ak-stage-authenticator-validate-webauthn
|
||||
.host=${this}
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
.deviceChallenge=${this.selectedDeviceChallenge}
|
||||
.showBackButton=${(this.challenge?.device_challenges.length || []) > 1}>
|
||||
.showBackButton=${(this.challenge?.deviceChallenges.length || []) > 1}>
|
||||
</ak-stage-authenticator-validate-webauthn>`;
|
||||
case DeviceClasses.DUO:
|
||||
return html`<ak-stage-authenticator-validate-duo
|
||||
.host=${this}
|
||||
.host=${this as StageHost}
|
||||
.challenge=${this.challenge}
|
||||
.deviceChallenge=${this.selectedDeviceChallenge}
|
||||
.showBackButton=${(this.challenge?.device_challenges.length || []) > 1}>
|
||||
.showBackButton=${(this.challenge?.deviceChallenges.length || []) > 1}>
|
||||
</ak-stage-authenticator-validate-duo>`;
|
||||
}
|
||||
return html``;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
|
@ -174,8 +161,8 @@ export class AuthenticatorValidateStage extends BaseStage implements StageHost {
|
|||
</ak-empty-state>`;
|
||||
}
|
||||
// User only has a single device class, so we don't show a picker
|
||||
if (this.challenge?.device_challenges.length === 1) {
|
||||
this.selectedDeviceChallenge = this.challenge.device_challenges[0];
|
||||
if (this.challenge?.deviceChallenges.length === 1) {
|
||||
this.selectedDeviceChallenge = this.challenge.deviceChallenges[0];
|
||||
}
|
||||
return html`<header class="pf-c-login__main-header">
|
||||
<h1 class="pf-c-title pf-m-3xl">
|
||||
|
|
|
@ -8,18 +8,20 @@ import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
|||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
import AKGlobal from "../../../authentik.css";
|
||||
import { BaseStage } from "../base";
|
||||
import { AuthenticatorValidateStage, AuthenticatorValidateStageChallenge, DeviceChallenge } from "./AuthenticatorValidateStage";
|
||||
import { AuthenticatorValidateStage } from "./AuthenticatorValidateStage";
|
||||
import "../../../elements/forms/FormElement";
|
||||
import "../../../elements/EmptyState";
|
||||
import { PasswordManagerPrefill } from "../identification/IdentificationStage";
|
||||
import "../../FormStatic";
|
||||
import { FlowURLManager } from "../../../api/legacy";
|
||||
import { AuthenticatorValidationChallenge } from "authentik-api/dist/models/AuthenticatorValidationChallenge";
|
||||
import { DeviceChallenge } from "authentik-api";
|
||||
|
||||
@customElement("ak-stage-authenticator-validate-code")
|
||||
export class AuthenticatorValidateStageWebCode extends BaseStage {
|
||||
|
||||
@property({ attribute: false })
|
||||
challenge?: AuthenticatorValidateStageChallenge;
|
||||
challenge?: AuthenticatorValidationChallenge;
|
||||
|
||||
@property({ attribute: false })
|
||||
deviceChallenge?: DeviceChallenge;
|
||||
|
@ -42,8 +44,8 @@ export class AuthenticatorValidateStageWebCode extends BaseStage {
|
|||
<form class="pf-c-form" @submit=${(e: Event) => { this.submitForm(e); }}>
|
||||
<ak-form-static
|
||||
class="pf-c-form__group"
|
||||
userAvatar="${this.challenge.pending_user_avatar}"
|
||||
user=${this.challenge.pending_user}>
|
||||
userAvatar="${this.challenge.pendingUserAvatar}"
|
||||
user=${this.challenge.pendingUser}>
|
||||
<div slot="link">
|
||||
<a href="${FlowURLManager.cancel()}">${t`Not you?`}</a>
|
||||
</div>
|
||||
|
@ -52,7 +54,7 @@ export class AuthenticatorValidateStageWebCode extends BaseStage {
|
|||
label="${t`Code`}"
|
||||
?required="${true}"
|
||||
class="pf-c-form__group"
|
||||
.errors=${(this.challenge?.response_errors || {})["code"]}>
|
||||
.errors=${(this.challenge?.responseErrors || {})["code"]}>
|
||||
<!-- @ts-ignore -->
|
||||
<input type="text"
|
||||
name="code"
|
||||
|
|
|
@ -8,18 +8,19 @@ import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
|||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
import AKGlobal from "../../../authentik.css";
|
||||
import { BaseStage } from "../base";
|
||||
import { AuthenticatorValidateStage, AuthenticatorValidateStageChallenge, DeviceChallenge } from "./AuthenticatorValidateStage";
|
||||
import { AuthenticatorValidateStage } from "./AuthenticatorValidateStage";
|
||||
import "../../../elements/forms/FormElement";
|
||||
import "../../../elements/EmptyState";
|
||||
import { PasswordManagerPrefill } from "../identification/IdentificationStage";
|
||||
import "../../FormStatic";
|
||||
import { FlowURLManager } from "../../../api/legacy";
|
||||
import { AuthenticatorValidationChallenge } from "authentik-api/dist/models/AuthenticatorValidationChallenge";
|
||||
import { DeviceChallenge } from "authentik-api";
|
||||
|
||||
@customElement("ak-stage-authenticator-validate-duo")
|
||||
export class AuthenticatorValidateStageWebDuo extends BaseStage {
|
||||
|
||||
@property({ attribute: false })
|
||||
challenge?: AuthenticatorValidateStageChallenge;
|
||||
challenge?: AuthenticatorValidationChallenge;
|
||||
|
||||
@property({ attribute: false })
|
||||
deviceChallenge?: DeviceChallenge;
|
||||
|
@ -33,7 +34,7 @@ export class AuthenticatorValidateStageWebDuo extends BaseStage {
|
|||
|
||||
firstUpdated(): void {
|
||||
this.host?.submit({
|
||||
"duo": this.deviceChallenge?.device_uid
|
||||
"duo": this.deviceChallenge?.deviceUid
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -48,8 +49,8 @@ export class AuthenticatorValidateStageWebDuo extends BaseStage {
|
|||
<form class="pf-c-form" @submit=${(e: Event) => { this.submitForm(e); }}>
|
||||
<ak-form-static
|
||||
class="pf-c-form__group"
|
||||
userAvatar="${this.challenge.pending_user_avatar}"
|
||||
user=${this.challenge.pending_user}>
|
||||
userAvatar="${this.challenge.pendingUserAvatar}"
|
||||
user=${this.challenge.pendingUser}>
|
||||
<div slot="link">
|
||||
<a href="${FlowURLManager.cancel()}">${t`Not you?`}</a>
|
||||
</div>
|
||||
|
|
|
@ -10,13 +10,15 @@ import AKGlobal from "../../../authentik.css";
|
|||
import { PFSize } from "../../../elements/Spinner";
|
||||
import { transformAssertionForServer, transformCredentialRequestOptions } from "../authenticator_webauthn/utils";
|
||||
import { BaseStage } from "../base";
|
||||
import { AuthenticatorValidateStage, AuthenticatorValidateStageChallenge, DeviceChallenge } from "./AuthenticatorValidateStage";
|
||||
import { AuthenticatorValidateStage } from "./AuthenticatorValidateStage";
|
||||
import { AuthenticatorValidationChallenge } from "authentik-api/dist/models/AuthenticatorValidationChallenge";
|
||||
import { DeviceChallenge } from "authentik-api";
|
||||
|
||||
@customElement("ak-stage-authenticator-validate-webauthn")
|
||||
export class AuthenticatorValidateStageWebAuthn extends BaseStage {
|
||||
|
||||
@property({attribute: false})
|
||||
challenge?: AuthenticatorValidateStageChallenge;
|
||||
challenge?: AuthenticatorValidationChallenge;
|
||||
|
||||
@property({attribute: false})
|
||||
deviceChallenge?: DeviceChallenge;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { t } from "@lingui/macro";
|
||||
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { WithUserInfoChallenge } from "../../../api/Flows";
|
||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
|
@ -11,10 +10,7 @@ import AKGlobal from "../../../authentik.css";
|
|||
import { PFSize } from "../../../elements/Spinner";
|
||||
import { BaseStage } from "../base";
|
||||
import { Assertion, transformCredentialCreateOptions, transformNewAssertionForServer } from "./utils";
|
||||
|
||||
export interface WebAuthnAuthenticatorRegisterChallenge extends WithUserInfoChallenge {
|
||||
registration: PublicKeyCredentialCreationOptions;
|
||||
}
|
||||
import { AuthenticatorWebAuthnChallenge } from "authentik-api";
|
||||
|
||||
export interface WebAuthnAuthenticatorRegisterChallengeResponse {
|
||||
response: Assertion;
|
||||
|
@ -24,7 +20,7 @@ export interface WebAuthnAuthenticatorRegisterChallengeResponse {
|
|||
export class WebAuthnAuthenticatorRegisterStage extends BaseStage {
|
||||
|
||||
@property({ attribute: false })
|
||||
challenge?: WebAuthnAuthenticatorRegisterChallenge;
|
||||
challenge?: AuthenticatorWebAuthnChallenge;
|
||||
|
||||
@property({type: Boolean})
|
||||
registerRunning = false;
|
||||
|
@ -42,7 +38,7 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage {
|
|||
}
|
||||
// convert certain members of the PublicKeyCredentialCreateOptions into
|
||||
// byte arrays as expected by the spec.
|
||||
const publicKeyCredentialCreateOptions = transformCredentialCreateOptions(this.challenge?.registration);
|
||||
const publicKeyCredentialCreateOptions = transformCredentialCreateOptions(this.challenge?.registration as PublicKeyCredentialCreationOptions);
|
||||
|
||||
// request the authenticator(s) to create a new credential keypair.
|
||||
let credential;
|
||||
|
@ -106,8 +102,8 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage {
|
|||
</div>`:
|
||||
html`
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
${this.challenge?.response_errors ?
|
||||
html`<p class="pf-m-block">${this.challenge.response_errors["response"][0].string}</p>`:
|
||||
${this.challenge?.responseErrors ?
|
||||
html`<p class="pf-m-block">${this.challenge.responseErrors["response"][0].string}</p>`:
|
||||
html``}
|
||||
<p class="pf-m-block">${this.registerMessage}</p>
|
||||
<button class="pf-c-button pf-m-primary pf-m-block" @click=${() => {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { t } from "@lingui/macro";
|
||||
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { WithUserInfoChallenge } from "../../../api/Flows";
|
||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
|
@ -10,11 +9,7 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
|||
import AKGlobal from "../../../authentik.css";
|
||||
import { BaseStage } from "../base";
|
||||
import "../../../elements/EmptyState";
|
||||
|
||||
export interface AutosubmitChallenge extends WithUserInfoChallenge {
|
||||
url: string;
|
||||
attrs: { [key: string]: string };
|
||||
}
|
||||
import { AutosubmitChallenge } from "authentik-api";
|
||||
|
||||
@customElement("ak-stage-autosubmit")
|
||||
export class AutosubmitStage extends BaseStage {
|
||||
|
|
|
@ -1,20 +1,25 @@
|
|||
import { Challenge } from "authentik-api";
|
||||
import { ChallengeResponseRequest } from "authentik-api/dist/models/ChallengeResponseRequest";
|
||||
import { LitElement } from "lit-element";
|
||||
|
||||
export interface StageHost {
|
||||
challenge?: Challenge;
|
||||
submit<T>(formData?: T): Promise<void>;
|
||||
submit(payload: ChallengeResponseRequest): Promise<void>;
|
||||
}
|
||||
|
||||
export class BaseStage extends LitElement {
|
||||
|
||||
host?: StageHost;
|
||||
challenge?: Challenge;
|
||||
|
||||
submitForm(e: Event): void {
|
||||
e.preventDefault();
|
||||
const object: {
|
||||
component: string;
|
||||
[key: string]: unknown;
|
||||
} = {};
|
||||
} = {
|
||||
component: this.challenge.component,
|
||||
};
|
||||
const form = new FormData(this.shadowRoot?.querySelector("form") || undefined);
|
||||
form.forEach((value, key) => object[key] = value);
|
||||
this.host?.submit(object);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { t } from "@lingui/macro";
|
||||
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { WithUserInfoChallenge } from "../../../api/Flows";
|
||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
|
@ -14,10 +13,7 @@ import "../../../elements/forms/FormElement";
|
|||
import "../../../elements/EmptyState";
|
||||
import "../../FormStatic";
|
||||
import { FlowURLManager } from "../../../api/legacy";
|
||||
|
||||
export interface CaptchaChallenge extends WithUserInfoChallenge {
|
||||
site_key: string;
|
||||
}
|
||||
import { CaptchaChallenge } from "authentik-api";
|
||||
|
||||
@customElement("ak-stage-captcha")
|
||||
export class CaptchaStage extends BaseStage {
|
||||
|
@ -39,10 +35,10 @@ export class CaptchaStage extends BaseStage {
|
|||
script.onload = () => {
|
||||
console.debug("authentik/stages/captcha: script loaded");
|
||||
grecaptcha.ready(() => {
|
||||
if (!this.challenge?.site_key) return;
|
||||
if (!this.challenge?.siteKey) return;
|
||||
console.debug("authentik/stages/captcha: ready");
|
||||
const captchaId = grecaptcha.render(captchaContainer, {
|
||||
sitekey: this.challenge.site_key,
|
||||
sitekey: this.challenge.siteKey,
|
||||
callback: (token) => {
|
||||
this.host?.submit({
|
||||
"token": token,
|
||||
|
@ -72,8 +68,8 @@ export class CaptchaStage extends BaseStage {
|
|||
<form class="pf-c-form">
|
||||
<ak-form-static
|
||||
class="pf-c-form__group"
|
||||
userAvatar="${this.challenge.pending_user_avatar}"
|
||||
user=${this.challenge.pending_user}>
|
||||
userAvatar="${this.challenge.pendingUserAvatar}"
|
||||
user=${this.challenge.pendingUser}>
|
||||
<div slot="link">
|
||||
<a href="${FlowURLManager.cancel()}">${t`Not you?`}</a>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { t } from "@lingui/macro";
|
||||
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { WithUserInfoChallenge } from "../../../api/Flows";
|
||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
|
@ -12,18 +11,8 @@ import { BaseStage } from "../base";
|
|||
import "../../../elements/EmptyState";
|
||||
import "../../FormStatic";
|
||||
import { FlowURLManager } from "../../../api/legacy";
|
||||
import { ConsentChallenge } from "authentik-api";
|
||||
|
||||
export interface Permission {
|
||||
name: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface ConsentChallenge extends WithUserInfoChallenge {
|
||||
|
||||
header_text: string;
|
||||
permissions?: Permission[];
|
||||
|
||||
}
|
||||
|
||||
@customElement("ak-stage-consent")
|
||||
export class ConsentStage extends BaseStage {
|
||||
|
@ -51,15 +40,15 @@ export class ConsentStage extends BaseStage {
|
|||
<form class="pf-c-form" @submit=${(e: Event) => { this.submitForm(e); }}>
|
||||
<ak-form-static
|
||||
class="pf-c-form__group"
|
||||
userAvatar="${this.challenge.pending_user_avatar}"
|
||||
user=${this.challenge.pending_user}>
|
||||
userAvatar="${this.challenge.pendingUserAvatar}"
|
||||
user=${this.challenge.pendingUser}>
|
||||
<div slot="link">
|
||||
<a href="${FlowURLManager.cancel()}">${t`Not you?`}</a>
|
||||
</div>
|
||||
</ak-form-static>
|
||||
<div class="pf-c-form__group">
|
||||
<p id="header-text">
|
||||
${this.challenge.header_text}
|
||||
${this.challenge.headerText}
|
||||
</p>
|
||||
<p>${t`Application requires following permissions`}</p>
|
||||
<ul class="pf-c-list" id="permmissions">
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { t } from "@lingui/macro";
|
||||
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { Challenge } from "../../../api/Flows";
|
||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
|
@ -11,12 +10,13 @@ import AKGlobal from "../../../authentik.css";
|
|||
import { BaseStage } from "../base";
|
||||
import "../../../elements/EmptyState";
|
||||
import "../../FormStatic";
|
||||
import { DummyChallenge } from "authentik-api";
|
||||
|
||||
@customElement("ak-stage-dummy")
|
||||
export class DummyStage extends BaseStage {
|
||||
|
||||
@property({ attribute: false })
|
||||
challenge?: Challenge;
|
||||
challenge?: DummyChallenge;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFLogin, PFForm, PFFormControl, PFTitle, PFButton, AKGlobal];
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { t } from "@lingui/macro";
|
||||
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { Challenge } from "authentik-api";
|
||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
|
@ -10,8 +9,7 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
|||
import AKGlobal from "../../../authentik.css";
|
||||
import { BaseStage } from "../base";
|
||||
import "../../../elements/EmptyState";
|
||||
|
||||
export type EmailChallenge = Challenge;
|
||||
import { EmailChallenge } from "authentik-api";
|
||||
|
||||
@customElement("ak-stage-email")
|
||||
export class EmailStage extends BaseStage {
|
||||
|
|
|
@ -10,7 +10,7 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
|||
import AKGlobal from "../../../authentik.css";
|
||||
import "../../../elements/forms/FormElement";
|
||||
import "../../../elements/EmptyState";
|
||||
import { Challenge } from "../../../api/Flows";
|
||||
import { IdentificationChallenge, UILoginButton } from "authentik-api";
|
||||
|
||||
export const PasswordManagerPrefill: {
|
||||
password: string | undefined;
|
||||
|
@ -20,24 +20,6 @@ export const PasswordManagerPrefill: {
|
|||
totp: undefined,
|
||||
};
|
||||
|
||||
export interface IdentificationChallenge extends Challenge {
|
||||
|
||||
user_fields?: string[];
|
||||
primary_action: string;
|
||||
sources?: UILoginButton[];
|
||||
|
||||
application_pre?: string;
|
||||
|
||||
enroll_url?: string;
|
||||
recovery_url?: string;
|
||||
|
||||
}
|
||||
|
||||
export interface UILoginButton {
|
||||
name: string;
|
||||
challenge: Challenge;
|
||||
icon_url?: string;
|
||||
}
|
||||
|
||||
@customElement("ak-stage-identification")
|
||||
export class IdentificationStage extends BaseStage {
|
||||
|
@ -131,8 +113,8 @@ export class IdentificationStage extends BaseStage {
|
|||
|
||||
renderSource(source: UILoginButton): TemplateResult {
|
||||
let icon = html`<i class="fas fas fa-share-square" title="${source.name}"></i>`;
|
||||
if (source.icon_url) {
|
||||
icon = html`<img src="${source.icon_url}" alt="${source.name}">`;
|
||||
if (source.iconUrl) {
|
||||
icon = html`<img src="${source.iconUrl}" alt="${source.name}">`;
|
||||
}
|
||||
return html`<li class="pf-c-login__main-footer-links-item">
|
||||
<button type="button" @click=${() => {
|
||||
|
@ -145,18 +127,18 @@ export class IdentificationStage extends BaseStage {
|
|||
}
|
||||
|
||||
renderFooter(): TemplateResult {
|
||||
if (!this.challenge?.enroll_url && !this.challenge?.recovery_url) {
|
||||
if (!this.challenge?.enrollUrl && !this.challenge?.recoveryUrl) {
|
||||
return html``;
|
||||
}
|
||||
return html`<div class="pf-c-login__main-footer-band">
|
||||
${this.challenge.enroll_url ? html`
|
||||
${this.challenge.enrollUrl ? html`
|
||||
<p class="pf-c-login__main-footer-band-item">
|
||||
${t`Need an account?`}
|
||||
<a id="enroll" href="${this.challenge.enroll_url}">${t`Sign up.`}</a>
|
||||
<a id="enroll" href="${this.challenge.enrollUrl}">${t`Sign up.`}</a>
|
||||
</p>` : html``}
|
||||
${this.challenge.recovery_url ? html`
|
||||
${this.challenge.recoveryUrl ? html`
|
||||
<p class="pf-c-login__main-footer-band-item">
|
||||
<a id="recovery" href="${this.challenge.recovery_url}">${t`Forgot username or password?`}</a>
|
||||
<a id="recovery" href="${this.challenge.recoveryUrl}">${t`Forgot username or password?`}</a>
|
||||
</p>` : html``}
|
||||
</div>`;
|
||||
}
|
||||
|
@ -164,15 +146,15 @@ export class IdentificationStage extends BaseStage {
|
|||
renderInput(): TemplateResult {
|
||||
let label = "";
|
||||
let type = "text";
|
||||
if (!this.challenge?.user_fields) {
|
||||
if (!this.challenge?.userFields) {
|
||||
return html`<p>
|
||||
${t`Select one of the sources below to login.`}
|
||||
</p>`;
|
||||
}
|
||||
if (this.challenge?.user_fields === ["email"]) {
|
||||
if (this.challenge?.userFields === ["email"]) {
|
||||
label = t`Email`;
|
||||
type = "email";
|
||||
} else if (this.challenge?.user_fields === ["username"]) {
|
||||
} else if (this.challenge?.userFields === ["username"]) {
|
||||
label = t`Username`;
|
||||
} else {
|
||||
label = t`Email or username`;
|
||||
|
@ -181,10 +163,10 @@ export class IdentificationStage extends BaseStage {
|
|||
label=${label}
|
||||
?required="${true}"
|
||||
class="pf-c-form__group"
|
||||
.errors=${(this.challenge?.response_errors || {})["uid_field"]}>
|
||||
.errors=${(this.challenge?.responseErrors || {})["uidField"]}>
|
||||
<!-- @ts-ignore -->
|
||||
<input type=${type}
|
||||
name="uid_field"
|
||||
name="uidField"
|
||||
placeholder="Email or Username"
|
||||
autofocus=""
|
||||
autocomplete="username"
|
||||
|
@ -193,7 +175,7 @@ export class IdentificationStage extends BaseStage {
|
|||
</ak-form-element>
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
|
||||
${this.challenge.primary_action}
|
||||
${this.challenge.primaryAction}
|
||||
</button>
|
||||
</div>`;
|
||||
}
|
||||
|
@ -212,9 +194,9 @@ export class IdentificationStage extends BaseStage {
|
|||
</header>
|
||||
<div class="pf-c-login__main-body">
|
||||
<form class="pf-c-form" @submit=${(e: Event) => {this.submitForm(e);}}>
|
||||
${this.challenge.application_pre ?
|
||||
${this.challenge.applicationPre ?
|
||||
html`<p>
|
||||
${t`Login to continue to ${this.challenge.application_pre}.`}
|
||||
${t`Login to continue to ${this.challenge.applicationPre}.`}
|
||||
</p>`:
|
||||
html``}
|
||||
${this.renderInput()}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { t } from "@lingui/macro";
|
||||
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { WithUserInfoChallenge } from "../../../api/Flows";
|
||||
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
|
@ -14,10 +13,7 @@ import "../../../elements/EmptyState";
|
|||
import { PasswordManagerPrefill } from "../identification/IdentificationStage";
|
||||
import "../../FormStatic";
|
||||
import { FlowURLManager } from "../../../api/legacy";
|
||||
|
||||
export interface PasswordChallenge extends WithUserInfoChallenge {
|
||||
recovery_url?: string;
|
||||
}
|
||||
import { PasswordChallenge } from "authentik-api";
|
||||
|
||||
@customElement("ak-stage-password")
|
||||
export class PasswordStage extends BaseStage {
|
||||
|
@ -45,18 +41,18 @@ export class PasswordStage extends BaseStage {
|
|||
<form class="pf-c-form" @submit=${(e: Event) => {this.submitForm(e);}}>
|
||||
<ak-form-static
|
||||
class="pf-c-form__group"
|
||||
userAvatar="${this.challenge.pending_user_avatar}"
|
||||
user=${this.challenge.pending_user}>
|
||||
userAvatar="${this.challenge.pendingUserAvatar}"
|
||||
user=${this.challenge.pendingUser}>
|
||||
<div slot="link">
|
||||
<a href="${FlowURLManager.cancel()}">${t`Not you?`}</a>
|
||||
</div>
|
||||
</ak-form-static>
|
||||
<input name="username" autocomplete="username" type="hidden" value="${this.challenge.pending_user}">
|
||||
<input name="username" autocomplete="username" type="hidden" value="${this.challenge.pendingUser}">
|
||||
<ak-form-element
|
||||
label="${t`Password`}"
|
||||
?required="${true}"
|
||||
class="pf-c-form__group"
|
||||
.errors=${(this.challenge?.response_errors || {})["password"]}>
|
||||
.errors=${(this.challenge?.responseErrors || {})["password"]}>
|
||||
<input type="password"
|
||||
name="password"
|
||||
placeholder="${t`Please enter your password`}"
|
||||
|
@ -67,8 +63,8 @@ export class PasswordStage extends BaseStage {
|
|||
value=${PasswordManagerPrefill.password || ""}>
|
||||
</ak-form-element>
|
||||
|
||||
${this.challenge.recovery_url ?
|
||||
html`<a href="${this.challenge.recovery_url}">
|
||||
${this.challenge.recoveryUrl ?
|
||||
html`<a href="${this.challenge.recoveryUrl}">
|
||||
${t`Forgot password?`}</a>` : ""}
|
||||
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
|
|
|
@ -13,20 +13,9 @@ import { BaseStage } from "../base";
|
|||
import "../../../elements/forms/FormElement";
|
||||
import "../../../elements/EmptyState";
|
||||
import "../../../elements/Divider";
|
||||
import { Challenge, Error } from "../../../api/Flows";
|
||||
import { Error } from "../../../api/Flows";
|
||||
import { Prompt, PromptChallenge } from "authentik-api";
|
||||
|
||||
export interface Prompt {
|
||||
field_key: string;
|
||||
label: string;
|
||||
type: string;
|
||||
required: boolean;
|
||||
placeholder: string;
|
||||
order: number;
|
||||
}
|
||||
|
||||
export interface PromptChallenge extends Challenge {
|
||||
fields: Prompt[];
|
||||
}
|
||||
|
||||
@customElement("ak-stage-prompt")
|
||||
export class PromptStage extends BaseStage {
|
||||
|
@ -43,7 +32,7 @@ export class PromptStage extends BaseStage {
|
|||
case "text":
|
||||
return `<input
|
||||
type="text"
|
||||
name="${prompt.field_key}"
|
||||
name="${prompt.fieldKey}"
|
||||
placeholder="${prompt.placeholder}"
|
||||
autocomplete="off"
|
||||
class="pf-c-form-control"
|
||||
|
@ -52,7 +41,7 @@ export class PromptStage extends BaseStage {
|
|||
case "username":
|
||||
return `<input
|
||||
type="text"
|
||||
name="${prompt.field_key}"
|
||||
name="${prompt.fieldKey}"
|
||||
placeholder="${prompt.placeholder}"
|
||||
autocomplete="username"
|
||||
class="pf-c-form-control"
|
||||
|
@ -61,7 +50,7 @@ export class PromptStage extends BaseStage {
|
|||
case "email":
|
||||
return `<input
|
||||
type="email"
|
||||
name="${prompt.field_key}"
|
||||
name="${prompt.fieldKey}"
|
||||
placeholder="${prompt.placeholder}"
|
||||
class="pf-c-form-control"
|
||||
?required=${prompt.required}
|
||||
|
@ -69,7 +58,7 @@ export class PromptStage extends BaseStage {
|
|||
case "password":
|
||||
return `<input
|
||||
type="password"
|
||||
name="${prompt.field_key}"
|
||||
name="${prompt.fieldKey}"
|
||||
placeholder="${prompt.placeholder}"
|
||||
autocomplete="new-password"
|
||||
class="pf-c-form-control"
|
||||
|
@ -77,28 +66,28 @@ export class PromptStage extends BaseStage {
|
|||
case "number":
|
||||
return `<input
|
||||
type="number"
|
||||
name="${prompt.field_key}"
|
||||
name="${prompt.fieldKey}"
|
||||
placeholder="${prompt.placeholder}"
|
||||
class="pf-c-form-control"
|
||||
?required=${prompt.required}>`;
|
||||
case "checkbox":
|
||||
return `<input
|
||||
type="checkbox"
|
||||
name="${prompt.field_key}"
|
||||
name="${prompt.fieldKey}"
|
||||
placeholder="${prompt.placeholder}"
|
||||
class="pf-c-form-control"
|
||||
?required=${prompt.required}>`;
|
||||
case "date":
|
||||
return `<input
|
||||
type="date"
|
||||
name="${prompt.field_key}"
|
||||
name="${prompt.fieldKey}"
|
||||
placeholder="${prompt.placeholder}"
|
||||
class="pf-c-form-control"
|
||||
?required=${prompt.required}>`;
|
||||
case "date-time":
|
||||
return `<input
|
||||
type="datetime"
|
||||
name="${prompt.field_key}"
|
||||
name="${prompt.fieldKey}"
|
||||
placeholder="${prompt.placeholder}"
|
||||
class="pf-c-form-control"
|
||||
?required=${prompt.required}>`;
|
||||
|
@ -107,7 +96,7 @@ export class PromptStage extends BaseStage {
|
|||
case "hidden":
|
||||
return `<input
|
||||
type="hidden"
|
||||
name="${prompt.field_key}"
|
||||
name="${prompt.fieldKey}"
|
||||
value="${prompt.placeholder}"
|
||||
class="pf-c-form-control"
|
||||
?required=${prompt.required}>`;
|
||||
|
@ -158,12 +147,12 @@ export class PromptStage extends BaseStage {
|
|||
label="${prompt.label}"
|
||||
?required="${prompt.required}"
|
||||
class="pf-c-form__group"
|
||||
.errors=${(this.challenge?.response_errors || {})[prompt.field_key]}>
|
||||
.errors=${(this.challenge?.responseErrors || {})[prompt.fieldKey]}>
|
||||
${unsafeHTML(this.renderPromptInner(prompt))}
|
||||
</ak-form-element>`;
|
||||
})}
|
||||
${"non_field_errors" in (this.challenge?.response_errors || {}) ?
|
||||
this.renderNonFieldErrors(this.challenge?.response_errors?.non_field_errors || []):
|
||||
${"non_field_errors" in (this.challenge?.responseErrors || {}) ?
|
||||
this.renderNonFieldErrors(this.challenge?.responseErrors?.non_field_errors || []):
|
||||
html``}
|
||||
<div class="pf-c-form__group pf-m-action">
|
||||
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
|
||||
|
|
|
@ -63,6 +63,10 @@ msgstr "ANY, any policy must match to grant access."
|
|||
msgid "ANY, any policy must match to include this stage access."
|
||||
msgstr "ANY, any policy must match to include this stage access."
|
||||
|
||||
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
|
||||
msgid "API Hostname"
|
||||
msgstr "API Hostname"
|
||||
|
||||
#: src/elements/notifications/APIDrawer.ts
|
||||
msgid "API Requests"
|
||||
msgstr "API Requests"
|
||||
|
@ -180,6 +184,10 @@ msgstr "Allows/denys requests based on the users and/or the IPs reputation."
|
|||
msgid "Also known as Entity ID. Defaults the Metadata URL."
|
||||
msgstr "Also known as Entity ID. Defaults the Metadata URL."
|
||||
|
||||
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts
|
||||
msgid "Alternatively, if your current device has Duo installed, click on this link:"
|
||||
msgstr "Alternatively, if your current device has Duo installed, click on this link:"
|
||||
|
||||
#: src/pages/stages/consent/ConsentStageForm.ts
|
||||
msgid "Always require consent"
|
||||
msgstr "Always require consent"
|
||||
|
@ -522,6 +530,10 @@ msgstr "Check IP"
|
|||
msgid "Check Username"
|
||||
msgstr "Check Username"
|
||||
|
||||
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts
|
||||
msgid "Check status"
|
||||
msgstr "Check status"
|
||||
|
||||
#: src/flows/stages/email/EmailStage.ts
|
||||
msgid "Check your Emails for a password reset link."
|
||||
msgstr "Check your Emails for a password reset link."
|
||||
|
@ -572,6 +584,7 @@ msgstr "Click to copy token"
|
|||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderViewPage.ts
|
||||
#: src/pages/sources/plex/PlexSourceForm.ts
|
||||
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
|
||||
msgid "Client ID"
|
||||
msgstr "Client ID"
|
||||
|
||||
|
@ -583,6 +596,7 @@ msgid "Client IP"
|
|||
msgstr "Client IP"
|
||||
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
|
||||
msgid "Client Secret"
|
||||
msgstr "Client Secret"
|
||||
|
||||
|
@ -616,6 +630,7 @@ msgstr "Confidential clients are capable of maintaining the confidentiality of t
|
|||
msgid "Configuration"
|
||||
msgstr "Configuration"
|
||||
|
||||
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
|
||||
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
|
||||
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
|
@ -715,6 +730,7 @@ msgstr "Context"
|
|||
#: src/flows/stages/authenticator_static/AuthenticatorStaticStage.ts
|
||||
#: src/flows/stages/authenticator_totp/AuthenticatorTOTPStage.ts
|
||||
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStageCode.ts
|
||||
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStageDuo.ts
|
||||
#: src/flows/stages/autosubmit/AutosubmitStage.ts
|
||||
#: src/flows/stages/consent/ConsentStage.ts
|
||||
#: src/flows/stages/dummy/DummyStage.ts
|
||||
|
@ -1010,6 +1026,10 @@ msgstr "Determines how authentik sends the response back to the Service Provider
|
|||
msgid "Determines how long a session lasts. Default of 0 seconds means that the sessions lasts until the browser is closed."
|
||||
msgstr "Determines how long a session lasts. Default of 0 seconds means that the sessions lasts until the browser is closed."
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Device classes"
|
||||
msgstr "Device classes"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Device classes which can be used to authenticate."
|
||||
msgstr "Device classes which can be used to authenticate."
|
||||
|
@ -1032,6 +1052,7 @@ msgstr "Digits"
|
|||
msgid "Disable"
|
||||
msgstr "Disable"
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
msgid "Disable Static Tokens"
|
||||
msgstr "Disable Static Tokens"
|
||||
|
@ -1070,6 +1091,22 @@ msgstr "Download Private key"
|
|||
msgid "Dummy stage used for testing. Shows a simple continue button and always passes."
|
||||
msgstr "Dummy stage used for testing. Shows a simple continue button and always passes."
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
msgid "Duo"
|
||||
msgstr "Duo"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
msgid "Duo Authenticators"
|
||||
msgstr "Duo Authenticators"
|
||||
|
||||
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts
|
||||
msgid "Duo activation"
|
||||
msgstr "Duo activation"
|
||||
|
||||
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts
|
||||
msgid "Duo push-notifications"
|
||||
msgstr "Duo push-notifications"
|
||||
|
||||
#: src/pages/providers/oauth2/OAuth2ProviderForm.ts
|
||||
msgid "Each provider has a different issuer, based on the application slug."
|
||||
msgstr "Each provider has a different issuer, based on the application slug."
|
||||
|
@ -1159,6 +1196,7 @@ msgstr "Enable"
|
|||
msgid "Enable StartTLS"
|
||||
msgstr "Enable StartTLS"
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
msgid "Enable Static Tokens"
|
||||
msgstr "Enable Static Tokens"
|
||||
|
@ -1431,6 +1469,7 @@ msgstr "Flow used before authentication."
|
|||
msgid "Flow used by an authenticated user to configure their password. If empty, user will not be able to configure change their password."
|
||||
msgstr "Flow used by an authenticated user to configure their password. If empty, user will not be able to configure change their password."
|
||||
|
||||
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
|
||||
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
|
||||
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
|
||||
msgid "Flow used by an authenticated user to configure this Stage. If empty, user will not be able to configure this stage."
|
||||
|
@ -1803,10 +1842,12 @@ msgstr "Load servers"
|
|||
#: src/flows/FlowExecutor.ts
|
||||
#: src/flows/FlowExecutor.ts
|
||||
#: src/flows/access_denied/FlowAccessDenied.ts
|
||||
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts
|
||||
#: src/flows/stages/authenticator_static/AuthenticatorStaticStage.ts
|
||||
#: src/flows/stages/authenticator_totp/AuthenticatorTOTPStage.ts
|
||||
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts
|
||||
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStageCode.ts
|
||||
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStageDuo.ts
|
||||
#: src/flows/stages/autosubmit/AutosubmitStage.ts
|
||||
#: src/flows/stages/captcha/CaptchaStage.ts
|
||||
#: src/flows/stages/consent/ConsentStage.ts
|
||||
|
@ -1866,6 +1907,7 @@ msgstr "Loading"
|
|||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
|
||||
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
|
||||
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
|
@ -2041,6 +2083,7 @@ msgstr "My Applications"
|
|||
#: src/pages/sources/saml/SAMLSourceForm.ts
|
||||
#: src/pages/sources/saml/SAMLSourceViewPage.ts
|
||||
#: src/pages/stages/StageListPage.ts
|
||||
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
|
||||
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
|
||||
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
|
@ -2173,9 +2216,11 @@ msgstr "Not found"
|
|||
msgid "Not synced yet."
|
||||
msgstr "Not synced yet."
|
||||
|
||||
#: src/flows/stages/authenticator_duo/AuthenticatorDuoStage.ts
|
||||
#: src/flows/stages/authenticator_static/AuthenticatorStaticStage.ts
|
||||
#: src/flows/stages/authenticator_totp/AuthenticatorTOTPStage.ts
|
||||
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStageCode.ts
|
||||
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStageDuo.ts
|
||||
#: src/flows/stages/captcha/CaptchaStage.ts
|
||||
#: src/flows/stages/consent/ConsentStage.ts
|
||||
#: src/flows/stages/password/PasswordStage.ts
|
||||
|
@ -2617,6 +2662,10 @@ msgstr "RSA-SHA512"
|
|||
msgid "Re-evaluate policies"
|
||||
msgstr "Re-evaluate policies"
|
||||
|
||||
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStage.ts
|
||||
msgid "Receive a push notification on your phone to prove your identity."
|
||||
msgstr "Receive a push notification on your phone to prove your identity."
|
||||
|
||||
#: src/pages/flows/FlowForm.ts
|
||||
msgid "Recovery"
|
||||
msgstr "Recovery"
|
||||
|
@ -2728,6 +2777,7 @@ msgid "Return home"
|
|||
msgstr "Return home"
|
||||
|
||||
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStageCode.ts
|
||||
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStageDuo.ts
|
||||
#: src/flows/stages/authenticator_validate/AuthenticatorValidateStageWebAuthn.ts
|
||||
msgid "Return to device picker"
|
||||
msgstr "Return to device picker"
|
||||
|
@ -3033,6 +3083,10 @@ msgstr "Stage used to configure a TOTP authenticator (i.e. Authy/Google Authenti
|
|||
msgid "Stage used to configure a WebAutnn authenticator (i.e. Yubikey, FaceID/Windows Hello)."
|
||||
msgstr "Stage used to configure a WebAutnn authenticator (i.e. Yubikey, FaceID/Windows Hello)."
|
||||
|
||||
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
|
||||
msgid "Stage used to configure a duo-based authenticator. This stage should be used for configuration flows."
|
||||
msgstr "Stage used to configure a duo-based authenticator. This stage should be used for configuration flows."
|
||||
|
||||
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
|
||||
msgid "Stage used to configure a static authenticator (i.e. static tokens). This stage should be used for configuration flows."
|
||||
msgstr "Stage used to configure a static authenticator (i.e. static tokens). This stage should be used for configuration flows."
|
||||
|
@ -3041,6 +3095,7 @@ msgstr "Stage used to configure a static authenticator (i.e. static tokens). Thi
|
|||
msgid "Stage used to validate any authenticator. This stage should be used during authentication or authorization flows."
|
||||
msgstr "Stage used to validate any authenticator. This stage should be used during authentication or authorization flows."
|
||||
|
||||
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
|
||||
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
|
||||
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
|
@ -3074,7 +3129,7 @@ msgstr "State"
|
|||
msgid "Static Tokens"
|
||||
msgstr "Static Tokens"
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
msgid "Static tokens"
|
||||
msgstr "Static tokens"
|
||||
|
||||
|
@ -3090,11 +3145,13 @@ msgstr "Statically deny the flow. To use this stage effectively, disable *Evalua
|
|||
msgid "Status"
|
||||
msgstr "Status"
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
msgid "Status: Disabled"
|
||||
msgstr "Status: Disabled"
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorDuo.ts
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
msgid "Status: Enabled"
|
||||
|
@ -3204,6 +3261,7 @@ msgstr "Successfully created service-connection."
|
|||
msgid "Successfully created source."
|
||||
msgstr "Successfully created source."
|
||||
|
||||
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
|
||||
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
|
||||
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
|
@ -3342,6 +3400,7 @@ msgstr "Successfully updated service-connection."
|
|||
msgid "Successfully updated source."
|
||||
msgstr "Successfully updated source."
|
||||
|
||||
#: src/pages/stages/authenticator_duo/AuthenticatorDuoStageForm.ts
|
||||
#: src/pages/stages/authenticator_static/AuthenticatorStaticStageForm.ts
|
||||
#: src/pages/stages/authenticator_totp/AuthenticatorTOTPStageForm.ts
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
|
@ -3533,7 +3592,7 @@ msgstr "Time in minutes the token sent is valid."
|
|||
msgid "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
msgstr "Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3)."
|
||||
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorStatic.ts
|
||||
#: src/pages/user-settings/settings/UserSettingsAuthenticatorTOTP.ts
|
||||
msgid "Time-based One-Time Passwords"
|
||||
msgstr "Time-based One-Time Passwords"
|
||||
|
||||
|
@ -3893,7 +3952,6 @@ msgstr "User details"
|
|||
msgid "User events"
|
||||
msgstr "User events"
|
||||
|
||||
#: src/pages/stages/authenticator_validate/AuthenticatorValidateStageForm.ts
|
||||
#: src/pages/stages/identification/IdentificationStageForm.ts
|
||||
msgid "User fields"
|
||||
msgstr "User fields"
|
||||
|
|
|
@ -63,6 +63,10 @@ msgstr ""
|
|||
msgid "ANY, any policy must match to include this stage access."
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "API Hostname"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "API Requests"
|
||||
msgstr ""
|
||||
|
@ -180,6 +184,10 @@ msgstr ""
|
|||
msgid "Also known as Entity ID. Defaults the Metadata URL."
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "Alternatively, if your current device has Duo installed, click on this link:"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "Always require consent"
|
||||
msgstr ""
|
||||
|
@ -518,6 +526,10 @@ msgstr ""
|
|||
msgid "Check Username"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "Check status"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "Check your Emails for a password reset link."
|
||||
msgstr ""
|
||||
|
@ -566,6 +578,7 @@ msgstr ""
|
|||
#:
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "Client ID"
|
||||
msgstr ""
|
||||
|
||||
|
@ -576,6 +589,7 @@ msgstr ""
|
|||
msgid "Client IP"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
#:
|
||||
msgid "Client Secret"
|
||||
msgstr ""
|
||||
|
@ -614,6 +628,7 @@ msgstr ""
|
|||
#:
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "Configuration flow"
|
||||
msgstr ""
|
||||
|
||||
|
@ -715,6 +730,7 @@ msgstr ""
|
|||
#:
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "Continue"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1002,6 +1018,10 @@ msgstr ""
|
|||
msgid "Determines how long a session lasts. Default of 0 seconds means that the sessions lasts until the browser is closed."
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "Device classes"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "Device classes which can be used to authenticate."
|
||||
msgstr ""
|
||||
|
@ -1024,6 +1044,7 @@ msgstr ""
|
|||
msgid "Disable"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
#:
|
||||
msgid "Disable Static Tokens"
|
||||
msgstr ""
|
||||
|
@ -1062,6 +1083,22 @@ msgstr ""
|
|||
msgid "Dummy stage used for testing. Shows a simple continue button and always passes."
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "Duo"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "Duo Authenticators"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "Duo activation"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "Duo push-notifications"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "Each provider has a different issuer, based on the application slug."
|
||||
msgstr ""
|
||||
|
@ -1151,6 +1188,7 @@ msgstr ""
|
|||
msgid "Enable StartTLS"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
#:
|
||||
msgid "Enable Static Tokens"
|
||||
msgstr ""
|
||||
|
@ -1423,6 +1461,7 @@ msgstr ""
|
|||
msgid "Flow used by an authenticated user to configure their password. If empty, user will not be able to configure change their password."
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "Flow used by an authenticated user to configure this Stage. If empty, user will not be able to configure this stage."
|
||||
|
@ -1811,6 +1850,8 @@ msgstr ""
|
|||
#:
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "Loading"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1867,6 +1908,7 @@ msgstr ""
|
|||
#:
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "Loading..."
|
||||
msgstr ""
|
||||
|
||||
|
@ -2054,6 +2096,7 @@ msgstr ""
|
|||
#:
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2171,6 +2214,8 @@ msgstr ""
|
|||
#:
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "Not you?"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2609,6 +2654,10 @@ msgstr ""
|
|||
msgid "Re-evaluate policies"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "Receive a push notification on your phone to prove your identity."
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "Recovery"
|
||||
msgstr ""
|
||||
|
@ -2719,6 +2768,7 @@ msgstr ""
|
|||
msgid "Return home"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "Return to device picker"
|
||||
|
@ -3025,6 +3075,10 @@ msgstr ""
|
|||
msgid "Stage used to configure a WebAutnn authenticator (i.e. Yubikey, FaceID/Windows Hello)."
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "Stage used to configure a duo-based authenticator. This stage should be used for configuration flows."
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
msgid "Stage used to configure a static authenticator (i.e. static tokens). This stage should be used for configuration flows."
|
||||
msgstr ""
|
||||
|
@ -3044,6 +3098,7 @@ msgstr ""
|
|||
#:
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "Stage-specific settings"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3082,11 +3137,13 @@ msgstr ""
|
|||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "Status: Disabled"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "Status: Enabled"
|
||||
|
@ -3213,6 +3270,7 @@ msgstr ""
|
|||
#:
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "Successfully created stage."
|
||||
msgstr ""
|
||||
|
||||
|
@ -3351,6 +3409,7 @@ msgstr ""
|
|||
#:
|
||||
#:
|
||||
#:
|
||||
#:
|
||||
msgid "Successfully updated stage."
|
||||
msgstr ""
|
||||
|
||||
|
@ -3881,7 +3940,6 @@ msgstr ""
|
|||
msgid "User events"
|
||||
msgstr ""
|
||||
|
||||
#:
|
||||
#:
|
||||
msgid "User fields"
|
||||
msgstr ""
|
||||
|
|
Reference in New Issue