diff --git a/web/rollup.config.js b/web/rollup.config.js
index 06895a392f..129c67c02a 100644
--- a/web/rollup.config.js
+++ b/web/rollup.config.js
@@ -3,6 +3,7 @@ import babel from "@rollup/plugin-babel";
import commonjs from "@rollup/plugin-commonjs";
import { nodeResolve } from "@rollup/plugin-node-resolve";
import replace from "@rollup/plugin-replace";
+import { cwd } from "process";
import copy from "rollup-plugin-copy";
import cssimport from "rollup-plugin-cssimport";
import { terser } from "rollup-plugin-terser";
@@ -82,6 +83,7 @@ export const defaultOptions = {
}),
replace({
"process.env.NODE_ENV": JSON.stringify(isProdBuild ? "production" : "development"),
+ "process.env.CWD": JSON.stringify(cwd()),
"process.env.AK_API_BASE_PATH": JSON.stringify(apiBasePath),
"preventAssignment": true,
}),
diff --git a/web/src/admin/providers/oauth2/OAuth2ProviderViewPage.ts b/web/src/admin/providers/oauth2/OAuth2ProviderViewPage.ts
index 3567c933ee..d85ff68460 100644
--- a/web/src/admin/providers/oauth2/OAuth2ProviderViewPage.ts
+++ b/web/src/admin/providers/oauth2/OAuth2ProviderViewPage.ts
@@ -259,7 +259,20 @@ export class OAuth2ProviderViewPage extends AKElement {
class="pf-c-card pf-l-grid__item pf-m-12-col pf-m-12-col-on-xl pf-m-12-col-on-2xl"
>
-
+
{
+ if (!this.provider) {
+ return input;
+ }
+ return input.replaceAll(
+ "<application slug>",
+ this.provider.assignedApplicationSlug,
+ );
+ },
+ ]}
+ .md=${MDProviderOAuth2}
+ >
`;
diff --git a/web/src/admin/providers/proxy/ProxyProviderViewPage.ts b/web/src/admin/providers/proxy/ProxyProviderViewPage.ts
index 31b61a5b55..a8acd2c267 100644
--- a/web/src/admin/providers/proxy/ProxyProviderViewPage.ts
+++ b/web/src/admin/providers/proxy/ProxyProviderViewPage.ts
@@ -13,7 +13,6 @@ import MDTraefikStandalone from "@goauthentik/docs/providers/proxy/_traefik_stan
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/CodeMirror";
import { PFColor } from "@goauthentik/elements/Label";
-import { MarkdownDocument } from "@goauthentik/elements/Markdown";
import "@goauthentik/elements/Markdown";
import "@goauthentik/elements/Tabs";
import "@goauthentik/elements/buttons/ModalButton";
@@ -104,25 +103,6 @@ export class ProxyProviderViewPage extends AKElement {
});
}
- renderConfigTemplate(markdown: MarkdownDocument): MarkdownDocument {
- const extHost = new URL(this.provider?.externalHost || "http://a");
- // See website/docs/providers/proxy/forward_auth.mdx
- if (this.provider?.mode === ProxyMode.ForwardSingle) {
- markdown.html = markdown.html
- .replaceAll("authentik.company", window.location.hostname)
- .replaceAll("outpost.company:9000", window.location.hostname)
- .replaceAll("https://app.company", extHost.toString())
- .replaceAll("app.company", extHost.hostname);
- } else if (this.provider?.mode == ProxyMode.ForwardDomain) {
- markdown.html = markdown.html
- .replaceAll("authentik.company", window.location.hostname)
- .replaceAll("outpost.company:9000", extHost.toString())
- .replaceAll("https://app.company", extHost.toString())
- .replaceAll("app.company", extHost.hostname);
- }
- return markdown;
- }
-
renderConfig(): TemplateResult {
const serves = [
{
@@ -154,6 +134,29 @@ export class ProxyProviderViewPage extends AKElement {
md: MDCaddyStandalone,
},
];
+ const replacers = [
+ (input: string): string => {
+ if (!this.provider) {
+ return input;
+ }
+ const extHost = new URL(this.provider.externalHost);
+ // See website/docs/providers/proxy/forward_auth.mdx
+ if (this.provider?.mode === ProxyMode.ForwardSingle) {
+ return input
+ .replaceAll("authentik.company", window.location.hostname)
+ .replaceAll("outpost.company:9000", window.location.hostname)
+ .replaceAll("https://app.company", extHost.toString())
+ .replaceAll("app.company", extHost.hostname);
+ } else if (this.provider?.mode == ProxyMode.ForwardDomain) {
+ return input
+ .replaceAll("authentik.company", window.location.hostname)
+ .replaceAll("outpost.company:9000", extHost.toString())
+ .replaceAll("https://app.company", extHost.toString())
+ .replaceAll("app.company", extHost.hostname);
+ }
+ return input;
+ },
+ ];
return html`
${serves.map((server) => {
return html``;
})}`;
diff --git a/web/src/elements/Alert.ts b/web/src/elements/Alert.ts
new file mode 100644
index 0000000000..bdcd80379d
--- /dev/null
+++ b/web/src/elements/Alert.ts
@@ -0,0 +1,41 @@
+import { AKElement } from "@goauthentik/elements/Base";
+
+import { CSSResult, TemplateResult, html } from "lit";
+import { customElement, property } from "lit/decorators.js";
+
+import AKGlobal from "@goauthentik/common/styles/authentik.css";
+import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
+import PFBase from "@patternfly/patternfly/patternfly-base.css";
+
+export enum Level {
+ Warning = "pf-m-warning",
+ Info = "pf-m-info",
+ Success = "pf-m-success",
+ Danger = "pf-m-danger",
+}
+
+@customElement("ak-alert")
+export class Alert extends AKElement {
+ @property({ type: Boolean })
+ inline = false;
+
+ @property()
+ level: Level = Level.Warning;
+
+ static get styles(): CSSResult[] {
+ return [PFBase, PFAlert, AKGlobal];
+ }
+
+ render(): TemplateResult {
+ return html``;
+ }
+}
diff --git a/web/src/elements/Markdown.ts b/web/src/elements/Markdown.ts
index 5078e478a7..436035dce5 100644
--- a/web/src/elements/Markdown.ts
+++ b/web/src/elements/Markdown.ts
@@ -1,3 +1,4 @@
+import "@goauthentik/elements/Alert";
import { AKElement } from "@goauthentik/elements/Base";
import { CSSResult, TemplateResult, html } from "lit";
@@ -12,22 +13,66 @@ export interface MarkdownDocument {
html: string;
metadata: { [key: string]: string };
filename: string;
+ path: string;
}
+export type Replacer = (input: string, md: MarkdownDocument) => string;
+
@customElement("ak-markdown")
export class Markdown extends AKElement {
@property({ attribute: false })
md?: MarkdownDocument;
+ @property({ attribute: false })
+ replacers: Replacer[] = [];
+
+ defaultReplacers: Replacer[] = [
+ this.replaceAdmonitions,
+ this.replaceList,
+ this.replaceRelativeLinks,
+ ];
+
static get styles(): CSSResult[] {
return [PFList, PFContent, AKGlobal];
}
+ replaceAdmonitions(input: string): string {
+ const admonitionStart = /:::(\w+)
/gm;
+ const admonitionEnd = /:::/gm;
+ return input
+ .replaceAll(admonitionStart, "")
+ .replaceAll(admonitionEnd, "");
+ }
+
+ replaceList(input: string): string {
+ return input.replace("", "");
+ }
+
+ replaceRelativeLinks(input: string, md: MarkdownDocument): string {
+ const relativeLink = /href=".(.*)"/gm;
+ const cwd = process.env.CWD as string;
+ // cwd will point to $root/web, but the docs are in $root/website/docs
+ let relPath = md.path.replace(cwd + "site", "");
+ if (md.filename === "index.md") {
+ relPath = relPath.replace("index.md", "");
+ }
+ const baseURL = "https://goauthentik.io";
+ const fullURL = `${baseURL}${relPath}.$1`;
+ return input.replace(relativeLink, `href="${fullURL}" target="_blank"`);
+ }
+
render(): TemplateResult {
if (!this.md) {
return html``;
}
- const finalHTML = this.md?.html.replace("", "");
+ let finalHTML = this.md.html;
+ const replacers = [...this.defaultReplacers, ...this.replacers];
+ replacers.forEach((r) => {
+ if (!this.md) {
+ return;
+ }
+ finalHTML = r(finalHTML, this.md);
+ });
return html`${this.md?.metadata.title ? html`${this.md.metadata.title}
` : html``}
${unsafeHTML(finalHTML)}`;
}
diff --git a/website/docs/core/applications.md b/website/docs/core/applications.md
index 668378af71..7778cdb447 100644
--- a/website/docs/core/applications.md
+++ b/website/docs/core/applications.md
@@ -47,6 +47,10 @@ To hide applications without modifying policy settings and without removing it,
Keep in mind, the users still have access, so they can still authorize access when the login process is started from the application.
-### Launch URLs (2022.3+)
+### Launch URLs
+
+:::info
+Requires authentik 2022.3
+:::
To give users direct links to applications, you can now use an URL like `https://authentik.company/application/launch//`. This will redirect the user directly if they're already logged in, and otherwise authenticate the user, and then forward them.