core: add configure_url to UserSettings for both stages and sources

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-06-08 17:56:35 +02:00
parent 029d58191e
commit fb8d67a9d9
44 changed files with 164 additions and 168 deletions

View File

@ -24,8 +24,7 @@ from structlog.stdlib import get_logger
from authentik.core.exceptions import PropertyMappingExpressionException from authentik.core.exceptions import PropertyMappingExpressionException
from authentik.core.signals import password_changed from authentik.core.signals import password_changed
from authentik.core.types import UILoginButton from authentik.core.types import UILoginButton, UserSettingSerializer
from authentik.flows.challenge import Challenge
from authentik.flows.models import Flow from authentik.flows.models import Flow
from authentik.lib.config import CONFIG from authentik.lib.config import CONFIG
from authentik.lib.models import CreatedUpdatedModel, SerializerModel from authentik.lib.models import CreatedUpdatedModel, SerializerModel
@ -342,9 +341,9 @@ class Source(ManagedModel, SerializerModel, PolicyBindingModel):
return None return None
@property @property
def ui_user_settings(self) -> Optional[Challenge]: def ui_user_settings(self) -> Optional[UserSettingSerializer]:
"""Entrypoint to integrate with User settings. Can either return None if no """Entrypoint to integrate with User settings. Can either return None if no
user settings are available, or a challenge.""" user settings are available, or UserSettingSerializer."""
return None return None
def __str__(self): def __str__(self):

View File

@ -36,3 +36,4 @@ class UserSettingSerializer(PassiveSerializer):
object_uid = CharField() object_uid = CharField()
component = CharField() component = CharField()
title = CharField() title = CharField()
configure_url = CharField()

View File

