diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 6222994e1f..ec04c345a8 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -21,6 +21,8 @@ optional_value = final [bumpversion:file:package.json] +[bumpversion:file:package-lock.json] + [bumpversion:file:docker-compose.yml] [bumpversion:file:schema.yml] @@ -31,6 +33,4 @@ optional_value = final [bumpversion:file:internal/constants/constants.go] -[bumpversion:file:web/src/common/constants.ts] - [bumpversion:file:lifecycle/aws/template.yaml] diff --git a/authentik/enterprise/search/tests.py b/authentik/enterprise/search/tests.py index 55d4fbef19..70779f6279 100644 --- a/authentik/enterprise/search/tests.py +++ b/authentik/enterprise/search/tests.py @@ -57,7 +57,7 @@ class QLTest(APITestCase): ) self.assertEqual(res.status_code, 200) content = loads(res.content) - self.assertEqual(content["pagination"]["count"], 1) + self.assertGreaterEqual(content["pagination"]["count"], 1) self.assertEqual(content["results"][0]["username"], self.user.username) def test_search_json(self): diff --git a/lifecycle/aws/package-lock.json b/lifecycle/aws/package-lock.json index 0d25412620..1d6d027d0d 100644 --- a/lifecycle/aws/package-lock.json +++ b/lifecycle/aws/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0", "license": "MIT", "devDependencies": { - "aws-cdk": "^2.1018.1", + "aws-cdk": "^2.1019.1", "cross-env": "^7.0.3" }, "engines": { @@ -17,9 +17,9 @@ } }, "node_modules/aws-cdk": { - "version": "2.1018.1", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1018.1.tgz", - "integrity": "sha512-kFPRox5kSm+ktJ451o0ng9rD+60p5Kt1CZIWw8kXnvqbsxN2xv6qbmyWSXw7sGVXVwqrRKVj+71/JeDr+LMAZw==", + "version": "2.1019.1", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1019.1.tgz", + "integrity": "sha512-G2jxKuTsYTrYZX80CDApCrKcZ+AuFxxd+b0dkb0KEkfUsela7RqrDGLm5wOzSCIc3iH6GocR8JDVZuJ+0nNuKg==", "dev": true, "license": "Apache-2.0", "bin": { diff --git a/lifecycle/aws/package.json b/lifecycle/aws/package.json index 0952d1eedd..16c30eb7b5 100644 --- a/lifecycle/aws/package.json +++ b/lifecycle/aws/package.json @@ -10,7 +10,7 @@ "node": ">=20" }, "devDependencies": { - "aws-cdk": "^2.1018.1", + "aws-cdk": "^2.1019.1", "cross-env": "^7.0.3" } } diff --git a/packages/docusaurus-config/.prettierignore b/packages/docusaurus-config/.prettierignore new file mode 100644 index 0000000000..83de4b2b7f --- /dev/null +++ b/packages/docusaurus-config/.prettierignore @@ -0,0 +1,10 @@ +# Prettier Ignorefile + +## Static Files +**/LICENSE + +## Build asset directories +coverage +dist +out +.docusaurus diff --git a/packages/docusaurus-config/index.js b/packages/docusaurus-config/index.js index 63f2704238..9feeaa8dce 100644 --- a/packages/docusaurus-config/index.js +++ b/packages/docusaurus-config/index.js @@ -4,3 +4,5 @@ export * from "./lib/theme.js"; export * from "./lib/common.js"; +export * from "./lib/routing.js"; +export * from "./lib/navbar.js"; diff --git a/packages/docusaurus-config/lib/common.js b/packages/docusaurus-config/lib/common.js index fc9039b5be..2f8080ecb6 100644 --- a/packages/docusaurus-config/lib/common.js +++ b/packages/docusaurus-config/lib/common.js @@ -1,8 +1,8 @@ /** * @file Common Docusaurus configuration utilities. * - * @import { Config as DocusaurusConfig } from "@docusaurus/types" - * @import { UserThemeConfig } from "./theme.js" + * @import { Config, DocusaurusConfig } from "@docusaurus/types" + * @import { UserThemeConfig, UserThemeConfigExtra } from "./theme.js" */ import { deepmerge } from "deepmerge-ts"; @@ -11,14 +11,14 @@ import { createThemeConfig } from "./theme.js"; //#region Types /** - * @typedef {Omit} DocusaurusConfigBase + * @typedef {Omit} DocusaurusConfigBase * * Represents the base configuration for Docusaurus, excluding the theme configuration. */ /** * @typedef DocusaurusConfigBaseTheme - * @property {UserThemeConfig} themeConfig The theme configuration. + * @property {UserThemeConfig & UserThemeConfigExtra} themeConfig The theme configuration. * * Represents a configuration object, only including the theme configuration. */ @@ -39,31 +39,66 @@ import { createThemeConfig } from "./theme.js"; //#region Functions /** - * Create a Docusaurus configuration. - * - * @param {DocusaurusConfigInit} [overrides] The options to override. - * @returns {DocusaurusConfig} + * Create a default Docusaurus configuration. */ -export function createDocusaurusConfig({ themeConfig, ...overrides } = {}) { +export function createDefaultDocusaurusConfig() { + const NodeEnvironment = process.env.AK_DOCUSAURUS_ENV || process.env.NODE_ENV || "development"; + const production = NodeEnvironment === "production"; + /** - * @type {DocusaurusConfig} + * @satisfies {Config} */ - const config = { + const DEFAULT_CONFIG = /** @type {const} */ ({ + trailingSlash: true, + future: { + v4: { + removeLegacyPostBuildHeadAttribute: true, + useCssCascadeLayers: false, + }, + experimental_faster: { + swcJsLoader: true, + rspackBundler: true, + lightningCssMinimizer: production, + swcJsMinimizer: production, + swcHtmlMinimizer: production, + ssgWorkerThreads: production, + mdxCrossCompilerCache: production, + rspackPersistentCache: production, + }, + }, title: "authentik", tagline: "Bring all of your authentication into a unified platform.", url: "https://docs.goauthentik.io", baseUrl: "/", onBrokenLinks: "throw", onBrokenAnchors: "throw", + onBrokenMarkdownLinks: "throw", + onDuplicateRoutes: "throw", favicon: "img/icon.png", organizationName: "Authentik Security Inc.", projectName: "authentik", markdown: { mermaid: true, }, + }); + + return DEFAULT_CONFIG; +} + +/** + * Create a Docusaurus configuration. + * + * @template {Partial} T + * @param {T} overrides The options to override. + * @returns {T & ReturnType} + */ +export function createDocusaurusConfig({ themeConfig, ...overrides }) { + const config = { + ...createDefaultDocusaurusConfig(), themeConfig: createThemeConfig(themeConfig), }; + // @ts-ignore return deepmerge(config, overrides); } diff --git a/packages/docusaurus-config/lib/navbar.js b/packages/docusaurus-config/lib/navbar.js new file mode 100644 index 0000000000..c2b6355209 --- /dev/null +++ b/packages/docusaurus-config/lib/navbar.js @@ -0,0 +1,110 @@ +/** + * @file Docusaurus navbar configuration for the authentik website. + * + * @import { NavbarItem } from "@docusaurus/theme-common"; + */ +import { DocusaurusURL, SocialURL } from "./routing.js"; + +/** + * The navbar items for the authentik website. + * + * @type {NavbarItem[]} + */ +export const SocialNavbarItems = /** @type {const} */ ([ + { + "href": SocialURL.GitHub, + "data-icon": "github", + "aria-label": "GitHub", + "position": "right", + }, + { + "href": SocialURL.Discord, + "data-icon": "discord", + "aria-label": "Discord", + "position": "right", + }, +]); + +/** + * The navbar items for the authentik website. + * + * @satisfies {NavbarItem[]} + */ +export const NavbarItemsTemplate = /** @type {const} */ ([ + { + to: "{{WWW_URL}}/features", + label: "Features", + position: "left", + target: "_self", + }, + { + to: "{{INTEGRATIONS_URL}}", + label: "Integrations", + target: "_self", + position: "left", + }, + { + to: "{{DOCS_URL}}", + + label: "Documentation", + position: "left", + target: "_self", + }, + { + to: "{{WWW_URL}}/pricing/", + label: "Pricing", + position: "left", + target: "_self", + }, + { + to: "{{WWW_URL}}/blog", + label: "Blog", + position: "left", + target: "_self", + }, + ...SocialNavbarItems, +]); + +/** + * @typedef {Object} NavbarItemOverrides + * + * @prop {string} WWW_URL The URL for the WWW environment. + * @prop {string} DOCS_URL The URL for the documentation. + * @prop {string} INTEGRATIONS_URL The URL for the integrations. + */ + +const DEFAULT_NAVBAR_REPLACEMENTS = /** @type {const} */ ({ + DOCS_URL: DocusaurusURL.Docs, + INTEGRATIONS_URL: DocusaurusURL.Integrations, + WWW_URL: DocusaurusURL.WWW, +}); + +/** + * Creates a navbar item array, replacing placeholders with the given replacements. + * + * @param {Partial} [overrides] + * @returns {NavbarItem[]} + */ +export function createNavbarItems(overrides) { + const replacements = { + ...DEFAULT_NAVBAR_REPLACEMENTS, + ...overrides, + }; + + return NavbarItemsTemplate.map((item) => { + if (typeof item.to !== "string") return item; + + return { + ...item, + to: item.to.replace( + /{{([^}]+)}}/g, + /** + * @param {keyof NavbarItemOverrides} key + */ + (_, key) => { + return replacements[key]; + }, + ), + }; + }); +} diff --git a/packages/docusaurus-config/lib/routing.js b/packages/docusaurus-config/lib/routing.js new file mode 100644 index 0000000000..e0794f5b4d --- /dev/null +++ b/packages/docusaurus-config/lib/routing.js @@ -0,0 +1,35 @@ +/** + * @file Docusaurus routing configuration. + */ + +/** + * @typedef {'production'|'development'} NodeEnvironment + */ + +const NodeEnvironment = /** @type {NodeEnvironment} */ (process.env.NODE_ENV || "development"); + +/** + * @satisfies {Record>} + */ +export const DocusaurusURLByEnvironment = /** @type {const} */ ({ + development: { + Docs: "http://localhost:3000", + Integrations: "http://localhost:3001", + WWW: "http://localhost:3002", + }, + production: { + Docs: "https://docs.goauthentik.io", + Integrations: "https://integrations.goauthentik.io", + WWW: "https://goauthentik.io", + }, +}); + +export const DocusaurusURL = DocusaurusURLByEnvironment[NodeEnvironment]; + +/** + * @satisfies {Record} + */ +export const SocialURL = /** @type {const} */ ({ + Discord: "https://goauthentik.io/discord", + GitHub: "https://github.com/goauthentik/authentik", +}); diff --git a/packages/docusaurus-config/lib/theme.js b/packages/docusaurus-config/lib/theme.js index 2751737b02..1381e85174 100644 --- a/packages/docusaurus-config/lib/theme.js +++ b/packages/docusaurus-config/lib/theme.js @@ -3,16 +3,26 @@ * * @import { UserThemeConfig as UserThemeConfigCommon } from "@docusaurus/theme-common"; * @import { UserThemeConfig as UserThemeConfigAlgolia } from "@docusaurus/theme-search-algolia"; + * @import { NavbarItemOverrides } from "./navbar.js" */ import { deepmerge } from "deepmerge-ts"; import { themes as prismThemes } from "prism-react-renderer"; +import { createNavbarItems } from "./navbar.js"; + //#region Types /** - * Combined theme configuration for Docusaurus and Algolia. + * @typedef {Object} UserThemeConfigExtra + * @property {Partial} [navbarReplacements] The replacements for the navbar. + * + */ + +/** + * Combined theme configuration for Docusaurus, Algolia, and our own configuration. * * @typedef {UserThemeConfigCommon & UserThemeConfigAlgolia} UserThemeConfig + * */ //#endregion @@ -57,10 +67,10 @@ export function createPrismConfig(overrides = {}) { /** * Creates a theme configuration for Docusaurus. * - * @param {Partial} overrides - Overrides for the default theme configuration. + * @param {Partial} overrides - Overrides for the default theme configuration. * @returns {UserThemeConfig} */ -export function createThemeConfig({ prism, ...overrides } = {}) { +export function createThemeConfig({ prism, navbarReplacements, ...overrides } = {}) { /** * @type {UserThemeConfig} */ @@ -77,6 +87,17 @@ export function createThemeConfig({ prism, ...overrides } = {}) { appId: "36ROD0O0FV", apiKey: "727db511300ca9aec5425645bbbddfb5", }, + footer: { + copyright: `Copyright © ${new Date().getFullYear()} Authentik Security Inc. Built with Docusaurus.`, + }, + navbar: { + logo: { + alt: "authentik logo", + src: "img/icon_left_brand.svg", + }, + + items: createNavbarItems(navbarReplacements), + }, prism: createPrismConfig(prism), }; diff --git a/packages/docusaurus-config/package-lock.json b/packages/docusaurus-config/package-lock.json index 70da5c1d0f..b8e90b0c16 100644 --- a/packages/docusaurus-config/package-lock.json +++ b/packages/docusaurus-config/package-lock.json @@ -1,43 +1,51 @@ { "name": "@goauthentik/docusaurus-config", - "version": "2.0.0", + "version": "2.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@goauthentik/docusaurus-config", - "version": "2.0.0", + "version": "2.1.1", "license": "MIT", "dependencies": { "deepmerge-ts": "^7.1.5", "prism-react-renderer": "^2.4.1" }, "devDependencies": { - "@docusaurus/theme-common": "^3.8.0", - "@docusaurus/theme-search-algolia": "^3.8.0", - "@docusaurus/types": "^3.8.0", + "@docusaurus/theme-common": "^3.8.1", + "@docusaurus/theme-search-algolia": "^3.8.1", + "@docusaurus/types": "^3.8.1", "@goauthentik/prettier-config": "^2.0.0", "@goauthentik/tsconfig": "^1.0.4", "@types/react": "^19.1.6", "@types/react-dom": "^19.1.5", "prettier": "^3.5.3", - "react": "^19.1.0", - "react-dom": "^19.1.0", "typescript": "^5.8.3" }, "engines": { "node": ">=22" }, + "optionalDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, "peerDependencies": { - "@docusaurus/theme-common": "^3.8.0", - "@docusaurus/theme-search-algolia": "^3.8.0", + "@docusaurus/theme-common": "^3.8.1", + "@docusaurus/theme-search-algolia": "^3.8.1", "@docusaurus/types": "^3.8.0", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" + "react": ">=18", + "react-dom": ">=18" }, "peerDependenciesMeta": { "@docusaurus/theme-search-algolia": { "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true } } }, @@ -320,9 +328,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.3.tgz", - "integrity": "sha512-V42wFfx1ymFte+ecf6iXghnnP8kWTO+ZLXIyZq+1LAXHHvTZdVxicn4yiVYdYMGaCO3tmqub11AorKkv+iodqw==", + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.5.tgz", + "integrity": "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==", "dev": true, "license": "MIT", "engines": { @@ -669,14 +677,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.27.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.4.tgz", - "integrity": "sha512-Y+bO6U+I7ZKaM5G5rDUZiYfUvQPUibYmAFe7EnKdnKBbVXDZxvp+MWOH5gYciY0EPk4EScsuFMQBbEfpdRKSCQ==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.27.2", - "@babel/types": "^7.27.3" + "@babel/types": "^7.27.6" }, "engines": { "node": ">=6.9.0" @@ -958,9 +966,9 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.3.tgz", - "integrity": "sha512-+F8CnfhuLhwUACIJMLWnjz6zvzYM2r0yeIHKlbgfw7ml8rOMJsXNXV/hyRcb3nb493gRs4WvYpQAndWj/qQmkQ==", + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.5.tgz", + "integrity": "sha512-JF6uE2s67f0y2RZcm2kpAUEbD50vH62TyWVebxwHAlbSdM49VqPz8t4a1uIjp4NIOIZ4xzLfjY5emt/RCyC7TQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1599,9 +1607,9 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.27.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.4.tgz", - "integrity": "sha512-Glp/0n8xuj+E1588otw5rjJkTXfzW7FjH3IIUrfqiZOPQCd2vbg8e+DQE8jK9g4V5/zrxFW+D9WM9gboRPELpQ==", + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.5.tgz", + "integrity": "sha512-uhB8yHerfe3MWnuLAhEbeQ4afVoqv8BQsPqrTv7e/jZ9y00kJL6l9a/f4OWaKxotmjzewfEyXE1vgDJenkQ2/Q==", "dev": true, "license": "MIT", "dependencies": { @@ -2007,9 +2015,9 @@ } }, "node_modules/@babel/runtime-corejs3": { - "version": "7.27.4", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.27.4.tgz", - "integrity": "sha512-H7QhL0ucCGOObsUETNbB2PuzF4gAvN8p32P6r91bX7M/hk4bx+3yz2hTwHL9d/Efzwu1upeb4/cd7oSxCzup3w==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.27.6.tgz", + "integrity": "sha512-vDVrlmRAY8z9Ul/HxT+8ceAru95LQgkSKiXkSYZvqtbkPSfhZJgpRp45Cldbh1GJ1kxzQkI70AqyrTI58KpaWQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2054,9 +2062,9 @@ } }, "node_modules/@babel/types": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.3.tgz", - "integrity": "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==", + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", + "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", "dev": true, "license": "MIT", "dependencies": { @@ -2619,9 +2627,9 @@ } }, "node_modules/@csstools/postcss-is-pseudo-class": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-5.0.1.tgz", - "integrity": "sha512-JLp3POui4S1auhDR0n8wHd/zTOWmMsmK3nQd3hhL6FhWPaox5W7j1se6zXOG/aP07wV2ww0lxbKYGwbBszOtfQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-5.0.3.tgz", + "integrity": "sha512-jS/TY4SpG4gszAtIg7Qnf3AS2pjcUM5SzxpApOrlndMeGhIbaTzWBzzP/IApXoNWEW7OhcjkRT48jnAUIFXhAQ==", "dev": true, "funding": [ { @@ -3305,9 +3313,9 @@ } }, "node_modules/@docusaurus/babel": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/babel/-/babel-3.8.0.tgz", - "integrity": "sha512-9EJwSgS6TgB8IzGk1L8XddJLhZod8fXT4ULYMx6SKqyCBqCFpVCEjR/hNXXhnmtVM2irDuzYoVLGWv7srG/VOA==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/babel/-/babel-3.8.1.tgz", + "integrity": "sha512-3brkJrml8vUbn9aeoZUlJfsI/GqyFcDgQJwQkmBtclJgWDEQBKKeagZfOgx0WfUQhagL1sQLNW0iBdxnI863Uw==", "dev": true, "license": "MIT", "dependencies": { @@ -3321,8 +3329,8 @@ "@babel/runtime": "^7.25.9", "@babel/runtime-corejs3": "^7.25.9", "@babel/traverse": "^7.25.9", - "@docusaurus/logger": "3.8.0", - "@docusaurus/utils": "3.8.0", + "@docusaurus/logger": "3.8.1", + "@docusaurus/utils": "3.8.1", "babel-plugin-dynamic-import-node": "^2.3.3", "fs-extra": "^11.1.1", "tslib": "^2.6.0" @@ -3332,31 +3340,31 @@ } }, "node_modules/@docusaurus/bundler": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/bundler/-/bundler-3.8.0.tgz", - "integrity": "sha512-Rq4Z/MSeAHjVzBLirLeMcjLIAQy92pF1OI+2rmt18fSlMARfTGLWRE8Vb+ljQPTOSfJxwDYSzsK6i7XloD2rNA==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/bundler/-/bundler-3.8.1.tgz", + "integrity": "sha512-/z4V0FRoQ0GuSLToNjOSGsk6m2lQUG4FRn8goOVoZSRsTrU8YR2aJacX5K3RG18EaX9b+52pN4m1sL3MQZVsQA==", "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.25.9", - "@docusaurus/babel": "3.8.0", - "@docusaurus/cssnano-preset": "3.8.0", - "@docusaurus/logger": "3.8.0", - "@docusaurus/types": "3.8.0", - "@docusaurus/utils": "3.8.0", + "@docusaurus/babel": "3.8.1", + "@docusaurus/cssnano-preset": "3.8.1", + "@docusaurus/logger": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils": "3.8.1", "babel-loader": "^9.2.1", - "clean-css": "^5.3.2", + "clean-css": "^5.3.3", "copy-webpack-plugin": "^11.0.0", - "css-loader": "^6.8.1", + "css-loader": "^6.11.0", "css-minimizer-webpack-plugin": "^5.0.1", "cssnano": "^6.1.2", "file-loader": "^6.2.0", "html-minifier-terser": "^7.2.0", - "mini-css-extract-plugin": "^2.9.1", + "mini-css-extract-plugin": "^2.9.2", "null-loader": "^4.0.1", - "postcss": "^8.4.26", - "postcss-loader": "^7.3.3", - "postcss-preset-env": "^10.1.0", + "postcss": "^8.5.4", + "postcss-loader": "^7.3.4", + "postcss-preset-env": "^10.2.1", "terser-webpack-plugin": "^5.3.9", "tslib": "^2.6.0", "url-loader": "^4.1.1", @@ -3376,19 +3384,19 @@ } }, "node_modules/@docusaurus/core": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.8.0.tgz", - "integrity": "sha512-c7u6zFELmSGPEP9WSubhVDjgnpiHgDqMh1qVdCB7rTflh4Jx0msTYmMiO91Ez0KtHj4sIsDsASnjwfJ2IZp3Vw==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.8.1.tgz", + "integrity": "sha512-ENB01IyQSqI2FLtOzqSI3qxG2B/jP4gQPahl2C3XReiLebcVh5B5cB9KYFvdoOqOWPyr5gXK4sjgTKv7peXCrA==", "dev": true, "license": "MIT", "dependencies": { - "@docusaurus/babel": "3.8.0", - "@docusaurus/bundler": "3.8.0", - "@docusaurus/logger": "3.8.0", - "@docusaurus/mdx-loader": "3.8.0", - "@docusaurus/utils": "3.8.0", - "@docusaurus/utils-common": "3.8.0", - "@docusaurus/utils-validation": "3.8.0", + "@docusaurus/babel": "3.8.1", + "@docusaurus/bundler": "3.8.1", + "@docusaurus/logger": "3.8.1", + "@docusaurus/mdx-loader": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-common": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "boxen": "^6.2.1", "chalk": "^4.1.2", "chokidar": "^3.5.3", @@ -3438,14 +3446,14 @@ } }, "node_modules/@docusaurus/cssnano-preset": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.8.0.tgz", - "integrity": "sha512-UJ4hAS2T0R4WNy+phwVff2Q0L5+RXW9cwlH6AEphHR5qw3m/yacfWcSK7ort2pMMbDn8uGrD38BTm4oLkuuNoQ==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.8.1.tgz", + "integrity": "sha512-G7WyR2N6SpyUotqhGznERBK+x84uyhfMQM2MmDLs88bw4Flom6TY46HzkRkSEzaP9j80MbTN8naiL1fR17WQug==", "dev": true, "license": "MIT", "dependencies": { "cssnano-preset-advanced": "^6.1.2", - "postcss": "^8.4.38", + "postcss": "^8.5.4", "postcss-sort-media-queries": "^5.2.0", "tslib": "^2.6.0" }, @@ -3454,9 +3462,9 @@ } }, "node_modules/@docusaurus/logger": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.8.0.tgz", - "integrity": "sha512-7eEMaFIam5Q+v8XwGqF/n0ZoCld4hV4eCCgQkfcN9Mq5inoZa6PHHW9Wu6lmgzoK5Kx3keEeABcO2SxwraoPDQ==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.8.1.tgz", + "integrity": "sha512-2wjeGDhKcExEmjX8k1N/MRDiPKXGF2Pg+df/bDDPnnJWHXnVEZxXj80d6jcxp1Gpnksl0hF8t/ZQw9elqj2+ww==", "dev": true, "license": "MIT", "dependencies": { @@ -3468,15 +3476,15 @@ } }, "node_modules/@docusaurus/mdx-loader": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.8.0.tgz", - "integrity": "sha512-mDPSzssRnpjSdCGuv7z2EIAnPS1MHuZGTaRLwPn4oQwszu4afjWZ/60sfKjTnjBjI8Vl4OgJl2vMmfmiNDX4Ng==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.8.1.tgz", + "integrity": "sha512-DZRhagSFRcEq1cUtBMo4TKxSNo/W6/s44yhr8X+eoXqCLycFQUylebOMPseHi5tc4fkGJqwqpWJLz6JStU9L4w==", "dev": true, "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.8.0", - "@docusaurus/utils": "3.8.0", - "@docusaurus/utils-validation": "3.8.0", + "@docusaurus/logger": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "@mdx-js/mdx": "^3.0.0", "@slorber/remark-comment": "^1.0.0", "escape-html": "^1.0.3", @@ -3508,13 +3516,13 @@ } }, "node_modules/@docusaurus/module-type-aliases": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.8.0.tgz", - "integrity": "sha512-/uMb4Ipt5J/QnD13MpnoC/A4EYAe6DKNWqTWLlGrqsPJwJv73vSwkA25xnYunwfqWk0FlUQfGv/Swdh5eCCg7g==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.8.1.tgz", + "integrity": "sha512-6xhvAJiXzsaq3JdosS7wbRt/PwEPWHr9eM4YNYqVlbgG1hSK3uQDXTVvQktasp3VO6BmfYWPozueLWuj4gB+vg==", "dev": true, "license": "MIT", "dependencies": { - "@docusaurus/types": "3.8.0", + "@docusaurus/types": "3.8.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -3528,21 +3536,21 @@ } }, "node_modules/@docusaurus/plugin-content-docs": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.8.0.tgz", - "integrity": "sha512-fRDMFLbUN6eVRXcjP8s3Y7HpAt9pzPYh1F/7KKXOCxvJhjjCtbon4VJW0WndEPInVz4t8QUXn5QZkU2tGVCE2g==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.8.1.tgz", + "integrity": "sha512-oByRkSZzeGNQByCMaX+kif5Nl2vmtj2IHQI2fWjCfCootsdKZDPFLonhIp5s3IGJO7PLUfe0POyw0Xh/RrGXJA==", "dev": true, "license": "MIT", "dependencies": { - "@docusaurus/core": "3.8.0", - "@docusaurus/logger": "3.8.0", - "@docusaurus/mdx-loader": "3.8.0", - "@docusaurus/module-type-aliases": "3.8.0", - "@docusaurus/theme-common": "3.8.0", - "@docusaurus/types": "3.8.0", - "@docusaurus/utils": "3.8.0", - "@docusaurus/utils-common": "3.8.0", - "@docusaurus/utils-validation": "3.8.0", + "@docusaurus/core": "3.8.1", + "@docusaurus/logger": "3.8.1", + "@docusaurus/mdx-loader": "3.8.1", + "@docusaurus/module-type-aliases": "3.8.1", + "@docusaurus/theme-common": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-common": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "@types/react-router-config": "^5.0.7", "combine-promises": "^1.1.0", "fs-extra": "^11.1.1", @@ -3562,16 +3570,16 @@ } }, "node_modules/@docusaurus/theme-common": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.8.0.tgz", - "integrity": "sha512-YqV2vAWpXGLA+A3PMLrOMtqgTHJLDcT+1Caa6RF7N4/IWgrevy5diY8oIHFkXR/eybjcrFFjUPrHif8gSGs3Tw==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.8.1.tgz", + "integrity": "sha512-UswMOyTnPEVRvN5Qzbo+l8k4xrd5fTFu2VPPfD6FcW/6qUtVLmJTQCktbAL3KJ0BVXGm5aJXz/ZrzqFuZERGPw==", "dev": true, "license": "MIT", "dependencies": { - "@docusaurus/mdx-loader": "3.8.0", - "@docusaurus/module-type-aliases": "3.8.0", - "@docusaurus/utils": "3.8.0", - "@docusaurus/utils-common": "3.8.0", + "@docusaurus/mdx-loader": "3.8.1", + "@docusaurus/module-type-aliases": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-common": "3.8.1", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -3591,20 +3599,20 @@ } }, "node_modules/@docusaurus/theme-search-algolia": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.8.0.tgz", - "integrity": "sha512-GBZ5UOcPgiu6nUw153+0+PNWvFKweSnvKIL6Rp04H9olKb475jfKjAwCCtju5D2xs5qXHvCMvzWOg5o9f6DtuQ==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.8.1.tgz", + "integrity": "sha512-NBFH5rZVQRAQM087aYSRKQ9yGEK9eHd+xOxQjqNpxMiV85OhJDD4ZGz6YJIod26Fbooy54UWVdzNU0TFeUUUzQ==", "dev": true, "license": "MIT", "dependencies": { "@docsearch/react": "^3.9.0", - "@docusaurus/core": "3.8.0", - "@docusaurus/logger": "3.8.0", - "@docusaurus/plugin-content-docs": "3.8.0", - "@docusaurus/theme-common": "3.8.0", - "@docusaurus/theme-translations": "3.8.0", - "@docusaurus/utils": "3.8.0", - "@docusaurus/utils-validation": "3.8.0", + "@docusaurus/core": "3.8.1", + "@docusaurus/logger": "3.8.1", + "@docusaurus/plugin-content-docs": "3.8.1", + "@docusaurus/theme-common": "3.8.1", + "@docusaurus/theme-translations": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-validation": "3.8.1", "algoliasearch": "^5.17.1", "algoliasearch-helper": "^3.22.6", "clsx": "^2.0.0", @@ -3623,9 +3631,9 @@ } }, "node_modules/@docusaurus/theme-translations": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.8.0.tgz", - "integrity": "sha512-1DTy/snHicgkCkryWq54fZvsAglTdjTx4qjOXgqnXJ+DIty1B+aPQrAVUu8LiM+6BiILfmNxYsxhKTj+BS3PZg==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.8.1.tgz", + "integrity": "sha512-OTp6eebuMcf2rJt4bqnvuwmm3NVXfzfYejL+u/Y1qwKhZPrjPoKWfk1CbOP5xH5ZOPkiAsx4dHdQBRJszK3z2g==", "dev": true, "license": "MIT", "dependencies": { @@ -3637,9 +3645,9 @@ } }, "node_modules/@docusaurus/types": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.8.0.tgz", - "integrity": "sha512-RDEClpwNxZq02c+JlaKLWoS13qwWhjcNsi2wG1UpzmEnuti/z1Wx4SGpqbUqRPNSd8QWWePR8Cb7DvG0VN/TtA==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.8.1.tgz", + "integrity": "sha512-ZPdW5AB+pBjiVrcLuw3dOS6BFlrG0XkS2lDGsj8TizcnREQg3J8cjsgfDviszOk4CweNfwo1AEELJkYaMUuOPg==", "dev": true, "license": "MIT", "dependencies": { @@ -3674,15 +3682,15 @@ } }, "node_modules/@docusaurus/utils": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.8.0.tgz", - "integrity": "sha512-2wvtG28ALCN/A1WCSLxPASFBFzXCnP0YKCAFIPcvEb6imNu1wg7ni/Svcp71b3Z2FaOFFIv4Hq+j4gD7gA0yfQ==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.8.1.tgz", + "integrity": "sha512-P1ml0nvOmEFdmu0smSXOqTS1sxU5tqvnc0dA4MTKV39kye+bhQnjkIKEE18fNOvxjyB86k8esoCIFM3x4RykOQ==", "dev": true, "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.8.0", - "@docusaurus/types": "3.8.0", - "@docusaurus/utils-common": "3.8.0", + "@docusaurus/logger": "3.8.1", + "@docusaurus/types": "3.8.1", + "@docusaurus/utils-common": "3.8.1", "escape-string-regexp": "^4.0.0", "execa": "5.1.1", "file-loader": "^6.2.0", @@ -3707,13 +3715,13 @@ } }, "node_modules/@docusaurus/utils-common": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.8.0.tgz", - "integrity": "sha512-3TGF+wVTGgQ3pAc9+5jVchES4uXUAhAt9pwv7uws4mVOxL4alvU3ue/EZ+R4XuGk94pDy7CNXjRXpPjlfZXQfw==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.8.1.tgz", + "integrity": "sha512-zTZiDlvpvoJIrQEEd71c154DkcriBecm4z94OzEE9kz7ikS3J+iSlABhFXM45mZ0eN5pVqqr7cs60+ZlYLewtg==", "dev": true, "license": "MIT", "dependencies": { - "@docusaurus/types": "3.8.0", + "@docusaurus/types": "3.8.1", "tslib": "^2.6.0" }, "engines": { @@ -3721,15 +3729,15 @@ } }, "node_modules/@docusaurus/utils-validation": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.8.0.tgz", - "integrity": "sha512-MrnEbkigr54HkdFeg8e4FKc4EF+E9dlVwsY3XQZsNkbv3MKZnbHQ5LsNJDIKDROFe8PBf5C4qCAg5TPBpsjrjg==", + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.8.1.tgz", + "integrity": "sha512-gs5bXIccxzEbyVecvxg6upTwaUbfa0KMmTj7HhHzc016AGyxH2o73k1/aOD0IFrdCsfJNt37MqNI47s2MgRZMA==", "dev": true, "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.8.0", - "@docusaurus/utils": "3.8.0", - "@docusaurus/utils-common": "3.8.0", + "@docusaurus/logger": "3.8.1", + "@docusaurus/utils": "3.8.1", + "@docusaurus/utils-common": "3.8.1", "fs-extra": "^11.2.0", "joi": "^17.9.2", "js-yaml": "^4.1.0", @@ -5402,9 +5410,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz", - "integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", + "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", "dev": true, "funding": [ { @@ -5422,8 +5430,8 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001716", - "electron-to-chromium": "^1.5.149", + "caniuse-lite": "^1.0.30001718", + "electron-to-chromium": "^1.5.160", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, @@ -6177,13 +6185,13 @@ } }, "node_modules/core-js-compat": { - "version": "3.42.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.42.0.tgz", - "integrity": "sha512-bQasjMfyDGyaeWKBIu33lHh9qlSR0MFE/Nmc6nMjf/iU9b3rSMdAYz1Baxrv4lPdGUsTqZudHA4jIGSJy0SWZQ==", + "version": "3.43.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.43.0.tgz", + "integrity": "sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.24.4" + "browserslist": "^4.25.0" }, "funding": { "type": "opencollective", @@ -6191,9 +6199,9 @@ } }, "node_modules/core-js-pure": { - "version": "3.42.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.42.0.tgz", - "integrity": "sha512-007bM04u91fF4kMgwom2I5cQxAFIy8jVulgr9eozILl/SZE53QOqnW/+vviC+wQWLv+AunBG+8Q0TLoeSsSxRQ==", + "version": "3.43.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.43.0.tgz", + "integrity": "sha512-i/AgxU2+A+BbJdMxh3v7/vxi2SbFqxiFmg6VsDwYB4jkucrd1BZNA9a9gphC0fYMG5IBSgQcbQnk865VCLe7xA==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -7140,9 +7148,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.155", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.155.tgz", - "integrity": "sha512-ps5KcGGmwL8VaeJlvlDlu4fORQpv3+GIcF5I3f9tUKUlJ/wsysh6HU8P5L1XWRYeXfA0oJd4PyM8ds8zTFf6Ng==", + "version": "1.5.170", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.170.tgz", + "integrity": "sha512-GP+M7aeluQo9uAyiTCxgIj/j+PrWhMlY7LFVj8prlsPljd0Fdg9AprlfUi+OCSFWy9Y5/2D/Jrj9HS8Z4rpKWA==", "dev": true, "license": "ISC" }, @@ -13119,9 +13127,9 @@ } }, "node_modules/postcss": { - "version": "8.5.4", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz", - "integrity": "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, "funding": [ { @@ -13370,9 +13378,9 @@ } }, "node_modules/postcss-custom-properties": { - "version": "14.0.5", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-14.0.5.tgz", - "integrity": "sha512-UWf/vhMapZatv+zOuqlfLmYXeOhhHLh8U8HAKGI2VJ00xLRYoAJh4xv8iX6FB6+TLXeDnm0DBLMi00E0hodbQw==", + "version": "14.0.6", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-14.0.6.tgz", + "integrity": "sha512-fTYSp3xuk4BUeVhxCSJdIPhDLpJfNakZKoiTDx7yRGCdlZrSJR7mWKVOBS4sBF+5poPQFMj2YdXx1VHItBGihQ==", "dev": true, "funding": [ { @@ -14010,9 +14018,9 @@ } }, "node_modules/postcss-nesting": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.1.tgz", - "integrity": "sha512-VbqqHkOBOt4Uu3G8Dm8n6lU5+9cJFxiuty9+4rcoyRPO9zZS1JIs6td49VIoix3qYqELHlJIn46Oih9SAKo+yQ==", + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.2.tgz", + "integrity": "sha512-1YCI290TX+VP0U/K/aFxzHzQWHWURL+CtHMSbex1lCdpXD1SoR2sYuxDu5aNI9lPoXpKTCggFZiDJbwylU0LEQ==", "dev": true, "funding": [ { @@ -14026,7 +14034,7 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/selector-resolve-nested": "^3.0.0", + "@csstools/selector-resolve-nested": "^3.1.0", "@csstools/selector-specificity": "^5.0.0", "postcss-selector-parser": "^7.0.0" }, @@ -14038,9 +14046,9 @@ } }, "node_modules/postcss-nesting/node_modules/@csstools/selector-resolve-nested": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.0.0.tgz", - "integrity": "sha512-ZoK24Yku6VJU1gS79a5PFmC8yn3wIapiKmPgun0hZgEI5AOqgH2kiPRsPz1qkGv4HL+wuDLH83yQyk6inMYrJQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.1.0.tgz", + "integrity": "sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g==", "dev": true, "funding": [ { @@ -14342,9 +14350,9 @@ } }, "node_modules/postcss-preset-env": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.2.0.tgz", - "integrity": "sha512-cl13sPBbSqo1Q7Ryb19oT5NZO5IHFolRbIMdgDq4f9w1MHYiL6uZS7uSsjXJ1KzRIcX5BMjEeyxmAevVXENa3Q==", + "version": "10.2.3", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.2.3.tgz", + "integrity": "sha512-zlQN1yYmA7lFeM1wzQI14z97mKoM8qGng+198w1+h6sCud/XxOjcKtApY9jWr7pXNS3yHDEafPlClSsWnkY8ow==", "dev": true, "funding": [ { @@ -14370,7 +14378,7 @@ "@csstools/postcss-hwb-function": "^4.0.10", "@csstools/postcss-ic-unit": "^4.0.2", "@csstools/postcss-initial": "^2.0.1", - "@csstools/postcss-is-pseudo-class": "^5.0.1", + "@csstools/postcss-is-pseudo-class": "^5.0.3", "@csstools/postcss-light-dark-function": "^2.0.9", "@csstools/postcss-logical-float-and-clear": "^3.0.0", "@csstools/postcss-logical-overflow": "^2.0.0", @@ -14392,7 +14400,7 @@ "@csstools/postcss-trigonometric-functions": "^4.0.9", "@csstools/postcss-unset-value": "^4.0.0", "autoprefixer": "^10.4.21", - "browserslist": "^4.24.5", + "browserslist": "^4.25.0", "css-blank-pseudo": "^7.0.1", "css-has-pseudo": "^7.0.2", "css-prefers-color-scheme": "^10.0.0", @@ -14403,7 +14411,7 @@ "postcss-color-hex-alpha": "^10.0.0", "postcss-color-rebeccapurple": "^10.0.0", "postcss-custom-media": "^11.0.6", - "postcss-custom-properties": "^14.0.5", + "postcss-custom-properties": "^14.0.6", "postcss-custom-selectors": "^8.0.5", "postcss-dir-pseudo-class": "^9.0.1", "postcss-double-position-gradients": "^6.0.2", @@ -14414,7 +14422,7 @@ "postcss-image-set-function": "^7.0.0", "postcss-lab-function": "^7.0.10", "postcss-logical": "^8.1.0", - "postcss-nesting": "^13.0.1", + "postcss-nesting": "^13.0.2", "postcss-opacity-percentage": "^3.0.0", "postcss-overflow-shorthand": "^6.0.0", "postcss-page-break": "^3.0.4", @@ -14989,7 +14997,7 @@ "version": "19.1.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "scheduler": "^0.26.0" @@ -15788,7 +15796,7 @@ "version": "0.26.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/schema-dts": { diff --git a/packages/docusaurus-config/package.json b/packages/docusaurus-config/package.json index 4e006c2994..92c993437e 100644 --- a/packages/docusaurus-config/package.json +++ b/packages/docusaurus-config/package.json @@ -1,10 +1,12 @@ { "name": "@goauthentik/docusaurus-config", - "version": "2.0.0", + "version": "2.1.1", "description": "authentik's Docusaurus config", "license": "MIT", "scripts": { - "build": "tsc -p ." + "build": "tsc -p .", + "prettier": "prettier --write .", + "prettier-check": "prettier --check ." }, "type": "module", "exports": { @@ -20,24 +22,26 @@ "prism-react-renderer": "^2.4.1" }, "devDependencies": { - "@docusaurus/theme-common": "^3.8.0", - "@docusaurus/theme-search-algolia": "^3.8.0", - "@docusaurus/types": "^3.8.0", + "@docusaurus/theme-common": "^3.8.1", + "@docusaurus/theme-search-algolia": "^3.8.1", + "@docusaurus/types": "^3.8.1", "@goauthentik/prettier-config": "^2.0.0", "@goauthentik/tsconfig": "^1.0.4", "@types/react": "^19.1.6", "@types/react-dom": "^19.1.5", "prettier": "^3.5.3", - "react": "^19.1.0", - "react-dom": "^19.1.0", "typescript": "^5.8.3" }, "peerDependencies": { - "@docusaurus/theme-common": "^3.8.0", - "@docusaurus/theme-search-algolia": "^3.8.0", + "@docusaurus/theme-common": "^3.8.1", + "@docusaurus/theme-search-algolia": "^3.8.1", "@docusaurus/types": "^3.8.0", - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" + "react": ">=18", + "react-dom": ">=18" + }, + "optionalDependencies": { + "react": ">=18", + "react-dom": ">=18" }, "engines": { "node": ">=22" @@ -53,6 +57,12 @@ "peerDependenciesMeta": { "@docusaurus/theme-search-algolia": { "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true } }, "publishConfig": { diff --git a/packages/prettier-config/.prettierignore b/packages/prettier-config/.prettierignore new file mode 100644 index 0000000000..e5e3e37cbc --- /dev/null +++ b/packages/prettier-config/.prettierignore @@ -0,0 +1,9 @@ +# Prettier Ignorefile + +## Static Files +**/LICENSE + +## Build asset directories +coverage +dist +out diff --git a/packages/prettier-config/lib/constants.js b/packages/prettier-config/lib/constants.js index 30976e7d26..14430b342c 100644 --- a/packages/prettier-config/lib/constants.js +++ b/packages/prettier-config/lib/constants.js @@ -45,7 +45,6 @@ export const AuthentikPrettierConfig = { "^(@goauthentik/|#)user.+", "^(@goauthentik/|#)admin.+", "^(@goauthentik/|#)flow.+", - "^(@goauthentik/|#)flow.+", "^#.+", "^@goauthentik.+", diff --git a/packages/prettier-config/package-lock.json b/packages/prettier-config/package-lock.json index f908d13b9f..77b1b428c6 100644 --- a/packages/prettier-config/package-lock.json +++ b/packages/prettier-config/package-lock.json @@ -1,29 +1,29 @@ { "name": "@goauthentik/prettier-config", - "version": "2.0.0", + "version": "2.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@goauthentik/prettier-config", - "version": "2.0.0", + "version": "2.0.1", "license": "MIT", "devDependencies": { "@goauthentik/tsconfig": "^1.0.1", "@trivago/prettier-plugin-sort-imports": "^5.2.2", "prettier": "^3.5.3", "prettier-plugin-organize-imports": "^4.1.0", - "prettier-plugin-packagejson": "^2.5.14", - "typescript": "^5.8.2" + "prettier-plugin-packagejson": "^2.5.15", + "typescript": "^5.8.3" }, "engines": { - "node": ">=20.11" + "node": ">=22" }, "peerDependencies": { "@trivago/prettier-plugin-sort-imports": "^5.2.2", "prettier": "^3.5.3", "prettier-plugin-organize-imports": "^4.1.0", - "prettier-plugin-packagejson": "^2.5.14" + "prettier-plugin-packagejson": "^2.5.15" } }, "node_modules/@babel/code-frame": { @@ -206,9 +206,9 @@ } }, "node_modules/@pkgr/core": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.4.tgz", - "integrity": "sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.7.tgz", + "integrity": "sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==", "dev": true, "license": "MIT", "engines": { @@ -437,14 +437,14 @@ } }, "node_modules/prettier-plugin-packagejson": { - "version": "2.5.14", - "resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.5.14.tgz", - "integrity": "sha512-h+3tSpr2nVpp+YOK1MDIYtYhHVXr8/0V59UUbJpIJFaqi3w4fvUokJo6eV8W+vELrUXIZzJ+DKm5G7lYzrMcKQ==", + "version": "2.5.15", + "resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.5.15.tgz", + "integrity": "sha512-2QSx6y4IT6LTwXtCvXAopENW5IP/aujC8fobEM2pDbs0IGkiVjW/ipPuYAHuXigbNe64aGWF7vIetukuzM3CBw==", "dev": true, "license": "MIT", "dependencies": { "sort-package-json": "3.2.1", - "synckit": "0.11.6" + "synckit": "0.11.8" }, "peerDependencies": { "prettier": ">= 1.16.0" @@ -495,9 +495,9 @@ } }, "node_modules/synckit": { - "version": "0.11.6", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.6.tgz", - "integrity": "sha512-2pR2ubZSV64f/vqm9eLPz/KOvR9Dm+Co/5ChLgeHl0yEDRc6h5hXHoxEQH8Y5Ljycozd3p1k5TTSVdzYGkPvLw==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", + "integrity": "sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==", "dev": true, "license": "MIT", "dependencies": { diff --git a/packages/prettier-config/package.json b/packages/prettier-config/package.json index f9557607d9..9f7ee0f1ef 100644 --- a/packages/prettier-config/package.json +++ b/packages/prettier-config/package.json @@ -1,10 +1,12 @@ { "name": "@goauthentik/prettier-config", - "version": "2.0.0", + "version": "2.0.1", "description": "authentik's Prettier config", "license": "MIT", "scripts": { - "build": "tsc -p ." + "build": "tsc -p .", + "prettier": "prettier --write .", + "prettier-check": "prettier --check ." }, "type": "module", "exports": "./index.js", @@ -13,17 +15,17 @@ "@trivago/prettier-plugin-sort-imports": "^5.2.2", "prettier": "^3.5.3", "prettier-plugin-organize-imports": "^4.1.0", - "prettier-plugin-packagejson": "^2.5.14", - "typescript": "^5.8.2" + "prettier-plugin-packagejson": "^2.5.15", + "typescript": "^5.8.3" }, "peerDependencies": { "@trivago/prettier-plugin-sort-imports": "^5.2.2", "prettier": "^3.5.3", "prettier-plugin-organize-imports": "^4.1.0", - "prettier-plugin-packagejson": "^2.5.14" + "prettier-plugin-packagejson": "^2.5.15" }, "engines": { - "node": ">=20.11" + "node": ">=22" }, "types": "./out/index.d.ts", "files": [ diff --git a/pyproject.toml b/pyproject.toml index 607aca6736..07eb90b764 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,7 +38,7 @@ dependencies = [ "fido2==2.0.0", "geoip2==5.1.0", "geopy==2.4.1", - "google-api-python-client==2.172.0", + "google-api-python-client==2.173.0", "gssapi==1.9.0", "gunicorn==23.0.0", "jsonpatch==1.33", diff --git a/uv.lock b/uv.lock index e9059cfc0b..6179a92b19 100644 --- a/uv.lock +++ b/uv.lock @@ -296,7 +296,7 @@ requires-dist = [ { name = "fido2", specifier = "==2.0.0" }, { name = "geoip2", specifier = "==5.1.0" }, { name = "geopy", specifier = "==2.4.1" }, - { name = "google-api-python-client", specifier = "==2.172.0" }, + { name = "google-api-python-client", specifier = "==2.173.0" }, { name = "gssapi", specifier = "==1.9.0" }, { name = "gunicorn", specifier = "==23.0.0" }, { name = "jsonpatch", specifier = "==1.33" }, @@ -1404,7 +1404,7 @@ wheels = [ [[package]] name = "google-api-python-client" -version = "2.172.0" +version = "2.173.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "google-api-core" }, @@ -1413,9 +1413,9 @@ dependencies = [ { name = "httplib2" }, { name = "uritemplate" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/02/69/c0cec6be5878d4de161f64096edb3d4a2d1a838f036b8425ea8358d0dfb3/google_api_python_client-2.172.0.tar.gz", hash = "sha256:dcb3b7e067154b2aa41f1776cf86584a5739c0ac74e6ff46fc665790dca0e6a6", size = 13074841, upload-time = "2025-06-10T16:58:41.181Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8f/7e/7c6e43e54f611f0f97f1678ea567fe06fecd545bd574db05e204e5b136fe/google_api_python_client-2.173.0.tar.gz", hash = "sha256:b537bc689758f4be3e6f40d59a6c0cd305abafdea91af4bc66ec31d40c08c804", size = 13091318, upload-time = "2025-06-19T19:39:05.881Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/15/fc/8850ccf21c5df43faeaf8bba8c4149ee880b41b8dc7066e3259bcfd921ca/google_api_python_client-2.172.0-py3-none-any.whl", hash = "sha256:9f1b9a268d5dc1228207d246c673d3a09ee211b41a11521d38d9212aeaa43af7", size = 13595800, upload-time = "2025-06-10T16:58:38.143Z" }, + { url = "https://files.pythonhosted.org/packages/e6/c9/dc9ca0537ee2ddac0f0b1e458903afe3f490a0f90dfd4b1b16eb339cdfbb/google_api_python_client-2.173.0-py3-none-any.whl", hash = "sha256:16a8e81c772dd116f5c4ee47d83643149e1367dc8fb4f47cb471fbcb5c7d7ac7", size = 13612778, upload-time = "2025-06-19T19:39:03.283Z" }, ] [[package]] diff --git a/web/package-lock.json b/web/package-lock.json index 9fdb9e06c8..3c5d5e198b 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -37,6 +37,7 @@ "@sentry/browser": "^9.30.0", "@spotlightjs/spotlight": "^3.0.1", "@webcomponents/webcomponentsjs": "^2.8.0", + "base64-js": "^1.5.1", "change-case": "^5.4.4", "chart.js": "^4.4.9", "chartjs-adapter-date-fns": "^3.0.0", @@ -68,7 +69,6 @@ "trusted-types": "^2.0.0", "ts-pattern": "^5.7.1", "unist-util-visit": "^5.0.0", - "webauthn-polyfills": "^0.1.7", "webcomponent-qr-code": "^1.2.0", "yaml": "^2.8.0" }, @@ -4768,12 +4768,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@simplewebauthn/types": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/@simplewebauthn/types/-/types-11.0.0.tgz", - "integrity": "sha512-b2o0wC5u2rWts31dTgBkAtSNKGX0cvL6h8QedNsKmj8O4QoLFQFR3DBVBUlpyVEhYKA+mXGUaXbcOc4JdQ3HzA==", - "license": "MIT" - }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -7361,12 +7355,6 @@ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" }, - "node_modules/@types/ua-parser-js": { - "version": "0.7.39", - "resolved": "https://registry.npmjs.org/@types/ua-parser-js/-/ua-parser-js-0.7.39.tgz", - "integrity": "sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==", - "license": "MIT" - }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -11989,7 +11977,8 @@ "node_modules/compare-versions": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", - "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==" + "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", + "dev": true }, "node_modules/compatx": { "version": "0.1.8", @@ -27224,32 +27213,6 @@ "node": ">=8" } }, - "node_modules/ua-parser-js": { - "version": "1.0.40", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz", - "integrity": "sha512-z6PJ8Lml+v3ichVojCiB8toQJBuwR42ySM4ezjXIqXK3M0HczmKQ3LF4rhU55PfD99KEEXQG6yb7iOMyvYuHew==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - }, - { - "type": "github", - "url": "https://github.com/sponsors/faisalman" - } - ], - "license": "MIT", - "bin": { - "ua-parser-js": "script/cli.js" - }, - "engines": { - "node": "*" - } - }, "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", @@ -28599,18 +28562,6 @@ "integrity": "sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==", "license": "Apache-2.0" }, - "node_modules/webauthn-polyfills": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/webauthn-polyfills/-/webauthn-polyfills-0.1.7.tgz", - "integrity": "sha512-tOA5KPHhN8j8EBA9I90bYmsEc6CAKd1SbWJzmVn0hmTfvfiNJLGGzRPlSW4fKiQPm8BC6doPQC0CnaQdhxsL3Q==", - "license": "Apache-2.0", - "dependencies": { - "@simplewebauthn/types": "^11.0.0", - "@types/ua-parser-js": "^0.7.39", - "compare-versions": "^6.1.1", - "ua-parser-js": "^1.0.39" - } - }, "node_modules/webcomponent-qr-code": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/webcomponent-qr-code/-/webcomponent-qr-code-1.2.0.tgz", @@ -29535,11 +29486,11 @@ "license": "MIT", "dependencies": { "@goauthentik/api": "^2024.6.0-1719577139", + "base64-js": "^1.5.1", "bootstrap": "^4.6.1", "formdata-polyfill": "^4.0.10", "jquery": "^3.7.1", - "weakmap-polyfill": "^2.0.4", - "webauthn-polyfills": "^0.1.7" + "weakmap-polyfill": "^2.0.4" }, "devDependencies": { "@goauthentik/core": "^1.0.0", diff --git a/web/package.json b/web/package.json index 8cb8dd12e4..c8aeeb2e4e 100644 --- a/web/package.json +++ b/web/package.json @@ -108,6 +108,7 @@ "@sentry/browser": "^9.30.0", "@spotlightjs/spotlight": "^3.0.1", "@webcomponents/webcomponentsjs": "^2.8.0", + "base64-js": "^1.5.1", "change-case": "^5.4.4", "chart.js": "^4.4.9", "chartjs-adapter-date-fns": "^3.0.0", @@ -139,7 +140,6 @@ "trusted-types": "^2.0.0", "ts-pattern": "^5.7.1", "unist-util-visit": "^5.0.0", - "webauthn-polyfills": "^0.1.7", "webcomponent-qr-code": "^1.2.0", "yaml": "^2.8.0" }, diff --git a/web/packages/sfe/package.json b/web/packages/sfe/package.json index 5d299ba060..c1602e7e04 100644 --- a/web/packages/sfe/package.json +++ b/web/packages/sfe/package.json @@ -11,11 +11,11 @@ }, "dependencies": { "@goauthentik/api": "^2024.6.0-1719577139", + "base64-js": "^1.5.1", "bootstrap": "^4.6.1", "formdata-polyfill": "^4.0.10", "jquery": "^3.7.1", - "weakmap-polyfill": "^2.0.4", - "webauthn-polyfills": "^0.1.7" + "weakmap-polyfill": "^2.0.4" }, "devDependencies": { "@goauthentik/core": "^1.0.0", diff --git a/web/packages/sfe/src/index.ts b/web/packages/sfe/src/index.ts index 2bbdfdf2cb..0da39dd481 100644 --- a/web/packages/sfe/src/index.ts +++ b/web/packages/sfe/src/index.ts @@ -1,7 +1,7 @@ +import { fromByteArray } from "base64-js"; import "formdata-polyfill"; import $ from "jquery"; import "weakmap-polyfill"; -import "webauthn-polyfills"; import { type AuthenticatorValidationChallenge, @@ -257,9 +257,47 @@ class AutosubmitStage extends Stage { } } +export interface Assertion { + id: string; + rawId: string; + type: string; + registrationClientExtensions: string; + response: { + clientDataJSON: string; + attestationObject: string; + }; +} + +export interface AuthAssertion { + id: string; + rawId: string; + type: string; + assertionClientExtensions: string; + response: { + clientDataJSON: string; + authenticatorData: string; + signature: string; + userHandle: string | null; + }; +} + class AuthenticatorValidateStage extends Stage { deviceChallenge?: DeviceChallenge; + b64enc(buf: Uint8Array): string { + return fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); + } + + b64RawEnc(buf: Uint8Array): string { + return fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_"); + } + + u8arr(input: string): Uint8Array { + return Uint8Array.from(atob(input.replace(/_/g, "/").replace(/-/g, "+")), (c) => + c.charCodeAt(0), + ); + } + checkWebAuthnSupport(): boolean { if ("credentials" in navigator) { return true; @@ -272,6 +310,98 @@ class AuthenticatorValidateStage extends Stage return false; } + /** + * Transforms items in the credentialCreateOptions generated on the server + * into byte arrays expected by the navigator.credentials.create() call + */ + transformCredentialCreateOptions( + credentialCreateOptions: PublicKeyCredentialCreationOptions, + userId: string, + ): PublicKeyCredentialCreationOptions { + const user = credentialCreateOptions.user; + // Because json can't contain raw bytes, the server base64-encodes the User ID + // So to get the base64 encoded byte array, we first need to convert it to a regular + // string, then a byte array, re-encode it and wrap that in an array. + const stringId = decodeURIComponent(window.atob(userId)); + user.id = this.u8arr(this.b64enc(this.u8arr(stringId))); + const challenge = this.u8arr(credentialCreateOptions.challenge.toString()); + + return Object.assign({}, credentialCreateOptions, { + challenge, + user, + }); + } + + /** + * Transforms the binary data in the credential into base64 strings + * for posting to the server. + * @param {PublicKeyCredential} newAssertion + */ + transformNewAssertionForServer(newAssertion: PublicKeyCredential): Assertion { + const attObj = new Uint8Array( + (newAssertion.response as AuthenticatorAttestationResponse).attestationObject, + ); + const clientDataJSON = new Uint8Array(newAssertion.response.clientDataJSON); + const rawId = new Uint8Array(newAssertion.rawId); + + const registrationClientExtensions = newAssertion.getClientExtensionResults(); + return { + id: newAssertion.id, + rawId: this.b64enc(rawId), + type: newAssertion.type, + registrationClientExtensions: JSON.stringify(registrationClientExtensions), + response: { + clientDataJSON: this.b64enc(clientDataJSON), + attestationObject: this.b64enc(attObj), + }, + }; + } + + transformCredentialRequestOptions( + credentialRequestOptions: PublicKeyCredentialRequestOptions, + ): PublicKeyCredentialRequestOptions { + const challenge = this.u8arr(credentialRequestOptions.challenge.toString()); + + const allowCredentials = (credentialRequestOptions.allowCredentials || []).map( + (credentialDescriptor) => { + const id = this.u8arr(credentialDescriptor.id.toString()); + return Object.assign({}, credentialDescriptor, { id }); + }, + ); + + return Object.assign({}, credentialRequestOptions, { + challenge, + allowCredentials, + }); + } + + /** + * Encodes the binary data in the assertion into strings for posting to the server. + * @param {PublicKeyCredential} newAssertion + */ + transformAssertionForServer(newAssertion: PublicKeyCredential): AuthAssertion { + const response = newAssertion.response as AuthenticatorAssertionResponse; + const authData = new Uint8Array(response.authenticatorData); + const clientDataJSON = new Uint8Array(response.clientDataJSON); + const rawId = new Uint8Array(newAssertion.rawId); + const sig = new Uint8Array(response.signature); + const assertionClientExtensions = newAssertion.getClientExtensionResults(); + + return { + id: newAssertion.id, + rawId: this.b64enc(rawId), + type: newAssertion.type, + assertionClientExtensions: JSON.stringify(assertionClientExtensions), + + response: { + clientDataJSON: this.b64RawEnc(clientDataJSON), + signature: this.b64RawEnc(sig), + authenticatorData: this.b64RawEnc(authData), + userHandle: null, + }, + }; + } + render() { if (this.challenge.deviceChallenges.length === 1) { this.deviceChallenge = this.challenge.deviceChallenges[0]; @@ -375,8 +505,8 @@ class AuthenticatorValidateStage extends Stage `); navigator.credentials .get({ - publicKey: PublicKeyCredential.parseRequestOptionsFromJSON( - this.deviceChallenge?.challenge as PublicKeyCredentialRequestOptionsJSON, + publicKey: this.transformCredentialRequestOptions( + this.deviceChallenge?.challenge as PublicKeyCredentialRequestOptions, ), }) .then((assertion) => { @@ -384,9 +514,15 @@ class AuthenticatorValidateStage extends Stage throw new Error("No assertion"); } try { + // we now have an authentication assertion! encode the byte arrays contained + // in the assertion data as strings for posting to the server + const transformedAssertionForServer = this.transformAssertionForServer( + assertion as PublicKeyCredential, + ); + // post the assertion to the server for verification. this.executor.submit({ - webauthn: (assertion as PublicKeyCredential).toJSON(), + webauthn: transformedAssertionForServer, }); } catch (err) { throw new Error(`Error when validating assertion on server: ${err}`); diff --git a/web/src/admin/events/EventListPage.ts b/web/src/admin/events/EventListPage.ts index ff2ecf92da..30a556f56e 100644 --- a/web/src/admin/events/EventListPage.ts +++ b/web/src/admin/events/EventListPage.ts @@ -99,12 +99,14 @@ export class EventListPage extends WithLicenseSummary(TablePage) { > `; } - return html``; + return html`
+ +
`; } row(item: EventWithContext): SlottedTemplateResult[] { diff --git a/web/src/admin/events/EventMap.ts b/web/src/admin/events/EventMap.ts index d56074dd4d..b482f7ec07 100644 --- a/web/src/admin/events/EventMap.ts +++ b/web/src/admin/events/EventMap.ts @@ -4,8 +4,7 @@ import { PaginatedResponse } from "#elements/table/Table"; import { AKElement } from "@goauthentik/elements/Base"; import "@openlayers-elements/core/ol-layer-vector"; import type OlLayerVector from "@openlayers-elements/core/ol-layer-vector"; -import "@openlayers-elements/core/ol-map"; -import type OlMap from "@openlayers-elements/core/ol-map"; +import OlMap from "@openlayers-elements/core/ol-map"; import "@openlayers-elements/maps/ol-layer-openstreetmap"; import "@openlayers-elements/maps/ol-select"; import Feature from "ol/Feature"; @@ -19,9 +18,32 @@ import { customElement, property, query } from "lit/decorators.js"; import PFCard from "@patternfly/patternfly/components/Card/card.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; +import OL from "ol/ol.css"; import { Event } from "@goauthentik/api"; +@customElement("ak-map") +export class Map extends OlMap { + public render() { + return html` + + +
+ + `; + } +} + /** * * @event {select-event} - Fired when an event is selected on the map. ID of the event is contained @@ -36,8 +58,8 @@ export class EventMap extends AKElement { @query("ol-layer-vector") vectorLayer?: OlLayerVector; - @query("ol-map") - map?: OlMap; + @query("ak-map") + map?: Map; @property({ type: Number }) zoomPaddingPx = 100; @@ -116,7 +138,7 @@ export class EventMap extends AKElement { render(): TemplateResult { return html`
- + ) => { const eventId = ev.detail.feature.getId(); @@ -133,7 +155,7 @@ export class EventMap extends AKElement { > - +
`; } } diff --git a/web/src/common/helpers/webauthn.ts b/web/src/common/helpers/webauthn.ts index cfa4c82000..68b29ce760 100644 --- a/web/src/common/helpers/webauthn.ts +++ b/web/src/common/helpers/webauthn.ts @@ -1,5 +1,21 @@ +import * as base64js from "base64-js"; + import { msg } from "@lit/localize"; +export function b64enc(buf: Uint8Array): string { + return base64js.fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); +} + +export function b64RawEnc(buf: Uint8Array): string { + return base64js.fromByteArray(buf).replace(/\+/g, "-").replace(/\//g, "_"); +} + +export function u8arr(input: string): Uint8Array { + return Uint8Array.from(atob(input.replace(/_/g, "/").replace(/-/g, "+")), (c) => + c.charCodeAt(0), + ); +} + export function checkWebAuthnSupport() { if ("credentials" in navigator) { return; @@ -9,3 +25,121 @@ export function checkWebAuthnSupport() { } throw new Error(msg("WebAuthn not supported by browser.")); } + +/** + * Transforms items in the credentialCreateOptions generated on the server + * into byte arrays expected by the navigator.credentials.create() call + */ +export function transformCredentialCreateOptions( + credentialCreateOptions: PublicKeyCredentialCreationOptions, + userId: string, +): PublicKeyCredentialCreationOptions { + const user = credentialCreateOptions.user; + // Because json can't contain raw bytes, the server base64-encodes the User ID + // So to get the base64 encoded byte array, we first need to convert it to a regular + // string, then a byte array, re-encode it and wrap that in an array. + const stringId = decodeURIComponent(window.atob(userId)); + user.id = u8arr(b64enc(u8arr(stringId))); + const challenge = u8arr(credentialCreateOptions.challenge.toString()); + + return { + ...credentialCreateOptions, + challenge, + user, + }; +} + +export interface Assertion { + id: string; + rawId: string; + type: string; + registrationClientExtensions: string; + response: { + clientDataJSON: string; + attestationObject: string; + }; +} + +/** + * Transforms the binary data in the credential into base64 strings + * for posting to the server. + * @param {PublicKeyCredential} newAssertion + */ +export function transformNewAssertionForServer(newAssertion: PublicKeyCredential): Assertion { + const attObj = new Uint8Array( + (newAssertion.response as AuthenticatorAttestationResponse).attestationObject, + ); + const clientDataJSON = new Uint8Array(newAssertion.response.clientDataJSON); + const rawId = new Uint8Array(newAssertion.rawId); + + const registrationClientExtensions = newAssertion.getClientExtensionResults(); + return { + id: newAssertion.id, + rawId: b64enc(rawId), + type: newAssertion.type, + registrationClientExtensions: JSON.stringify(registrationClientExtensions), + response: { + clientDataJSON: b64enc(clientDataJSON), + attestationObject: b64enc(attObj), + }, + }; +} + +export function transformCredentialRequestOptions( + credentialRequestOptions: PublicKeyCredentialRequestOptions, +): PublicKeyCredentialRequestOptions { + const challenge = u8arr(credentialRequestOptions.challenge.toString()); + + const allowCredentials = (credentialRequestOptions.allowCredentials || []).map( + (credentialDescriptor) => { + const id = u8arr(credentialDescriptor.id.toString()); + return Object.assign({}, credentialDescriptor, { id }); + }, + ); + + return { + ...credentialRequestOptions, + challenge, + allowCredentials, + }; +} + +export interface AuthAssertion { + id: string; + rawId: string; + type: string; + assertionClientExtensions: string; + response: { + clientDataJSON: string; + authenticatorData: string; + signature: string; + userHandle: string | null; + }; +} + +/** + * Encodes the binary data in the assertion into strings for posting to the server. + * @param {PublicKeyCredential} newAssertion + */ +export function transformAssertionForServer(newAssertion: PublicKeyCredential): AuthAssertion { + const response = newAssertion.response as AuthenticatorAssertionResponse; + const authData = new Uint8Array(response.authenticatorData); + const clientDataJSON = new Uint8Array(response.clientDataJSON); + const rawId = new Uint8Array(newAssertion.rawId); + const sig = new Uint8Array(response.signature); + const assertionClientExtensions = newAssertion.getClientExtensionResults(); + + return { + id: newAssertion.id, + rawId: b64enc(rawId), + type: newAssertion.type, + assertionClientExtensions: JSON.stringify(assertionClientExtensions), + + response: { + clientDataJSON: b64RawEnc(clientDataJSON), + signature: b64RawEnc(sig), + authenticatorData: b64RawEnc(authData), + userHandle: null, + }, + }; +} diff --git a/web/src/flow/stages/authenticator_validate/AuthenticatorValidateStageWebAuthn.ts b/web/src/flow/stages/authenticator_validate/AuthenticatorValidateStageWebAuthn.ts index 2fe87f5e0b..d211cbc180 100644 --- a/web/src/flow/stages/authenticator_validate/AuthenticatorValidateStageWebAuthn.ts +++ b/web/src/flow/stages/authenticator_validate/AuthenticatorValidateStageWebAuthn.ts @@ -1,4 +1,8 @@ -import { checkWebAuthnSupport } from "@goauthentik/common/helpers/webauthn"; +import { + checkWebAuthnSupport, + transformAssertionForServer, + transformCredentialRequestOptions, +} from "@goauthentik/common/helpers/webauthn"; import "@goauthentik/elements/EmptyState"; import { BaseDeviceStage } from "@goauthentik/flow/stages/authenticator_validate/base"; @@ -34,12 +38,12 @@ export class AuthenticatorValidateStageWebAuthn extends BaseDeviceStage< async authenticate(): Promise { // request the authenticator to create an assertion signature using the // credential private key - let assertion: PublicKeyCredential; + let assertion; checkWebAuthnSupport(); try { - assertion = (await navigator.credentials.get({ + assertion = await navigator.credentials.get({ publicKey: this.transformedCredentialRequestOptions, - })) as PublicKeyCredential; + }); if (!assertion) { throw new Error("Assertions is empty"); } @@ -47,11 +51,17 @@ export class AuthenticatorValidateStageWebAuthn extends BaseDeviceStage< throw new Error(`Error when creating credential: ${err}`); } + // we now have an authentication assertion! encode the byte arrays contained + // in the assertion data as strings for posting to the server + const transformedAssertionForServer = transformAssertionForServer( + assertion as PublicKeyCredential, + ); + // post the assertion to the server for verification. try { await this.host?.submit( { - webauthn: assertion.toJSON(), + webauthn: transformedAssertionForServer, }, { invisible: true, @@ -64,10 +74,12 @@ export class AuthenticatorValidateStageWebAuthn extends BaseDeviceStage< updated(changedProperties: PropertyValues) { if (changedProperties.has("challenge") && this.challenge !== undefined) { + // convert certain members of the PublicKeyCredentialRequestOptions into + // byte arrays as expected by the spec. const credentialRequestOptions = this.deviceChallenge - ?.challenge as unknown as PublicKeyCredentialRequestOptionsJSON; + ?.challenge as PublicKeyCredentialRequestOptions; this.transformedCredentialRequestOptions = - PublicKeyCredential.parseRequestOptionsFromJSON(credentialRequestOptions); + transformCredentialRequestOptions(credentialRequestOptions); this.authenticateWrapper(); } } diff --git a/web/src/flow/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage.ts b/web/src/flow/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage.ts index e9213101ea..6daaf88dc6 100644 --- a/web/src/flow/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage.ts +++ b/web/src/flow/stages/authenticator_webauthn/WebAuthnAuthenticatorRegisterStage.ts @@ -1,4 +1,9 @@ -import { checkWebAuthnSupport } from "@goauthentik/common/helpers/webauthn"; +import { + Assertion, + checkWebAuthnSupport, + transformCredentialCreateOptions, + transformNewAssertionForServer, +} from "@goauthentik/common/helpers/webauthn"; import "@goauthentik/elements/EmptyState"; import { BaseStage } from "@goauthentik/flow/stages/base"; @@ -19,6 +24,10 @@ import { AuthenticatorWebAuthnChallengeResponseRequest, } from "@goauthentik/api"; +export interface WebAuthnAuthenticatorRegisterChallengeResponse { + response: Assertion; +} + @customElement("ak-stage-authenticator-webauthn") export class WebAuthnAuthenticatorRegisterStage extends BaseStage< AuthenticatorWebAuthnChallenge, @@ -59,7 +68,7 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage< } checkWebAuthnSupport(); // request the authenticator(s) to create a new credential keypair. - let credential: PublicKeyCredential; + let credential; try { credential = (await navigator.credentials.create({ publicKey: this.publicKeyCredentialCreateOptions, @@ -71,12 +80,16 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage< throw new Error(msg(str`Error creating credential: ${err}`)); } + // we now have a new credential! We now need to encode the byte arrays + // in the credential into strings, for posting to our server. + const newAssertionForServer = transformNewAssertionForServer(credential); + // post the transformed credential data to the server for validation // and storing the public key try { await this.host?.submit( { - response: credential.toJSON(), + response: newAssertionForServer, }, { invisible: true, @@ -105,10 +118,12 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage< updated(changedProperties: PropertyValues) { if (changedProperties.has("challenge") && this.challenge !== undefined) { - this.publicKeyCredentialCreateOptions = - PublicKeyCredential.parseCreationOptionsFromJSON( - this.challenge?.registration as PublicKeyCredentialCreationOptionsJSON, - ); + // convert certain members of the PublicKeyCredentialCreateOptions into + // byte arrays as expected by the spec. + this.publicKeyCredentialCreateOptions = transformCredentialCreateOptions( + this.challenge?.registration as PublicKeyCredentialCreationOptions, + this.challenge?.registration.user.id, + ); this.registerWrapper(); } } diff --git a/web/src/polyfill/index.entrypoint.ts b/web/src/polyfill/index.entrypoint.ts index b272a911e3..2703f69c89 100644 --- a/web/src/polyfill/index.entrypoint.ts +++ b/web/src/polyfill/index.entrypoint.ts @@ -3,7 +3,6 @@ import "construct-style-sheets-polyfill"; import "@webcomponents/webcomponentsjs"; import "lit/polyfill-support.js"; import "core-js/actual"; -import "webauthn-polyfills"; import "@formatjs/intl-listformat/polyfill"; import "@formatjs/intl-listformat/locale-data/en"; diff --git a/website/integrations/services/actual-budget/index.mdx b/website/integrations/services/actual-budget/index.mdx index 3f56b71dd8..762c4cb400 100644 --- a/website/integrations/services/actual-budget/index.mdx +++ b/website/integrations/services/actual-budget/index.mdx @@ -108,4 +108,4 @@ To confirm that authentik is properly configured with Actual Budget, visit your ## Resources -- [Official Actual Budget documentation on OpenID Connect integration](https://actualbudget.org/docs/experimental/oauth-auth/) +- [Actual Budget docs - Authenticating With an OpenID Provider](https://actualbudget.org/docs/config/oauth-auth/)