diff --git a/web/rollup.config.js b/web/rollup.config.js
index 06895a392..129c67c02 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 3567c933e..d85ff6846 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 31b61a5b5..a8acd2c26 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 000000000..bdcd80379
--- /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 5078e478a..436035dce 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 668378af7..7778cdb44 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.