@ -5,7 +5,6 @@ from django.urls.base import reverse
from drf_spectacular.utils import extend_schema from drf_spectacular.utils import extend_schema
from rest_framework import mixins from rest_framework import mixins
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.fields import BooleanField, CharField
from rest_framework.request import Request from rest_framework.request import Request
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer, SerializerMethodField from rest_framework.serializers import ModelSerializer, SerializerMethodField
@ -21,12 +20,6 @@ from authentik.lib.utils.reflection import all_subclasses
LOGGER = get_logger() LOGGER = get_logger()
class StageUserSettingSerializer(UserSettingSerializer):
"""User settings but can include a configure flow"""
configure_flow = CharField(required=False)
class StageSerializer(ModelSerializer, MetaNameSerializer): class StageSerializer(ModelSerializer, MetaNameSerializer):
"""Stage Serializer""" """Stage Serializer"""
@ -87,7 +80,7 @@ class StageViewSet(
data = sorted(data, key=lambda x: x["name"]) data = sorted(data, key=lambda x: x["name"])
return Response(TypeCreateSerializer(data, many=True).data) return Response(TypeCreateSerializer(data, many=True).data)
@extend_schema(responses={200: StageUserSettingSerializer(many=True)}) @extend_schema(responses={200: UserSettingSerializer(many=True)})
@action(detail=False, pagination_class=None, filter_backends=[]) @action(detail=False, pagination_class=None, filter_backends=[])
def user_settings(self, request: Request) -> Response: def user_settings(self, request: Request) -> Response:
"""Get all stages the user can configure""" """Get all stages the user can configure"""
@ -98,8 +91,8 @@ class StageViewSet(
if not user_settings: if not user_settings:
continue continue
user_settings.initial_data["object_uid"] = str(stage.pk) user_settings.initial_data["object_uid"] = str(stage.pk)
if hasattr(stage, "configure_flow"): if hasattr(stage, "configure_url"):
user_settings.initial_data["configure_flow"] = reverse( user_settings.initial_data["configure_url"] = reverse(
"authentik_flows:configure", "authentik_flows:configure",
kwargs={"stage_uuid": stage.uuid.hex}, kwargs={"stage_uuid": stage.uuid.hex},
) )

View File

@ -43,7 +43,7 @@ class Challenge(PassiveSerializer):
type = ChoiceField( type = ChoiceField(
choices=[(x.value, x.name) for x in ChallengeTypes], choices=[(x.value, x.name) for x in ChallengeTypes],
) )
flow_info = ContextualFlowInfo() flow_info = ContextualFlowInfo(required=False)
component = CharField(default="") component = CharField(default="")
response_errors = DictField( response_errors = DictField(

View File

@ -93,7 +93,11 @@ class TestFlowExecutor(TestCase):
{ {
"component": "ak-stage-access-denied", "component": "ak-stage-access-denied",
"error_message": FlowNonApplicableException.__doc__, "error_message": FlowNonApplicableException.__doc__,
"title": "", "flow_info": {
"background": flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
"type": ChallengeTypes.NATIVE.value, "type": ChallengeTypes.NATIVE.value,
}, },
) )
@ -422,10 +426,13 @@ class TestFlowExecutor(TestCase):
self.assertJSONEqual( self.assertJSONEqual(
force_str(response.content), force_str(response.content),
{ {
"background": flow.background_url,
"type": ChallengeTypes.NATIVE.value, "type": ChallengeTypes.NATIVE.value,
"component": "ak-stage-dummy", "component": "ak-stage-dummy",
"title": binding.stage.name, "flow_info": {
"background": flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
}, },
) )
@ -453,10 +460,13 @@ class TestFlowExecutor(TestCase):
self.assertJSONEqual( self.assertJSONEqual(
force_str(response.content), force_str(response.content),
{ {
"background": flow.background_url,
"type": ChallengeTypes.NATIVE.value, "type": ChallengeTypes.NATIVE.value,
"component": "ak-stage-dummy", "component": "ak-stage-dummy",
"title": binding4.stage.name, "flow_info": {
"background": flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
}, },
) )

View File

@ -8,6 +8,7 @@ from django.http import Http404, HttpRequest, HttpResponse, HttpResponseRedirect
from django.http.request import QueryDict from django.http.request import QueryDict
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.urls.base import reverse
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.views.decorators.clickjacking import xframe_options_sameorigin from django.views.decorators.clickjacking import xframe_options_sameorigin
from django.views.generic import View from django.views.generic import View
@ -309,9 +310,13 @@ class FlowExecutorView(APIView):
AccessDeniedChallenge( AccessDeniedChallenge(
{ {
"error_message": error_message, "error_message": error_message,
"title": self.flow.title,
"type": ChallengeTypes.NATIVE.value, "type": ChallengeTypes.NATIVE.value,
"component": "ak-stage-access-denied", "component": "ak-stage-access-denied",
"flow_info": {
"title": self.flow.title,
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
},
} }
) )
) )
@ -413,7 +418,10 @@ def to_stage_response(request: HttpRequest, source: HttpResponse) -> HttpRespons
) )
return HttpChallengeResponse( return HttpChallengeResponse(
RedirectChallenge( RedirectChallenge(
{"type": ChallengeTypes.REDIRECT, "to": str(redirect_url)} {
"type": ChallengeTypes.REDIRECT,
"to": str(redirect_url),
}
) )
) )
if isinstance(source, TemplateResponse): if isinstance(source, TemplateResponse):
@ -425,7 +433,7 @@ def to_stage_response(request: HttpRequest, source: HttpResponse) -> HttpRespons
} }
) )
) )
# Check for actual HttpResponse (without isinstance as we dont want to check inheritance) # Check for actual HttpResponse (without isinstance as we don't want to check inheritance)
if source.__class__ == HttpResponse: if source.__class__ == HttpResponse:
return HttpChallengeResponse( return HttpChallengeResponse(
ShellChallenge( ShellChallenge(

View File

@ -87,6 +87,10 @@ class OAuthSource(Source):
data={ data={
"title": f"OAuth {self.name}", "title": f"OAuth {self.name}",
"component": "ak-user-settings-source-oauth", "component": "ak-user-settings-source-oauth",
"configure_url": reverse(
"authentik_sources_oauth:oauth-client-login",
kwargs={"source_slug": self.slug},
),
} }
) )

View File

@ -47,7 +47,11 @@ class TestUserDenyStage(TestCase):
{ {
"component": "ak-stage-access-denied", "component": "ak-stage-access-denied",
"error_message": None, "error_message": None,
"title": "", "flow_info": {
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
"type": ChallengeTypes.NATIVE.value, "type": ChallengeTypes.NATIVE.value,
}, },
) )

View File

@ -108,7 +108,6 @@ class TestIdentificationStage(TestCase):
self.assertJSONEqual( self.assertJSONEqual(
force_str(response.content), force_str(response.content),
{ {
"background": self.flow.background_url,
"type": ChallengeTypes.NATIVE.value, "type": ChallengeTypes.NATIVE.value,
"component": "ak-stage-identification", "component": "ak-stage-identification",
"password_fields": True, "password_fields": True,
@ -118,6 +117,11 @@ class TestIdentificationStage(TestCase):
{"code": "invalid", "string": "Failed to " "authenticate."} {"code": "invalid", "string": "Failed to " "authenticate."}
] ]
}, },
"flow_info": {
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
"sources": [ "sources": [
{ {
"challenge": { "challenge": {
@ -129,7 +133,6 @@ class TestIdentificationStage(TestCase):
"name": "test", "name": "test",
} }
], ],
"title": "",
"user_fields": ["email"], "user_fields": ["email"],
}, },
) )
@ -181,7 +184,6 @@ class TestIdentificationStage(TestCase):
self.assertJSONEqual( self.assertJSONEqual(
force_str(response.content), force_str(response.content),
{ {
"background": flow.background_url,
"type": ChallengeTypes.NATIVE.value, "type": ChallengeTypes.NATIVE.value,
"component": "ak-stage-identification", "component": "ak-stage-identification",
"user_fields": ["email"], "user_fields": ["email"],
@ -191,7 +193,11 @@ class TestIdentificationStage(TestCase):
kwargs={"flow_slug": "unique-enrollment-string"}, kwargs={"flow_slug": "unique-enrollment-string"},
), ),
"primary_action": "Log in", "primary_action": "Log in",
"title": self.flow.title, "flow_info": {
"background": flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": self.flow.title,
},
"sources": [ "sources": [
{ {
"icon_url": "/static/authentik/sources/.svg", "icon_url": "/static/authentik/sources/.svg",
@ -229,7 +235,6 @@ class TestIdentificationStage(TestCase):
self.assertJSONEqual( self.assertJSONEqual(
force_str(response.content), force_str(response.content),
{ {
"background": flow.background_url,
"type": ChallengeTypes.NATIVE.value, "type": ChallengeTypes.NATIVE.value,
"component": "ak-stage-identification", "component": "ak-stage-identification",
"user_fields": ["email"], "user_fields": ["email"],
@ -239,7 +244,11 @@ class TestIdentificationStage(TestCase):
kwargs={"flow_slug": "unique-recovery-string"}, kwargs={"flow_slug": "unique-recovery-string"},
), ),
"primary_action": "Log in", "primary_action": "Log in",
"title": self.flow.title, "flow_info": {
"background": flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": self.flow.title,
},
"sources": [ "sources": [
{ {
"challenge": { "challenge": {

View File

@ -62,8 +62,12 @@ class TestUserLoginStage(TestCase):
{ {
"component": "ak-stage-access-denied", "component": "ak-stage-access-denied",
"error_message": None, "error_message": None,
"title": "",
"type": ChallengeTypes.NATIVE.value, "type": ChallengeTypes.NATIVE.value,
"flow_info": {
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
}, },
) )

View File

@ -67,8 +67,12 @@ class TestPasswordStage(TestCase):
{ {
"component": "ak-stage-access-denied", "component": "ak-stage-access-denied",
"error_message": None, "error_message": None,
"title": "",
"type": ChallengeTypes.NATIVE.value, "type": ChallengeTypes.NATIVE.value,
"flow_info": {
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
}, },
) )
@ -205,7 +209,11 @@ class TestPasswordStage(TestCase):
{ {
"component": "ak-stage-access-denied", "component": "ak-stage-access-denied",
"error_message": None, "error_message": None,
"title": "", "flow_info": {
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
"type": ChallengeTypes.NATIVE.value, "type": ChallengeTypes.NATIVE.value,
}, },
) )

View File

@ -54,8 +54,12 @@ class TestUserDeleteStage(TestCase):
{ {
"component": "ak-stage-access-denied", "component": "ak-stage-access-denied",
"error_message": None, "error_message": None,
"title": "",
"type": ChallengeTypes.NATIVE.value, "type": ChallengeTypes.NATIVE.value,
"flow_info": {
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
}, },
) )

View File

@ -108,7 +108,11 @@ class TestUserLoginStage(TestCase):
{ {
"component": "ak-stage-access-denied", "component": "ak-stage-access-denied",
"error_message": None, "error_message": None,
"title": "",
"type": ChallengeTypes.NATIVE.value, "type": ChallengeTypes.NATIVE.value,
"flow_info": {
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
}, },
) )

View File

@ -151,8 +151,12 @@ class TestUserWriteStage(TestCase):
{ {
"component": "ak-stage-access-denied", "component": "ak-stage-access-denied",
"error_message": None, "error_message": None,
"title": "",
"type": ChallengeTypes.NATIVE.value, "type": ChallengeTypes.NATIVE.value,
"flow_info": {
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
}, },
) )
@ -184,8 +188,12 @@ class TestUserWriteStage(TestCase):
{ {
"component": "ak-stage-access-denied", "component": "ak-stage-access-denied",
"error_message": None, "error_message": None,
"title": "",
"type": ChallengeTypes.NATIVE.value, "type": ChallengeTypes.NATIVE.value,
"flow_info": {
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
}, },
) )
@ -217,7 +225,11 @@ class TestUserWriteStage(TestCase):
{ {
"component": "ak-stage-access-denied", "component": "ak-stage-access-denied",
"error_message": None, "error_message": None,
"title": "",
"type": ChallengeTypes.NATIVE.value, "type": ChallengeTypes.NATIVE.value,
"flow_info": {
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
}, },
) )

View File

@ -11170,7 +11170,7 @@ paths:
schema: schema:
type: array type: array
items: items:
$ref: '#/components/schemas/StageUserSetting' $ref: '#/components/schemas/UserSetting'
description: '' description: ''
'400': '400':
$ref: '#/components/schemas/ValidationError' $ref: '#/components/schemas/ValidationError'
@ -15361,7 +15361,6 @@ components:
error_message: error_message:
type: string type: string
required: required:
- flow_info
- type - type
ActionEnum: ActionEnum:
enum: enum:
@ -15692,7 +15691,6 @@ components:
required: required:
- activation_barcode - activation_barcode
- activation_code - activation_code
- flow_info
- pending_user - pending_user
- pending_user_avatar - pending_user_avatar
- stage_uuid - stage_uuid
@ -15801,7 +15799,6 @@ components:
type: string type: string
required: required:
- codes - codes
- flow_info
- pending_user - pending_user
- pending_user_avatar - pending_user_avatar
- type - type
@ -15899,7 +15896,6 @@ components:
type: string type: string
required: required:
- config_url - config_url
- flow_info
- pending_user - pending_user
- pending_user_avatar - pending_user_avatar
- type - type
@ -16077,7 +16073,6 @@ components:
$ref: '#/components/schemas/DeviceChallenge' $ref: '#/components/schemas/DeviceChallenge'
required: required:
- device_challenges - device_challenges
- flow_info
- pending_user - pending_user
- pending_user_avatar - pending_user_avatar
- type - type
@ -16120,7 +16115,6 @@ components:
type: object type: object
additionalProperties: {} additionalProperties: {}
required: required:
- flow_info
- pending_user - pending_user
- pending_user_avatar - pending_user_avatar
- registration - registration
@ -16169,7 +16163,6 @@ components:
type: string type: string
required: required:
- attrs - attrs
- flow_info
- type - type
- url - url
BackendsEnum: BackendsEnum:
@ -16222,7 +16215,6 @@ components:
site_key: site_key:
type: string type: string
required: required:
- flow_info
- pending_user - pending_user
- pending_user_avatar - pending_user_avatar
- site_key - site_key
@ -16430,7 +16422,6 @@ components:
items: items:
$ref: '#/components/schemas/Permission' $ref: '#/components/schemas/Permission'
required: required:
- flow_info
- header_text - header_text
- pending_user - pending_user
- pending_user_avatar - pending_user_avatar
@ -16730,7 +16721,6 @@ components:
items: items:
$ref: '#/components/schemas/ErrorDetail' $ref: '#/components/schemas/ErrorDetail'
required: required:
- flow_info
- type - type
DummyChallengeResponseRequest: DummyChallengeResponseRequest:
type: object type: object
@ -16889,7 +16879,6 @@ components:
items: items:
$ref: '#/components/schemas/ErrorDetail' $ref: '#/components/schemas/ErrorDetail'
required: required:
- flow_info
- type - type
EmailChallengeResponseRequest: EmailChallengeResponseRequest:
type: object type: object
@ -17700,7 +17689,6 @@ components:
items: items:
$ref: '#/components/schemas/UILoginButton' $ref: '#/components/schemas/UILoginButton'
required: required:
- flow_info
- password_fields - password_fields
- primary_action - primary_action
- type - type
@ -21644,7 +21632,6 @@ components:
recovery_url: recovery_url:
type: string type: string
required: required:
- flow_info
- pending_user - pending_user
- pending_user_avatar - pending_user_avatar
- type - type
@ -23394,7 +23381,6 @@ components:
type: string type: string
required: required:
- client_id - client_id
- flow_info
- slug - slug
- type - type
PlexAuthenticationChallengeResponseRequest: PlexAuthenticationChallengeResponseRequest:
@ -23754,7 +23740,6 @@ components:
$ref: '#/components/schemas/StagePrompt' $ref: '#/components/schemas/StagePrompt'
required: required:
- fields - fields
- flow_info
- type - type
PromptChallengeResponseRequest: PromptChallengeResponseRequest:
type: object type: object
@ -24210,7 +24195,6 @@ components:
to: to:
type: string type: string
required: required:
- flow_info
- to - to
- type - type
RefreshTokenModel: RefreshTokenModel:
@ -24927,7 +24911,6 @@ components:
type: string type: string
required: required:
- body - body
- flow_info
- type - type
SignatureAlgorithmEnum: SignatureAlgorithmEnum:
enum: enum:
@ -25093,22 +25076,6 @@ components:
$ref: '#/components/schemas/FlowRequest' $ref: '#/components/schemas/FlowRequest'
required: required:
- name - name
StageUserSetting:
type: object
description: User settings but can include a configure flow
properties:
object_uid:
type: string
component:
type: string
title:
type: string
configure_flow:
type: string
required:
- component
- object_uid
- title
StaticDevice: StaticDevice:
type: object type: object
description: Serializer for static authenticator devices description: Serializer for static authenticator devices
@ -25788,8 +25755,11 @@ components:
type: string type: string
title: title:
type: string type: string
configure_url:
type: string
required: required:
- component - component
- configure_url
- object_uid - object_uid
- title - title
UserWriteStage: UserWriteStage:

View File

@ -1,7 +0,0 @@
export class AppURLManager {
static sourceOAuth(slug: string, action: string): string {
return `/source/oauth/${action}/${slug}/`;
}
}

View File

@ -86,8 +86,8 @@ export class FlowExecutor extends LitElement implements StageHost {
private postUpdate(): void { private postUpdate(): void {
tenant().then(tenant => { tenant().then(tenant => {
if (this.challenge?.flowInfo.title) { if (this.challenge?.flowInfo?.title) {
document.title = `${this.challenge.flowInfo.title} - ${tenant.brandingTitle}`; document.title = `${this.challenge.flowInfo?.title} - ${tenant.brandingTitle}`;
} else { } else {
document.title = tenant.brandingTitle || TITLE_DEFAULT; document.title = tenant.brandingTitle || TITLE_DEFAULT;
} }
@ -124,7 +124,7 @@ export class FlowExecutor extends LitElement implements StageHost {
}).then((challenge) => { }).then((challenge) => {
this.challenge = challenge; this.challenge = challenge;
// Only set background on first update, flow won't change throughout execution // Only set background on first update, flow won't change throughout execution
if (this.challenge?.flowInfo.background) { if (this.challenge?.flowInfo?.background) {
this.setBackground(this.challenge.flowInfo.background); this.setBackground(this.challenge.flowInfo.background);
} }
this.postUpdate(); this.postUpdate();
@ -271,7 +271,7 @@ export class FlowExecutor extends LitElement implements StageHost {
${this.tenant?.brandingTitle != "authentik" ? html` ${this.tenant?.brandingTitle != "authentik" ? html`
<li><a href="https://goauthentik.io">${t`Powered by authentik`}</a></li> <li><a href="https://goauthentik.io">${t`Powered by authentik`}</a></li>
` : html``} ` : html``}
${this.challenge?.flowInfo.background?.startsWith("/static") ? html` ${this.challenge?.flowInfo?.background?.startsWith("/static") ? html`
<li><a href="https://unsplash.com/@danasaki">${t`Background image`}</a></li> <li><a href="https://unsplash.com/@danasaki">${t`Background image`}</a></li>
` : html``} ` : html``}
</ul> </ul>

View File

@ -28,7 +28,7 @@ export class FlowAccessDenied extends BaseStage<AccessDeniedChallenge, FlowChall
} }
return html`<header class="pf-c-login__main-header"> return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl"> <h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title} ${this.challenge.flowInfo?.title}
</h1> </h1>
</header> </header>
<div class="pf-c-login__main-body"> <div class="pf-c-login__main-body">

View File

@ -14,6 +14,7 @@ import "../../FormStatic";
import { AuthenticatorDuoChallenge, StagesApi } from "authentik-api"; import { AuthenticatorDuoChallenge, StagesApi } from "authentik-api";
import { DEFAULT_CONFIG } from "../../../api/Config"; import { DEFAULT_CONFIG } from "../../../api/Config";
import { AuthenticatorDuoChallengeResponseRequest } from "authentik-api/dist/models/AuthenticatorDuoChallengeResponseRequest"; import { AuthenticatorDuoChallengeResponseRequest } from "authentik-api/dist/models/AuthenticatorDuoChallengeResponseRequest";
import { ifDefined } from "lit-html/directives/if-defined";
@customElement("ak-stage-authenticator-duo") @customElement("ak-stage-authenticator-duo")
export class AuthenticatorDuoStage extends BaseStage<AuthenticatorDuoChallenge, AuthenticatorDuoChallengeResponseRequest> { export class AuthenticatorDuoStage extends BaseStage<AuthenticatorDuoChallenge, AuthenticatorDuoChallengeResponseRequest> {
@ -49,7 +50,7 @@ export class AuthenticatorDuoStage extends BaseStage<AuthenticatorDuoChallenge,
} }
return html`<header class="pf-c-login__main-header"> return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl"> <h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title} ${this.challenge.flowInfo?.title}
</h1> </h1>
</header> </header>
<div class="pf-c-login__main-body"> <div class="pf-c-login__main-body">
@ -59,7 +60,7 @@ export class AuthenticatorDuoStage extends BaseStage<AuthenticatorDuoChallenge,
userAvatar="${this.challenge.pendingUserAvatar}" userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}> user=${this.challenge.pendingUser}>
<div slot="link"> <div slot="link">
<a href="${this.challenge.flowInfo.cancelUrl}">${t`Not you?`}</a> <a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}">${t`Not you?`}</a>
</div> </div>
</ak-form-static> </ak-form-static>
<img src=${this.challenge.activationBarcode} /> <img src=${this.challenge.activationBarcode} />

View File

@ -13,6 +13,7 @@ import "../../../elements/EmptyState";
import "../../FormStatic"; import "../../FormStatic";
import { AuthenticatorStaticChallenge } from "authentik-api"; import { AuthenticatorStaticChallenge } from "authentik-api";
import { AuthenticatorStaticChallengeResponseRequest } from "authentik-api/dist/models/AuthenticatorStaticChallengeResponseRequest"; import { AuthenticatorStaticChallengeResponseRequest } from "authentik-api/dist/models/AuthenticatorStaticChallengeResponseRequest";
import { ifDefined } from "lit-html/directives/if-defined";
export const STATIC_TOKEN_STYLE = css` export const STATIC_TOKEN_STYLE = css`
/* Static OTP Tokens */ /* Static OTP Tokens */
@ -46,7 +47,7 @@ export class AuthenticatorStaticStage extends BaseStage<AuthenticatorStaticChall
} }
return html`<header class="pf-c-login__main-header"> return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl"> <h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title} ${this.challenge.flowInfo?.title}
</h1> </h1>
</header> </header>
<div class="pf-c-login__main-body"> <div class="pf-c-login__main-body">
@ -56,7 +57,7 @@ export class AuthenticatorStaticStage extends BaseStage<AuthenticatorStaticChall
userAvatar="${this.challenge.pendingUserAvatar}" userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}> user=${this.challenge.pendingUser}>
<div slot="link"> <div slot="link">
<a href="${this.challenge.flowInfo.cancelUrl}">${t`Not you?`}</a> <a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}">${t`Not you?`}</a>
</div> </div>
</ak-form-static> </ak-form-static>
<ak-form-element <ak-form-element

View File

@ -15,6 +15,7 @@ import "../../../elements/EmptyState";
import "../../FormStatic"; import "../../FormStatic";
import { MessageLevel } from "../../../elements/messages/Message"; import { MessageLevel } from "../../../elements/messages/Message";
import { AuthenticatorTOTPChallenge, AuthenticatorTOTPChallengeResponseRequest } from "authentik-api"; import { AuthenticatorTOTPChallenge, AuthenticatorTOTPChallengeResponseRequest } from "authentik-api";
import { ifDefined } from "lit-html/directives/if-defined";
@customElement("ak-stage-authenticator-totp") @customElement("ak-stage-authenticator-totp")
@ -33,7 +34,7 @@ export class AuthenticatorTOTPStage extends BaseStage<AuthenticatorTOTPChallenge
} }
return html`<header class="pf-c-login__main-header"> return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl"> <h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title} ${this.challenge.flowInfo?.title}
</h1> </h1>
</header> </header>
<div class="pf-c-login__main-body"> <div class="pf-c-login__main-body">
@ -43,7 +44,7 @@ export class AuthenticatorTOTPStage extends BaseStage<AuthenticatorTOTPChallenge
userAvatar="${this.challenge.pendingUserAvatar}" userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}> user=${this.challenge.pendingUser}>
<div slot="link"> <div slot="link">
<a href="${this.challenge.flowInfo.cancelUrl}">${t`Not you?`}</a> <a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}">${t`Not you?`}</a>
</div> </div>
</ak-form-static> </ak-form-static>
<input type="hidden" name="otp_uri" value=${this.challenge.configUrl} /> <input type="hidden" name="otp_uri" value=${this.challenge.configUrl} />

View File

@ -162,7 +162,7 @@ export class AuthenticatorValidateStage extends BaseStage<AuthenticatorValidatio
} }
return html`<header class="pf-c-login__main-header"> return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl"> <h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title} ${this.challenge.flowInfo?.title}
</h1> </h1>
${this.selectedDeviceChallenge ? "" : html`<p class="pf-c-login__main-header-desc"> ${this.selectedDeviceChallenge ? "" : html`<p class="pf-c-login__main-header-desc">
${t`Select an identification method.`} ${t`Select an identification method.`}

View File

@ -15,6 +15,7 @@ import { PasswordManagerPrefill } from "../identification/IdentificationStage";
import "../../FormStatic"; import "../../FormStatic";
import { AuthenticatorValidationChallenge } from "authentik-api/dist/models/AuthenticatorValidationChallenge"; import { AuthenticatorValidationChallenge } from "authentik-api/dist/models/AuthenticatorValidationChallenge";
import { AuthenticatorValidationChallengeResponseRequest, DeviceChallenge } from "authentik-api"; import { AuthenticatorValidationChallengeResponseRequest, DeviceChallenge } from "authentik-api";
import { ifDefined } from "lit-html/directives/if-defined";
@customElement("ak-stage-authenticator-validate-code") @customElement("ak-stage-authenticator-validate-code")
export class AuthenticatorValidateStageWebCode extends BaseStage<AuthenticatorValidationChallenge, AuthenticatorValidationChallengeResponseRequest> { export class AuthenticatorValidateStageWebCode extends BaseStage<AuthenticatorValidationChallenge, AuthenticatorValidationChallengeResponseRequest> {
@ -43,7 +44,7 @@ export class AuthenticatorValidateStageWebCode extends BaseStage<AuthenticatorVa
userAvatar="${this.challenge.pendingUserAvatar}" userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}> user=${this.challenge.pendingUser}>
<div slot="link"> <div slot="link">
<a href="${this.challenge.flowInfo.cancelUrl}">${t`Not you?`}</a> <a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}">${t`Not you?`}</a>
</div> </div>
</ak-form-static> </ak-form-static>
<ak-form-element <ak-form-element

View File

@ -14,6 +14,7 @@ import "../../../elements/EmptyState";
import "../../FormStatic"; import "../../FormStatic";
import { AuthenticatorValidationChallenge } from "authentik-api/dist/models/AuthenticatorValidationChallenge"; import { AuthenticatorValidationChallenge } from "authentik-api/dist/models/AuthenticatorValidationChallenge";
import { AuthenticatorValidationChallengeResponseRequest, DeviceChallenge } from "authentik-api"; import { AuthenticatorValidationChallengeResponseRequest, DeviceChallenge } from "authentik-api";
import { ifDefined } from "lit-html/directives/if-defined";
@customElement("ak-stage-authenticator-validate-duo") @customElement("ak-stage-authenticator-validate-duo")
export class AuthenticatorValidateStageWebDuo extends BaseStage<AuthenticatorValidationChallenge, AuthenticatorValidationChallengeResponseRequest> { export class AuthenticatorValidateStageWebDuo extends BaseStage<AuthenticatorValidationChallenge, AuthenticatorValidationChallengeResponseRequest> {
@ -48,7 +49,7 @@ export class AuthenticatorValidateStageWebDuo extends BaseStage<AuthenticatorVal
userAvatar="${this.challenge.pendingUserAvatar}" userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}> user=${this.challenge.pendingUser}>
<div slot="link"> <div slot="link">
<a href="${this.challenge.flowInfo.cancelUrl}">${t`Not you?`}</a> <a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}">${t`Not you?`}</a>
</div> </div>
</ak-form-static> </ak-form-static>

View File

@ -85,7 +85,7 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage<AuthenticatorW
render(): TemplateResult { render(): TemplateResult {
return html`<header class="pf-c-login__main-header"> return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl"> <h1 class="pf-c-title pf-m-3xl">
${this.challenge?.flowInfo.title} ${this.challenge?.flowInfo?.title}
</h1> </h1>
</header> </header>
<div class="pf-c-login__main-body"> <div class="pf-c-login__main-body">

View File

@ -32,7 +32,7 @@ export class AutosubmitStage extends BaseStage<AutosubmitChallenge, AutoSubmitCh
} }
return html`<header class="pf-c-login__main-header"> return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl"> <h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title} ${this.challenge.flowInfo?.title}
</h1> </h1>
</header> </header>
<div class="pf-c-login__main-body"> <div class="pf-c-login__main-body">

View File

@ -13,6 +13,7 @@ import "../../../elements/forms/FormElement";
import "../../../elements/EmptyState"; import "../../../elements/EmptyState";
import "../../FormStatic"; import "../../FormStatic";
import { CaptchaChallenge, CaptchaChallengeResponseRequest } from "authentik-api"; import { CaptchaChallenge, CaptchaChallengeResponseRequest } from "authentik-api";
import { ifDefined } from "lit-html/directives/if-defined";
@customElement("ak-stage-captcha") @customElement("ak-stage-captcha")
export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeResponseRequest> { export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeResponseRequest> {
@ -57,7 +58,7 @@ export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeRe
} }
return html`<header class="pf-c-login__main-header"> return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl"> <h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title} ${this.challenge.flowInfo?.title}
</h1> </h1>
</header> </header>
<div class="pf-c-login__main-body"> <div class="pf-c-login__main-body">
@ -67,7 +68,7 @@ export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeRe
userAvatar="${this.challenge.pendingUserAvatar}" userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}> user=${this.challenge.pendingUser}>
<div slot="link"> <div slot="link">
<a href="${this.challenge.flowInfo.cancelUrl}">${t`Not you?`}</a> <a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}">${t`Not you?`}</a>
</div> </div>
</ak-form-static> </ak-form-static>
<div class="ak-loading"> <div class="ak-loading">

View File

@ -11,6 +11,7 @@ import { BaseStage } from "../base";
import "../../../elements/EmptyState"; import "../../../elements/EmptyState";
import "../../FormStatic"; import "../../FormStatic";
import { ConsentChallenge, ConsentChallengeResponseRequest } from "authentik-api"; import { ConsentChallenge, ConsentChallengeResponseRequest } from "authentik-api";
import { ifDefined } from "lit-html/directives/if-defined";
@customElement("ak-stage-consent") @customElement("ak-stage-consent")
@ -29,7 +30,7 @@ export class ConsentStage extends BaseStage<ConsentChallenge, ConsentChallengeRe
} }
return html`<header class="pf-c-login__main-header"> return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl"> <h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title} ${this.challenge.flowInfo?.title}
</h1> </h1>
</header> </header>
<div class="pf-c-login__main-body"> <div class="pf-c-login__main-body">
@ -39,7 +40,7 @@ export class ConsentStage extends BaseStage<ConsentChallenge, ConsentChallengeRe
userAvatar="${this.challenge.pendingUserAvatar}" userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}> user=${this.challenge.pendingUser}>
<div slot="link"> <div slot="link">
<a href="${this.challenge.flowInfo.cancelUrl}">${t`Not you?`}</a> <a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}">${t`Not you?`}</a>
</div> </div>
</ak-form-static> </ak-form-static>
<div class="pf-c-form__group"> <div class="pf-c-form__group">

View File

@ -28,7 +28,7 @@ export class DummyStage extends BaseStage<DummyChallenge, DummyChallengeResponse
} }
return html`<header class="pf-c-login__main-header"> return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl"> <h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title} ${this.challenge.flowInfo?.title}
</h1> </h1>
</header> </header>
<div class="pf-c-login__main-body"> <div class="pf-c-login__main-body">

