From 7fe2d1b371715f956ca2522fae6d5e35e4859ad8 Mon Sep 17 00:00:00 2001 From: Jonas Hungershausen Date: Tue, 24 Jun 2025 16:40:40 -0400 Subject: [PATCH 1/2] feat: add interactive Ory Project chart --- docs/ecosystem/projects.mdx | 6 +- package-lock.json | 10 + package.json | 1 + src/pages/_assets/project-overview-graph.tsx | 293 +++++++++++++++++++ src/theme/Mermaid.js | 6 + 5 files changed, 315 insertions(+), 1 deletion(-) create mode 100644 src/pages/_assets/project-overview-graph.tsx diff --git a/docs/ecosystem/projects.mdx b/docs/ecosystem/projects.mdx index 8e2e6c47d..66a6d219b 100644 --- a/docs/ecosystem/projects.mdx +++ b/docs/ecosystem/projects.mdx @@ -86,7 +86,11 @@ demands. If you're delivering enterprise-grade SaaS and need SSO that just works ## All of Ory Open Source -![Full Ory Ecosystem](./_static/projects/1.png) +```mdx-code-block +import { ProjectOverviewGraph } from "@site/src/pages/_assets/project-overview-graph" + + +``` If you were to use the full Ory Ecosystem, it would probably look something like this. Keep in mind that any component shown here can be replaced or removed, depending on your use case. diff --git a/package-lock.json b/package-lock.json index 02fd1360b..8a6e69573 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@docusaurus/preset-classic": "3.8.0", "@docusaurus/theme-classic": "3.8.0", "@docusaurus/theme-search-algolia": "3.8.0", + "@iconify-json/tabler": "1.2.19", "@octokit/rest": "20.0.2", "@ory/client-fetch": "1.15.16", "@rjsf/bootstrap-4": "5.24.1", @@ -4240,6 +4241,15 @@ "@hapi/hoek": "^9.0.0" } }, + "node_modules/@iconify-json/tabler": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@iconify-json/tabler/-/tabler-1.2.19.tgz", + "integrity": "sha512-JDeQTQxHD8KE12pAbPVHX1WFVOPq8D0XfRb/LwYHwGwYE0HP9OIjJ//TKxS1Gt++RirYu6Xsx+Jm5LA5KbykoA==", + "license": "MIT", + "dependencies": { + "@iconify/types": "*" + } + }, "node_modules/@iconify/types": { "version": "2.0.0", "license": "MIT" diff --git a/package.json b/package.json index 48d15a420..2b5d5a87e 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "@docusaurus/preset-classic": "3.8.0", "@docusaurus/theme-classic": "3.8.0", "@docusaurus/theme-search-algolia": "3.8.0", + "@iconify-json/tabler": "1.2.19", "@octokit/rest": "20.0.2", "@ory/client-fetch": "1.15.16", "@rjsf/bootstrap-4": "5.24.1", diff --git a/src/pages/_assets/project-overview-graph.tsx b/src/pages/_assets/project-overview-graph.tsx new file mode 100644 index 000000000..f39615b5f --- /dev/null +++ b/src/pages/_assets/project-overview-graph.tsx @@ -0,0 +1,293 @@ +import Mermaid from "@site/src/theme/Mermaid" +import { useState } from "react" + +// Types +type ComponentKey = + | "kratos" + | "hydra" + | "keto" + | "polis" + | "oathkeeper" + | "elements" + +interface ComponentConfig { + key: ComponentKey + label: string + logoUrl: string + description: string +} + +interface ChartNode { + id: string + config: string + condition?: (components: ComponentKey[]) => boolean +} + +interface ChartConnection { + from: string + to: string + label?: string + condition?: (components: ComponentKey[]) => boolean +} + +// Configuration +const COMPONENT_CONFIGS: ComponentConfig[] = [ + { + key: "kratos", + label: "Identity Management", + logoUrl: + "https://raw.githubusercontent.com/ory/meta/refs/heads/master/static/logos/logo-kratos.svg", + description: "Identity Management", + }, + { + key: "hydra", + label: "OAuth2 & OpenID Connect Server", + logoUrl: + "https://raw.githubusercontent.com/ory/meta/refs/heads/master/static/logos/logo-hydra.svg", + description: "OAuth2 & OpenID Connect Server", + }, + { + key: "keto", + label: "Permissions", + logoUrl: + "https://raw.githubusercontent.com/ory/meta/refs/heads/master/static/logos/logo-keto.svg", + description: "Permissions", + }, + { + key: "polis", + label: "Enterprise SSO bridge / SAML support", + logoUrl: + "https://raw.githubusercontent.com/ory/meta/refs/heads/master/static/logos/logo-polis.svg", + description: "Enterprise SSO bridge", + }, + { + key: "oathkeeper", + label: "Identity and Access Proxy IAP", + logoUrl: + "https://raw.githubusercontent.com/ory/meta/refs/heads/master/static/logos/logo-oathkeeper.svg", + description: "Identity and Access Proxy IAP", + }, + { + key: "elements", + label: "Pre-built UI", + logoUrl: + "https://raw.githubusercontent.com/ory/meta/master/static/logos/logo-elements.svg", + description: "Ory Elements", + }, +] + +const COMPONENT_KEYS: ComponentKey[] = COMPONENT_CONFIGS.map( + (config) => config.key, +) + +const LABELS: Record = Object.fromEntries( + COMPONENT_CONFIGS.map((config) => [config.key, config.label]), +) as Record + +// Chart configuration +const CHART_NODES: ChartNode[] = [ + { id: "User", config: '@{ icon: "tabler:user" }' }, + { id: "Devices", config: '@{ icon: "tabler:devices"}' }, + { + id: "Kratos", + config: + '@{ img: "https://raw.githubusercontent.com/ory/meta/refs/heads/master/static/logos/logo-kratos.svg", pos: "b", w: 120, constraint: "on", label: "Identity Management" }', + }, + { + id: "Hydra", + config: + '@{ img: "https://raw.githubusercontent.com/ory/meta/refs/heads/master/static/logos/logo-hydra.svg", pos: "b", w: 120, constraint: "on", label: "OAuth2 & OpenID Connect Server" }', + condition: (components) => components.includes("hydra"), + }, + { + id: "Keto", + config: + '@{ img: "https://raw.githubusercontent.com/ory/meta/refs/heads/master/static/logos/logo-keto.svg", pos: "b", w: 120, constraint: "on", label: "Permissions" }', + condition: (components) => components.includes("keto"), + }, + { + id: "Polis", + config: + '@{ img: "https://raw.githubusercontent.com/ory/meta/refs/heads/master/static/logos/logo-polis.svg", pos: "b", w: 120, constraint: "on", label: "Enterprise SSO bridge" }', + condition: (components) => components.includes("polis"), + }, + { + id: "Oathkeeper", + config: + '@{ img: "https://raw.githubusercontent.com/ory/meta/refs/heads/master/static/logos/logo-oathkeeper.svg", pos: "b", w: 120, constraint: "on", label: "Identity and Access Proxy IAP" }', + condition: (components) => components.includes("oathkeeper"), + }, + { + id: "API", + config: + '@{ icon: "tabler:code", label: "API Endpoint 1
API Endpoint 2" }', + }, + { + id: "Elements", + config: + '@{ img: "https://raw.githubusercontent.com/ory/meta/master/static/logos/logo-elements.svg", pos: "b", w: 120, constraint: "on", label: "Ory Elements" }', + condition: (components) => components.includes("elements"), + }, +] + +const CHART_CONNECTIONS: ChartConnection[] = [ + { from: "User", to: "Devices" }, + { + from: "Devices", + to: "Oathkeeper", + condition: (components) => components.includes("oathkeeper"), + }, + { + from: "Oathkeeper", + to: "API", + label: "protects", + condition: (components) => components.includes("oathkeeper"), + }, + { + from: "Oathkeeper", + to: "Hydra", + label: "authenticates credentials with", + condition: (components) => + components.includes("hydra") && components.includes("oathkeeper"), + }, + { + from: "User", + to: "Kratos", + label: "Registers, log in,
manages profile via API", + }, + { + from: "User", + to: "Elements", + label: "Registers, log in,
manages profile via prebuilt UI", + condition: (components) => components.includes("elements"), + }, + { + from: "Elements", + to: "Kratos", + condition: (components) => components.includes("elements"), + }, + { + from: "Elements", + to: "Hydra", + condition: (components) => + components.includes("elements") && components.includes("hydra"), + }, + { + from: "Oathkeeper", + to: "Kratos", + label: "checks session with", + condition: (components) => components.includes("oathkeeper"), + }, + { + from: "Oathkeeper", + to: "Keto", + label: "checks permissions with", + condition: (components) => + components.includes("keto") && components.includes("oathkeeper"), + }, + { + from: "yourCode", + to: "Keto", + label: "Checks permissions with", + condition: (components) => + components.includes("keto") && !components.includes("oathkeeper"), + }, + { + from: "Devices", + to: "yourCode", + condition: (components) => !components.includes("oathkeeper"), + }, + { + from: "Kratos", + to: "Polis", + label: "OIDC", + condition: (components) => components.includes("polis"), + }, + { + from: "yourCode", + to: "Hydra", + label: "OAuth2", + condition: (components) => + !components.includes("oathkeeper") && + !components.includes("elements") && + components.includes("hydra"), + }, + { + from: "yourCode", + to: "Kratos", + label: "Checks session with", + condition: (components) => !components.includes("oathkeeper"), + }, +] + +// Helper functions +function generateChartNodes(components: ComponentKey[]): string { + return CHART_NODES.filter( + (node) => !node.condition || node.condition(components), + ) + .map((node) => `${node.id}${node.config}`) + .join("\n") +} + +function generateChartConnections(components: ComponentKey[]): string { + return CHART_CONNECTIONS.filter( + (connection) => !connection.condition || connection.condition(components), + ) + .map((connection) => { + const label = connection.label ? `|${connection.label}|` : "" + return `${connection.from} -->${label} ${connection.to}` + }) + .join("\n") +} + +function generateMermaidChart(components: ComponentKey[]): string { + const nodes = generateChartNodes(components) + const connections = generateChartConnections(components) + + return `graph LR + +${nodes} + +subgraph yourCode[Your Code] + API@{ icon: "tabler:code", label: "API Endpoint 1
API Endpoint 2" } + ${components.includes("elements") ? `Elements@{ img: "https://raw.githubusercontent.com/ory/meta/master/static/logos/logo-elements.svg", pos: "b", w: 120, constraint: "on", label: "Ory Elements" }` : ""} +end + +${connections} +` +} + +// Component +export function ProjectOverviewGraph() { + const [components, setComponents] = useState(COMPONENT_KEYS) + + const toggleComponent = (component: ComponentKey) => () => { + setComponents((prev) => + prev.includes(component) + ? prev.filter((c) => c !== component) + : [...prev, component], + ) + } + + const chart = generateMermaidChart(components) + + return ( +
+ I need: +
+ {COMPONENT_KEYS.map((component) => ( +
+ + +
+ ))} + +
+ ) +} diff --git a/src/theme/Mermaid.js b/src/theme/Mermaid.js index 9cc750fb9..026b6fa45 100644 --- a/src/theme/Mermaid.js +++ b/src/theme/Mermaid.js @@ -38,6 +38,12 @@ mermaid.initialize({ useMaxWidth: true, }, }) +mermaid.registerIconPacks([ + { + name: 'tabler', + loader: () => import('@iconify-json/tabler').then((module) => module.icons), + }, +]); const Mermaid = ({ chart }) => { const [zoomed, setZoomed] = useState(false) From e833b3347393e070f1561b0f2c00101f75ce2339 Mon Sep 17 00:00:00 2001 From: Jonas Hungershausen Date: Tue, 24 Jun 2025 17:05:36 -0400 Subject: [PATCH 2/2] chore: format --- src/theme/Mermaid.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/theme/Mermaid.js b/src/theme/Mermaid.js index 026b6fa45..4ec915748 100644 --- a/src/theme/Mermaid.js +++ b/src/theme/Mermaid.js @@ -40,10 +40,10 @@ mermaid.initialize({ }) mermaid.registerIconPacks([ { - name: 'tabler', - loader: () => import('@iconify-json/tabler').then((module) => module.icons), + name: "tabler", + loader: () => import("@iconify-json/tabler").then((module) => module.icons), }, -]); +]) const Mermaid = ({ chart }) => { const [zoomed, setZoomed] = useState(false)