web/sdk: embed authentik settings into anything
the web part Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
@ -13,6 +13,7 @@
|
||||
<link rel="shortcut icon" href="{{ brand.branding_favicon }}">
|
||||
{% block head_before %}
|
||||
{% endblock %}
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/patternfly-base.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'dist/custom.css' %}" data-inject>
|
||||
{% versioned_script "dist/poly-%v.js" %}
|
||||
|
||||
@ -41,6 +41,7 @@ const definitions = {
|
||||
|
||||
const otherFiles = [
|
||||
["node_modules/@patternfly/patternfly/patternfly.min.css", "."],
|
||||
["node_modules/@patternfly/patternfly/patternfly-base.css", "."],
|
||||
["node_modules/@patternfly/patternfly/assets/**", ".", "node_modules/@patternfly/patternfly/"],
|
||||
["src/custom.css", "."],
|
||||
["src/common/styles/**", "."],
|
||||
@ -79,6 +80,11 @@ const interfaces = [
|
||||
["polyfill/poly.ts", "."],
|
||||
];
|
||||
|
||||
const extraTargets = [
|
||||
["sdk/index.ts", "sdk", { entryNames: "[dir]/[name]" }],
|
||||
["sdk/user-settings.ts", "sdk/user-settings", { entryNames: "[dir]/[name]" }],
|
||||
];
|
||||
|
||||
const baseArgs = {
|
||||
bundle: true,
|
||||
write: true,
|
||||
@ -101,7 +107,11 @@ function getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
async function buildOneSource(source, dest) {
|
||||
function getAllTargets() {
|
||||
return [...interfaces, ...extraTargets];
|
||||
}
|
||||
|
||||
async function buildSingleTarget(source, dest, options) {
|
||||
const DIST = path.join(__dirname, "./dist", dest);
|
||||
console.log(`[${new Date(Date.now()).toISOString()}] Starting build for target ${source}`);
|
||||
|
||||
@ -112,6 +122,7 @@ async function buildOneSource(source, dest) {
|
||||
entryPoints: [`./src/${source}`],
|
||||
entryNames: `[dir]/[name]-${getVersion()}`,
|
||||
outdir: DIST,
|
||||
...options,
|
||||
});
|
||||
const end = Date.now();
|
||||
console.log(
|
||||
@ -124,8 +135,10 @@ async function buildOneSource(source, dest) {
|
||||
}
|
||||
}
|
||||
|
||||
async function buildAuthentik(interfaces) {
|
||||
await Promise.allSettled(interfaces.map(([source, dest]) => buildOneSource(source, dest)));
|
||||
async function buildTargets(targets) {
|
||||
await Promise.allSettled(
|
||||
targets.map(([source, dest, options]) => buildSingleTarget(source, dest, options)),
|
||||
);
|
||||
}
|
||||
|
||||
let timeoutId = null;
|
||||
@ -135,7 +148,7 @@ function debouncedBuild() {
|
||||
}
|
||||
timeoutId = setTimeout(() => {
|
||||
console.clear();
|
||||
buildAuthentik(interfaces);
|
||||
buildTargets(getAllTargets());
|
||||
}, 250);
|
||||
}
|
||||
|
||||
@ -143,7 +156,7 @@ if (process.argv.length > 2 && (process.argv[2] === "-h" || process.argv[2] ===
|
||||
console.log(`Build the authentikUI
|
||||
|
||||
options:
|
||||
-w, --watch: Build all ${interfaces.length} interfaces
|
||||
-w, --watch: Build all ${getAllTargets().length} interfaces
|
||||
-p, --proxy: Build only the polyfills and the loading application
|
||||
-h, --help: This help message
|
||||
`);
|
||||
@ -163,11 +176,11 @@ if (process.argv.length > 2 && (process.argv[2] === "-w" || process.argv[2] ===
|
||||
});
|
||||
} else if (process.argv.length > 2 && (process.argv[2] === "-p" || process.argv[2] === "--proxy")) {
|
||||
// There's no watch-for-proxy, sorry.
|
||||
await buildAuthentik(
|
||||
await buildTargets(
|
||||
interfaces.filter(([_, dest]) => ["standalone/loading", "."].includes(dest)),
|
||||
);
|
||||
process.exit(0);
|
||||
} else {
|
||||
// And the fallback: just build it.
|
||||
await buildAuthentik(interfaces);
|
||||
await buildTargets(interfaces);
|
||||
}
|
||||
|
||||
@ -7,6 +7,9 @@ export function renderSourceIcon(name: string, iconUrl: string | undefined | nul
|
||||
const url = iconUrl.replaceAll("fa://", "");
|
||||
return html`<i class="fas ${url}" title="${name}"></i>`;
|
||||
}
|
||||
if (window.authentik_sdk?.base) {
|
||||
return html`<img src="${window.authentik_sdk?.base}${iconUrl}" alt="${name}" />`;
|
||||
}
|
||||
return html`<img src="${iconUrl}" alt="${name}" />`;
|
||||
}
|
||||
return icon;
|
||||
|
||||
@ -2,6 +2,7 @@ import {
|
||||
CSRFMiddleware,
|
||||
EventMiddleware,
|
||||
LoggingMiddleware,
|
||||
SDKMiddleware,
|
||||
} from "@goauthentik/common/api/middleware";
|
||||
import { EVENT_LOCALE_REQUEST, VERSION } from "@goauthentik/common/constants";
|
||||
import { globalAK } from "@goauthentik/common/global";
|
||||
@ -67,8 +68,18 @@ export function getMetaContent(key: string): string {
|
||||
return metaEl.content;
|
||||
}
|
||||
|
||||
export function apiBase(): string {
|
||||
if (process.env.AK_API_BASE_PATH) {
|
||||
return process.env.AK_API_BASE_PATH;
|
||||
}
|
||||
if (window.authentik_sdk?.base) {
|
||||
return window.authentik_sdk?.base;
|
||||
}
|
||||
return window.location.origin;
|
||||
}
|
||||
|
||||
export const DEFAULT_CONFIG = new Configuration({
|
||||
basePath: (process.env.AK_API_BASE_PATH || window.location.origin) + "/api/v3",
|
||||
basePath: `${apiBase()}/api/v3`,
|
||||
headers: {
|
||||
"sentry-trace": getMetaContent("sentry-trace"),
|
||||
},
|
||||
@ -76,6 +87,7 @@ export const DEFAULT_CONFIG = new Configuration({
|
||||
new CSRFMiddleware(),
|
||||
new EventMiddleware(),
|
||||
new LoggingMiddleware(globalAK().brand),
|
||||
new SDKMiddleware(),
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
@ -44,6 +44,21 @@ export class CSRFMiddleware implements Middleware {
|
||||
}
|
||||
}
|
||||
|
||||
export class SDKMiddleware implements Middleware {
|
||||
token?: string;
|
||||
constructor() {
|
||||
this.token = window.authentik_sdk?.token;
|
||||
}
|
||||
pre?(context: RequestContext): Promise<FetchParams | void> {
|
||||
if (this.token) {
|
||||
context.init.credentials = "include";
|
||||
// @ts-ignore
|
||||
context.init.headers["Authorization"] = `Bearer ${this.token}`;
|
||||
}
|
||||
return Promise.resolve(context);
|
||||
}
|
||||
}
|
||||
|
||||
export class EventMiddleware implements Middleware {
|
||||
post?(context: ResponseContext): Promise<Response | void> {
|
||||
const request: RequestInfo = {
|
||||
|
||||
@ -1,4 +1,10 @@
|
||||
import { Config, ConfigFromJSON, CurrentBrand, CurrentBrandFromJSON } from "@goauthentik/api";
|
||||
import {
|
||||
Config,
|
||||
ConfigFromJSON,
|
||||
CurrentBrand,
|
||||
CurrentBrandFromJSON,
|
||||
UiThemeEnum,
|
||||
} from "@goauthentik/api";
|
||||
|
||||
export interface GlobalAuthentik {
|
||||
_converted?: boolean;
|
||||
@ -30,7 +36,9 @@ export function globalAK(): GlobalAuthentik {
|
||||
capabilities: [],
|
||||
}),
|
||||
brand: CurrentBrandFromJSON({
|
||||
matched_domain: window.location.host,
|
||||
ui_footer_links: [],
|
||||
ui_theme: window.authentik_sdk?.forceTheme ?? UiThemeEnum.Automatic,
|
||||
}),
|
||||
versionFamily: "",
|
||||
versionSubdomain: "",
|
||||
@ -40,6 +48,10 @@ export function globalAK(): GlobalAuthentik {
|
||||
return ak;
|
||||
}
|
||||
|
||||
export function isEmbedded() {
|
||||
return !!window.authentik_sdk;
|
||||
}
|
||||
|
||||
export function docLink(path: string): string {
|
||||
const ak = globalAK();
|
||||
// Default case or beta build which should always point to latest
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { isEmbedded } from "@goauthentik/common/global";
|
||||
import { UIConfig, uiConfig } from "@goauthentik/common/ui/config";
|
||||
import { ModalOrchestrationController } from "@goauthentik/elements/controllers/ModalOrchestrationController.js";
|
||||
import { ensureCSSStyleSheet } from "@goauthentik/elements/utils/ensureCSSStyleSheet";
|
||||
|
||||
import { state } from "lit/decorators.js";
|
||||
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
import PFVariables from "@patternfly/patternfly/base/patternfly-variables.css";
|
||||
|
||||
import type { Config, CurrentBrand, LicenseSummary } from "@goauthentik/api";
|
||||
import { UiThemeEnum } from "@goauthentik/api";
|
||||
@ -43,7 +44,10 @@ export class Interface extends AKElement implements AkInterface {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
document.adoptedStyleSheets = [...document.adoptedStyleSheets, ensureCSSStyleSheet(PFBase)];
|
||||
document.adoptedStyleSheets = [
|
||||
...document.adoptedStyleSheets,
|
||||
ensureCSSStyleSheet(PFVariables),
|
||||
];
|
||||
this[brandContext] = new BrandContextController(this);
|
||||
this[configContext] = new ConfigContextController(this);
|
||||
this[modalController] = new ModalOrchestrationController(this);
|
||||
@ -61,7 +65,9 @@ export class Interface extends AKElement implements AkInterface {
|
||||
// Instead of calling ._activateTheme() twice, we insert the root document in the call
|
||||
// since multiple calls to ._activateTheme() would not do anything after the first call
|
||||
// as the theme is already enabled.
|
||||
roots.unshift(document as unknown as DocumentOrShadowRoot);
|
||||
if (!isEmbedded()) {
|
||||
roots.unshift(document as unknown as DocumentOrShadowRoot);
|
||||
}
|
||||
super._activateTheme(theme, ...roots);
|
||||
}
|
||||
|
||||
|
||||
8
web/src/global.d.ts
vendored
8
web/src/global.d.ts
vendored
@ -12,3 +12,11 @@ declare namespace Intl {
|
||||
public format: (items: string[]) => string;
|
||||
}
|
||||
}
|
||||
|
||||
declare interface Window {
|
||||
authentik_sdk?: {
|
||||
base: string;
|
||||
token?: string;
|
||||
forceTheme?: string;
|
||||
};
|
||||
}
|
||||
|
||||
19
web/src/sdk/common.ts
Normal file
19
web/src/sdk/common.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { Interface } from "@goauthentik/elements/Interface/Interface";
|
||||
|
||||
import { html } from "lit";
|
||||
import { customElement } from "lit/decorators.js";
|
||||
|
||||
import { UiThemeEnum, UiThemeEnumFromJSON } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-sdk-interface")
|
||||
export class SDKInterface extends Interface {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
render() {
|
||||
return html`<slot></slot>`;
|
||||
}
|
||||
async getTheme(): Promise<UiThemeEnum> {
|
||||
return UiThemeEnumFromJSON(window.authentik_sdk?.forceTheme) || UiThemeEnum.Automatic;
|
||||
}
|
||||
}
|
||||
1
web/src/sdk/index.ts
Normal file
1
web/src/sdk/index.ts
Normal file
@ -0,0 +1 @@
|
||||
import "@goauthentik/sdk/user-settings";
|
||||
3
web/src/sdk/user-settings.ts
Normal file
3
web/src/sdk/user-settings.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import "@goauthentik/elements/messages/MessageContainer";
|
||||
import "@goauthentik/sdk/common";
|
||||
import "@goauthentik/user/user-settings/UserSettingsPage";
|
||||
@ -21,6 +21,7 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
import {
|
||||
ChallengeTypes,
|
||||
CoreApi,
|
||||
FlowChallengeResponseRequest,
|
||||
FlowErrorChallenge,
|
||||
FlowsApi,
|
||||
@ -82,8 +83,11 @@ export class UserSettingsFlowExecutor
|
||||
});
|
||||
}
|
||||
|
||||
firstUpdated(): void {
|
||||
this.flowSlug = this.brand?.flowUserSettings;
|
||||
async firstUpdated(): Promise<void> {
|
||||
if (!this.brand) {
|
||||
this.brand = await new CoreApi(DEFAULT_CONFIG).coreBrandsCurrentRetrieve();
|
||||
}
|
||||
this.flowSlug = this.brand.flowUserSettings;
|
||||
if (!this.flowSlug) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
"@goauthentik/flow/*": ["./src/flow/*"],
|
||||
"@goauthentik/locales/*": ["./src/locales/*"],
|
||||
"@goauthentik/polyfill/*": ["./src/polyfill/*"],
|
||||
"@goauthentik/sdk/*": ["./src/sdk/*"],
|
||||
"@goauthentik/standalone/*": ["./src/standalone/*"],
|
||||
"@goauthentik/user/*": ["./src/user/*"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user