View File

@ -27,7 +27,7 @@ export class EmailStage extends BaseStage<EmailChallenge, EmailChallengeResponse
} }
return html`<header class="pf-c-login__main-header"> return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl"> <h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title} ${this.challenge.flowInfo?.title}
</h1> </h1>
</header> </header>
<div class="pf-c-login__main-body"> <div class="pf-c-login__main-body">

View File

@ -206,7 +206,7 @@ export class IdentificationStage extends BaseStage<IdentificationChallenge, Iden
} }
return html`<header class="pf-c-login__main-header"> return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl"> <h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title} ${this.challenge.flowInfo?.title}
</h1> </h1>
</header> </header>
<div class="pf-c-login__main-body"> <div class="pf-c-login__main-body">

View File

@ -13,6 +13,7 @@ import "../../../elements/EmptyState";
import { PasswordManagerPrefill } from "../identification/IdentificationStage"; import { PasswordManagerPrefill } from "../identification/IdentificationStage";
import "../../FormStatic"; import "../../FormStatic";
import { PasswordChallenge, PasswordChallengeResponseRequest } from "authentik-api"; import { PasswordChallenge, PasswordChallengeResponseRequest } from "authentik-api";
import { ifDefined } from "lit-html/directives/if-defined";
@customElement("ak-stage-password") @customElement("ak-stage-password")
export class PasswordStage extends BaseStage<PasswordChallenge, PasswordChallengeResponseRequest> { export class PasswordStage extends BaseStage<PasswordChallenge, PasswordChallengeResponseRequest> {
@ -30,7 +31,7 @@ export class PasswordStage extends BaseStage<PasswordChallenge, PasswordChalleng
} }
return html`<header class="pf-c-login__main-header"> return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl"> <h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title} ${this.challenge.flowInfo?.title}
</h1> </h1>
</header> </header>
<div class="pf-c-login__main-body"> <div class="pf-c-login__main-body">
@ -40,7 +41,7 @@ export class PasswordStage extends BaseStage<PasswordChallenge, PasswordChalleng
userAvatar="${this.challenge.pendingUserAvatar}" userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}> user=${this.challenge.pendingUser}>
<div slot="link"> <div slot="link">
<a href="${this.challenge.flowInfo.cancelUrl}">${t`Not you?`}</a> <a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}">${t`Not you?`}</a>
</div> </div>
</ak-form-static> </ak-form-static>
<input name="username" autocomplete="username" type="hidden" value="${this.challenge.pendingUser}"> <input name="username" autocomplete="username" type="hidden" value="${this.challenge.pendingUser}">

View File

@ -111,7 +111,7 @@ export class PromptStage extends BaseStage<PromptChallenge, PromptChallengeRespo
} }
return html`<header class="pf-c-login__main-header"> return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl"> <h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title} ${this.challenge.flowInfo?.title}
</h1> </h1>
</header> </header>
<div class="pf-c-login__main-body"> <div class="pf-c-login__main-body">

