stages: Add ability to set user friendly names for MFA stages (#5005)
* Added ability to name MFA stage * Schema * Changed Charfield to Textfield * Regenerated schema * Add explicit required * set null instead of blank so title check works Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add help text and adjust wording Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io> Co-authored-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
parent
fd2677af1f
commit
6192d01b7e
|
@ -27,6 +27,6 @@ class UserSettingSerializer(PassiveSerializer):
|
||||||
|
|
||||||
object_uid = CharField()
|
object_uid = CharField()
|
||||||
component = CharField()
|
component = CharField()
|
||||||
title = CharField()
|
title = CharField(required=True)
|
||||||
configure_url = CharField(required=False)
|
configure_url = CharField(required=False)
|
||||||
icon_url = CharField(required=False)
|
icon_url = CharField(required=False)
|
||||||
|
|
|
@ -271,6 +271,15 @@ class ConfigurableStage(models.Model):
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
|
class FriendlyNamedStage(models.Model):
|
||||||
|
"""Abstract base class for a Stage that can have a user friendly name configured."""
|
||||||
|
|
||||||
|
friendly_name = models.TextField(null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
class FlowToken(Token):
|
class FlowToken(Token):
|
||||||
"""Subclass of a standard Token, stores the currently active flow plan upon creation.
|
"""Subclass of a standard Token, stores the currently active flow plan upon creation.
|
||||||
Can be used to later resume a flow."""
|
Can be used to later resume a flow."""
|
||||||
|
|
|
@ -33,6 +33,7 @@ class AuthenticatorDuoStageSerializer(StageSerializer):
|
||||||
model = AuthenticatorDuoStage
|
model = AuthenticatorDuoStage
|
||||||
fields = StageSerializer.Meta.fields + [
|
fields = StageSerializer.Meta.fields + [
|
||||||
"configure_flow",
|
"configure_flow",
|
||||||
|
"friendly_name",
|
||||||
"client_id",
|
"client_id",
|
||||||
"client_secret",
|
"client_secret",
|
||||||
"api_hostname",
|
"api_hostname",
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Generated by Django 4.1.7 on 2023-04-02 14:19
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
(
|
||||||
|
"authentik_stages_authenticator_duo",
|
||||||
|
"0004_authenticatorduostage_admin_integration_key_and_more",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="authenticatorduostage",
|
||||||
|
name="friendly_name",
|
||||||
|
field=models.TextField(null=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -12,12 +12,12 @@ from rest_framework.serializers import BaseSerializer, Serializer
|
||||||
|
|
||||||
from authentik import __version__
|
from authentik import __version__
|
||||||
from authentik.core.types import UserSettingSerializer
|
from authentik.core.types import UserSettingSerializer
|
||||||
from authentik.flows.models import ConfigurableStage, Stage
|
from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
|
||||||
from authentik.lib.models import SerializerModel
|
from authentik.lib.models import SerializerModel
|
||||||
from authentik.lib.utils.http import authentik_user_agent
|
from authentik.lib.utils.http import authentik_user_agent
|
||||||
|
|
||||||
|
|
||||||
class AuthenticatorDuoStage(ConfigurableStage, Stage):
|
class AuthenticatorDuoStage(ConfigurableStage, FriendlyNamedStage, Stage):
|
||||||
"""Setup Duo authenticator devices"""
|
"""Setup Duo authenticator devices"""
|
||||||
|
|
||||||
api_hostname = models.TextField()
|
api_hostname = models.TextField()
|
||||||
|
@ -68,7 +68,7 @@ class AuthenticatorDuoStage(ConfigurableStage, Stage):
|
||||||
def ui_user_settings(self) -> Optional[UserSettingSerializer]:
|
def ui_user_settings(self) -> Optional[UserSettingSerializer]:
|
||||||
return UserSettingSerializer(
|
return UserSettingSerializer(
|
||||||
data={
|
data={
|
||||||
"title": str(self._meta.verbose_name),
|
"title": self.friendly_name or str(self._meta.verbose_name),
|
||||||
"component": "ak-user-settings-authenticator-duo",
|
"component": "ak-user-settings-authenticator-duo",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -19,6 +19,7 @@ class AuthenticatorSMSStageSerializer(StageSerializer):
|
||||||
model = AuthenticatorSMSStage
|
model = AuthenticatorSMSStage
|
||||||
fields = StageSerializer.Meta.fields + [
|
fields = StageSerializer.Meta.fields + [
|
||||||
"configure_flow",
|
"configure_flow",
|
||||||
|
"friendly_name",
|
||||||
"provider",
|
"provider",
|
||||||
"from_number",
|
"from_number",
|
||||||
"account_sid",
|
"account_sid",
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Generated by Django 4.1.7 on 2023-04-02 14:19
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("authentik_stages_authenticator_sms", "0005_authenticatorsmsstage_mapping"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="authenticatorsmsstage",
|
||||||
|
name="friendly_name",
|
||||||
|
field=models.TextField(null=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -17,7 +17,7 @@ from twilio.rest import Client
|
||||||
from authentik.core.types import UserSettingSerializer
|
from authentik.core.types import UserSettingSerializer
|
||||||
from authentik.events.models import Event, EventAction, NotificationWebhookMapping
|
from authentik.events.models import Event, EventAction, NotificationWebhookMapping
|
||||||
from authentik.events.utils import sanitize_item
|
from authentik.events.utils import sanitize_item
|
||||||
from authentik.flows.models import ConfigurableStage, Stage
|
from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
|
||||||
from authentik.lib.models import SerializerModel
|
from authentik.lib.models import SerializerModel
|
||||||
from authentik.lib.utils.errors import exception_to_string
|
from authentik.lib.utils.errors import exception_to_string
|
||||||
from authentik.lib.utils.http import get_http_session
|
from authentik.lib.utils.http import get_http_session
|
||||||
|
@ -39,7 +39,7 @@ class SMSAuthTypes(models.TextChoices):
|
||||||
BEARER = "bearer"
|
BEARER = "bearer"
|
||||||
|
|
||||||
|
|
||||||
class AuthenticatorSMSStage(ConfigurableStage, Stage):
|
class AuthenticatorSMSStage(ConfigurableStage, FriendlyNamedStage, Stage):
|
||||||
"""Use SMS-based TOTP instead of authenticator-based."""
|
"""Use SMS-based TOTP instead of authenticator-based."""
|
||||||
|
|
||||||
provider = models.TextField(choices=SMSProviders.choices)
|
provider = models.TextField(choices=SMSProviders.choices)
|
||||||
|
@ -168,7 +168,7 @@ class AuthenticatorSMSStage(ConfigurableStage, Stage):
|
||||||
def ui_user_settings(self) -> Optional[UserSettingSerializer]:
|
def ui_user_settings(self) -> Optional[UserSettingSerializer]:
|
||||||
return UserSettingSerializer(
|
return UserSettingSerializer(
|
||||||
data={
|
data={
|
||||||
"title": str(self._meta.verbose_name),
|
"title": self.friendly_name or str(self._meta.verbose_name),
|
||||||
"component": "ak-user-settings-authenticator-sms",
|
"component": "ak-user-settings-authenticator-sms",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -18,7 +18,7 @@ class AuthenticatorStaticStageSerializer(StageSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = AuthenticatorStaticStage
|
model = AuthenticatorStaticStage
|
||||||
fields = StageSerializer.Meta.fields + ["configure_flow", "token_count"]
|
fields = StageSerializer.Meta.fields + ["configure_flow", "friendly_name", "token_count"]
|
||||||
|
|
||||||
|
|
||||||
class AuthenticatorStaticStageViewSet(UsedByMixin, ModelViewSet):
|
class AuthenticatorStaticStageViewSet(UsedByMixin, ModelViewSet):
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Generated by Django 4.1.7 on 2023-04-02 14:19
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("authentik_stages_authenticator_static", "0005_default_setup_flow"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="authenticatorstaticstage",
|
||||||
|
name="friendly_name",
|
||||||
|
field=models.TextField(null=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -7,10 +7,10 @@ from django.views import View
|
||||||
from rest_framework.serializers import BaseSerializer
|
from rest_framework.serializers import BaseSerializer
|
||||||
|
|
||||||
from authentik.core.types import UserSettingSerializer
|
from authentik.core.types import UserSettingSerializer
|
||||||
from authentik.flows.models import ConfigurableStage, Stage
|
from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
|
||||||
|
|
||||||
|
|
||||||
class AuthenticatorStaticStage(ConfigurableStage, Stage):
|
class AuthenticatorStaticStage(ConfigurableStage, FriendlyNamedStage, Stage):
|
||||||
"""Generate static tokens for the user as a backup."""
|
"""Generate static tokens for the user as a backup."""
|
||||||
|
|
||||||
token_count = models.IntegerField(default=6)
|
token_count = models.IntegerField(default=6)
|
||||||
|
@ -34,7 +34,7 @@ class AuthenticatorStaticStage(ConfigurableStage, Stage):
|
||||||
def ui_user_settings(self) -> Optional[UserSettingSerializer]:
|
def ui_user_settings(self) -> Optional[UserSettingSerializer]:
|
||||||
return UserSettingSerializer(
|
return UserSettingSerializer(
|
||||||
data={
|
data={
|
||||||
"title": str(self._meta.verbose_name),
|
"title": self.friendly_name or str(self._meta.verbose_name),
|
||||||
"component": "ak-user-settings-authenticator-static",
|
"component": "ak-user-settings-authenticator-static",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -18,7 +18,7 @@ class AuthenticatorTOTPStageSerializer(StageSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = AuthenticatorTOTPStage
|
model = AuthenticatorTOTPStage
|
||||||
fields = StageSerializer.Meta.fields + ["configure_flow", "digits"]
|
fields = StageSerializer.Meta.fields + ["configure_flow", "friendly_name", "digits"]
|
||||||
|
|
||||||
|
|
||||||
class AuthenticatorTOTPStageViewSet(UsedByMixin, ModelViewSet):
|
class AuthenticatorTOTPStageViewSet(UsedByMixin, ModelViewSet):
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Generated by Django 4.1.7 on 2023-04-02 14:19
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("authentik_stages_authenticator_totp", "0006_default_setup_flow"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="authenticatortotpstage",
|
||||||
|
name="friendly_name",
|
||||||
|
field=models.TextField(null=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -7,7 +7,7 @@ from django.views import View
|
||||||
from rest_framework.serializers import BaseSerializer
|
from rest_framework.serializers import BaseSerializer
|
||||||
|
|
||||||
from authentik.core.types import UserSettingSerializer
|
from authentik.core.types import UserSettingSerializer
|
||||||
from authentik.flows.models import ConfigurableStage, Stage
|
from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
|
||||||
|
|
||||||
|
|
||||||
class TOTPDigits(models.IntegerChoices):
|
class TOTPDigits(models.IntegerChoices):
|
||||||
|
@ -17,7 +17,7 @@ class TOTPDigits(models.IntegerChoices):
|
||||||
EIGHT = 8, _("8 digits, not compatible with apps like Google Authenticator")
|
EIGHT = 8, _("8 digits, not compatible with apps like Google Authenticator")
|
||||||
|
|
||||||
|
|
||||||
class AuthenticatorTOTPStage(ConfigurableStage, Stage):
|
class AuthenticatorTOTPStage(ConfigurableStage, FriendlyNamedStage, Stage):
|
||||||
"""Enroll a user's device into Time-based OTP."""
|
"""Enroll a user's device into Time-based OTP."""
|
||||||
|
|
||||||
digits = models.IntegerField(choices=TOTPDigits.choices)
|
digits = models.IntegerField(choices=TOTPDigits.choices)
|
||||||
|
@ -41,7 +41,7 @@ class AuthenticatorTOTPStage(ConfigurableStage, Stage):
|
||||||
def ui_user_settings(self) -> Optional[UserSettingSerializer]:
|
def ui_user_settings(self) -> Optional[UserSettingSerializer]:
|
||||||
return UserSettingSerializer(
|
return UserSettingSerializer(
|
||||||
data={
|
data={
|
||||||
"title": str(self._meta.verbose_name),
|
"title": self.friendly_name or str(self._meta.verbose_name),
|
||||||
"component": "ak-user-settings-authenticator-totp",
|
"component": "ak-user-settings-authenticator-totp",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -19,6 +19,7 @@ class AuthenticateWebAuthnStageSerializer(StageSerializer):
|
||||||
model = AuthenticateWebAuthnStage
|
model = AuthenticateWebAuthnStage
|
||||||
fields = StageSerializer.Meta.fields + [
|
fields = StageSerializer.Meta.fields + [
|
||||||
"configure_flow",
|
"configure_flow",
|
||||||
|
"friendly_name",
|
||||||
"user_verification",
|
"user_verification",
|
||||||
"authenticator_attachment",
|
"authenticator_attachment",
|
||||||
"resident_key_requirement",
|
"resident_key_requirement",
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Generated by Django 4.1.7 on 2023-04-02 14:19
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("authentik_stages_authenticator_webauthn", "0008_alter_webauthndevice_credential_id"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="authenticatewebauthnstage",
|
||||||
|
name="friendly_name",
|
||||||
|
field=models.TextField(null=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -12,7 +12,7 @@ from webauthn.helpers.base64url_to_bytes import base64url_to_bytes
|
||||||
from webauthn.helpers.structs import PublicKeyCredentialDescriptor
|
from webauthn.helpers.structs import PublicKeyCredentialDescriptor
|
||||||
|
|
||||||
from authentik.core.types import UserSettingSerializer
|
from authentik.core.types import UserSettingSerializer
|
||||||
from authentik.flows.models import ConfigurableStage, Stage
|
from authentik.flows.models import ConfigurableStage, FriendlyNamedStage, Stage
|
||||||
from authentik.lib.models import SerializerModel
|
from authentik.lib.models import SerializerModel
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ class AuthenticatorAttachment(models.TextChoices):
|
||||||
CROSS_PLATFORM = "cross-platform"
|
CROSS_PLATFORM = "cross-platform"
|
||||||
|
|
||||||
|
|
||||||
class AuthenticateWebAuthnStage(ConfigurableStage, Stage):
|
class AuthenticateWebAuthnStage(ConfigurableStage, FriendlyNamedStage, Stage):
|
||||||
"""WebAuthn stage"""
|
"""WebAuthn stage"""
|
||||||
|
|
||||||
user_verification = models.TextField(
|
user_verification = models.TextField(
|
||||||
|
@ -100,7 +100,7 @@ class AuthenticateWebAuthnStage(ConfigurableStage, Stage):
|
||||||
def ui_user_settings(self) -> Optional[UserSettingSerializer]:
|
def ui_user_settings(self) -> Optional[UserSettingSerializer]:
|
||||||
return UserSettingSerializer(
|
return UserSettingSerializer(
|
||||||
data={
|
data={
|
||||||
"title": str(self._meta.verbose_name),
|
"title": self.friendly_name or str(self._meta.verbose_name),
|
||||||
"component": "ak-user-settings-authenticator-webauthn",
|
"component": "ak-user-settings-authenticator-webauthn",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
71
schema.yml
71
schema.yml
|
@ -20345,6 +20345,10 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
|
- in: query
|
||||||
|
name: friendly_name
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
- in: query
|
- in: query
|
||||||
name: from_number
|
name: from_number
|
||||||
schema:
|
schema:
|
||||||
|
@ -20650,6 +20654,10 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
|
- in: query
|
||||||
|
name: friendly_name
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
- in: query
|
- in: query
|
||||||
name: name
|
name: name
|
||||||
schema:
|
schema:
|
||||||
|
@ -20946,6 +20954,10 @@ paths:
|
||||||
|
|
||||||
* `6` - 6 digits, widely compatible
|
* `6` - 6 digits, widely compatible
|
||||||
* `8` - 8 digits, not compatible with apps like Google Authenticator
|
* `8` - 8 digits, not compatible with apps like Google Authenticator
|
||||||
|
- in: query
|
||||||
|
name: friendly_name
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
- in: query
|
- in: query
|
||||||
name: name
|
name: name
|
||||||
schema:
|
schema:
|
||||||
|
@ -21533,6 +21545,10 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
format: uuid
|
format: uuid
|
||||||
|
- in: query
|
||||||
|
name: friendly_name
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
- in: query
|
- in: query
|
||||||
name: name
|
name: name
|
||||||
schema:
|
schema:
|
||||||
|
@ -26560,6 +26576,9 @@ components:
|
||||||
nullable: true
|
nullable: true
|
||||||
description: Flow used by an authenticated user to configure this Stage.
|
description: Flow used by an authenticated user to configure this Stage.
|
||||||
If empty, user will not be able to configure this stage.
|
If empty, user will not be able to configure this stage.
|
||||||
|
friendly_name:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
user_verification:
|
user_verification:
|
||||||
$ref: '#/components/schemas/UserVerificationEnum'
|
$ref: '#/components/schemas/UserVerificationEnum'
|
||||||
authenticator_attachment:
|
authenticator_attachment:
|
||||||
|
@ -26592,6 +26611,10 @@ components:
|
||||||
nullable: true
|
nullable: true
|
||||||
description: Flow used by an authenticated user to configure this Stage.
|
description: Flow used by an authenticated user to configure this Stage.
|
||||||
If empty, user will not be able to configure this stage.
|
If empty, user will not be able to configure this stage.
|
||||||
|
friendly_name:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
minLength: 1
|
||||||
user_verification:
|
user_verification:
|
||||||
$ref: '#/components/schemas/UserVerificationEnum'
|
$ref: '#/components/schemas/UserVerificationEnum'
|
||||||
authenticator_attachment:
|
authenticator_attachment:
|
||||||
|
@ -26819,6 +26842,9 @@ components:
|
||||||
nullable: true
|
nullable: true
|
||||||
description: Flow used by an authenticated user to configure this Stage.
|
description: Flow used by an authenticated user to configure this Stage.
|
||||||
If empty, user will not be able to configure this stage.
|
If empty, user will not be able to configure this stage.
|
||||||
|
friendly_name:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
client_id:
|
client_id:
|
||||||
type: string
|
type: string
|
||||||
api_hostname:
|
api_hostname:
|
||||||
|
@ -26875,6 +26901,10 @@ components:
|
||||||
nullable: true
|
nullable: true
|
||||||
description: Flow used by an authenticated user to configure this Stage.
|
description: Flow used by an authenticated user to configure this Stage.
|
||||||
If empty, user will not be able to configure this stage.
|
If empty, user will not be able to configure this stage.
|
||||||
|
friendly_name:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
minLength: 1
|
||||||
client_id:
|
client_id:
|
||||||
type: string
|
type: string
|
||||||
minLength: 1
|
minLength: 1
|
||||||
|
@ -26973,6 +27003,9 @@ components:
|
||||||
nullable: true
|
nullable: true
|
||||||
description: Flow used by an authenticated user to configure this Stage.
|
description: Flow used by an authenticated user to configure this Stage.
|
||||||
If empty, user will not be able to configure this stage.
|
If empty, user will not be able to configure this stage.
|
||||||
|
friendly_name:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
provider:
|
provider:
|
||||||
$ref: '#/components/schemas/ProviderEnum'
|
$ref: '#/components/schemas/ProviderEnum'
|
||||||
from_number:
|
from_number:
|
||||||
|
@ -27023,6 +27056,10 @@ components:
|
||||||
nullable: true
|
nullable: true
|
||||||
description: Flow used by an authenticated user to configure this Stage.
|
description: Flow used by an authenticated user to configure this Stage.
|
||||||
If empty, user will not be able to configure this stage.
|
If empty, user will not be able to configure this stage.
|
||||||
|
friendly_name:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
minLength: 1
|
||||||
provider:
|
provider:
|
||||||
$ref: '#/components/schemas/ProviderEnum'
|
$ref: '#/components/schemas/ProviderEnum'
|
||||||
from_number:
|
from_number:
|
||||||
|
@ -27129,6 +27166,9 @@ components:
|
||||||
nullable: true
|
nullable: true
|
||||||
description: Flow used by an authenticated user to configure this Stage.
|
description: Flow used by an authenticated user to configure this Stage.
|
||||||
If empty, user will not be able to configure this stage.
|
If empty, user will not be able to configure this stage.
|
||||||
|
friendly_name:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
token_count:
|
token_count:
|
||||||
type: integer
|
type: integer
|
||||||
maximum: 2147483647
|
maximum: 2147483647
|
||||||
|
@ -27157,6 +27197,10 @@ components:
|
||||||
nullable: true
|
nullable: true
|
||||||
description: Flow used by an authenticated user to configure this Stage.
|
description: Flow used by an authenticated user to configure this Stage.
|
||||||
If empty, user will not be able to configure this stage.
|
If empty, user will not be able to configure this stage.
|
||||||
|
friendly_name:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
minLength: 1
|
||||||
token_count:
|
token_count:
|
||||||
type: integer
|
type: integer
|
||||||
maximum: 2147483647
|
maximum: 2147483647
|
||||||
|
@ -27240,6 +27284,9 @@ components:
|
||||||
nullable: true
|
nullable: true
|
||||||
description: Flow used by an authenticated user to configure this Stage.
|
description: Flow used by an authenticated user to configure this Stage.
|
||||||
If empty, user will not be able to configure this stage.
|
If empty, user will not be able to configure this stage.
|
||||||
|
friendly_name:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
digits:
|
digits:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/DigitsEnum'
|
- $ref: '#/components/schemas/DigitsEnum'
|
||||||
|
@ -27270,6 +27317,10 @@ components:
|
||||||
nullable: true
|
nullable: true
|
||||||
description: Flow used by an authenticated user to configure this Stage.
|
description: Flow used by an authenticated user to configure this Stage.
|
||||||
If empty, user will not be able to configure this stage.
|
If empty, user will not be able to configure this stage.
|
||||||
|
friendly_name:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
minLength: 1
|
||||||
digits:
|
digits:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/DigitsEnum'
|
- $ref: '#/components/schemas/DigitsEnum'
|
||||||
|
@ -35347,6 +35398,10 @@ components:
|
||||||
nullable: true
|
nullable: true
|
||||||
description: Flow used by an authenticated user to configure this Stage.
|
description: Flow used by an authenticated user to configure this Stage.
|
||||||
If empty, user will not be able to configure this stage.
|
If empty, user will not be able to configure this stage.
|
||||||
|
friendly_name:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
minLength: 1
|
||||||
user_verification:
|
user_verification:
|
||||||
$ref: '#/components/schemas/UserVerificationEnum'
|
$ref: '#/components/schemas/UserVerificationEnum'
|
||||||
authenticator_attachment:
|
authenticator_attachment:
|
||||||
|
@ -35372,6 +35427,10 @@ components:
|
||||||
nullable: true
|
nullable: true
|
||||||
description: Flow used by an authenticated user to configure this Stage.
|
description: Flow used by an authenticated user to configure this Stage.
|
||||||
If empty, user will not be able to configure this stage.
|
If empty, user will not be able to configure this stage.
|
||||||
|
friendly_name:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
minLength: 1
|
||||||
client_id:
|
client_id:
|
||||||
type: string
|
type: string
|
||||||
minLength: 1
|
minLength: 1
|
||||||
|
@ -35404,6 +35463,10 @@ components:
|
||||||
nullable: true
|
nullable: true
|
||||||
description: Flow used by an authenticated user to configure this Stage.
|
description: Flow used by an authenticated user to configure this Stage.
|
||||||
If empty, user will not be able to configure this stage.
|
If empty, user will not be able to configure this stage.
|
||||||
|
friendly_name:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
minLength: 1
|
||||||
provider:
|
provider:
|
||||||
$ref: '#/components/schemas/ProviderEnum'
|
$ref: '#/components/schemas/ProviderEnum'
|
||||||
from_number:
|
from_number:
|
||||||
|
@ -35446,6 +35509,10 @@ components:
|
||||||
nullable: true
|
nullable: true
|
||||||
description: Flow used by an authenticated user to configure this Stage.
|
description: Flow used by an authenticated user to configure this Stage.
|
||||||
If empty, user will not be able to configure this stage.
|
If empty, user will not be able to configure this stage.
|
||||||
|
friendly_name:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
minLength: 1
|
||||||
token_count:
|
token_count:
|
||||||
type: integer
|
type: integer
|
||||||
maximum: 2147483647
|
maximum: 2147483647
|
||||||
|
@ -35467,6 +35534,10 @@ components:
|
||||||
nullable: true
|
nullable: true
|
||||||
description: Flow used by an authenticated user to configure this Stage.
|
description: Flow used by an authenticated user to configure this Stage.
|
||||||
If empty, user will not be able to configure this stage.
|
If empty, user will not be able to configure this stage.
|
||||||
|
friendly_name:
|
||||||
|
type: string
|
||||||
|
nullable: true
|
||||||
|
minLength: 1
|
||||||
digits:
|
digits:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/DigitsEnum'
|
- $ref: '#/components/schemas/DigitsEnum'
|
||||||
|
|
|
@ -10,7 +10,6 @@ import { t } from "@lingui/macro";
|
||||||
|
|
||||||
import { TemplateResult, html } from "lit";
|
import { TemplateResult, html } from "lit";
|
||||||
import { customElement } from "lit/decorators.js";
|
import { customElement } from "lit/decorators.js";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AuthenticatorDuoStage,
|
AuthenticatorDuoStage,
|
||||||
|
@ -59,11 +58,25 @@ export class AuthenticatorDuoStageForm extends ModelForm<AuthenticatorDuoStage,
|
||||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value="${ifDefined(this.instance?.name || "")}"
|
value="${first(this.instance?.name, "")}"
|
||||||
class="pf-c-form-control"
|
class="pf-c-form-control"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
<ak-form-element-horizontal
|
||||||
|
label=${t`Authenticator type name`}
|
||||||
|
?required=${false}
|
||||||
|
name="friendlyName"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value="${first(this.instance?.friendlyName, "")}"
|
||||||
|
class="pf-c-form-control"
|
||||||
|
/>
|
||||||
|
<p class="pf-c-form__helper-text">
|
||||||
|
${t`Display name of this authenticator, used by users when they enroll an authenticator.`}
|
||||||
|
</p>
|
||||||
|
</ak-form-element-horizontal>
|
||||||
<ak-form-element-horizontal
|
<ak-form-element-horizontal
|
||||||
label=${t`API Hostname`}
|
label=${t`API Hostname`}
|
||||||
?required=${true}
|
?required=${true}
|
||||||
|
|
|
@ -11,7 +11,6 @@ import { t } from "@lingui/macro";
|
||||||
|
|
||||||
import { TemplateResult, html } from "lit";
|
import { TemplateResult, html } from "lit";
|
||||||
import { customElement, property } from "lit/decorators.js";
|
import { customElement, property } from "lit/decorators.js";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AuthTypeEnum,
|
AuthTypeEnum,
|
||||||
|
@ -76,7 +75,7 @@ export class AuthenticatorSMSStageForm extends ModelForm<AuthenticatorSMSStage,
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value="${ifDefined(this.instance?.accountSid || "")}"
|
value="${first(this.instance?.accountSid, "")}"
|
||||||
class="pf-c-form-control"
|
class="pf-c-form-control"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
@ -87,7 +86,7 @@ export class AuthenticatorSMSStageForm extends ModelForm<AuthenticatorSMSStage,
|
||||||
<ak-form-element-horizontal label=${t`Twilio Auth Token`} ?required=${true} name="auth">
|
<ak-form-element-horizontal label=${t`Twilio Auth Token`} ?required=${true} name="auth">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value="${ifDefined(this.instance?.auth || "")}"
|
value="${first(this.instance?.auth, "")}"
|
||||||
class="pf-c-form-control"
|
class="pf-c-form-control"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
@ -131,7 +130,7 @@ export class AuthenticatorSMSStageForm extends ModelForm<AuthenticatorSMSStage,
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value="${ifDefined(this.instance?.accountSid || "")}"
|
value="${first(this.instance?.accountSid, "")}"
|
||||||
class="pf-c-form-control"
|
class="pf-c-form-control"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
@ -142,7 +141,7 @@ export class AuthenticatorSMSStageForm extends ModelForm<AuthenticatorSMSStage,
|
||||||
<ak-form-element-horizontal label=${t`API Auth Username`} ?required=${true} name="auth">
|
<ak-form-element-horizontal label=${t`API Auth Username`} ?required=${true} name="auth">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value="${ifDefined(this.instance?.auth || "")}"
|
value="${first(this.instance?.auth, "")}"
|
||||||
class="pf-c-form-control"
|
class="pf-c-form-control"
|
||||||
/>
|
/>
|
||||||
<p class="pf-c-form__helper-text">
|
<p class="pf-c-form__helper-text">
|
||||||
|
@ -156,7 +155,7 @@ export class AuthenticatorSMSStageForm extends ModelForm<AuthenticatorSMSStage,
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value="${ifDefined(this.instance?.authPassword)}"
|
value="${first(this.instance?.authPassword, "")}"
|
||||||
class="pf-c-form-control"
|
class="pf-c-form-control"
|
||||||
/>
|
/>
|
||||||
<p class="pf-c-form__helper-text">
|
<p class="pf-c-form__helper-text">
|
||||||
|
@ -206,11 +205,25 @@ export class AuthenticatorSMSStageForm extends ModelForm<AuthenticatorSMSStage,
|
||||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value="${ifDefined(this.instance?.name || "")}"
|
value="${first(this.instance?.name, "")}"
|
||||||
class="pf-c-form-control"
|
class="pf-c-form-control"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
<ak-form-element-horizontal
|
||||||
|
label=${t`Authenticator type name`}
|
||||||
|
?required=${false}
|
||||||
|
name="friendlyName"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value="${first(this.instance?.friendlyName, "")}"
|
||||||
|
class="pf-c-form-control"
|
||||||
|
/>
|
||||||
|
<p class="pf-c-form__helper-text">
|
||||||
|
${t`Display name of this authenticator, used by users when they enroll an authenticator.`}
|
||||||
|
</p>
|
||||||
|
</ak-form-element-horizontal>
|
||||||
<ak-form-group .expanded=${true}>
|
<ak-form-group .expanded=${true}>
|
||||||
<span slot="header"> ${t`Stage-specific settings`} </span>
|
<span slot="header"> ${t`Stage-specific settings`} </span>
|
||||||
<div slot="body" class="pf-c-form">
|
<div slot="body" class="pf-c-form">
|
||||||
|
@ -247,7 +260,7 @@ export class AuthenticatorSMSStageForm extends ModelForm<AuthenticatorSMSStage,
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value="${ifDefined(this.instance?.fromNumber || "")}"
|
value="${first(this.instance?.fromNumber, "")}"
|
||||||
class="pf-c-form-control"
|
class="pf-c-form-control"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -9,7 +9,6 @@ import { t } from "@lingui/macro";
|
||||||
|
|
||||||
import { TemplateResult, html } from "lit";
|
import { TemplateResult, html } from "lit";
|
||||||
import { customElement } from "lit/decorators.js";
|
import { customElement } from "lit/decorators.js";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AuthenticatorStaticStage,
|
AuthenticatorStaticStage,
|
||||||
|
@ -57,11 +56,25 @@ export class AuthenticatorStaticStageForm extends ModelForm<AuthenticatorStaticS
|
||||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value="${ifDefined(this.instance?.name || "")}"
|
value="${first(this.instance?.name, "")}"
|
||||||
class="pf-c-form-control"
|
class="pf-c-form-control"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
<ak-form-element-horizontal
|
||||||
|
label=${t`Authenticator type name`}
|
||||||
|
?required=${false}
|
||||||
|
name="friendlyName"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value="${first(this.instance?.friendlyName, "")}"
|
||||||
|
class="pf-c-form-control"
|
||||||
|
/>
|
||||||
|
<p class="pf-c-form__helper-text">
|
||||||
|
${t`Display name of this authenticator, used by users when they enroll an authenticator.`}
|
||||||
|
</p>
|
||||||
|
</ak-form-element-horizontal>
|
||||||
<ak-form-group .expanded=${true}>
|
<ak-form-group .expanded=${true}>
|
||||||
<span slot="header"> ${t`Stage-specific settings`} </span>
|
<span slot="header"> ${t`Stage-specific settings`} </span>
|
||||||
<div slot="body" class="pf-c-form">
|
<div slot="body" class="pf-c-form">
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { RenderFlowOption } from "@goauthentik/admin/flows/utils";
|
import { RenderFlowOption } from "@goauthentik/admin/flows/utils";
|
||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
|
import { first } from "@goauthentik/common/utils";
|
||||||
import "@goauthentik/elements/forms/FormGroup";
|
import "@goauthentik/elements/forms/FormGroup";
|
||||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||||
|
@ -9,7 +10,6 @@ import { t } from "@lingui/macro";
|
||||||
|
|
||||||
import { TemplateResult, html } from "lit";
|
import { TemplateResult, html } from "lit";
|
||||||
import { customElement } from "lit/decorators.js";
|
import { customElement } from "lit/decorators.js";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AuthenticatorTOTPStage,
|
AuthenticatorTOTPStage,
|
||||||
|
@ -58,11 +58,25 @@ export class AuthenticatorTOTPStageForm extends ModelForm<AuthenticatorTOTPStage
|
||||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value="${ifDefined(this.instance?.name || "")}"
|
value="${first(this.instance?.name, "")}"
|
||||||
class="pf-c-form-control"
|
class="pf-c-form-control"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
<ak-form-element-horizontal
|
||||||
|
label=${t`Authenticator type name`}
|
||||||
|
?required=${false}
|
||||||
|
name="friendlyName"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value="${first(this.instance?.friendlyName, "")}"
|
||||||
|
class="pf-c-form-control"
|
||||||
|
/>
|
||||||
|
<p class="pf-c-form__helper-text">
|
||||||
|
${t`Display name of this authenticator, used by users when they enroll an authenticator.`}
|
||||||
|
</p>
|
||||||
|
</ak-form-element-horizontal>
|
||||||
<ak-form-group .expanded=${true}>
|
<ak-form-group .expanded=${true}>
|
||||||
<span slot="header"> ${t`Stage-specific settings`} </span>
|
<span slot="header"> ${t`Stage-specific settings`} </span>
|
||||||
<div slot="body" class="pf-c-form">
|
<div slot="body" class="pf-c-form">
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { RenderFlowOption } from "@goauthentik/admin/flows/utils";
|
import { RenderFlowOption } from "@goauthentik/admin/flows/utils";
|
||||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||||
|
import { first } from "@goauthentik/common/utils";
|
||||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||||
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
|
||||||
import "@goauthentik/elements/forms/Radio";
|
import "@goauthentik/elements/forms/Radio";
|
||||||
|
@ -9,7 +10,6 @@ import { t } from "@lingui/macro";
|
||||||
|
|
||||||
import { TemplateResult, html } from "lit";
|
import { TemplateResult, html } from "lit";
|
||||||
import { customElement } from "lit/decorators.js";
|
import { customElement } from "lit/decorators.js";
|
||||||
import { ifDefined } from "lit/directives/if-defined.js";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AuthenticateWebAuthnStage,
|
AuthenticateWebAuthnStage,
|
||||||
|
@ -63,11 +63,25 @@ export class AuthenticateWebAuthnStageForm extends ModelForm<AuthenticateWebAuth
|
||||||
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
<ak-form-element-horizontal label=${t`Name`} ?required=${true} name="name">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value="${ifDefined(this.instance?.name || "")}"
|
value="${first(this.instance?.name, "")}"
|
||||||
class="pf-c-form-control"
|
class="pf-c-form-control"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
<ak-form-element-horizontal
|
||||||
|
label=${t`Authenticator type name`}
|
||||||
|
?required=${false}
|
||||||
|
name="friendlyName"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value="${first(this.instance?.friendlyName, "")}"
|
||||||
|
class="pf-c-form-control"
|
||||||
|
/>
|
||||||
|
<p class="pf-c-form__helper-text">
|
||||||
|
${t`Display name of this authenticator, used by users when they enroll an authenticator.`}
|
||||||
|
</p>
|
||||||
|
</ak-form-element-horizontal>
|
||||||
<ak-form-group .expanded=${true}>
|
<ak-form-group .expanded=${true}>
|
||||||
<span slot="header"> ${t`Stage-specific settings`} </span>
|
<span slot="header"> ${t`Stage-specific settings`} </span>
|
||||||
<div slot="body" class="pf-c-form">
|
<div slot="body" class="pf-c-form">
|
||||||
|
|
|
@ -4,8 +4,7 @@ import "@goauthentik/elements/buttons/ModalButton";
|
||||||
import "@goauthentik/elements/buttons/TokenCopyButton";
|
import "@goauthentik/elements/buttons/TokenCopyButton";
|
||||||
import "@goauthentik/elements/forms/DeleteBulkForm";
|
import "@goauthentik/elements/forms/DeleteBulkForm";
|
||||||
import "@goauthentik/elements/forms/ModalForm";
|
import "@goauthentik/elements/forms/ModalForm";
|
||||||
import { PaginatedResponse } from "@goauthentik/elements/table/Table";
|
import { PaginatedResponse, Table, TableColumn } from "@goauthentik/elements/table/Table";
|
||||||
import { Table, TableColumn } from "@goauthentik/elements/table/Table";
|
|
||||||
import "@goauthentik/user/user-settings/mfa/MFADeviceForm";
|
import "@goauthentik/user/user-settings/mfa/MFADeviceForm";
|
||||||
|
|
||||||
import { t } from "@lingui/macro";
|
import { t } from "@lingui/macro";
|
||||||
|
@ -17,17 +16,8 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||||
import { AuthenticatorsApi, Device, UserSetting } from "@goauthentik/api";
|
import { AuthenticatorsApi, Device, UserSetting } from "@goauthentik/api";
|
||||||
|
|
||||||
export function stageToAuthenticatorName(stage: UserSetting): string {
|
export function stageToAuthenticatorName(stage: UserSetting): string {
|
||||||
switch (stage.component) {
|
if (stage.title) {
|
||||||
case "ak-user-settings-authenticator-duo":
|
return stage.title;
|
||||||
return t`Duo authenticator`;
|
|
||||||
case "ak-user-settings-authenticator-sms":
|
|
||||||
return t`SMS authenticator`;
|
|
||||||
case "ak-user-settings-authenticator-static":
|
|
||||||
return t`Static authenticator`;
|
|
||||||
case "ak-user-settings-authenticator-totp":
|
|
||||||
return t`TOTP authenticator`;
|
|
||||||
case "ak-user-settings-authenticator-webauthn":
|
|
||||||
return t`Security key authenticator`;
|
|
||||||
}
|
}
|
||||||
return `Invalid stage component ${stage.component}`;
|
return `Invalid stage component ${stage.component}`;
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue