web/flows: fix bottom padding when loading challenge (#15372)

* web/flows: fix bottom padding when loading challenge

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add base class that does layout for login cards

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* ok actually rework the whole thing

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix leftover div

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix other stages

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* better declare loading state

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* format

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens L.
2025-07-03 22:07:22 +02:00
committed by GitHub
parent 6919838c12
commit 0021e5fa25
34 changed files with 843 additions and 961 deletions
+4 -2
View File
@@ -118,7 +118,8 @@ class TestProviderProxy(SeleniumTestCase):
sleep(2)
flow_executor = self.get_shadow_root("ak-flow-executor")
session_end_stage = self.get_shadow_root("ak-stage-session-end", flow_executor)
title = session_end_stage.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text
flow_card = self.get_shadow_root("ak-flow-card", session_end_stage)
title = flow_card.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text
self.assertIn("You've logged out of", title)
@retry()
@@ -195,7 +196,8 @@ class TestProviderProxy(SeleniumTestCase):
sleep(2)
flow_executor = self.get_shadow_root("ak-flow-executor")
session_end_stage = self.get_shadow_root("ak-stage-session-end", flow_executor)
title = session_end_stage.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text
flow_card = self.get_shadow_root("ak-flow-card", session_end_stage)
title = flow_card.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text
self.assertIn("You've logged out of", title)
+8 -4
View File
@@ -121,7 +121,8 @@ class TestProviderProxyForward(SeleniumTestCase):
sleep(2)
flow_executor = self.get_shadow_root("ak-flow-executor")
session_end_stage = self.get_shadow_root("ak-stage-session-end", flow_executor)
title = session_end_stage.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text
flow_card = self.get_shadow_root("ak-flow-card", session_end_stage)
title = flow_card.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text
self.assertIn("You've logged out of", title)
@skip("Flaky test")
@@ -156,7 +157,8 @@ class TestProviderProxyForward(SeleniumTestCase):
sleep(2)
flow_executor = self.get_shadow_root("ak-flow-executor")
session_end_stage = self.get_shadow_root("ak-stage-session-end", flow_executor)
title = session_end_stage.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text
flow_card = self.get_shadow_root("ak-flow-card", session_end_stage)
title = flow_card.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text
self.assertIn("You've logged out of", title)
@retry()
@@ -189,7 +191,8 @@ class TestProviderProxyForward(SeleniumTestCase):
sleep(2)
flow_executor = self.get_shadow_root("ak-flow-executor")
session_end_stage = self.get_shadow_root("ak-stage-session-end", flow_executor)
title = session_end_stage.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text
flow_card = self.get_shadow_root("ak-flow-card", session_end_stage)
title = flow_card.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text
self.assertIn("You've logged out of", title)
@retry()
@@ -225,5 +228,6 @@ class TestProviderProxyForward(SeleniumTestCase):
sleep(2)
flow_executor = self.get_shadow_root("ak-flow-executor")
session_end_stage = self.get_shadow_root("ak-stage-session-end", flow_executor)
title = session_end_stage.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text
flow_card = self.get_shadow_root("ak-flow-card", session_end_stage)
title = flow_card.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text
self.assertIn("You've logged out of", title)
+2 -1
View File
@@ -10,6 +10,7 @@ import { WithBrandConfig } from "#elements/mixins/branding";
import { WithCapabilitiesConfig } from "#elements/mixins/capabilities";
import { themeImage } from "#elements/utils/images";
import "#flow/components/ak-brand-footer";
import "#flow/components/ak-flow-card";
import "#flow/sources/apple/AppleLoginInit";
import "#flow/sources/plex/PlexLoginInit";
import "#flow/stages/FlowErrorStage";
@@ -304,7 +305,7 @@ export class FlowExecutor
async renderChallenge(): Promise<TemplateResult> {
if (!this.challenge) {
return html`<ak-empty-state loading default-label> </ak-empty-state>`;
return html`<ak-flow-card loading></ak-flow-card>`;
}
switch (this.challenge?.component) {
case "ak-stage-access-denied":
+1
View File
@@ -6,6 +6,7 @@ import {
pluckErrorDetail,
} from "@goauthentik/common/errors/network";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/EmptyState";
import "@goauthentik/elements/Expand";
import { msg } from "@lit/localize";
+87
View File
@@ -0,0 +1,87 @@
import { AKElement } from "#elements/Base";
import "@goauthentik/elements/EmptyState";
import { CSSResult, css, html, nothing } from "lit";
import { customElement, property } from "lit/decorators.js";
import PFLogin from "@patternfly/patternfly/components/Login/login.css";
import PFTitle from "@patternfly/patternfly/components/Title/title.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import { ChallengeTypes } from "@goauthentik/api";
/**
* @element ak-flow-card
* @class FlowCard
* @slot title - Title of the card, optional, when not set uses the flow title
* @slot - Main body of the card
* @slot footer - Footer links, optional
* @slot footer-band - Band in the footer, option
*
*/
@customElement("ak-flow-card")
export class FlowCard extends AKElement {
@property({ type: Object })
challenge?: ChallengeTypes;
@property({ type: Boolean })
loading = false;
static get styles(): CSSResult[] {
return [
PFBase,
PFLogin,
PFTitle,
css`
/* login page's icons */
.pf-c-login__main-footer-links-item button {
background-color: transparent;
border: 0;
display: flex;
align-items: stretch;
}
.pf-c-login__main-footer-links-item img {
fill: var(--pf-c-login__main-footer-links-item-link-svg--Fill);
width: 100px;
max-width: var(--pf-c-login__main-footer-links-item-link-svg--Width);
height: 100%;
max-height: var(--pf-c-login__main-footer-links-item-link-svg--Height);
}
`,
];
}
render() {
if (!this.challenge || this.loading) {
return html`<ak-empty-state loading default-label></ak-empty-state>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
}
// No title if the challenge doesn't provide a title and no custom title is set
let title = undefined;
if (this.hasSlotted("title")) {
title = html`<h1 class="pf-c-title pf-m-3xl"><slot name="title"></slot></h1>`;
} else if (this.challenge.flowInfo?.title) {
title = html`<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo.title}</h1>`;
}
return html`${title
? html`<header class="pf-c-login__main-header">${title}</header>`
: nothing}
<div class="pf-c-login__main-body">
<slot></slot>
</div>
<footer class="pf-c-login__main-footer">
<slot name="footer">
<ul class="pf-c-login__main-footer-links"></ul>
</slot>
<slot name="footer-band"></slot>
</footer>`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ak-flow-card": FlowCard;
}
}
+28 -32
View File
@@ -1,5 +1,6 @@
import { globalAK } from "@goauthentik/common/global";
import "@goauthentik/flow/FormStatic";
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { msg, str } from "@lit/localize";
@@ -23,13 +24,7 @@ export class SessionEnd extends BaseStage<SessionEndChallenge, unknown> {
}
render(): TemplateResult {
if (!this.challenge) {
return html`<ak-empty-state default-label></ak-empty-state>`;
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
return html`<ak-flow-card .challenge=${this.challenge}>
<form class="pf-c-form">
<ak-form-static
class="pf-c-form__group"
@@ -50,31 +45,32 @@ export class SessionEnd extends BaseStage<SessionEndChallenge, unknown> {
<a href="${globalAK().api.base}" class="pf-c-button pf-m-primary">
${msg("Go back to overview")}
</a>
${this.challenge.invalidationFlowUrl
? html`
<a
href="${this.challenge.invalidationFlowUrl}"
class="pf-c-button pf-m-secondary"
id="logout"
>
${msg(str`Log out of ${this.challenge.brandName}`)}
</a>
`
: nothing}
${this.challenge.applicationLaunchUrl && this.challenge.applicationName
? html`
<a
href="${this.challenge.applicationLaunchUrl}"
class="pf-c-button pf-m-secondary"
>
${msg(str`Log back into ${this.challenge.applicationName}`)}
</a>
`
: nothing}
${
this.challenge.invalidationFlowUrl
? html`
<a
href="${this.challenge.invalidationFlowUrl}"
class="pf-c-button pf-m-secondary"
id="logout"
>
${msg(str`Log out of ${this.challenge.brandName}`)}
</a>
`
: nothing
}
${
this.challenge.applicationLaunchUrl && this.challenge.applicationName
? html`
<a
href="${this.challenge.applicationLaunchUrl}"
class="pf-c-button pf-m-secondary"
>
${msg(str`Log back into ${this.challenge.applicationName}`)}
</a>
`
: nothing
}
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
</div></ak-flow-card>`;
}
}
+3 -11
View File
@@ -1,6 +1,6 @@
import "@goauthentik/elements/EmptyState";
import "@goauthentik/elements/forms/FormElement";
import "@goauthentik/flow/FormStatic";
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { msg } from "@lit/localize";
@@ -29,13 +29,7 @@ export class OAuth2DeviceCode extends BaseStage<
}
render(): TemplateResult {
if (!this.challenge) {
return html`<ak-empty-state loading> </ak-empty-state>`;
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
return html`<ak-flow-card .challenge=${this.challenge}>
<form
class="pf-c-form"
@submit=${(e: Event) => {
@@ -70,9 +64,7 @@ export class OAuth2DeviceCode extends BaseStage<
</div>
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
</ak-flow-card>`;
}
}
@@ -1,5 +1,6 @@
import "@goauthentik/elements/EmptyState";
import "@goauthentik/flow/FormStatic";
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { msg } from "@lit/localize";
@@ -14,13 +15,12 @@ export class DeviceCodeFinish extends BaseStage<
OAuthDeviceCodeFinishChallenge
> {
render(): TemplateResult {
if (!this.challenge) {
return html`<ak-empty-state loading> </ak-empty-state>`;
}
return html`<ak-empty-state icon="fas fa-check">
<span>${msg("You may close this page now.")}</span>
<span slot="body"> ${msg("You've successfully authenticated your device.")} </span>
</ak-empty-state>`;
return html`<ak-flow-card .challenge=${this.challenge}>
<ak-empty-state icon="fas fa-check">
<span>${msg("You may close this page now.")}</span>
<span slot="body"> ${msg("You've successfully authenticated your device.")} </span>
</ak-empty-state>
</ak-flow-card>`;
}
}
+18 -23
View File
@@ -1,8 +1,8 @@
import "@goauthentik/elements/EmptyState";
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit";
import { CSSResult, TemplateResult, html, nothing } from "lit";
import { customElement, property } from "lit/decorators.js";
import PFButton from "@patternfly/patternfly/components/Button/button.css";
@@ -52,27 +52,22 @@ export class AppleLoginInit extends BaseStage<AppleLoginChallenge, AppleChalleng
}
render(): TemplateResult {
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${msg("Authenticating with Apple...")}</h1>
</header>
<div class="pf-c-login__main-body">
<form class="pf-c-form">
<ak-empty-state loading></ak-empty-state>
${!this.isModalShown
? html`<button
class="pf-c-button pf-m-primary pf-m-block"
@click=${() => {
AppleID.auth.signIn();
}}
>
${msg("Retry")}
</button>`
: html``}
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
return html`<ak-flow-card .challenge=${this.challenge}>
<span slot="title">${msg("Authenticating with Apple...")}</span>
<form class="pf-c-form">
<ak-empty-state loading></ak-empty-state>
${!this.isModalShown
? html`<button
class="pf-c-button pf-m-primary pf-m-block"
@click=${() => {
AppleID.auth.signIn();
}}
>
${msg("Retry")}
</button>`
: nothing}
</form>
</ak-flow-card>`;
}
}
+21 -24
View File
@@ -1,7 +1,9 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { parseAPIResponseError } from "@goauthentik/common/errors/network";
import { PlexAPIClient, popupCenterScreen } from "@goauthentik/common/helpers/plex";
import "@goauthentik/elements/EmptyState";
import { showAPIErrorMessage } from "@goauthentik/elements/messages/MessageContainer";
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { msg } from "@lit/localize";
@@ -64,30 +66,25 @@ export class PlexLoginInit extends BaseStage<
}
render(): TemplateResult {
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${msg("Authenticating with Plex...")}</h1>
</header>
<div class="pf-c-login__main-body">
<form class="pf-c-form">
<ak-empty-state loading
><span>${msg("Waiting for authentication...")}></span>
</ak-empty-state>
<hr class="pf-c-divider" />
<p>${msg("If no Plex popup opens, click the button below.")}</p>
<button
class="pf-c-button pf-m-block pf-m-primary"
type="button"
@click=${() => {
window.open(this.authUrl, "_blank");
}}
>
${msg("Open login")}
</button>
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
return html`<ak-flow-card .challenge=${this.challenge}>
<span slot="title">${msg("Authenticating with Plex...")}</span>
<form class="pf-c-form">
<ak-empty-state loading
><span>${msg("Waiting for authentication...")}></span>
</ak-empty-state>
<hr class="pf-c-divider" />
<p>${msg("If no Plex popup opens, click the button below.")}</p>
<button
class="pf-c-button pf-m-block pf-m-primary"
type="button"
@click=${() => {
window.open(this.authUrl, "_blank");
}}
>
${msg("Open login")}
</button>
</form>
</ak-flow-card>`;
}
}
+25 -33
View File
@@ -1,5 +1,6 @@
import "@goauthentik/elements/EmptyState";
import "@goauthentik/flow/FormStatic";
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { msg } from "@lit/localize";
@@ -37,39 +38,30 @@ export class FlowErrorStage extends BaseStage<FlowErrorChallenge, FlowChallengeR
}
render(): TemplateResult {
if (!this.challenge) {
return html`<ak-empty-state loading> </ak-empty-state>`;
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
<form class="pf-c-form">
<ak-empty-state icon="fa-times"
><span>
${this.challenge.error
? this.challenge.error
: msg("Something went wrong! Please try again later.")}</span
>
<div slot="body">
${this.challenge?.traceback
? html`<div class="pf-c-form__group">
<pre class="ak-exception">${this.challenge.traceback}</pre>
</div>`
: nothing}
${this.challenge?.requestId
? html`<div class="pf-c-form__group">
<p>${msg("Request ID")}</p>
<code>${this.challenge.requestId}</code>
</div>`
: nothing}
</div>
</ak-empty-state>
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
return html`<ak-flow-card .challenge=${this.challenge}>
<form class="pf-c-form">
<ak-empty-state icon="fa-times"
><span>
${this.challenge.error
? this.challenge.error
: msg("Something went wrong! Please try again later.")}</span
>
<div slot="body">
${this.challenge?.traceback
? html`<div class="pf-c-form__group">
<pre class="ak-exception">${this.challenge.traceback}</pre>
</div>`
: nothing}
${this.challenge?.requestId
? html`<div class="pf-c-form__group">
<p>${msg("Request ID")}</p>
<code>${this.challenge.requestId}</code>
</div>`
: nothing}
</div>
</ak-empty-state>
</form>
</ak-flow-card>`;
}
}
+15 -20
View File
@@ -1,5 +1,6 @@
import "@goauthentik/elements/EmptyState";
import "@goauthentik/flow/FormStatic";
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { CSSResult, TemplateResult, css, html, nothing } from "lit";
@@ -20,30 +21,24 @@ export class FlowFrameStage extends BaseStage<FrameChallenge, FrameChallengeResp
}
render(): TemplateResult {
if (!this.challenge) {
return html`<ak-empty-state loading> </ak-empty-state>`;
}
return html` <header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
${this.challenge.loadingOverlay
? html`<ak-empty-state loading
>${this.challenge.loadingText
? html`<span>${this.challenge.loadingText}}</span>`
: nothing}
</ak-empty-state>`
: nothing}
return html`<ak-flow-card .challenge=${this.challenge}>
${
this.challenge.loadingOverlay
? html`<ak-empty-state loading
>${this.challenge.loadingText
? html`<span>${this.challenge.loadingText}}</span>`
: nothing}
</ak-empty-state>`
: nothing
}
<iframe
style=${this.challenge.loadingOverlay
? "width:0;height:0;position:absolute;"
: ""}
style=${
this.challenge.loadingOverlay ? "width:0;height:0;position:absolute;" : ""
}
src=${this.challenge.url}
></iframe>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
</ak-flow-card>`;
}
}
+28 -30
View File
@@ -1,3 +1,4 @@
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { msg } from "@lit/localize";
@@ -69,43 +70,40 @@ export class RedirectStage extends BaseStage<RedirectChallenge, FlowChallengeRes
// As this wouldn't really be a redirect, show a message that the page can be closed
// and try to close it ourselves
if (!url.protocol.startsWith("http")) {
return html`<ak-empty-state icon="fas fa-check"
><span>${msg("You may close this page now.")}</span>
</ak-empty-state>`;
return html`<ak-flow-card .challenge=${this.challenge}>
<ak-empty-state icon="fas fa-check"
><span>${msg("You may close this page now.")}</span>
</ak-empty-state>
</ak-flow-card>`;
}
return html`<ak-empty-state default-label></ak-empty-state>`;
return html`<ak-flow-card .challenge=${this.challenge} loading></ak-flow-card>`;
}
render(): TemplateResult {
if (this.startedRedirect || !this.promptUser) {
return this.renderLoading();
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${msg("Redirect")}</h1>
</header>
<div class="pf-c-login__main-body">
<form class="pf-c-form">
<div class="pf-c-form__group">
<p>${msg("You're about to be redirect to the following URL.")}</p>
<code>${this.getURL()}</code>
</div>
<div class="pf-c-form__group pf-m-action">
<a
type="submit"
class="pf-c-button pf-m-primary pf-m-block"
href=${this.challenge.to}
@click=${() => {
this.startedRedirect = true;
}}
>
${msg("Follow redirect")}
</a>
</div>
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer> `;
return html`<ak-flow-card .challenge=${this.challenge}>
<span slot="title">${msg("Redirect")}</span>
<form class="pf-c-form">
<div class="pf-c-form__group">
<p>${msg("You're about to be redirect to the following URL.")}</p>
<code>${this.getURL()}</code>
</div>
<div class="pf-c-form__group pf-m-action">
<a
type="submit"
class="pf-c-button pf-m-primary pf-m-block"
href=${this.challenge.to}
@click=${() => {
this.startedRedirect = true;
}}
>
${msg("Follow redirect")}
</a>
</div>
</form>
</ak-flow-card>`;
}
}
@@ -1,5 +1,5 @@
import "@goauthentik/elements/EmptyState";
import "@goauthentik/flow/FormStatic";
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { msg } from "@lit/localize";
@@ -25,40 +25,31 @@ export class AccessDeniedStage extends BaseStage<
}
render(): TemplateResult {
if (!this.challenge) {
return html`<ak-empty-state loading> </ak-empty-state>`;
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
<form class="pf-c-form">
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}
>
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
</div>
</ak-form-static>
<ak-empty-state icon="fa-times"
><span>${msg("Request has been denied.")}</span>
${this.challenge.errorMessage
? html`
<div slot="body">
<p>${this.challenge.errorMessage}</p>
</div>
`
: nothing}
</ak-empty-state>
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
return html`<ak-flow-card .challenge=${this.challenge}>
<form class="pf-c-form">
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}
>
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
</div>
</ak-form-static>
<ak-empty-state icon="fa-times"
><span>${msg("Request has been denied.")}</span>
${this.challenge.errorMessage
? html`
<div slot="body">
<p>${this.challenge.errorMessage}</p>
</div>
`
: nothing}
</ak-empty-state>
</form>
</ak-flow-card>`;
}
}
@@ -1,7 +1,7 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/elements/EmptyState";
import "@goauthentik/elements/forms/FormElement";
import "@goauthentik/flow/FormStatic";
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { msg } from "@lit/localize";
@@ -64,57 +64,45 @@ export class AuthenticatorDuoStage extends BaseStage<
}
render(): TemplateResult {
if (!this.challenge) {
return html`<ak-empty-state loading> </ak-empty-state>`;
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
return html`<ak-flow-card .challenge=${this.challenge}>
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}
>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}
>
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
</div>
</ak-form-static>
<img
src=${this.challenge.activationBarcode}
alt=${msg("Duo activation QR code")}
/>
<p>
${msg(
"Alternatively, if your current device has Duo installed, click on this link:",
)}
</p>
<a href=${this.challenge.activationCode}>${msg("Duo activation")}</a>
<div class="pf-c-form__group pf-m-action">
<button
type="button"
class="pf-c-button pf-m-primary pf-m-block"
@click=${() => {
this.checkEnrollStatus();
}}
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
${msg("Check status")}
</button>
</div>
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
</ak-form-static>
<img src=${this.challenge.activationBarcode} alt=${msg("Duo activation QR code")} />
<p>
${msg(
"Alternatively, if your current device has Duo installed, click on this link:",
)}
</p>
<a href=${this.challenge.activationCode}>${msg("Duo activation")}</a>
<div class="pf-c-form__group pf-m-action">
<button
type="button"
class="pf-c-button pf-m-primary pf-m-block"
@click=${() => {
this.checkEnrollStatus();
}}
>
${msg("Check status")}
</button>
</div>
</form>
</ak-flow-card>`;
}
}
@@ -1,6 +1,6 @@
import "@goauthentik/elements/EmptyState";
import "@goauthentik/elements/forms/FormElement";
import "@goauthentik/flow/FormStatic";
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { msg } from "@lit/localize";
@@ -31,61 +31,13 @@ export class AuthenticatorEmailStage extends BaseStage<
}
renderEmailInput(): TemplateResult {
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}
>
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
</div>
</ak-form-static>
<ak-form-element
label="${msg("Configure your email")}"
required
class="pf-c-form__group"
.errors=${(this.challenge?.responseErrors || {}).email}
>
<input
type="email"
name="email"
placeholder="${msg("Please enter your email address.")}"
autofocus=""
autocomplete="email"
class="pf-c-form-control"
required
/>
</ak-form-element>
${this.renderNonFieldErrors()}
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
${msg("Continue")}
</button>
</div>
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
}
renderEmailOTPInput(): TemplateResult {
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
return html`<ak-flow-card .challenge=${this.challenge}>
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
@@ -97,71 +49,85 @@ export class AuthenticatorEmailStage extends BaseStage<
>
</div>
</ak-form-static>
A verification token has been sent to your configured email address
${ifDefined(this.challenge.email)}
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
<ak-form-element
label="${msg("Configure your email")}"
required
class="pf-c-form__group"
.errors=${(this.challenge?.responseErrors || {}).email}
>
<ak-form-element
label="${msg("Code")}"
<input
type="email"
name="email"
placeholder="${msg("Please enter your email address.")}"
autofocus=""
autocomplete="email"
class="pf-c-form-control"
required
class="pf-c-form__group"
.errors=${(this.challenge?.responseErrors || {}).code}
/>
</ak-form-element>
${this.renderNonFieldErrors()}
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
${msg("Continue")}
</button>
</div>
</form>
</ak-flow-card>`;
}
renderEmailOTPInput(): TemplateResult {
return html`<ak-flow-card .challenge=${this.challenge}>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}
>
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
<input
type="text"
name="code"
inputmode="numeric"
pattern="[0-9]*"
placeholder="${msg("Please enter the code you received via email")}"
autofocus=""
autocomplete="one-time-code"
class="pf-c-form-control"
required
/>
</ak-form-element>
${this.renderNonFieldErrors()}
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
${msg("Continue")}
</button>
</div>
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
</div>
</ak-form-static>
A verification token has been sent to your configured email address
${ifDefined(this.challenge.email)}
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
>
<ak-form-element
label="${msg("Code")}"
required
class="pf-c-form__group"
.errors=${(this.challenge?.responseErrors || {}).code}
>
<input
type="text"
name="code"
inputmode="numeric"
pattern="[0-9]*"
placeholder="${msg("Please enter the code you received via email")}"
autofocus=""
autocomplete="one-time-code"
class="pf-c-form-control"
required
/>
</ak-form-element>
${this.renderNonFieldErrors()}
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
${msg("Continue")}
</button>
</div>
</form>
</ak-flow-card>`;
}
render(): TemplateResult {
console.debug(
"authentik/stages/authenticator_email:",
this.challenge ? this.challenge.emailRequired : undefined,
);
if (!this.challenge) {
console.debug(
"authentik/stages/authenticator_email: AuthenticatorEmailStage.render() called without challenge",
);
return html`<ak-empty-state loading> </ak-empty-state>`;
}
if (this.challenge.emailRequired) {
console.debug(
"authentik/stages/authenticator_email: AuthenticatorEmailStage.render() called with challenge",
this.challenge,
);
return this.renderEmailInput();
}
console.debug(
"authentik/stages/authenticator_email: AuthenticatorEmailStage.render() called without emailRequired challenge",
this.challenge,
);
return this.renderEmailOTPInput();
}
}
@@ -1,6 +1,6 @@
import "@goauthentik/elements/EmptyState";
import "@goauthentik/elements/forms/FormElement";
import "@goauthentik/flow/FormStatic";
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { msg } from "@lit/localize";
@@ -31,10 +31,7 @@ export class AuthenticatorSMSStage extends BaseStage<
}
renderPhoneNumber(): TemplateResult {
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
return html`<ak-flow-card .challenge=${this.challenge}>
<form
class="pf-c-form"
@submit=${(e: Event) => {
@@ -76,68 +73,57 @@ export class AuthenticatorSMSStage extends BaseStage<
</div>
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
</ak-flow-card>`;
}
renderCode(): TemplateResult {
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
return html`<ak-flow-card .challenge=${this.challenge}>
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}
>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}
>
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
</div>
</ak-form-static>
<ak-form-element
label="${msg("Code")}"
required
class="pf-c-form__group"
.errors=${(this.challenge?.responseErrors || {}).code}
>
<input
type="text"
name="code"
inputmode="numeric"
pattern="[0-9]*"
placeholder="${msg("Please enter the code you received via SMS")}"
autofocus=""
autocomplete="one-time-code"
class="pf-c-form-control"
required
/>
</ak-form-element>
${this.renderNonFieldErrors()}
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
${msg("Continue")}
</button>
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
</div>
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
</ak-form-static>
<ak-form-element
label="${msg("Code")}"
required
class="pf-c-form__group"
.errors=${(this.challenge?.responseErrors || {}).code}
>
<input
type="text"
name="code"
inputmode="numeric"
pattern="[0-9]*"
placeholder="${msg("Please enter the code you received via SMS")}"
autofocus=""
autocomplete="one-time-code"
class="pf-c-form-control"
required
/>
</ak-form-element>
${this.renderNonFieldErrors()}
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
${msg("Continue")}
</button>
</div>
</form>
</ak-flow-card>`;
}
render(): TemplateResult {
if (!this.challenge) {
return html`<ak-empty-state loading> </ak-empty-state>`;
}
if (this.challenge.phoneNumberRequired) {
return this.renderPhoneNumber();
}
@@ -1,6 +1,6 @@
import "@goauthentik/elements/EmptyState";
import "@goauthentik/elements/forms/FormElement";
import "@goauthentik/flow/FormStatic";
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { msg } from "@lit/localize";
@@ -52,49 +52,40 @@ export class AuthenticatorStaticStage extends BaseStage<
}
render(): TemplateResult {
if (!this.challenge) {
return html`<ak-empty-state loading> </ak-empty-state>`;
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
return html`<ak-flow-card .challenge=${this.challenge}>
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}
>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}
>
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
</div>
</ak-form-static>
<ak-form-element label="" class="pf-c-form__group">
<ul>
${this.challenge.codes.map((token) => {
return html`<li class="pf-m-monospace">${token}</li>`;
})}
</ul>
</ak-form-element>
<p>${msg("Make sure to keep these tokens in a safe place.")}</p>
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
${msg("Continue")}
</button>
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
</div>
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
</ak-form-static>
<ak-form-element label="" class="pf-c-form__group">
<ul>
${this.challenge.codes.map((token) => {
return html`<li class="pf-m-monospace">${token}</li>`;
})}
</ul>
</ak-form-element>
<p>${msg("Make sure to keep these tokens in a safe place.")}</p>
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
${msg("Continue")}
</button>
</div>
</form>
</ak-flow-card>`;
}
}
@@ -1,8 +1,8 @@
import { MessageLevel } from "@goauthentik/common/messages";
import "@goauthentik/elements/EmptyState";
import "@goauthentik/elements/forms/FormElement";
import { showMessage } from "@goauthentik/elements/messages/MessageContainer";
import "@goauthentik/flow/FormStatic";
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import "webcomponent-qr-code";
@@ -47,100 +47,89 @@ export class AuthenticatorTOTPStage extends BaseStage<
}
render(): TemplateResult {
if (!this.challenge) {
return html`<ak-empty-state loading> </ak-empty-state>`;
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
return html`<ak-flow-card .challenge=${this.challenge}>
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}
>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}
>
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
</div>
</ak-form-static>
<input type="hidden" name="otp_uri" value=${this.challenge.configUrl} />
<ak-form-element>
<div class="qr-container">
<qr-code data="${this.challenge.configUrl}"></qr-code>
<button
type="button"
class="pf-c-button pf-m-secondary pf-m-progress pf-m-in-progress"
@click=${(e: Event) => {
e.preventDefault();
if (!this.challenge?.configUrl) return;
if (!navigator.clipboard) {
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
</div>
</ak-form-static>
<input type="hidden" name="otp_uri" value=${this.challenge.configUrl} />
<ak-form-element>
<div class="qr-container">
<qr-code data="${this.challenge.configUrl}"></qr-code>
<button
type="button"
class="pf-c-button pf-m-secondary pf-m-progress pf-m-in-progress"
@click=${(e: Event) => {
e.preventDefault();
if (!this.challenge?.configUrl) return;
if (!navigator.clipboard) {
showMessage({
level: MessageLevel.info,
message: this.challenge?.configUrl,
});
return;
}
navigator.clipboard
.writeText(this.challenge?.configUrl)
.then(() => {
showMessage({
level: MessageLevel.info,
message: this.challenge?.configUrl,
level: MessageLevel.success,
message: msg("Successfully copied TOTP Config."),
});
return;
}
navigator.clipboard
.writeText(this.challenge?.configUrl)
.then(() => {
showMessage({
level: MessageLevel.success,
message: msg("Successfully copied TOTP Config."),
});
});
}}
>
<span class="pf-c-button__progress"
><i class="fas fa-copy"></i
></span>
${msg("Copy")}
</button>
</div>
</ak-form-element>
<p>
${msg(
"Please scan the QR code above using the Microsoft Authenticator, Google Authenticator, or other authenticator apps on your device, and enter the code the device displays below to finish setting up the MFA device.",
)}
</p>
<ak-form-element
label="${msg("Code")}"
required
class="pf-c-form__group"
.errors=${(this.challenge?.responseErrors || {}).code}
>
<!-- @ts-ignore -->
<input
type="text"
name="code"
inputmode="numeric"
pattern="[0-9]*"
placeholder="${msg("Please enter your TOTP Code")}"
autofocus=""
autocomplete="one-time-code"
class="pf-c-form-control pf-m-monospace"
spellcheck="false"
required
/>
</ak-form-element>
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
${msg("Continue")}
});
}}
>
<span class="pf-c-button__progress"><i class="fas fa-copy"></i></span>
${msg("Copy")}
</button>
</div>
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
</ak-form-element>
<p>
${msg(
"Please scan the QR code above using the Microsoft Authenticator, Google Authenticator, or other authenticator apps on your device, and enter the code the device displays below to finish setting up the MFA device.",
)}
</p>
<ak-form-element
label="${msg("Code")}"
required
class="pf-c-form__group"
.errors=${(this.challenge?.responseErrors || {}).code}
>
<!-- @ts-ignore -->
<input
type="text"
name="code"
inputmode="numeric"
pattern="[0-9]*"
placeholder="${msg("Please enter your TOTP Code")}"
autofocus=""
autocomplete="one-time-code"
class="pf-c-form-control pf-m-monospace"
spellcheck="false"
required
/>
</ak-form-element>
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
${msg("Continue")}
</button>
</div>
</form>
</ak-flow-card>`;
}
}
@@ -1,4 +1,5 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/flow/components/ak-flow-card.js";
import "@goauthentik/flow/stages/authenticator_validate/AuthenticatorValidateStageCode";
import "@goauthentik/flow/stages/authenticator_validate/AuthenticatorValidateStageDuo";
import "@goauthentik/flow/stages/authenticator_validate/AuthenticatorValidateStageWebAuthn";
@@ -276,28 +277,20 @@ export class AuthenticatorValidateStage
}
render(): TemplateResult {
return this.challenge
? html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
${this.selectedDeviceChallenge
? this.renderDeviceChallenge()
: html`<div class="pf-c-login__main-body">
<form class="pf-c-form">
${this.renderUserInfo()}
${this.selectedDeviceChallenge
? ""
: html`<p>${msg("Select an authentication method.")}</p>`}
${this.challenge.configurationStages.length > 0
? this.renderStagePicker()
: html``}
</form>
${this.renderDevicePicker()}
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`}`
: html`<ak-empty-state loading> </ak-empty-state>`;
return html`<ak-flow-card .challenge=${this.challenge}>
${this.selectedDeviceChallenge
? this.renderDeviceChallenge()
: html`<form class="pf-c-form">
${this.renderUserInfo()}
${this.selectedDeviceChallenge
? ""
: html`<p>${msg("Select an authentication method.")}</p>`}
${this.challenge.configurationStages.length > 0
? this.renderStagePicker()
: html``}
</form>
${this.renderDevicePicker()}`}
</ak-flow-card>`;
}
}
@@ -1,5 +1,5 @@
import "@goauthentik/elements/EmptyState";
import "@goauthentik/elements/forms/FormElement";
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseDeviceStage } from "@goauthentik/flow/stages/authenticator_validate/base";
import { PasswordManagerPrefill } from "@goauthentik/flow/stages/identification/IdentificationStage";
@@ -66,61 +66,53 @@ export class AuthenticatorValidateStageWebCode extends BaseDeviceStage<
}
render(): TemplateResult {
if (!this.challenge) {
return html`<ak-empty-state loading> </ak-empty-state>`;
}
return html`<div class="pf-c-login__main-body">
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
return html`<ak-flow-card .challenge=${this.challenge}>
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
>
${this.renderUserInfo()}
<div class="icon-description">
<i class="fa ${this.deviceIcon()}" aria-hidden="true"></i>
<p>${this.deviceMessage()}</p>
</div>
<ak-form-element
label="${this.deviceChallenge?.deviceClass === DeviceClassesEnum.Static
? msg("Static token")
: msg("Authentication code")}"
required
class="pf-c-form__group"
.errors=${(this.challenge?.responseErrors || {}).code}
>
${this.renderUserInfo()}
<div class="icon-description">
<i class="fa ${this.deviceIcon()}" aria-hidden="true"></i>
<p>${this.deviceMessage()}</p>
</div>
<ak-form-element
label="${this.deviceChallenge?.deviceClass === DeviceClassesEnum.Static
? msg("Static token")
: msg("Authentication code")}"
<!-- @ts-ignore -->
<input
type="text"
name="code"
inputmode="${this.deviceChallenge?.deviceClass === DeviceClassesEnum.Static
? "text"
: "numeric"}"
pattern="${this.deviceChallenge?.deviceClass === DeviceClassesEnum.Static
? "[0-9a-zA-Z]*"
: "[0-9]*"}"
placeholder="${msg("Please enter your code")}"
autofocus=""
autocomplete="one-time-code"
class="pf-c-form-control"
value="${PasswordManagerPrefill.totp || ""}"
required
class="pf-c-form__group"
.errors=${(this.challenge?.responseErrors || {}).code}
>
<!-- @ts-ignore -->
<input
type="text"
name="code"
inputmode="${this.deviceChallenge?.deviceClass ===
DeviceClassesEnum.Static
? "text"
: "numeric"}"
pattern="${this.deviceChallenge?.deviceClass ===
DeviceClassesEnum.Static
? "[0-9a-zA-Z]*"
: "[0-9]*"}"
placeholder="${msg("Please enter your code")}"
autofocus=""
autocomplete="one-time-code"
class="pf-c-form-control"
value="${PasswordManagerPrefill.totp || ""}"
required
/>
</ak-form-element>
/>
</ak-form-element>
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
${msg("Continue")}
</button>
${this.renderReturnToDevicePicker()}
</div>
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
${msg("Continue")}
</button>
${this.renderReturnToDevicePicker()}
</div>
</form>
</ak-flow-card>`;
}
}
@@ -51,24 +51,22 @@ export class AuthenticatorValidateStageWebDuo extends BaseDeviceStage<
}
const errors = this.challenge.responseErrors?.duo || [];
const errorMessage = errors.map((err) => err.string);
return html`<div class="pf-c-login__main-body">
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
>
${this.renderUserInfo()}
<ak-empty-state ?loading="${this.authenticating}" icon="fas fa-times"
><span
>${this.authenticating
? msg("Sending Duo push notification...")
: errorMessage.join(", ") || msg("Failed to authenticate")}</span
>
</ak-empty-state>
<div class="pf-c-form__group pf-m-action">${this.renderReturnToDevicePicker()}</div>
</form>
</div>`;
return html` <form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
>
${this.renderUserInfo()}
<ak-empty-state ?loading="${this.authenticating}" icon="fas fa-times"
><span
>${this.authenticating
? msg("Sending Duo push notification...")
: errorMessage.join(", ") || msg("Failed to authenticate")}</span
>
</ak-empty-state>
<div class="pf-c-form__group pf-m-action">${this.renderReturnToDevicePicker()}</div>
</form>`;
}
}
@@ -103,32 +103,30 @@ export class AuthenticatorValidateStageWebAuthn extends BaseDeviceStage<
}
render(): TemplateResult {
return html`<div class="pf-c-login__main-body">
<form class="pf-c-form">
${this.renderUserInfo()}
<ak-empty-state ?loading="${this.authenticating}" icon="fa-times">
<span
>${this.authenticating
? msg("Authenticating...")
: this.errorMessage || msg("Loading")}</span
>
</ak-empty-state>
<div class="pf-c-form__group pf-m-action">
${!this.authenticating
? html` <button
class="pf-c-button pf-m-primary pf-m-block"
@click=${() => {
this.authenticateWrapper();
}}
type="button"
>
${msg("Retry authentication")}
</button>`
: nothing}
${this.renderReturnToDevicePicker()}
</div>
</form>
</div>`;
return html` <form class="pf-c-form">
${this.renderUserInfo()}
<ak-empty-state ?loading="${this.authenticating}" icon="fa-times">
<span
>${this.authenticating
? msg("Authenticating...")
: this.errorMessage || msg("Loading")}</span
>
</ak-empty-state>
<div class="pf-c-form__group pf-m-action">
${!this.authenticating
? html` <button
class="pf-c-button pf-m-primary pf-m-block"
@click=${() => {
this.authenticateWrapper();
}}
type="button"
>
${msg("Retry authentication")}
</button>`
: nothing}
${this.renderReturnToDevicePicker()}
</div>
</form>`;
}
}
@@ -5,6 +5,7 @@ import {
transformNewAssertionForServer,
} from "@goauthentik/common/helpers/webauthn";
import "@goauthentik/elements/EmptyState";
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { msg, str } from "@lit/localize";
@@ -129,49 +130,46 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage<
}
render(): TemplateResult {
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge?.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
<form class="pf-c-form">
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}
>
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
</div>
</ak-form-static>
<ak-empty-state ?loading="${this.registerRunning}" icon="fa-times">
<span
>${this.registerRunning
? msg("Registering...")
: this.registerMessage || msg("Failed to register")}
</span>
</ak-empty-state>
${this.challenge?.responseErrors
? html`<p class="pf-m-block">
${this.challenge.responseErrors.response[0].string}
</p>`
: nothing}
<div class="pf-c-form__group pf-m-action">
${!this.registerRunning
? html` <button
class="pf-c-button pf-m-primary pf-m-block"
@click=${() => {
this.registerWrapper();
}}
type="button"
>
${msg("Retry registration")}
</button>`
: nothing}
return html`<ak-flow-card .challenge=${this.challenge}>
<form class="pf-c-form">
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}
>
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
</div>
</form>
</div>`;
</ak-form-static>
<ak-empty-state ?loading="${this.registerRunning}" icon="fa-times">
<span
>${this.registerRunning
? msg("Registering...")
: this.registerMessage || msg("Failed to register")}
</span>
</ak-empty-state>
${this.challenge?.responseErrors
? html`<p class="pf-m-block">
${this.challenge.responseErrors.response[0].string}
</p>`
: nothing}
<div class="pf-c-form__group pf-m-action">
${!this.registerRunning
? html` <button
class="pf-c-button pf-m-primary pf-m-block"
@click=${() => {
this.registerWrapper();
}}
type="button"
>
${msg("Retry registration")}
</button>`
: nothing}
</div>
</form>
</ak-flow-card>`;
}
}
@@ -1,4 +1,5 @@
import "@goauthentik/elements/EmptyState";
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { msg } from "@lit/localize";
@@ -31,9 +32,6 @@ export class AutosubmitStage extends BaseStage<
}
render(): TemplateResult {
if (!this.challenge) {
return html`<ak-empty-state loading> </ak-empty-state>`;
}
let title = this.challenge.flowInfo?.title;
if (this.challenge.title && this.challenge.title !== "") {
title = this.challenge.title;
@@ -41,16 +39,18 @@ export class AutosubmitStage extends BaseStage<
if (!title) {
title = msg("Loading");
}
return html`<form class="pf-c-form" action="${this.challenge.url}" method="POST">
${Object.entries(this.challenge.attrs).map(([key, value]) => {
return html`<input
type="hidden"
name="${key as string}"
value="${value as string}"
/>`;
})}
<ak-empty-state loading title=${title}> </ak-empty-state>
</form>`;
return html`<ak-flow-card .challenge=${this.challenge}>
<form class="pf-c-form" action="${this.challenge.url}" method="POST">
${Object.entries(this.challenge.attrs).map(([key, value]) => {
return html`<input
type="hidden"
name="${key as string}"
value="${value as string}"
/>`;
})}
<ak-empty-state loading title=${title}> </ak-empty-state>
</form>
</ak-flow-card>`;
}
}
+3 -8
View File
@@ -1,7 +1,6 @@
/// <reference types="@hcaptcha/types"/>
/// <reference types="turnstile-types"/>
import { renderStaticHTMLUnsafe } from "@goauthentik/common/purify";
import "@goauthentik/elements/EmptyState";
import { akEmptyState } from "@goauthentik/elements/EmptyState";
import { bound } from "@goauthentik/elements/decorators/bound";
import "@goauthentik/elements/forms/FormElement";
@@ -9,6 +8,7 @@ import { createIFrameHTMLWrapper } from "@goauthentik/elements/utils/iframe";
import { ListenerController } from "@goauthentik/elements/utils/listenerController.js";
import { randomId } from "@goauthentik/elements/utils/randomId";
import "@goauthentik/flow/FormStatic";
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { P, match } from "ts-pattern";
@@ -334,10 +334,7 @@ export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeRe
}
renderMain() {
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
return html`<ak-flow-card .challenge=${this.challenge}>
<form class="pf-c-form">
<ak-form-static
class="pf-c-form__group"
@@ -353,9 +350,7 @@ export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeRe
${this.renderBody()}
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
</ak-flow-card>`;
}
render() {
+8 -14
View File
@@ -1,5 +1,5 @@
import "@goauthentik/elements/EmptyState";
import "@goauthentik/flow/FormStatic";
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { msg } from "@lit/localize";
@@ -94,13 +94,7 @@ export class ConsentStage extends BaseStage<ConsentChallenge, ConsentChallengeRe
}
render(): TemplateResult {
if (!this.challenge) {
return html`<ak-empty-state loading> </ak-empty-state>`;
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
return html`<ak-flow-card .challenge=${this.challenge}>
<form
class="pf-c-form"
@submit=${(e: Event) => {
@@ -120,9 +114,11 @@ export class ConsentStage extends BaseStage<ConsentChallenge, ConsentChallengeRe
>
</div>
</ak-form-static>
${this.challenge.additionalPermissions.length > 0
? this.renderAdditional()
: this.renderNoPrevious()}
${
this.challenge.additionalPermissions.length > 0
? this.renderAdditional()
: this.renderNoPrevious()
}
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
@@ -131,9 +127,7 @@ export class ConsentStage extends BaseStage<ConsentChallenge, ConsentChallengeRe
</div>
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
</ak-flow-card>`;
}
}
+16 -25
View File
@@ -1,5 +1,5 @@
import "@goauthentik/elements/EmptyState";
import "@goauthentik/flow/FormStatic";
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { msg, str } from "@lit/localize";
@@ -22,30 +22,21 @@ export class DummyStage extends BaseStage<DummyChallenge, DummyChallengeResponse
}
render(): TemplateResult {
if (!this.challenge) {
return html`<ak-empty-state loading> </ak-empty-state>`;
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
>
<p>${msg(str`Stage name: ${this.challenge.name}`)}</p>
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
${msg("Continue")}
</button>
</div>
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
return html`<ak-flow-card .challenge=${this.challenge}>
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
>
<p>${msg(str`Stage name: ${this.challenge.name}`)}</p>
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
${msg("Continue")}
</button>
</div>
</form>
</ak-flow-card>`;
}
}
+18 -27
View File
@@ -1,4 +1,4 @@
import "@goauthentik/elements/EmptyState";
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { msg } from "@lit/localize";
@@ -21,33 +21,24 @@ export class EmailStage extends BaseStage<EmailChallenge, EmailChallengeResponse
}
render(): TemplateResult {
if (!this.challenge) {
return html`<ak-empty-state loading> </ak-empty-state>`;
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
>
<div class="pf-c-form__group">
<p>${msg("Check your Inbox for a verification email.")}</p>
</div>
return html`<ak-flow-card .challenge=${this.challenge}>
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
>
<div class="pf-c-form__group">
<p>${msg("Check your Inbox for a verification email.")}</p>
</div>
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
${msg("Send Email again.")}
</button>
</div>
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
${msg("Send Email again.")}
</button>
</div>
</form>
</ak-flow-card>`;
}
}
@@ -2,6 +2,7 @@ import { renderSourceIcon } from "@goauthentik/admin/sources/utils";
import "@goauthentik/elements/Divider";
import "@goauthentik/elements/EmptyState";
import "@goauthentik/elements/forms/FormElement";
import "@goauthentik/flow/components/ak-flow-card";
import "@goauthentik/flow/components/ak-flow-password-input.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import "@goauthentik/flow/stages/captcha/CaptchaStage";
@@ -221,7 +222,7 @@ export class IdentificationStage extends BaseStage<
if (!this.challenge?.enrollUrl && !this.challenge?.recoveryUrl) {
return nothing;
}
return html`<div class="pf-c-login__main-footer-band">
return html`<div slot="footer-band" class="pf-c-login__main-footer-band">
${this.challenge.enrollUrl
? html`<p class="pf-c-login__main-footer-band-item">
${msg("Need an account?")}
@@ -320,47 +321,39 @@ export class IdentificationStage extends BaseStage<
}
render(): TemplateResult {
if (!this.challenge) {
return html`<ak-empty-state loading> </ak-empty-state>`;
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
>
${this.challenge.applicationPre
? html`<p>
${msg(str`Login to continue to ${this.challenge.applicationPre}.`)}
</p>`
: nothing}
${this.renderInput()}
${this.challenge.passwordlessUrl
? html`
<div>
<a
href=${this.challenge.passwordlessUrl}
class="pf-c-button pf-m-secondary pf-m-block"
>
${msg("Use a security key")}
</a>
</div>
`
: nothing}
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links">
${(this.challenge.sources || []).map((source) => {
return this.renderSource(source);
})}
</ul>
${this.renderFooter()}
</footer>`;
return html`<ak-flow-card .challenge=${this.challenge}>
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
>
${this.challenge.applicationPre
? html`<p>
${msg(str`Login to continue to ${this.challenge.applicationPre}.`)}
</p>`
: nothing}
${this.renderInput()}
${this.challenge.passwordlessUrl
? html`
<div>
<a
href=${this.challenge.passwordlessUrl}
class="pf-c-button pf-m-secondary pf-m-block"
>
${msg("Use a security key")}
</a>
</div>
`
: nothing}
</form>
<ul slot="footer" class="pf-c-login__main-footer-links">
${(this.challenge.sources || []).map((source) => {
return this.renderSource(source);
})}
</ul>
${this.renderFooter()}
</ak-flow-card>`;
}
}
+45 -56
View File
@@ -1,6 +1,6 @@
import "@goauthentik/elements/EmptyState";
import "@goauthentik/elements/forms/FormElement";
import "@goauthentik/flow/FormStatic";
import "@goauthentik/flow/components/ak-flow-card.js";
import "@goauthentik/flow/components/ak-flow-password-input.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { PasswordManagerPrefill } from "@goauthentik/flow/stages/identification/IdentificationStage";
@@ -32,63 +32,52 @@ export class PasswordStage extends BaseStage<PasswordChallenge, PasswordChalleng
}
render(): TemplateResult {
if (!this.challenge) {
return html`<ak-empty-state loading> </ak-empty-state>`;
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
return html`<ak-flow-card .challenge=${this.challenge}>
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}
>
<ak-form-static
class="pf-c-form__group"
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}
>
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
</div>
</ak-form-static>
<input
name="username"
autocomplete="username"
type="hidden"
value="${this.challenge.pendingUser}"
/>
<ak-flow-input-password
label=${msg("Password")}
required
grab-focus
class="pf-c-form__group"
.errors=${(this.challenge?.responseErrors || {}).password}
?allow-show-password=${this.challenge.allowShowPassword}
invalid=${this.hasError("password").toString()}
prefill=${PasswordManagerPrefill.password ?? ""}
></ak-flow-input-password>
${this.challenge.recoveryUrl
? html`<a href="${this.challenge.recoveryUrl}">
${msg("Forgot password?")}</a
>`
: ""}
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
${msg("Continue")}
</button>
<div slot="link">
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}"
>${msg("Not you?")}</a
>
</div>
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
</ak-form-static>
<input
name="username"
autocomplete="username"
type="hidden"
value="${this.challenge.pendingUser}"
/>
<ak-flow-input-password
label=${msg("Password")}
required
grab-focus
class="pf-c-form__group"
.errors=${(this.challenge?.responseErrors || {}).password}
?allow-show-password=${this.challenge.allowShowPassword}
invalid=${this.hasError("password").toString()}
prefill=${PasswordManagerPrefill.password ?? ""}
></ak-flow-input-password>
${this.challenge.recoveryUrl
? html`<a href="${this.challenge.recoveryUrl}"> ${msg("Forgot password?")}</a>`
: ""}
<div class="pf-c-form__group pf-m-action">
<button type="submit" class="pf-c-button pf-m-primary pf-m-block">
${msg("Continue")}
</button>
</div>
</form>
</ak-flow-card>`;
}
}
+14 -23
View File
@@ -1,8 +1,8 @@
import { CapabilitiesEnum, WithCapabilitiesConfig } from "#elements/mixins/capabilities";
import "@goauthentik/elements/Divider";
import "@goauthentik/elements/EmptyState";
import { LOCALES } from "@goauthentik/elements/ak-locale-context/definitions";
import "@goauthentik/elements/forms/FormElement";
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { msg } from "@lit/localize";
@@ -277,28 +277,19 @@ ${prompt.initialValue}</textarea
}
render(): TemplateResult {
if (!this.challenge) {
return html`<ak-empty-state loading> </ak-empty-state>`;
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
>
${this.challenge.fields.map((prompt) => {
return this.renderField(prompt);
})}
${this.renderNonFieldErrors()} ${this.renderContinue()}
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
return html`<ak-flow-card .challenge=${this.challenge}>
<form
class="pf-c-form"
@submit=${(e: Event) => {
this.submitForm(e);
}}
>
${this.challenge.fields.map((prompt) => {
return this.renderField(prompt);
})}
${this.renderNonFieldErrors()} ${this.renderContinue()}
</form>
</ak-flow-card>`;
}
}
@@ -1,6 +1,6 @@
import "@goauthentik/elements/EmptyState";
import "@goauthentik/elements/forms/FormElement";
import "@goauthentik/flow/FormStatic";
import "@goauthentik/flow/components/ak-flow-card.js";
import { BaseStage } from "@goauthentik/flow/stages/base";
import { msg } from "@lit/localize";
@@ -28,13 +28,7 @@ export class PasswordStage extends BaseStage<
}
render(): TemplateResult {
if (!this.challenge) {
return html`<ak-empty-state loading> </ak-empty-state>`;
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">${this.challenge.flowInfo?.title}</h1>
</header>
<div class="pf-c-login__main-body">
return html`<ak-flow-card .challenge=${this.challenge}>
<form class="pf-c-form">
<ak-form-static
class="pf-c-form__group"
@@ -82,9 +76,7 @@ export class PasswordStage extends BaseStage<
</div>
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
</ak-flow-card>`;
}
}
@@ -1,5 +1,6 @@
import { globalAK } from "@goauthentik/common/global";
import "@goauthentik/elements/forms/HorizontalFormElement";
import "@goauthentik/flow/components/ak-flow-card.js";
import { PromptStage } from "@goauthentik/flow/stages/prompt/PromptStage";
import { msg, str } from "@lit/localize";
@@ -63,10 +64,7 @@ export class UserSettingsPromptStage extends PromptStage {
}
render(): TemplateResult {
if (!this.challenge) {
return html`<ak-empty-state default-label></ak-empty-state>`;
}
return html`<div class="pf-c-login__main-body">
return html`<ak-flow-card .challenge=${this.challenge}>
<form
class="pf-c-form"
@submit=${(e: Event) => {
@@ -79,9 +77,7 @@ export class UserSettingsPromptStage extends PromptStage {
${this.renderNonFieldErrors()} ${this.renderContinue()}
</form>
</div>
<footer class="pf-c-login__main-footer">
<ul class="pf-c-login__main-footer-links"></ul>
</footer>`;
</ak-flow-card>`;
}
}