@cdmbase/wiki-browser 12.0.18-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (367) hide show
  1. package/LICENSE +21 -0
  2. package/lib/components/Logo.d.ts +4 -0
  3. package/lib/components/Logo.d.ts.map +1 -0
  4. package/lib/components/Logo.js +16 -0
  5. package/lib/components/Logo.js.map +1 -0
  6. package/lib/components/help/SidebarSearch.d.ts +8 -0
  7. package/lib/components/help/SidebarSearch.d.ts.map +1 -0
  8. package/lib/components/help/SidebarSearch.js +111 -0
  9. package/lib/components/help/SidebarSearch.js.map +1 -0
  10. package/lib/components/help/index.d.ts +2 -0
  11. package/lib/components/help/index.d.ts.map +1 -0
  12. package/lib/components/landing/FeatureCard.d.ts +13 -0
  13. package/lib/components/landing/FeatureCard.d.ts.map +1 -0
  14. package/lib/components/landing/FeatureCard.js +85 -0
  15. package/lib/components/landing/FeatureCard.js.map +1 -0
  16. package/lib/components/landing/QuickLinkCard.d.ts +8 -0
  17. package/lib/components/landing/QuickLinkCard.d.ts.map +1 -0
  18. package/lib/components/landing/QuickLinkCard.js +26 -0
  19. package/lib/components/landing/QuickLinkCard.js.map +1 -0
  20. package/lib/components/landing/SearchInput.d.ts +10 -0
  21. package/lib/components/landing/SearchInput.d.ts.map +1 -0
  22. package/lib/components/landing/SearchInput.js +223 -0
  23. package/lib/components/landing/SearchInput.js.map +1 -0
  24. package/lib/components/landing/index.d.ts +4 -0
  25. package/lib/components/landing/index.d.ts.map +1 -0
  26. package/lib/components/welcome.d.ts +3 -0
  27. package/lib/components/welcome.d.ts.map +1 -0
  28. package/lib/compute.d.ts +4 -0
  29. package/lib/compute.d.ts.map +1 -0
  30. package/lib/compute.js +96 -0
  31. package/lib/compute.js.map +1 -0
  32. package/lib/config/env-config.d.ts +4 -0
  33. package/lib/config/env-config.d.ts.map +1 -0
  34. package/lib/config/env-config.js +7 -0
  35. package/lib/config/env-config.js.map +1 -0
  36. package/lib/docs.config.d.ts +48 -0
  37. package/lib/docs.config.d.ts.map +1 -0
  38. package/lib/index.d.ts +4 -0
  39. package/lib/index.d.ts.map +1 -0
  40. package/lib/index.js +2 -0
  41. package/lib/index.js.map +1 -0
  42. package/lib/loaders/search.d.ts +1 -0
  43. package/lib/loaders/search.d.ts.map +1 -0
  44. package/lib/module.d.ts +4 -0
  45. package/lib/module.d.ts.map +1 -0
  46. package/lib/module.js +11 -0
  47. package/lib/module.js.map +1 -0
  48. package/lib/pages/ArticlePage/ArticlePage.d.ts +4 -0
  49. package/lib/pages/ArticlePage/ArticlePage.d.ts.map +1 -0
  50. package/lib/pages/ArticlePage/ArticlePage.js +222 -0
  51. package/lib/pages/ArticlePage/ArticlePage.js.map +1 -0
  52. package/lib/pages/ArticlePage/index.d.ts +3 -0
  53. package/lib/pages/ArticlePage/index.d.ts.map +1 -0
  54. package/lib/pages/ArticlePage/index.js +3 -0
  55. package/lib/pages/ArticlePage/index.js.map +1 -0
  56. package/lib/pages/CategoryCollection/CategoryCollection.d.ts +4 -0
  57. package/lib/pages/CategoryCollection/CategoryCollection.d.ts.map +1 -0
  58. package/lib/pages/CategoryCollection/CategoryCollection.js +103 -0
  59. package/lib/pages/CategoryCollection/CategoryCollection.js.map +1 -0
  60. package/lib/pages/CategoryCollection/index.d.ts +3 -0
  61. package/lib/pages/CategoryCollection/index.d.ts.map +1 -0
  62. package/lib/pages/CategoryCollection/index.js +3 -0
  63. package/lib/pages/CategoryCollection/index.js.map +1 -0
  64. package/lib/pages/Help/HelpIndex.d.ts +4 -0
  65. package/lib/pages/Help/HelpIndex.d.ts.map +1 -0
  66. package/lib/pages/Help/HelpIndex.js +44 -0
  67. package/lib/pages/Help/HelpIndex.js.map +1 -0
  68. package/lib/pages/Help/index.d.ts +4 -0
  69. package/lib/pages/Help/index.d.ts.map +1 -0
  70. package/lib/pages/Help/index.js +226 -0
  71. package/lib/pages/Help/index.js.map +1 -0
  72. package/lib/pages/Landing/index.d.ts +3 -0
  73. package/lib/pages/Landing/index.d.ts.map +1 -0
  74. package/lib/pages/Landing/index.js +281 -0
  75. package/lib/pages/Landing/index.js.map +1 -0
  76. package/lib/routes.json +2533 -0
  77. package/lib/seo.d.ts +22 -0
  78. package/lib/seo.d.ts.map +1 -0
  79. package/lib/slot-fill/FooterFill.d.ts +3 -0
  80. package/lib/slot-fill/FooterFill.d.ts.map +1 -0
  81. package/lib/slot-fill/FooterFill.js +18 -0
  82. package/lib/slot-fill/FooterFill.js.map +1 -0
  83. package/lib/slot-fill/LogoFill.d.ts +5 -0
  84. package/lib/slot-fill/LogoFill.d.ts.map +1 -0
  85. package/lib/slot-fill/LogoFill.js +74 -0
  86. package/lib/slot-fill/LogoFill.js.map +1 -0
  87. package/lib/slot-fill/consts.d.ts +5 -0
  88. package/lib/slot-fill/consts.d.ts.map +1 -0
  89. package/lib/slot-fill/consts.js +1 -0
  90. package/lib/slot-fill/consts.js.map +1 -0
  91. package/lib/slot-fill/index.d.ts +4 -0
  92. package/lib/slot-fill/index.d.ts.map +1 -0
  93. package/lib/templates/assets/images/add-link-frontend.png +0 -0
  94. package/lib/templates/assets/images/add-package-backend.png +0 -0
  95. package/lib/templates/assets/images/add-to-backend-module.png +0 -0
  96. package/lib/templates/assets/images/add-upload-client-frontend.png +0 -0
  97. package/lib/templates/assets/images/additional-parameters.png +0 -0
  98. package/lib/templates/assets/images/aeh-implementation.png +0 -0
  99. package/lib/templates/assets/images/aeh-usage.png +0 -0
  100. package/lib/templates/assets/images/apollo-client/recommendation_cache_mgmt.png +0 -0
  101. package/lib/templates/assets/images/app-deploy-new-version/jenkins1.PNG +0 -0
  102. package/lib/templates/assets/images/app-deploy-new-version/jenkins2.PNG +0 -0
  103. package/lib/templates/assets/images/auth-wrapper-code.png +0 -0
  104. package/lib/templates/assets/images/cdebase.png +0 -0
  105. package/lib/templates/assets/images/cdm-locales-directory.png +0 -0
  106. package/lib/templates/assets/images/client-settings.png +0 -0
  107. package/lib/templates/assets/images/codegen_file_update.png +0 -0
  108. package/lib/templates/assets/images/configuration.png +0 -0
  109. package/lib/templates/assets/images/copy-plugin.png +0 -0
  110. package/lib/templates/assets/images/docusaurus.png +0 -0
  111. package/lib/templates/assets/images/error-link.png +0 -0
  112. package/lib/templates/assets/images/error-sample.png +0 -0
  113. package/lib/templates/assets/images/extension copy.png +0 -0
  114. package/lib/templates/assets/images/extension.png +0 -0
  115. package/lib/templates/assets/images/graphql/graphql-folder-backend.png +0 -0
  116. package/lib/templates/assets/images/graphql/graphql-folder-with-gql.png +0 -0
  117. package/lib/templates/assets/images/i18n-config.png +0 -0
  118. package/lib/templates/assets/images/image.png +0 -0
  119. package/lib/templates/assets/images/logo.svg +10 -0
  120. package/lib/templates/assets/images/logo1.svg +1 -0
  121. package/lib/templates/assets/images/modify-upload-false-server.png +0 -0
  122. package/lib/templates/assets/images/navigation-auth-enabled.png +0 -0
  123. package/lib/templates/assets/images/org-dashboard-navigation.png +0 -0
  124. package/lib/templates/assets/images/org-navigation.png +0 -0
  125. package/lib/templates/assets/images/preferences_graphql_type.png +0 -0
  126. package/lib/templates/assets/images/provider.png +0 -0
  127. package/lib/templates/assets/images/route-config.png +0 -0
  128. package/lib/templates/assets/images/service-accounts.png +0 -0
  129. package/lib/templates/assets/images/source-code/source-code-environments.png +0 -0
  130. package/lib/templates/assets/images/source-code/source-code-organization.png +0 -0
  131. package/lib/templates/assets/images/spin-clone-develop-deployment/jenkins-changes.png +0 -0
  132. package/lib/templates/assets/images/spin-clone-develop-deployment/lerna-changes.png +0 -0
  133. package/lib/templates/assets/images/spin-clone-develop-deployment/root-package-json-changes.png +0 -0
  134. package/lib/templates/assets/images/spin-clone-develop-deployment/values-dev-changes.png +0 -0
  135. package/lib/templates/assets/images/sso-mappers.png +0 -0
  136. package/lib/templates/assets/images/sso-picture-mapper.png +0 -0
  137. package/lib/templates/assets/images/sso-settings.png +0 -0
  138. package/lib/templates/assets/images/timesheet_apollo_cache.png +0 -0
  139. package/lib/templates/assets/images/timesheet_query.png +0 -0
  140. package/lib/templates/assets/images/tutorial/docsVersionDropdown.png +0 -0
  141. package/lib/templates/assets/images/tutorial/localeDropdown.png +0 -0
  142. package/lib/templates/assets/images/unauthenticated.png +0 -0
  143. package/lib/templates/assets/images/undraw_docusaurus_mountain.svg +170 -0
  144. package/lib/templates/assets/images/undraw_docusaurus_react.svg +169 -0
  145. package/lib/templates/assets/images/undraw_docusaurus_tree.svg +1 -0
  146. package/lib/templates/assets/images/vite-plugin-config.png +0 -0
  147. package/lib/templates/content/docs/Generators/Project/generate-fullproject.md +12 -0
  148. package/lib/templates/content/docs/LLM/Logger.llm.md +194 -0
  149. package/lib/templates/content/docs/LLM/backend-proxies-services-llm.md +2687 -0
  150. package/lib/templates/content/docs/LLM/backend-service-llm.md +3384 -0
  151. package/lib/templates/content/docs/LLM/db_migration_llm.md +954 -0
  152. package/lib/templates/content/docs/LLM/frontend/REMIX-15.3-upgrade-llm.md +1245 -0
  153. package/lib/templates/content/docs/LLM/inngest/INNGEST_FUNCTION_DEVELOPMENT_GUIDE_LLM.md +1241 -0
  154. package/lib/templates/content/docs/LLM/inngest/INNGEST_NAMESPACE_LLM.md +384 -0
  155. package/lib/templates/content/docs/LLM/llm_workflow_namespace.md +384 -0
  156. package/lib/templates/content/docs/LLM/organization-components-form-llm.md +1395 -0
  157. package/lib/templates/content/docs/LLM/page-component-llm.md +173 -0
  158. package/lib/templates/content/docs/LLM/preferences-settings-llm.md +2781 -0
  159. package/lib/templates/content/docs/LLM/tailwind-css-llm.md +502 -0
  160. package/lib/templates/content/docs/UI/SchemaBasedUI.md +334 -0
  161. package/lib/templates/content/docs/UI/SlotFillComponent.md +334 -0
  162. package/lib/templates/content/docs/adminide-modules/account/auth0-login.md +31 -0
  163. package/lib/templates/content/docs/adminide-modules/account/index.md +14 -0
  164. package/lib/templates/content/docs/adminide-modules/account/keycloak-remix-setup.md +86 -0
  165. package/lib/templates/content/docs/adminide-modules/account/remix-auth-setup.md +79 -0
  166. package/lib/templates/content/docs/adminide-modules/account/various-auth-qatest.md +157 -0
  167. package/lib/templates/content/docs/adminide-modules/api-builders/graphql.md +906 -0
  168. package/lib/templates/content/docs/adminide-modules/billing/payments/index.md +14 -0
  169. package/lib/templates/content/docs/adminide-modules/billing/payments/stripe/index.md +14 -0
  170. package/lib/templates/content/docs/adminide-modules/billing/payments/stripe/settingup-stripe-locally.md +25 -0
  171. package/lib/templates/content/docs/adminide-modules/billing/tier-config.md +293 -0
  172. package/lib/templates/content/docs/adminide-modules/connectors/Connector.md +207 -0
  173. package/lib/templates/content/docs/adminide-modules/file-upload/index.md +16 -0
  174. package/lib/templates/content/docs/adminide-modules/file-upload/setup.md +435 -0
  175. package/lib/templates/content/docs/adminide-modules/file-upload/upload-file-using-signed-url.md +161 -0
  176. package/lib/templates/content/docs/adminide-modules/preferences/AddAdditionalPermissions.md +151 -0
  177. package/lib/templates/content/docs/adminide-modules/preferences/Configuration.md +241 -0
  178. package/lib/templates/content/docs/adminide-modules/preferences/Policy-Configuration.md +61 -0
  179. package/lib/templates/content/docs/adminide-modules/preferences/UI-components/ResourceSettingsLoader.md +319 -0
  180. package/lib/templates/content/docs/adminide-modules/preferences/contribute_scope_target.md +280 -0
  181. package/lib/templates/content/docs/adminide-modules/preferences/generate-urii.md +94 -0
  182. package/lib/templates/content/docs/adminide-modules/preferences/index.md +28 -0
  183. package/lib/templates/content/docs/adminide-modules/preferences/machine-configuration.md +157 -0
  184. package/lib/templates/content/docs/adminide-modules/preferences/pageSettings/generateCdecodeUri.md +1289 -0
  185. package/lib/templates/content/docs/adminide-modules/preferences/pageSettings/migratingFromUseSettings.md +215 -0
  186. package/lib/templates/content/docs/adminide-modules/preferences/permissions/Roles-Permissions.md +72 -0
  187. package/lib/templates/content/docs/adminide-modules/preferences/permissions/settingUserPermissions.md +139 -0
  188. package/lib/templates/content/docs/adminide-modules/preferences/preference-dependency.md +138 -0
  189. package/lib/templates/content/docs/adminide-modules/preferences/route-based-configuration.md +41 -0
  190. package/lib/templates/content/docs/adminide-modules/preferences/schema-configuration.md +71 -0
  191. package/lib/templates/content/docs/adminide-modules/preferences/supported.md +24 -0
  192. package/lib/templates/content/docs/adminide-modules/preferences/useSettingsLoader.md +248 -0
  193. package/lib/templates/content/docs/adminide-modules/project-tools/auth-providers.md +1317 -0
  194. package/lib/templates/content/docs/adminide-modules/project-tools/keycloak-guide.md +543 -0
  195. package/lib/templates/content/docs/adminide-modules/project-tools/tenant-management/tenant-based-authentication.md +846 -0
  196. package/lib/templates/content/docs/adminide-modules/project-tools/tenant-management/tenant-management.md +708 -0
  197. package/lib/templates/content/docs/adminide-modules/project-tools/tenant-management/tenants.md +1117 -0
  198. package/lib/templates/content/docs/chrome-extension/index.md +14 -0
  199. package/lib/templates/content/docs/chrome-extension/setup.md +30 -0
  200. package/lib/templates/content/docs/contributing/adding-package.md +23 -0
  201. package/lib/templates/content/docs/contributing/adding_new_modules.md +99 -0
  202. package/lib/templates/content/docs/contributing/architecture-updates.md +19 -0
  203. package/lib/templates/content/docs/contributing/avoid-using-promises-ui.md +116 -0
  204. package/lib/templates/content/docs/contributing/coding-guidelines.md +111 -0
  205. package/lib/templates/content/docs/contributing/do-and-dont.md +42 -0
  206. package/lib/templates/content/docs/contributing/faq.md +22 -0
  207. package/lib/templates/content/docs/contributing/folder-setup/browser.md +12 -0
  208. package/lib/templates/content/docs/contributing/folder-setup/config.md +12 -0
  209. package/lib/templates/content/docs/contributing/folder-setup/containers-server.md +12 -0
  210. package/lib/templates/content/docs/contributing/folder-setup/core.md +12 -0
  211. package/lib/templates/content/docs/contributing/folder-setup/graphql.md +12 -0
  212. package/lib/templates/content/docs/contributing/folder-setup/index.md +30 -0
  213. package/lib/templates/content/docs/contributing/folder-setup/module.md +12 -0
  214. package/lib/templates/content/docs/contributing/folder-setup/server.md +12 -0
  215. package/lib/templates/content/docs/contributing/folder-setup/services.md +12 -0
  216. package/lib/templates/content/docs/contributing/folder-setup/store.md +12 -0
  217. package/lib/templates/content/docs/contributing/frontend-coding.md +30 -0
  218. package/lib/templates/content/docs/contributing/git-subtree-sharing.md +73 -0
  219. package/lib/templates/content/docs/contributing/graphql-subscriptions.md +69 -0
  220. package/lib/templates/content/docs/contributing/how-to-contribute.md +30 -0
  221. package/lib/templates/content/docs/contributing/how_to_check_pure_esm.md +29 -0
  222. package/lib/templates/content/docs/contributing/index.md +60 -0
  223. package/lib/templates/content/docs/contributing/installation-issues.md +23 -0
  224. package/lib/templates/content/docs/contributing/keyboard-shortcut.md +131 -0
  225. package/lib/templates/content/docs/contributing/language/locale-support.md +12 -0
  226. package/lib/templates/content/docs/contributing/lerna-build-tools.md +516 -0
  227. package/lib/templates/content/docs/contributing/lerna-yarn-workspaces.md +95 -0
  228. package/lib/templates/content/docs/contributing/lint-and-formatter.md +20 -0
  229. package/lib/templates/content/docs/contributing/mobile-setup.md +16 -0
  230. package/lib/templates/content/docs/contributing/project-setup.md +233 -0
  231. package/lib/templates/content/docs/contributing/react/index.md +14 -0
  232. package/lib/templates/content/docs/contributing/react/lazy-component.md +70 -0
  233. package/lib/templates/content/docs/contributing/run-various-options.md +124 -0
  234. package/lib/templates/content/docs/contributing/schema-first-graphql-types.md +37 -0
  235. package/lib/templates/content/docs/contributing/source-code-organization.md +57 -0
  236. package/lib/templates/content/docs/contributing/staging-docker.md +88 -0
  237. package/lib/templates/content/docs/contributing/third-party/apollo-client-v3-tutorials.md +28 -0
  238. package/lib/templates/content/docs/contributing/third-party/index.md +18 -0
  239. package/lib/templates/content/docs/contributing/typescript-contribution.md +16 -0
  240. package/lib/templates/content/docs/devops/app-deploy-new-version.md +30 -0
  241. package/lib/templates/content/docs/devops/index.md +14 -0
  242. package/lib/templates/content/docs/devops/mobile-jenkins-build.md +40 -0
  243. package/lib/templates/content/docs/devops/versioning-the-project.md +128 -0
  244. package/lib/templates/content/docs/error-handler/application-error-handler.md +40 -0
  245. package/lib/templates/content/docs/error-handler/error-handling.md +26 -0
  246. package/lib/templates/content/docs/error-handler/index.md +16 -0
  247. package/lib/templates/content/docs/error-handler/logging-errors.md +14 -0
  248. package/lib/templates/content/docs/feature-api/copy-operation.md +427 -0
  249. package/lib/templates/content/docs/feature-api/feature-browser/assets.md +46 -0
  250. package/lib/templates/content/docs/feature-api/feature-browser/auth-permissions.md +12 -0
  251. package/lib/templates/content/docs/feature-api/feature-browser/feature.md +131 -0
  252. package/lib/templates/content/docs/feature-api/feature-browser/index.md +22 -0
  253. package/lib/templates/content/docs/feature-api/feature-browser/routes-menu.md +110 -0
  254. package/lib/templates/content/docs/feature-api/feature-browser/routing-convention.md +124 -0
  255. package/lib/templates/content/docs/feature-api/feature-browser/routing.md +338 -0
  256. package/lib/templates/content/docs/feature-api/feature-mobile/auth-permissions.md +20 -0
  257. package/lib/templates/content/docs/feature-api/feature-mobile/feature.md +130 -0
  258. package/lib/templates/content/docs/feature-api/feature-mobile/index.md +18 -0
  259. package/lib/templates/content/docs/feature-api/feature-mobile/navigation.md +187 -0
  260. package/lib/templates/content/docs/feature-api/feature-server/Scheduling.md +44 -0
  261. package/lib/templates/content/docs/feature-api/feature-server/dataloader.md +320 -0
  262. package/lib/templates/content/docs/feature-api/feature-server/dependency-injection.md +81 -0
  263. package/lib/templates/content/docs/feature-api/feature-server/feature.md +65 -0
  264. package/lib/templates/content/docs/feature-api/feature-server/generic-dataloader.md +135 -0
  265. package/lib/templates/content/docs/feature-api/feature-server/index.md +40 -0
  266. package/lib/templates/content/docs/feature-api/feature-server/migration.md +127 -0
  267. package/lib/templates/content/docs/feature-api/feature-server/mongo-model.md +72 -0
  268. package/lib/templates/content/docs/feature-api/feature-server/permissions.md +12 -0
  269. package/lib/templates/content/docs/feature-api/feature-server/policies.md +57 -0
  270. package/lib/templates/content/docs/feature-api/feature-server/preferences.md +57 -0
  271. package/lib/templates/content/docs/feature-api/feature-server/repositories.md +114 -0
  272. package/lib/templates/content/docs/feature-api/feature-server/resolvers.md +126 -0
  273. package/lib/templates/content/docs/feature-api/feature-server/rules.md +132 -0
  274. package/lib/templates/content/docs/feature-api/feature-server/schema.md +12 -0
  275. package/lib/templates/content/docs/feature-api/feature-server/services.md +102 -0
  276. package/lib/templates/content/docs/feature-api/feature-server/setup-resource-crud.md +359 -0
  277. package/lib/templates/content/docs/feature-api/index.md +18 -0
  278. package/lib/templates/content/docs/graphql/apolloClient-mutation.md +94 -0
  279. package/lib/templates/content/docs/graphql/index.md +14 -0
  280. package/lib/templates/content/docs/graphql/scalars.md +15 -0
  281. package/lib/templates/content/docs/help/index.md +14 -0
  282. package/lib/templates/content/docs/help/intro.md +16 -0
  283. package/lib/templates/content/docs/intl/ant-design-menu-translation.md +74 -0
  284. package/lib/templates/content/docs/intl/intl-namespace.md +129 -0
  285. package/lib/templates/content/docs/intl/vite-plugin-intl.md +87 -0
  286. package/lib/templates/content/docs/intl/webpack-plugin-intl.md +12 -0
  287. package/lib/templates/content/docs/intro.md +18 -0
  288. package/lib/templates/content/docs/knowledge/basic-fullstack.md +238 -0
  289. package/lib/templates/content/docs/mailing/index.md +14 -0
  290. package/lib/templates/content/docs/mailing/mailing-template.md +148 -0
  291. package/lib/templates/content/docs/mobile/App-navigation-generator.md +410 -0
  292. package/lib/templates/content/docs/mobile/MobileTestCases.md +264 -0
  293. package/lib/templates/content/docs/mobile/eas-profile-build.md +107 -0
  294. package/lib/templates/content/docs/mobile/expo-push-notification-setup.md +216 -0
  295. package/lib/templates/content/docs/mobile/index.md +14 -0
  296. package/lib/templates/content/docs/mobile/routes.md +83 -0
  297. package/lib/templates/content/docs/organization/adding-account-context.md +116 -0
  298. package/lib/templates/content/docs/organization/adding-org-mobile-navigation.md +22 -0
  299. package/lib/templates/content/docs/organization/adding-org-web-navigation.md +12 -0
  300. package/lib/templates/content/docs/organization/index.md +20 -0
  301. package/lib/templates/content/docs/organization/initialization.md +20 -0
  302. package/lib/templates/content/docs/organization/organization-resource-vs-resource.md +112 -0
  303. package/lib/templates/content/docs/remix/configuration/component-structure-best-practices.md +152 -0
  304. package/lib/templates/content/docs/remix/configuration/configurations.md +218 -0
  305. package/lib/templates/content/docs/remix/configuration/css-import-and-stylesheets.md +142 -0
  306. package/lib/templates/content/docs/remix/configuration/dont-subcomponent-network.md +166 -0
  307. package/lib/templates/content/docs/remix/configuration/generated-data-loaders.md +122 -0
  308. package/lib/templates/content/docs/remix/configuration/generated-resource-loaders.md +257 -0
  309. package/lib/templates/content/docs/remix/configuration/query-params-generator.md +216 -0
  310. package/lib/templates/content/docs/remix/configuration/routes-extra-icons.md +103 -0
  311. package/lib/templates/content/docs/remix/configuration/routes-json-advanced.md +86 -0
  312. package/lib/templates/content/docs/remix/configuration/routes-json-auth.md +113 -0
  313. package/lib/templates/content/docs/remix/configuration/routes-json-best-practices.md +55 -0
  314. package/lib/templates/content/docs/remix/configuration/routes-json-fields.md +79 -0
  315. package/lib/templates/content/docs/remix/configuration/routes-json-graphql.md +79 -0
  316. package/lib/templates/content/docs/remix/configuration/routes-json-index.md +112 -0
  317. package/lib/templates/content/docs/remix/configuration/routes-json-loaders.md +165 -0
  318. package/lib/templates/content/docs/remix/configuration/routes-json-middleware.md +196 -0
  319. package/lib/templates/content/docs/remix/configuration/routes-json-overview.md +53 -0
  320. package/lib/templates/content/docs/remix/data-loaders.md +43 -0
  321. package/lib/templates/content/docs/remix/devtools/remix-devtools.md +58 -0
  322. package/lib/templates/content/docs/remix/examples/changes-using-servercode.md +79 -0
  323. package/lib/templates/content/docs/remix/extra-icons.md +62 -0
  324. package/lib/templates/content/docs/remix/extra-links.md +65 -0
  325. package/lib/templates/content/docs/remix/generated-data-loaders.md +114 -0
  326. package/lib/templates/content/docs/remix/queryParamsGenerator.md +89 -0
  327. package/lib/templates/content/docs/remix/resources.md +16 -0
  328. package/lib/templates/content/docs/remix/styles.md +132 -0
  329. package/lib/templates/content/docs/remix/wiki.md +12 -0
  330. package/lib/templates/content/docs/security/auth-wrapper/auth-wrapper.md +24 -0
  331. package/lib/templates/content/docs/security/index.md +18 -0
  332. package/lib/templates/content/docs/security/secure-button-mobilenative.md +88 -0
  333. package/lib/templates/content/docs/security/secure-button-web.md +89 -0
  334. package/lib/templates/content/docs/server-side/account-customization.md +82 -0
  335. package/lib/templates/content/docs/server-side/apollo/caching.md +164 -0
  336. package/lib/templates/content/docs/server-side/backend-architecture/FINAL-DECISION.md +209 -0
  337. package/lib/templates/content/docs/server-side/backend-architecture/TRUE-FINAL-ARCHITECTURE.md +603 -0
  338. package/lib/templates/content/docs/server-side/backend-architecture/index1.md +0 -0
  339. package/lib/templates/content/docs/server-side/backend-coding.md +839 -0
  340. package/lib/templates/content/docs/server-side/e2b/manageing-template.md +197 -0
  341. package/lib/templates/content/docs/server-side/index.md +14 -0
  342. package/lib/templates/content/docs/server-side/inngest-functions-module.md +309 -0
  343. package/lib/templates/content/docs/server-side/listen-stripe-events.md +43 -0
  344. package/lib/templates/content/docs/server-side/slug-service.md +323 -0
  345. package/lib/templates/content/docs/tests/index.md +18 -0
  346. package/lib/templates/content/docs/tests/jest-test-debug-vscode.md +40 -0
  347. package/lib/templates/content/docs/tests/known-errors.md +116 -0
  348. package/lib/templates/content/docs/tests/service-test-template.md +118 -0
  349. package/lib/templates/content/docs/tests/test-setup.md +93 -0
  350. package/lib/templates/content/docs/xstate.md +23 -0
  351. package/lib/types.d.ts +37 -0
  352. package/lib/types.d.ts.map +1 -0
  353. package/lib/utils/docsNavigation.d.ts +9 -0
  354. package/lib/utils/docsNavigation.d.ts.map +1 -0
  355. package/lib/utils/docsNavigation.js +37 -0
  356. package/lib/utils/docsNavigation.js.map +1 -0
  357. package/lib/utils/helpCenterUtils.d.ts +26 -0
  358. package/lib/utils/helpCenterUtils.d.ts.map +1 -0
  359. package/lib/utils/index.d.ts +3 -0
  360. package/lib/utils/index.d.ts.map +1 -0
  361. package/lib/utils/index.js +3 -0
  362. package/lib/utils/index.js.map +1 -0
  363. package/lib/utils/markdownLoader.d.ts +36 -0
  364. package/lib/utils/markdownLoader.d.ts.map +1 -0
  365. package/lib/utils/markdownLoader.js +2242 -0
  366. package/lib/utils/markdownLoader.js.map +1 -0
  367. package/package.json +71 -0