View File

@ -4169,10 +4169,6 @@ msgstr "Use the user's email address, but deny enrollment when the email address
msgid "Use the user's username, but deny enrollment when the username already exists." msgid "Use the user's username, but deny enrollment when the username already exists."
msgstr "Use the user's username, but deny enrollment when the username already exists." msgstr "Use the user's username, but deny enrollment when the username already exists."
#: src/pages/sources/oauth/OAuthSourceForm.ts
msgid "Use this redirect URL:"
msgstr "Use this redirect URL:"
#: src/pages/tenants/TenantForm.ts #: src/pages/tenants/TenantForm.ts
msgid "Use this tenant for each domain that doesn't have a dedicated tenant." msgid "Use this tenant for each domain that doesn't have a dedicated tenant."
msgstr "Use this tenant for each domain that doesn't have a dedicated tenant." msgstr "Use this tenant for each domain that doesn't have a dedicated tenant."

View File

@ -4157,10 +4157,6 @@ msgstr ""
msgid "Use the user's username, but deny enrollment when the username already exists." msgid "Use the user's username, but deny enrollment when the username already exists."
msgstr "" msgstr ""
#:
msgid "Use this redirect URL:"
msgstr ""
#: #:
msgid "Use this tenant for each domain that doesn't have a dedicated tenant." msgid "Use this tenant for each domain that doesn't have a dedicated tenant."
msgstr "" msgstr ""

