diff --git a/web/src/flow/FlowExecutor.ts b/web/src/flow/FlowExecutor.ts
index ad7ecb5ab..08df46748 100644
--- a/web/src/flow/FlowExecutor.ts
+++ b/web/src/flow/FlowExecutor.ts
@@ -12,17 +12,7 @@ import { Interface } from "@goauthentik/elements/Base";
import "@goauthentik/elements/LoadingOverlay";
import "@goauthentik/flow/stages/FlowErrorStage";
import "@goauthentik/flow/stages/RedirectStage";
-import "@goauthentik/flow/stages/access_denied/AccessDeniedStage";
-// Import webauthn-related stages to prevent issues on safari
-// Which is overly sensitive to allowing things only in the context of a
-// user interaction
-import "@goauthentik/flow/stages/authenticator_validate/AuthenticatorValidateStage";
-import "@goauthentik/flow/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage";
-import "@goauthentik/flow/stages/autosubmit/AutosubmitStage";
import { StageHost } from "@goauthentik/flow/stages/base";
-import "@goauthentik/flow/stages/captcha/CaptchaStage";
-import "@goauthentik/flow/stages/identification/IdentificationStage";
-import "@goauthentik/flow/stages/password/PasswordStage";
import { t } from "@lingui/macro";
@@ -43,7 +33,6 @@ import {
ChallengeChoices,
ChallengeTypes,
ContextualFlowInfo,
- CurrentTenant,
FlowChallengeResponseRequest,
FlowErrorChallenge,
FlowsApi,
@@ -92,9 +81,6 @@ export class FlowExecutor extends Interface implements StageHost {
@property({ type: Boolean })
loading = false;
- @property({ attribute: false })
- tenant!: CurrentTenant;
-
@state()
inspectorOpen = false;
@@ -186,7 +172,6 @@ export class FlowExecutor extends Interface implements StageHost {
this.addEventListener(EVENT_FLOW_INSPECTOR_TOGGLE, () => {
this.inspectorOpen = !this.inspectorOpen;
});
- tenant().then((tenant) => (this.tenant = tenant));
}
async getTheme(): Promise
{
@@ -283,25 +268,25 @@ export class FlowExecutor extends Interface implements StageHost {
async renderChallengeNativeElement(): Promise {
switch (this.challenge?.component) {
case "ak-stage-access-denied":
- // Statically imported for performance reasons
+ await import("@goauthentik/flow/stages/access_denied/AccessDeniedStage");
return html``;
case "ak-stage-identification":
- // Statically imported for performance reasons
+ await import("@goauthentik/flow/stages/identification/IdentificationStage");
return html``;
case "ak-stage-password":
- // Statically imported for performance reasons
+ await import("@goauthentik/flow/stages/password/PasswordStage");
return html``;
case "ak-stage-captcha":
- // Statically imported to prevent browsers blocking urls
+ await import("@goauthentik/flow/stages/captcha/CaptchaStage");
return html``;
case "ak-stage-autosubmit":
- // Statically imported for performance reasons
+ await import("@goauthentik/flow/stages/autosubmit/AutosubmitStage");
return html``;
case "ak-stage-authenticator-validate":
+ await import(
+ "@goauthentik/flow/stages/authenticator_validate/AuthenticatorValidateStage"
+ );
return html``;
default:
- break;
+ return html`Invalid native challenge element`;
}
- return html`Invalid native challenge element`;
}
async renderChallenge(): Promise {
diff --git a/web/src/flow/FlowInterface.ts b/web/src/flow/FlowInterface.ts
index 9cd9254c5..c46396e26 100644
--- a/web/src/flow/FlowInterface.ts
+++ b/web/src/flow/FlowInterface.ts
@@ -1,5 +1,18 @@
import { autoDetectLanguage } from "@goauthentik/common/ui/locale";
import "@goauthentik/elements/messages/MessageContainer";
import "@goauthentik/flow/FlowExecutor";
+// Statically import some stages to speed up load speed
+import "@goauthentik/flow/stages/access_denied/AccessDeniedStage";
+// Import webauthn-related stages to prevent issues on safari
+// Which is overly sensitive to allowing things only in the context of a
+// user interaction
+import "@goauthentik/flow/stages/authenticator_validate/AuthenticatorValidateStage";
+import "@goauthentik/flow/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage";
+import "@goauthentik/flow/stages/autosubmit/AutosubmitStage";
+import "@goauthentik/flow/stages/captcha/CaptchaStage";
+import "@goauthentik/flow/stages/identification/IdentificationStage";
+import "@goauthentik/flow/stages/password/PasswordStage";
+
+// end of stage import
autoDetectLanguage();
diff --git a/web/src/flow/stages/authenticator_validate/AuthenticatorValidateStage.ts b/web/src/flow/stages/authenticator_validate/AuthenticatorValidateStage.ts
index 1525a063b..893c108d0 100644
--- a/web/src/flow/stages/authenticator_validate/AuthenticatorValidateStage.ts
+++ b/web/src/flow/stages/authenticator_validate/AuthenticatorValidateStage.ts
@@ -44,7 +44,7 @@ export class AuthenticatorValidateStage
return this.host.loading;
}
- get tenant(): CurrentTenant {
+ get tenant(): CurrentTenant | undefined {
return this.host.tenant;
}
diff --git a/web/src/flow/stages/base.ts b/web/src/flow/stages/base.ts
index 69a8ad28c..90a568592 100644
--- a/web/src/flow/stages/base.ts
+++ b/web/src/flow/stages/base.ts
@@ -12,7 +12,7 @@ export interface StageHost {
loading: boolean;
submit(payload: unknown): Promise;
- readonly tenant: CurrentTenant;
+ readonly tenant?: CurrentTenant;
}
export function readFileAsync(file: Blob) {
diff --git a/web/src/standalone/api-browser/index.ts b/web/src/standalone/api-browser/index.ts
new file mode 100644
index 000000000..2e4545c0c
--- /dev/null
+++ b/web/src/standalone/api-browser/index.ts
@@ -0,0 +1,103 @@
+import { EVENT_THEME_CHANGE } from "@goauthentik/common/constants";
+import { first, getCookie } from "@goauthentik/common/utils";
+import { Interface } from "@goauthentik/elements/Base";
+import { DefaultTenant } from "@goauthentik/elements/sidebar/SidebarBrand";
+import "rapidoc";
+
+import { CSSResult, TemplateResult, css, html } from "lit";
+import { customElement, property, state } from "lit/decorators.js";
+import { ifDefined } from "lit/directives/if-defined.js";
+
+import { UiThemeEnum } from "@goauthentik/api";
+
+@customElement("ak-api-browser")
+export class APIBrowser extends Interface {
+ @property()
+ schemaPath?: string;
+
+ static get styles(): CSSResult[] {
+ return [
+ css`
+ img.logo {
+ width: 100%;
+ padding: 1rem 0.5rem 1.5rem 0.5rem;
+ min-height: 48px;
+ }
+ `,
+ ];
+ }
+
+ @state()
+ bgColor = "#000000";
+
+ @state()
+ textColor = "#000000";
+
+ firstUpdated(): void {
+ this.addEventListener(EVENT_THEME_CHANGE, ((ev: CustomEvent) => {
+ const style = getComputedStyle(document.documentElement);
+ if (ev.detail === UiThemeEnum.Light) {
+ this.bgColor = style
+ .getPropertyValue("--pf-global--BackgroundColor--light-300")
+ .trim();
+ this.textColor = style.getPropertyValue("--pf-global--Color--300").trim();
+ } else {
+ this.bgColor = style.getPropertyValue("--ak-dark-background").trim();
+ this.textColor = style.getPropertyValue("--ak-dark-foreground").trim();
+ }
+ }) as EventListener);
+ this.dispatchEvent(
+ new CustomEvent(EVENT_THEME_CHANGE, {
+ bubbles: true,
+ composed: true,
+ detail: UiThemeEnum.Automatic,
+ }),
+ );
+ }
+
+ render(): TemplateResult {
+ return html`
+ ,
+ ) => {
+ e.detail.request.headers.append(
+ "X-authentik-CSRF",
+ getCookie("authentik_csrf"),
+ );
+ }}
+ >
+
+
+
+
+ `;
+ }
+}
diff --git a/web/src/standalone/loading/index.ts b/web/src/standalone/loading/index.ts
new file mode 100644
index 000000000..728be4e52
--- /dev/null
+++ b/web/src/standalone/loading/index.ts
@@ -0,0 +1,56 @@
+import { globalAK } from "@goauthentik/common/global";
+import { Interface } from "@goauthentik/elements/Base";
+
+import { t } from "@lingui/macro";
+
+import { CSSResult, TemplateResult, css, html } from "lit";
+import { customElement } from "lit/decorators.js";
+
+import PFEmptyState from "@patternfly/patternfly/components/EmptyState/empty-state.css";
+import PFPage from "@patternfly/patternfly/components/Page/page.css";
+import PFSpinner from "@patternfly/patternfly/components/Spinner/spinner.css";
+import PFBase from "@patternfly/patternfly/patternfly-base.css";
+
+import { UiThemeEnum } from "@goauthentik/api";
+
+@customElement("ak-loading")
+export class Loading extends Interface {
+ static get styles(): CSSResult[] {
+ return [
+ PFBase,
+ PFPage,
+ PFSpinner,
+ PFEmptyState,
+ css`
+ :host([theme="dark"]) h1 {
+ color: var(--ak-dark-foreground);
+ }
+ `,
+ ];
+ }
+
+ async getTheme(): Promise {
+ return globalAK()?.tenant.uiTheme || UiThemeEnum.Automatic;
+ }
+
+ render(): TemplateResult {
+ return html`
+
+
+
+
+
+
+
+
${t`Loading...`}
+
+
+ `;
+ }
+}
diff --git a/web/src/user/UserInterface.ts b/web/src/user/UserInterface.ts
index 5daae57a2..7368e7af0 100644
--- a/web/src/user/UserInterface.ts
+++ b/web/src/user/UserInterface.ts
@@ -1,4 +1,4 @@
-import { DEFAULT_CONFIG, tenant } from "@goauthentik/common/api/config";
+import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import {
EVENT_API_DRAWER_TOGGLE,
EVENT_NOTIFICATION_DRAWER_TOGGLE,
@@ -36,7 +36,7 @@ import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";
-import { CurrentTenant, EventsApi, SessionUser } from "@goauthentik/api";
+import { EventsApi, SessionUser } from "@goauthentik/api";
autoDetectLanguage();
@@ -50,9 +50,6 @@ export class UserInterface extends Interface {
ws: WebsocketClient;
- @property({ attribute: false })
- tenant: CurrentTenant = DefaultTenant;
-
@property({ type: Number })
notificationsCount = 0;
@@ -128,7 +125,6 @@ export class UserInterface extends Interface {
}
async firstUpdated(): Promise {
- this.tenant = await tenant();
this.me = await me();
this.config = await uiConfig();
const notifications = await new EventsApi(DEFAULT_CONFIG).eventsNotificationsList({
@@ -165,8 +161,8 @@ export class UserInterface extends Interface {
diff --git a/web/src/user/user-settings/details/stages/prompt/PromptStage.ts b/web/src/user/user-settings/details/stages/prompt/PromptStage.ts
index 3eaa7325c..c77366d49 100644
--- a/web/src/user/user-settings/details/stages/prompt/PromptStage.ts
+++ b/web/src/user/user-settings/details/stages/prompt/PromptStage.ts
@@ -62,7 +62,7 @@ export class UserSettingsPromptStage extends PromptStage {