@@ -0,0 +1,846 @@
1
+ @@ -0,0 +1,860 @@
2
+
3
+ # Tenant Authentication System - Technical Guide
4
+
5
+ ## Overview
6
+
7
+ This document provides a **technical deep-dive** into how tenant authentication works in the system, specifically focusing on the `cachedTenantInfoMiddleware`.
8
+
9
+ **Location:** `packages-modules/user-auth0/server/src/modules/user-tenants/middleware/cached-tenant-info.middleware.ts`
10
+
11
+ ---
12
+
13
+ ## Table of Contents
14
+
15
+ 1. [Authentication Flow](#authentication-flow)
16
+ 2. [Middleware Implementation](#middleware-implementation)
17
+ 3. [Keycloak Validation](#keycloak-validation)
18
+ 4. [Request Headers](#request-headers)
19
+ 5. [Error Handling](#error-handling)
20
+ 6. [Security Considerations](#security-considerations)
21
+ 7. [Integration Guide](#integration-guide)
22
+ 8. [Testing & Debugging](#testing--debugging)
23
+
24
+ ---
25
+
26
+ ## Authentication Flow
27
+
28
+ ### High-Level Flow Diagram
29
+
30
+ ```
31
+ ┌─────────────────────────────────────────────────────────────────────┐
32
+ │ CLIENT APPLICATION │
33
+ └───────────────────────────────┬─────────────────────────────────────┘
34
+
35
+ │ 1. HTTP Request with Headers:
36
+ │ x-tenant-id: {tenantId}
37
+ │ x-tenant-key: base64(clientId:secret)
38
+
39
+
40
+ ┌─────────────────────────────────────────────────────────────────────┐
41
+ │ CACHED TENANT INFO MIDDLEWARE │
42
+ │ │
43
+ │ ┌──────────────────────────────────────────────────────────────┐ │
44
+ │ │ STEP 1: Extract Headers │ │
45
+ │ │ - Read x-tenant-id │ │
46
+ │ │ - Read x-tenant-key │ │
47
+ │ └──────────────────────────────────────────────────────────────┘ │
48
+ │ │ │
49
+ │ ▼ │
50
+ │ ┌──────────────────────────────────────────────────────────────┐ │
51
+ │ │ STEP 2: Lookup Tenant in MongoDB │ │
52
+ │ │ - Connect to database │ │
53
+ │ │ - Find tenant by MongoDB _id (tenantId from header) │ │
54
+ │ │ - Retrieve tenant data with clientConfigurations │ │
55
+ │ └──────────────────────────────────────────────────────────────┘ │
56
+ │ │ │
57
+ │ ▼ │
58
+ │ ┌──────────────────────────────────────────────────────────────┐ │
59
+ │ │ STEP 3: Decode Credentials │ │
60
+ │ │ - Base64 decode x-tenant-key │ │
61
+ │ │ - Split into clientId:clientSecret │ │
62
+ │ │ - Validate format │ │
63
+ │ └──────────────────────────────────────────────────────────────┘ │
64
+ │ │ │
65
+ │ ▼ │
66
+ │ ┌──────────────────────────────────────────────────────────────┐ │
67
+ │ │ STEP 4: Match Client Configuration │ │
68
+ │ │ - Find clientConfig with matching clientId │ │
69
+ │ │ - Ensure client belongs to this tenant │ │
70
+ │ └──────────────────────────────────────────────────────────────┘ │
71
+ │ │ │
72
+ │ ▼ │
73
+ │ ┌──────────────────────────────────────────────────────────────┐ │
74
+ │ │ STEP 5: Validate with Keycloak │ │
75
+ │ │ - Call keycloakAdminService.verifyClientCredentials() │ │
76
+ │ │ - Verify clientId and clientSecret are valid │ │
77
+ │ │ - Check client is enabled │ │
78
+ │ └──────────────────────────────────────────────────────────────┘ │
79
+ │ │ │
80
+ └────────────────────────────────┼────────────────────────────────────┘
81
+
82
+ ┌────────────┴─────────────┐
83
+ │ │
84
+ ▼ ▼
85
+ ┌──────────────────┐ ┌──────────────────────┐
86
+ │ ✅ SUCCESS │ │ ❌ FAILURE │
87
+ │ │ │ │
88
+ │ - Set req.tenant │ │ - Return 401 Error │
89
+ │ - Call next() │ │ - Block request │
90
+ └──────────────────┘ └──────────────────────┘
91
+
92
+
93
+ ┌──────────────────────┐
94
+ │ APPLICATION │
95
+ │ ROUTE HANDLER │
96
+ │ │
97
+ │ - Access req.tenant │
98
+ │ - Process request │
99
+ └──────────────────────┘
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Middleware Implementation
105
+
106
+ ### Full Source Code with Annotations
107
+
108
+ ```typescript
109
+ /* packages-modules/user-auth0/server/src/modules/user-tenants/middleware/cached-tenant-info.middleware.ts */
110
+
111
+ import type { CustomRequest } from '@adminide-stack/auth0-server-core';
112
+ import { NextFunction, Response } from 'express';
113
+ import { IClientConfigurations, ITenantModel } from 'common/server';
114
+ import { keycloakAdminService } from '@adminide-stack/auth0-server-core';
115
+ import { model, connect } from 'mongoose';
116
+ import { config } from '../../../config';
117
+ import { TenantSchema } from '../store';
118
+
119
+ /**
120
+ * Validate tenant credentials against Keycloak
121
+ *
122
+ * @param encodedCredentials - Base64 encoded "clientId:secret"
123
+ * @param tenantData - Tenant document from MongoDB
124
+ * @returns true if valid, false otherwise
125
+ */
126
+ const validateTenantWithKeycloak = async (encodedCredentials: string, tenantData: ITenantModel): Promise<boolean> => {
127
+ try {
128
+ // 1. Decode Base64 credentials
129
+ const decodedCredentials = Buffer.from(encodedCredentials, 'base64').toString('utf-8');
130
+
131
+ // 2. Split into clientId and clientSecret
132
+ const [clientId, clientSecret] = decodedCredentials.split(':');
133
+
134
+ // 3. Validate credential format
135
+ if (!clientId || !clientSecret) {
136
+ return false; // Invalid format
137
+ }
138
+
139
+ // 4. Ensure tenant exists
140
+ if (!tenantData) {
141
+ return false;
142
+ }
143
+
144
+ // 5. Find matching client configuration in tenant
145
+ const clientConfig = (tenantData.clientConfigurations as unknown as IClientConfigurations[])?.find(
146
+ (config) => config.clientId === clientId,
147
+ );
148
+
149
+ if (!clientConfig) {
150
+ return false; // Client doesn't belong to this tenant
151
+ }
152
+
153
+ // 6. Verify credentials with Keycloak
154
+ const isValid = await keycloakAdminService.verifyClientCredentials(clientId, clientSecret);
155
+
156
+ return isValid.valid;
157
+ } catch (error) {
158
+ console.error('Error validating tenant with Keycloak:', error);
159
+ return false;
160
+ }
161
+ };
162
+
163
+ /**
164
+ * Middleware: Authenticate tenant using header-based credentials
165
+ *
166
+ * Workflow:
167
+ * 1. Extract x-tenant-id and x-tenant-key headers
168
+ * 2. Lookup tenant in MongoDB
169
+ * 3. Validate credentials with Keycloak
170
+ * 4. Attach tenant to req.tenant if valid
171
+ * 5. Call next() or return 401
172
+ */
173
+ export const cachedTenantInfoMiddleware = async (req: CustomRequest, res: Response, next: NextFunction) => {
174
+ try {
175
+ // ============================================
176
+ // STEP 1: Extract Request Headers
177
+ // ============================================
178
+ const tenantId = req.headers['x-tenant-id'] as string;
179
+ const tenantKey = req.headers['x-tenant-key'] as string;
180
+
181
+ // Only process if both headers are present
182
+ if (tenantId && tenantKey) {
183
+ // ============================================
184
+ // STEP 2: Connect to MongoDB
185
+ // ============================================
186
+ const mongoUrl = config.MONGO_URL;
187
+ await connect(mongoUrl);
188
+
189
+ // ============================================
190
+ // STEP 3: Lookup Tenant by ID
191
+ // ============================================
192
+ const TenantModel = model('UserTenant', TenantSchema);
193
+ const tenantData = (await TenantModel.findById(tenantId).exec()) as ITenantModel;
194
+
195
+ // ============================================
196
+ // STEP 4: Validate with Keycloak
197
+ // ============================================
198
+ const isValid = await validateTenantWithKeycloak(tenantKey, tenantData);
199
+
200
+ // ============================================
201
+ // STEP 5: Handle Validation Result
202
+ // ============================================
203
+ if (!isValid) {
204
+ // ❌ Invalid credentials
205
+ return res.status(401).json({
206
+ error: 'Unauthorized',
207
+ message: 'Invalid tenant credentials',
208
+ });
209
+ }
210
+
211
+ // ✅ Valid credentials - attach tenant to request
212
+ if (tenantData) {
213
+ req.tenant = tenantData as ITenantModel;
214
+ }
215
+
216
+ return next(); // Continue to route handler
217
+ }
218
+
219
+ // ============================================
220
+ // STEP 6: No Headers - Continue Without Auth
221
+ // ============================================
222
+ // If no tenant headers, skip validation
223
+ // (Other auth methods may handle this)
224
+ return next();
225
+ } catch (error) {
226
+ // ============================================
227
+ // STEP 7: Handle Errors
228
+ // ============================================
229
+ console.error('Error in cached tenant middleware:', error);
230
+ return res.status(500).json({
231
+ error: 'Internal Server Error',
232
+ message: 'Failed to process tenant information',
233
+ });
234
+ }
235
+ };
236
+ ```
237
+
238
+ ---
239
+
240
+ ## Keycloak Validation
241
+
242
+ ### `keycloakAdminService.verifyClientCredentials()`
243
+
244
+ This method validates client credentials against Keycloak.
245
+
246
+ **Implementation (Conceptual):**
247
+
248
+ ```typescript
249
+ class KeycloakAdminService {
250
+ async verifyClientCredentials(clientId: string, clientSecret: string): Promise<{ valid: boolean }> {
251
+ try {
252
+ // 1. Get Keycloak admin client
253
+ const adminClient = await this.getAdminClient();
254
+
255
+ // 2. Find client by clientId
256
+ const client = await adminClient.clients.find({
257
+ clientId: clientId,
258
+ realm: this.config.KEYCLOAK_REALM,
259
+ });
260
+
261
+ if (!client || client.length === 0) {
262
+ return { valid: false };
263
+ }
264
+
265
+ // 3. Get client secret from Keycloak
266
+ const secretData = await adminClient.clients.getClientSecret({
267
+ id: client[0].id,
268
+ realm: this.config.KEYCLOAK_REALM,
269
+ });
270
+
271
+ // 4. Compare secrets
272
+ const isValid = secretData.value === clientSecret;
273
+
274
+ // 5. Check if client is enabled
275
+ const isEnabled = client[0].enabled === true;
276
+
277
+ return { valid: isValid && isEnabled };
278
+ } catch (error) {
279
+ console.error('Keycloak validation error:', error);
280
+ return { valid: false };
281
+ }
282
+ }
283
+ }
284
+ ```
285
+
286
+ **What it checks:**
287
+
288
+ - ✅ Client exists in Keycloak
289
+ - ✅ ClientId matches
290
+ - ✅ ClientSecret matches
291
+ - ✅ Client is enabled (not disabled)
292
+ - ✅ Client is in correct realm
293
+
294
+ ---
295
+
296
+ ## Request Headers
297
+
298
+ ### Required Headers
299
+
300
+ | Header Name | Type | Format | Description |
301
+ | -------------- | ------ | ------------------------------- | ---------------------------------------------------- |
302
+ | `x-tenant-id` | String | MongoDB ObjectId (24 hex chars) | Identifies the tenant (MongoDB `_id`) |
303
+ | `x-tenant-key` | String | Base64 encoded | Encoded credentials: `base64(clientId:clientSecret)` |
304
+
305
+ ### Header Format Examples
306
+
307
+ **Valid `x-tenant-id`:**
308
+
309
+ ```
310
+ 64f3a1b2c9d8e7f6a5b4c3d2
311
+ ```
312
+
313
+ **Valid `x-tenant-key`:**
314
+
315
+ ```bash
316
+ # Original credentials
317
+ clientId: "abc123-def456-ghi789"
318
+ clientSecret: "mySecret123ABC"
319
+
320
+ # Encoded format
321
+ echo -n "abc123-def456-ghi789:mySecret123ABC" | base64
322
+ # Output: YWJjMTIzLWRlZjQ1Ni1naGk3ODk6bXlTZWNyZXQxMjNBQkM=
323
+
324
+ # Header value
325
+ x-tenant-key: YWJjMTIzLWRlZjQ1Ni1naGk3ODk6bXlTZWNyZXQxMjNBQkM=
326
+ ```
327
+
328
+ ### Complete HTTP Request Example
329
+
330
+ ```http
331
+ POST /graphql HTTP/1.1
332
+ Host: api.yourapp.com
333
+ Content-Type: application/json
334
+ x-tenant-id: 64f3a1b2c9d8e7f6a5b4c3d2
335
+ x-tenant-key: YWJjMTIzLWRlZjQ1Ni1naGk3ODk6bXlTZWNyZXQxMjNBQkM=
336
+
337
+ {
338
+ "query": "query { myData { id name } }"
339
+ }
340
+ ```
341
+
342
+ ---
343
+
344
+ ## Error Handling
345
+
346
+ ### Error Scenarios and Responses
347
+
348
+ #### 1. Missing Headers
349
+
350
+ **Condition:** No `x-tenant-id` or `x-tenant-key` provided
351
+
352
+ **Behavior:** Middleware passes through (no error)
353
+
354
+ **Reason:** Other authentication methods may handle the request
355
+
356
+ **Code Path:**
357
+
358
+ ```typescript
359
+ if (tenantId && tenantKey) {
360
+ // Validation logic
361
+ } else {
362
+ return next(); // ← Pass through
363
+ }
364
+ ```
365
+
366
+ #### 2. Invalid Tenant ID
367
+
368
+ **Condition:** Tenant not found in MongoDB
369
+
370
+ **Response:**
371
+
372
+ ```json
373
+ {
374
+ "error": "Unauthorized",
375
+ "message": "Invalid tenant credentials"
376
+ }
377
+ ```
378
+
379
+ **HTTP Status:** `401 Unauthorized`
380
+
381
+ **Reason:** `tenantData` is null/undefined
382
+
383
+ #### 3. Malformed Credentials
384
+
385
+ **Condition:** `x-tenant-key` not in `clientId:secret` format
386
+
387
+ **Response:**
388
+
389
+ ```json
390
+ {
391
+ "error": "Unauthorized",
392
+ "message": "Invalid tenant credentials"
393
+ }
394
+ ```
395
+
396
+ **HTTP Status:** `401 Unauthorized`
397
+
398
+ **Triggered by:**
399
+
400
+ ```typescript
401
+ const [clientId, clientSecret] = decodedCredentials.split(':');
402
+ if (!clientId || !clientSecret) {
403
+ return false; // ← Validation fails
404
+ }
405
+ ```
406
+
407
+ #### 4. Client Not in Tenant
408
+
409
+ **Condition:** `clientId` doesn't match any client in tenant's `clientConfigurations`
410
+
411
+ **Response:**
412
+
413
+ ```json
414
+ {
415
+ "error": "Unauthorized",
416
+ "message": "Invalid tenant credentials"
417
+ }
418
+ ```
419
+
420
+ **HTTP Status:** `401 Unauthorized`
421
+
422
+ **Reason:** Prevents using valid Keycloak client for wrong tenant
423
+
424
+ #### 5. Invalid Keycloak Credentials
425
+
426
+ **Condition:** Client exists but secret is wrong
427
+
428
+ **Response:**
429
+
430
+ ```json
431
+ {
432
+ "error": "Unauthorized",
433
+ "message": "Invalid tenant credentials"
434
+ }
435
+ ```
436
+
437
+ **HTTP Status:** `401 Unauthorized`
438
+
439
+ **Keycloak Check:**
440
+
441
+ ```typescript
442
+ const isValid = await keycloakAdminService.verifyClientCredentials(clientId, clientSecret);
443
+
444
+ if (!isValid.valid) {
445
+ return false; // ← Validation fails
446
+ }
447
+ ```
448
+
449
+ #### 6. System Error
450
+
451
+ **Condition:** Database connection fails, Keycloak unreachable, etc.
452
+
453
+ **Response:**
454
+
455
+ ```json
456
+ {
457
+ "error": "Internal Server Error",
458
+ "message": "Failed to process tenant information"
459
+ }
460
+ ```
461
+
462
+ **HTTP Status:** `500 Internal Server Error`
463
+
464
+ **Triggered by:** Exception in try/catch block
465
+
466
+ ---
467
+
468
+ ## Security Considerations
469
+
470
+ ### 🔒 Security Features
471
+
472
+ 1. **Multi-Layer Validation**
473
+ - Database lookup (tenant exists)
474
+ - Client ownership check (client belongs to tenant)
475
+ - Keycloak verification (credentials valid)
476
+
477
+ 2. **No Secret Storage**
478
+ - Secrets not stored in MongoDB
479
+ - Always validated against Keycloak (source of truth)
480
+ - Regenerated deterministically when needed
481
+
482
+ 3. **Base64 Encoding**
483
+ - Credentials encoded in transit
484
+ - Not encrypted (use HTTPS!)
485
+ - Prevents accidental logging
486
+
487
+ 4. **Tenant Isolation**
488
+ - Client must belong to tenant's `clientConfigurations`
489
+ - Prevents cross-tenant credential reuse
490
+
491
+ ### ⚠️ Security Best Practices
492
+
493
+ #### DO ✅
494
+
495
+ - Always use **HTTPS** in production
496
+ - Rotate client secrets regularly
497
+ - Log authentication failures for monitoring
498
+ - Rate-limit authentication attempts
499
+ - Use separate clients per service/environment
500
+
501
+ #### DON'T ❌
502
+
503
+ - Never expose credentials in frontend code
504
+ - Don't hardcode credentials in source code
505
+ - Don't share credentials between environments
506
+ - Don't disable HTTPS "for testing"
507
+ - Don't log decoded credentials
508
+
509
+ ### 🛡️ Attack Mitigation
510
+
511
+ | Attack Type | Mitigation |
512
+ | ----------------------- | ---------------------------------------- |
513
+ | **Credential Stuffing** | Rate limiting, Keycloak lockout policies |
514
+ | **MITM** | HTTPS enforcement |
515
+ | **Replay Attacks** | Short-lived tokens (if implemented) |
516
+ | **Cross-Tenant Access** | Client ownership validation |
517
+ | **SQL Injection** | Mongoose parameterization |
518
+ | **Brute Force** | Keycloak client lockout |
519
+
520
+ ---
521
+
522
+ ## Integration Guide
523
+
524
+ ### Adding Middleware to Express App
525
+
526
+ ```typescript
527
+ import express from 'express';
528
+ import { cachedTenantInfoMiddleware } from './middleware/cached-tenant-info.middleware';
529
+
530
+ const app = express();
531
+
532
+ // Apply globally to all routes
533
+ app.use(cachedTenantInfoMiddleware);
534
+
535
+ // Apply to specific routes
536
+ app.use('/api/protected', cachedTenantInfoMiddleware, (req, res) => {
537
+ // Access tenant data
538
+ if (req.tenant) {
539
+ res.json({
540
+ message: `Welcome, ${req.tenant.name}`,
541
+ tenantId: req.tenant.tenantId,
542
+ });
543
+ } else {
544
+ res.status(401).json({ error: 'No tenant authenticated' });
545
+ }
546
+ });
547
+
548
+ app.listen(3000);
549
+ ```
550
+
551
+ ### Accessing Tenant in Route Handlers
552
+
553
+ ```typescript
554
+ import { CustomRequest } from '@adminide-stack/auth0-server-core';
555
+ import { Response } from 'express';
556
+
557
+ app.get('/api/data', cachedTenantInfoMiddleware, async (req: CustomRequest, res: Response) => {
558
+ // Tenant is available in req.tenant
559
+ const tenant = req.tenant;
560
+
561
+ if (!tenant) {
562
+ return res.status(401).json({ error: 'Tenant required' });
563
+ }
564
+
565
+ // Use tenant data
566
+ console.log(`Request from tenant: ${tenant.name}`);
567
+ console.log(`Tenant ID: ${tenant.tenantId}`);
568
+ console.log(`Organization: ${tenant.organization}`);
569
+
570
+ // Query data scoped to tenant
571
+ const data = await DataModel.find({
572
+ tenantId: tenant.tenantId,
573
+ });
574
+
575
+ res.json({ data });
576
+ });
577
+ ```
578
+
579
+ ### GraphQL Context Integration
580
+
581
+ ```typescript
582
+ import { ApolloServer } from 'apollo-server-express';
583
+ import { CustomRequest } from '@adminide-stack/auth0-server-core';
584
+
585
+ const server = new ApolloServer({
586
+ typeDefs,
587
+ resolvers,
588
+ context: ({ req }: { req: CustomRequest }) => ({
589
+ // Tenant attached by middleware
590
+ tenant: req.tenant,
591
+ tenantId: req.tenant?.tenantId,
592
+ tenantName: req.tenant?.name,
593
+ }),
594
+ });
595
+
596
+ // In resolvers
597
+ const resolvers = {
598
+ Query: {
599
+ myData: async (_, __, { tenant }) => {
600
+ if (!tenant) {
601
+ throw new Error('Tenant authentication required');
602
+ }
603
+
604
+ return DataModel.find({
605
+ tenantId: tenant.tenantId,
606
+ });
607
+ },
608
+ },
609
+ };
610
+ ```
611
+
612
+ ---
613
+
614
+ ## Testing & Debugging
615
+
616
+ ### Manual Testing with cURL
617
+
618
+ ```bash
619
+ #!/bin/bash
620
+
621
+ # Configuration
622
+ TENANT_ID="64f3a1b2c9d8e7f6a5b4c3d2"
623
+ CLIENT_ID="your-keycloak-client-id"
624
+ CLIENT_SECRET="your-client-secret"
625
+
626
+ # Encode credentials
627
+ TENANT_KEY=$(echo -n "${CLIENT_ID}:${CLIENT_SECRET}" | base64)
628
+
629
+ # Make request
630
+ curl -X POST https://api.yourapp.com/graphql \
631
+ -H "Content-Type: application/json" \
632
+ -H "x-tenant-id: ${TENANT_ID}" \
633
+ -H "x-tenant-key: ${TENANT_KEY}" \
634
+ -d '{
635
+ "query": "query { tenant(id: \"'${TENANT_ID}'\") { id name } }"
636
+ }' \
637
+ -v # Verbose output for debugging
638
+ ```
639
+
640
+ ### Automated Tests
641
+
642
+ ```typescript
643
+ import request from 'supertest';
644
+ import app from '../app';
645
+
646
+ describe('Tenant Authentication Middleware', () => {
647
+ const validTenantId = '64f3a1b2c9d8e7f6a5b4c3d2';
648
+ const validClientId = 'test-client-id';
649
+ const validClientSecret = 'test-client-secret';
650
+
651
+ const encodeCredentials = (clientId: string, secret: string) => {
652
+ return Buffer.from(`${clientId}:${secret}`).toString('base64');
653
+ };
654
+
655
+ it('should authenticate valid tenant credentials', async () => {
656
+ const response = await request(app)
657
+ .post('/api/protected')
658
+ .set('x-tenant-id', validTenantId)
659
+ .set('x-tenant-key', encodeCredentials(validClientId, validClientSecret))
660
+ .expect(200);
661
+
662
+ expect(response.body.tenant).toBeDefined();
663
+ });
664
+
665
+ it('should reject invalid tenant ID', async () => {
666
+ await request(app)
667
+ .post('/api/protected')
668
+ .set('x-tenant-id', 'invalid-id')
669
+ .set('x-tenant-key', encodeCredentials(validClientId, validClientSecret))
670
+ .expect(401);
671
+ });
672
+
673
+ it('should reject invalid credentials', async () => {
674
+ await request(app)
675
+ .post('/api/protected')
676
+ .set('x-tenant-id', validTenantId)
677
+ .set('x-tenant-key', encodeCredentials(validClientId, 'wrong-secret'))
678
+ .expect(401);
679
+ });
680
+
681
+ it('should pass through when no headers provided', async () => {
682
+ await request(app).post('/api/public').expect(200); // Or whatever your public route returns
683
+ });
684
+ });
685
+ ```
686
+
687
+ ### Debugging Checklist
688
+
689
+ When authentication fails, check:
690
+
691
+ 1. **Headers Present?**
692
+
693
+ ```bash
694
+ # Check request headers
695
+ curl -v ... | grep "x-tenant"
696
+ ```
697
+
698
+ 2. **Valid Base64?**
699
+
700
+ ```bash
701
+ # Decode and inspect
702
+ echo "YWJjOmRlZg==" | base64 -d
703
+ # Should output: "abc:def"
704
+ ```
705
+
706
+ 3. **Tenant Exists?**
707
+
708
+ ```javascript
709
+ // Check MongoDB
710
+ db.UserTenant.findById('64f3a1b2c9d8e7f6a5b4c3d2');
711
+ ```
712
+
713
+ 4. **Client in Tenant?**
714
+
715
+ ```javascript
716
+ // Check clientConfigurations
717
+ db.UserTenant.findOne({ _id: '64f3a1b2c9d8e7f6a5b4c3d2' }, { clientConfigurations: 1 });
718
+ ```
719
+
720
+ 5. **Keycloak Client Valid?**
721
+
722
+ ```bash
723
+ # Check Keycloak admin console
724
+ # Realm > Clients > Search for clientId
725
+ ```
726
+
727
+ 6. **Keycloak Reachable?**
728
+ ```bash
729
+ curl https://keycloak.yourapp.com/health
730
+ ```
731
+
732
+ ### Logging for Debugging
733
+
734
+ Add detailed logging to middleware:
735
+
736
+ ```typescript
737
+ const validateTenantWithKeycloak = async (encodedCredentials: string, tenantData: ITenantModel): Promise<boolean> => {
738
+ try {
739
+ console.log('[AUTH] Starting validation');
740
+
741
+ const decodedCredentials = Buffer.from(encodedCredentials, 'base64').toString('utf-8');
742
+
743
+ console.log('[AUTH] Decoded credentials format:', decodedCredentials.includes(':') ? 'valid' : 'invalid');
744
+
745
+ const [clientId, clientSecret] = decodedCredentials.split(':');
746
+ console.log('[AUTH] ClientId:', clientId ? 'present' : 'missing');
747
+ console.log('[AUTH] ClientSecret:', clientSecret ? 'present' : 'missing');
748
+
749
+ if (!clientId || !clientSecret) {
750
+ console.log('[AUTH] Invalid credential format');
751
+ return false;
752
+ }
753
+
754
+ const clientConfig = (tenantData.clientConfigurations as unknown as IClientConfigurations[])?.find(
755
+ (config) => config.clientId === clientId,
756
+ );
757
+
758
+ console.log('[AUTH] Client found in tenant:', !!clientConfig);
759
+
760
+ if (!clientConfig) {
761
+ console.log('[AUTH] Client not in tenant configurations');
762
+ return false;
763
+ }
764
+
765
+ console.log('[AUTH] Verifying with Keycloak...');
766
+ const isValid = await keycloakAdminService.verifyClientCredentials(clientId, clientSecret);
767
+
768
+ console.log('[AUTH] Keycloak validation result:', isValid.valid);
769
+
770
+ return isValid.valid;
771
+ } catch (error) {
772
+ console.error('[AUTH] Validation error:', error);
773
+ return false;
774
+ }
775
+ };
776
+ ```
777
+
778
+ ---
779
+
780
+ ## Performance Considerations
781
+
782
+ ### Database Connection Pooling
783
+
784
+ **Current Implementation:**
785
+
786
+ ```typescript
787
+ await connect(mongoUrl); // Creates new connection each time
788
+ ```
789
+
790
+ **Recommended Optimization:**
791
+
792
+ ```typescript
793
+ // Create connection pool at startup
794
+ let connection: Connection;
795
+
796
+ export const getConnection = async () => {
797
+ if (!connection) {
798
+ connection = await connect(mongoUrl, {
799
+ maxPoolSize: 10,
800
+ minPoolSize: 2,
801
+ });
802
+ }
803
+ return connection;
804
+ };
805
+
806
+ // Use in middleware
807
+ const connection = await getConnection();
808
+ const TenantModel = connection.model('UserTenant', TenantSchema);
809
+ ```
810
+
811
+ ### Caching Tenant Data
812
+
813
+ Consider adding Redis cache:
814
+
815
+ ```typescript
816
+ import { Redis } from 'ioredis';
817
+
818
+ const redis = new Redis();
819
+
820
+ const getCachedTenant = async (tenantId: string): Promise<ITenantModel | null> => {
821
+ // Try cache first
822
+ const cached = await redis.get(`tenant:${tenantId}`);
823
+ if (cached) {
824
+ return JSON.parse(cached);
825
+ }
826
+
827
+ // Fallback to database
828
+ const tenant = await TenantModel.findById(tenantId);
829
+ if (tenant) {
830
+ // Cache for 5 minutes
831
+ await redis.setex(`tenant:${tenantId}`, 300, JSON.stringify(tenant));
832
+ }
833
+
834
+ return tenant;
835
+ };
836
+ ```
837
+
838
+ ---
839
+
840
+ ## Related Documentation
841
+
842
+ - [Complete Setup Guide](./TENANT_SETUP_GUIDE.md) - End-user documentation
843
+ - [Project & Vault Setup](./PROJECT_VAULT_SETUP.md) - Project configuration
844
+ - [Keycloak Documentation](https://www.keycloak.org/docs/latest/) - External reference
845
+
846
+ ---