View File

@ -8,7 +8,6 @@ import "../../../elements/forms/HorizontalFormElement";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
import { first } from "../../../utils"; import { first } from "../../../utils";
import { AppURLManager } from "../../../api/legacy";
import { ModelForm } from "../../../elements/forms/ModelForm"; import { ModelForm } from "../../../elements/forms/ModelForm";
@customElement("ak-source-oauth-form") @customElement("ak-source-oauth-form")
@ -95,14 +94,6 @@ export class OAuthSourceForm extends ModelForm<OAuthSource, string> {
</ak-form-group>`; </ak-form-group>`;
} }
getRedirectURI(slug?: string): string {
if (!slug) {
return "";
}
const path = AppURLManager.sourceOAuth(slug, "callback");
return `${window.location.protocol}//${window.location.host}${path}`;
}
renderForm(): TemplateResult { renderForm(): TemplateResult {
return html`<form class="pf-c-form pf-m-horizontal"> return html`<form class="pf-c-form pf-m-horizontal">
<ak-form-element-horizontal <ak-form-element-horizontal
@ -115,16 +106,7 @@ export class OAuthSourceForm extends ModelForm<OAuthSource, string> {
label=${t`Slug`} label=${t`Slug`}
?required=${true} ?required=${true}
name="slug"> name="slug">
<input type="text" value="${ifDefined(this.instance?.slug)}" class="pf-c-form-control" required @input=${(ev: Event) => { <input type="text" value="${ifDefined(this.instance?.slug)}" class="pf-c-form-control" required>
const current = (ev.target as HTMLInputElement).value;
const label = this.shadowRoot?.querySelector<HTMLSpanElement>("#callback-url");
if (!label) return;
label.innerText = this.getRedirectURI(current);
}}>
<p class="pf-c-form__helper-text">
${t`Use this redirect URL:`}
<span id="callback-url">${this.getRedirectURI(this.instance?.slug)}</span>
</p>
</ak-form-element-horizontal> </ak-form-element-horizontal>
<ak-form-element-horizontal name="enabled"> <ak-form-element-horizontal name="enabled">
<div class="pf-c-check"> <div class="pf-c-check">

View File

@ -13,7 +13,7 @@ import AKGlobal from "../../authentik.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css"; import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css"; import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
import { SourcesApi, StagesApi, StageUserSetting, UserSetting } from "authentik-api"; import { SourcesApi, StagesApi, UserSetting } from "authentik-api";
import { DEFAULT_CONFIG } from "../../api/Config"; import { DEFAULT_CONFIG } from "../../api/Config";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
import { ifDefined } from "lit-html/directives/if-defined"; import { ifDefined } from "lit-html/directives/if-defined";
@ -35,22 +35,22 @@ export class UserSettingsPage extends LitElement {
return [PFBase, PFPage, PFFlex, PFDisplay, PFGallery, PFContent, PFCard, PFDescriptionList, PFSizing, PFForm, PFFormControl, AKGlobal]; return [PFBase, PFPage, PFFlex, PFDisplay, PFGallery, PFContent, PFCard, PFDescriptionList, PFSizing, PFForm, PFFormControl, AKGlobal];
} }
renderStageSettings(stage: StageUserSetting): TemplateResult { renderStageSettings(stage: UserSetting): TemplateResult {
switch (stage.component) { switch (stage.component) {
case "ak-user-settings-authenticator-webauthn": case "ak-user-settings-authenticator-webauthn":
return html`<ak-user-settings-authenticator-webauthn objectId=${stage.objectUid} configureFlow=${stage.configureFlow}> return html`<ak-user-settings-authenticator-webauthn objectId=${stage.objectUid} configureUrl=${stage.configureUrl}>
</ak-user-settings-authenticator-webauthn>`; </ak-user-settings-authenticator-webauthn>`;
case "ak-user-settings-password": case "ak-user-settings-password":
return html`<ak-user-settings-password objectId=${stage.objectUid}> return html`<ak-user-settings-password objectId=${stage.objectUid} configureUrl=${stage.configureUrl}>
</ak-user-settings-password>`; </ak-user-settings-password>`;
case "ak-user-settings-authenticator-totp": case "ak-user-settings-authenticator-totp":
return html`<ak-user-settings-authenticator-totp objectId=${stage.objectUid} configureFlow=${stage.configureFlow}> return html`<ak-user-settings-authenticator-totp objectId=${stage.objectUid} configureUrl=${stage.configureUrl}>
</ak-user-settings-authenticator-totp>`; </ak-user-settings-authenticator-totp>`;
case "ak-user-settings-authenticator-static": case "ak-user-settings-authenticator-static":
return html`<ak-user-settings-authenticator-static objectId=${stage.objectUid} configureFlow=${stage.configureFlow}> return html`<ak-user-settings-authenticator-static objectId=${stage.objectUid} configureUrl=${stage.configureUrl}>
</ak-user-settings-authenticator-static>`; </ak-user-settings-authenticator-static>`;
case "ak-user-settings-authenticator-duo": case "ak-user-settings-authenticator-duo":
return html`<ak-user-settings-authenticator-duo objectId=${stage.objectUid} configureFlow=${stage.configureFlow}> return html`<ak-user-settings-authenticator-duo objectId=${stage.objectUid} configureUrl=${stage.configureUrl}>
</ak-user-settings-authenticator-duo>`; </ak-user-settings-authenticator-duo>`;
default: default:
return html`<p>${t`Error: unsupported stage settings: ${stage.component}`}</p>`; return html`<p>${t`Error: unsupported stage settings: ${stage.component}`}</p>`;
@ -60,7 +60,7 @@ export class UserSettingsPage extends LitElement {
renderSourceSettings(source: UserSetting): TemplateResult { renderSourceSettings(source: UserSetting): TemplateResult {
switch (source.component) { switch (source.component) {
case "ak-user-settings-source-oauth": case "ak-user-settings-source-oauth":
return html`<ak-user-settings-source-oauth objectId=${source.objectUid} title=${source.title}> return html`<ak-user-settings-source-oauth objectId=${source.objectUid} title=${source.title} configureUrl=${source.configureUrl}>
</ak-user-settings-source-oauth>`; </ak-user-settings-source-oauth>`;
default: default:
return html`<p>${t`Error: unsupported source settings: ${source.component}`}</p>`; return html`<p>${t`Error: unsupported source settings: ${source.component}`}</p>`;

View File

@ -11,6 +11,9 @@ export abstract class BaseUserSettings extends LitElement {
@property() @property()
objectId!: string; objectId!: string;
@property()
configureUrl?: string;
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
return [PFBase, PFCard, PFButton, PFForm, PFFormControl, AKGlobal]; return [PFBase, PFCard, PFButton, PFForm, PFFormControl, AKGlobal];
} }

View File

@ -4,7 +4,7 @@ import { SourcesApi } from "authentik-api";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
import { DEFAULT_CONFIG } from "../../../api/Config"; import { DEFAULT_CONFIG } from "../../../api/Config";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { AppURLManager } from "../../../api/legacy"; import { ifDefined } from "lit-html/directives/if-defined";
@customElement("ak-user-settings-source-oauth") @customElement("ak-user-settings-source-oauth")
export class SourceSettingsOAuth extends BaseUserSettings { export class SourceSettingsOAuth extends BaseUserSettings {
@ -40,7 +40,7 @@ export class SourceSettingsOAuth extends BaseUserSettings {
} }
return html`<p>${t`Not connected.`}</p> return html`<p>${t`Not connected.`}</p>
<a class="pf-c-button pf-m-primary" <a class="pf-c-button pf-m-primary"
href=${AppURLManager.sourceOAuth(this.objectId, "login")}> href=${ifDefined(this.configureUrl)}>
${t`Connect`} ${t`Connect`}
</a>`; </a>`;
}))}`; }))}`;

View File

@ -1,6 +1,6 @@
import { AuthenticatorsApi } from "authentik-api"; import { AuthenticatorsApi } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement, html, property, TemplateResult } from "lit-element"; import { customElement, html, TemplateResult } from "lit-element";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
import { DEFAULT_CONFIG } from "../../../api/Config"; import { DEFAULT_CONFIG } from "../../../api/Config";
import { BaseUserSettings } from "./BaseUserSettings"; import { BaseUserSettings } from "./BaseUserSettings";
@ -8,9 +8,6 @@ import { BaseUserSettings } from "./BaseUserSettings";
@customElement("ak-user-settings-authenticator-duo") @customElement("ak-user-settings-authenticator-duo")
export class UserSettingsAuthenticatorDuo extends BaseUserSettings { export class UserSettingsAuthenticatorDuo extends BaseUserSettings {
@property()
configureFlow?: string;
renderEnabled(): TemplateResult { renderEnabled(): TemplateResult {
return html`<div class="pf-c-card__body"> return html`<div class="pf-c-card__body">
<p> <p>
@ -48,8 +45,8 @@ export class UserSettingsAuthenticatorDuo extends BaseUserSettings {
</p> </p>
</div> </div>
<div class="pf-c-card__footer"> <div class="pf-c-card__footer">
${this.configureFlow ? ${this.configureUrl ?
html`<a href="${this.configureFlow}?next=/%23%2Fuser" html`<a href="${this.configureUrl}?next=/%23%2Fuser"
class="pf-c-button pf-m-primary">${t`Enable Static Tokens`} class="pf-c-button pf-m-primary">${t`Enable Static Tokens`}
</a>`: html``} </a>`: html``}
</div>`; </div>`;

View File

@ -1,6 +1,6 @@
import { AuthenticatorsApi } from "authentik-api"; import { AuthenticatorsApi } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element"; import { CSSResult, customElement, html, TemplateResult } from "lit-element";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
import { DEFAULT_CONFIG } from "../../../api/Config"; import { DEFAULT_CONFIG } from "../../../api/Config";
import { STATIC_TOKEN_STYLE } from "../../../flows/stages/authenticator_static/AuthenticatorStaticStage"; import { STATIC_TOKEN_STYLE } from "../../../flows/stages/authenticator_static/AuthenticatorStaticStage";
@ -9,9 +9,6 @@ import { BaseUserSettings } from "./BaseUserSettings";
@customElement("ak-user-settings-authenticator-static") @customElement("ak-user-settings-authenticator-static")
export class UserSettingsAuthenticatorStatic extends BaseUserSettings { export class UserSettingsAuthenticatorStatic extends BaseUserSettings {
@property()
configureFlow?: string;
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
return super.styles.concat(STATIC_TOKEN_STYLE); return super.styles.concat(STATIC_TOKEN_STYLE);
} }
@ -63,8 +60,8 @@ export class UserSettingsAuthenticatorStatic extends BaseUserSettings {
</p> </p>
</div> </div>
<div class="pf-c-card__footer"> <div class="pf-c-card__footer">
${this.configureFlow ? ${this.configureUrl ?
html`<a href="${this.configureFlow}?next=/%23%2Fuser" html`<a href="${this.configureUrl}?next=/%23%2Fuser"
class="pf-c-button pf-m-primary">${t`Enable Static Tokens`} class="pf-c-button pf-m-primary">${t`Enable Static Tokens`}
</a>`: html``} </a>`: html``}
</div>`; </div>`;

View File

@ -1,6 +1,6 @@
import { AuthenticatorsApi } from "authentik-api"; import { AuthenticatorsApi } from "authentik-api";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { customElement, html, property, TemplateResult } from "lit-element"; import { customElement, html, TemplateResult } from "lit-element";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
import { DEFAULT_CONFIG } from "../../../api/Config"; import { DEFAULT_CONFIG } from "../../../api/Config";
import { BaseUserSettings } from "./BaseUserSettings"; import { BaseUserSettings } from "./BaseUserSettings";
@ -8,9 +8,6 @@ import { BaseUserSettings } from "./BaseUserSettings";
@customElement("ak-user-settings-authenticator-totp") @customElement("ak-user-settings-authenticator-totp")
export class UserSettingsAuthenticatorTOTP extends BaseUserSettings { export class UserSettingsAuthenticatorTOTP extends BaseUserSettings {
@property()
configureFlow?: string;
renderEnabled(): TemplateResult { renderEnabled(): TemplateResult {
return html`<div class="pf-c-card__body"> return html`<div class="pf-c-card__body">
<p> <p>
@ -48,8 +45,8 @@ export class UserSettingsAuthenticatorTOTP extends BaseUserSettings {
</p> </p>
</div> </div>
<div class="pf-c-card__footer"> <div class="pf-c-card__footer">
${this.configureFlow ? ${this.configureUrl ?
html`<a href="${this.configureFlow}?next=/%23%2Fuser" html`<a href="${this.configureUrl}?next=/%23%2Fuser"
class="pf-c-button pf-m-primary">${t`Enable TOTP`} class="pf-c-button pf-m-primary">${t`Enable TOTP`}
</a>`: html``} </a>`: html``}
</div>`; </div>`;

View File

@ -1,4 +1,4 @@
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element"; import { CSSResult, customElement, html, TemplateResult } from "lit-element";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { AuthenticatorsApi, WebAuthnDevice } from "authentik-api"; import { AuthenticatorsApi, WebAuthnDevice } from "authentik-api";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";
@ -16,9 +16,6 @@ import { ifDefined } from "lit-html/directives/if-defined";
@customElement("ak-user-settings-authenticator-webauthn") @customElement("ak-user-settings-authenticator-webauthn")
export class UserSettingsAuthenticatorWebAuthn extends BaseUserSettings { export class UserSettingsAuthenticatorWebAuthn extends BaseUserSettings {
@property()
configureFlow?: string;
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
return super.styles.concat(PFDataList); return super.styles.concat(PFDataList);
} }
@ -100,8 +97,8 @@ export class UserSettingsAuthenticatorWebAuthn extends BaseUserSettings {
</ul> </ul>
</div> </div>
<div class="pf-c-card__footer"> <div class="pf-c-card__footer">
${this.configureFlow ? ${this.configureUrl ?
html`<a href="${this.configureFlow}?next=/%23%2Fuser" html`<a href="${this.configureUrl}?next=/%23%2Fuser"
class="pf-c-button pf-m-primary">${t`Configure WebAuthn`} class="pf-c-button pf-m-primary">${t`Configure WebAuthn`}
</a>`: html``} </a>`: html``}
</div> </div>

View File

@ -1,7 +1,7 @@
import { customElement, html, TemplateResult } from "lit-element"; import { customElement, html, TemplateResult } from "lit-element";
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { FlowURLManager } from "../../../api/legacy";
import { BaseUserSettings } from "./BaseUserSettings"; import { BaseUserSettings } from "./BaseUserSettings";
import { ifDefined } from "lit-html/directives/if-defined";
@customElement("ak-user-settings-password") @customElement("ak-user-settings-password")
export class UserSettingsPassword extends BaseUserSettings { export class UserSettingsPassword extends BaseUserSettings {
@ -14,7 +14,7 @@ export class UserSettingsPassword extends BaseUserSettings {
${t`Change your password`} ${t`Change your password`}
</div> </div>
<div class="pf-c-card__body"> <div class="pf-c-card__body">
<a href="${FlowURLManager.configure(this.objectId, "?next=/%23%2Fuser")}" <a href="${ifDefined(this.configureUrl)}"
class="pf-c-button pf-m-primary"> class="pf-c-button pf-m-primary">
${t`Change password`} ${t`Change password`}
</a> </a>