@backstage/backend-defaults 0.5.3-next.1 → 0.5.3-next.3

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 (115) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/CreateBackend.cjs.js.map +1 -1
  3. package/dist/PackageDiscoveryService.cjs.js.map +1 -1
  4. package/dist/discoveryFeatureLoader.cjs.js.map +1 -1
  5. package/dist/entrypoints/auth/DefaultAuthService.cjs.js.map +1 -1
  6. package/dist/entrypoints/auth/JwksClient.cjs.js.map +1 -1
  7. package/dist/entrypoints/auth/authServiceFactory.cjs.js.map +1 -1
  8. package/dist/entrypoints/auth/external/ExternalTokenHandler.cjs.js.map +1 -1
  9. package/dist/entrypoints/auth/external/helpers.cjs.js.map +1 -1
  10. package/dist/entrypoints/auth/external/jwks.cjs.js.map +1 -1
  11. package/dist/entrypoints/auth/external/legacy.cjs.js.map +1 -1
  12. package/dist/entrypoints/auth/external/static.cjs.js.map +1 -1
  13. package/dist/entrypoints/auth/helpers.cjs.js.map +1 -1
  14. package/dist/entrypoints/auth/plugin/PluginTokenHandler.cjs.js +6 -1
  15. package/dist/entrypoints/auth/plugin/PluginTokenHandler.cjs.js.map +1 -1
  16. package/dist/entrypoints/auth/plugin/keys/DatabaseKeyStore.cjs.js.map +1 -1
  17. package/dist/entrypoints/auth/plugin/keys/DatabasePluginKeySource.cjs.js.map +1 -1
  18. package/dist/entrypoints/auth/plugin/keys/StaticConfigPluginKeySource.cjs.js.map +1 -1
  19. package/dist/entrypoints/auth/plugin/keys/createPluginKeySource.cjs.js.map +1 -1
  20. package/dist/entrypoints/auth/user/UserTokenHandler.cjs.js.map +1 -1
  21. package/dist/entrypoints/cache/CacheClient.cjs.js.map +1 -1
  22. package/dist/entrypoints/cache/CacheManager.cjs.js.map +1 -1
  23. package/dist/entrypoints/cache/cacheServiceFactory.cjs.js.map +1 -1
  24. package/dist/entrypoints/cache/types.cjs.js.map +1 -1
  25. package/dist/entrypoints/database/DatabaseManager.cjs.js +1 -3
  26. package/dist/entrypoints/database/DatabaseManager.cjs.js.map +1 -1
  27. package/dist/entrypoints/database/connectors/defaultNameOverride.cjs.js.map +1 -1
  28. package/dist/entrypoints/database/connectors/defaultSchemaOverride.cjs.js.map +1 -1
  29. package/dist/entrypoints/database/connectors/mergeDatabaseConfig.cjs.js.map +1 -1
  30. package/dist/entrypoints/database/connectors/mysql.cjs.js.map +1 -1
  31. package/dist/entrypoints/database/connectors/postgres.cjs.js.map +1 -1
  32. package/dist/entrypoints/database/connectors/sqlite3.cjs.js.map +1 -1
  33. package/dist/entrypoints/database/databaseServiceFactory.cjs.js.map +1 -1
  34. package/dist/entrypoints/discovery/HostDiscovery.cjs.js.map +1 -1
  35. package/dist/entrypoints/discovery/discoveryServiceFactory.cjs.js.map +1 -1
  36. package/dist/entrypoints/httpAuth/httpAuthServiceFactory.cjs.js.map +1 -1
  37. package/dist/entrypoints/httpRouter/createAuthIntegrationRouter.cjs.js.map +1 -1
  38. package/dist/entrypoints/httpRouter/createCookieAuthRefreshMiddleware.cjs.js.map +1 -1
  39. package/dist/entrypoints/httpRouter/createCredentialsBarrier.cjs.js.map +1 -1
  40. package/dist/entrypoints/httpRouter/createLifecycleMiddleware.cjs.js.map +1 -1
  41. package/dist/entrypoints/httpRouter/httpRouterServiceFactory.cjs.js.map +1 -1
  42. package/dist/entrypoints/lifecycle/lifecycleServiceFactory.cjs.js.map +1 -1
  43. package/dist/entrypoints/logger/loggerServiceFactory.cjs.js.map +1 -1
  44. package/dist/entrypoints/permissions/permissionsServiceFactory.cjs.js.map +1 -1
  45. package/dist/entrypoints/rootConfig/createConfigSecretEnumerator.cjs.js.map +1 -1
  46. package/dist/entrypoints/rootConfig/rootConfigServiceFactory.cjs.js.map +1 -1
  47. package/dist/entrypoints/rootHealth/rootHealthServiceFactory.cjs.js.map +1 -1
  48. package/dist/entrypoints/rootHttpRouter/DefaultRootHttpRouter.cjs.js.map +1 -1
  49. package/dist/entrypoints/rootHttpRouter/createHealthRouter.cjs.js.map +1 -1
  50. package/dist/entrypoints/rootHttpRouter/http/MiddlewareFactory.cjs.js.map +1 -1
  51. package/dist/entrypoints/rootHttpRouter/http/applyInternalErrorFilter.cjs.js.map +1 -1
  52. package/dist/entrypoints/rootHttpRouter/http/config.cjs.js.map +1 -1
  53. package/dist/entrypoints/rootHttpRouter/http/createHttpServer.cjs.js.map +1 -1
  54. package/dist/entrypoints/rootHttpRouter/http/getGeneratedCertificate.cjs.js.map +1 -1
  55. package/dist/entrypoints/rootHttpRouter/http/readCorsOptions.cjs.js.map +1 -1
  56. package/dist/entrypoints/rootHttpRouter/http/readHelmetOptions.cjs.js.map +1 -1
  57. package/dist/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.cjs.js.map +1 -1
  58. package/dist/entrypoints/rootLifecycle/rootLifecycleServiceFactory.cjs.js.map +1 -1
  59. package/dist/entrypoints/rootLogger/WinstonLogger.cjs.js.map +1 -1
  60. package/dist/entrypoints/rootLogger/rootLoggerServiceFactory.cjs.js.map +1 -1
  61. package/dist/entrypoints/scheduler/database/migrateBackendTasks.cjs.js.map +1 -1
  62. package/dist/entrypoints/scheduler/database/tables.cjs.js.map +1 -1
  63. package/dist/entrypoints/scheduler/lib/DefaultSchedulerService.cjs.js.map +1 -1
  64. package/dist/entrypoints/scheduler/lib/LocalTaskWorker.cjs.js.map +1 -1
  65. package/dist/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.cjs.js.map +1 -1
  66. package/dist/entrypoints/scheduler/lib/PluginTaskSchedulerJanitor.cjs.js.map +1 -1
  67. package/dist/entrypoints/scheduler/lib/TaskWorker.cjs.js.map +1 -1
  68. package/dist/entrypoints/scheduler/lib/types.cjs.js.map +1 -1
  69. package/dist/entrypoints/scheduler/lib/util.cjs.js.map +1 -1
  70. package/dist/entrypoints/scheduler/schedulerServiceFactory.cjs.js.map +1 -1
  71. package/dist/entrypoints/urlReader/lib/AwsCodeCommitUrlReader.cjs.js.map +1 -1
  72. package/dist/entrypoints/urlReader/lib/AwsS3UrlReader.cjs.js.map +1 -1
  73. package/dist/entrypoints/urlReader/lib/AzureUrlReader.cjs.js.map +1 -1
  74. package/dist/entrypoints/urlReader/lib/BitbucketCloudUrlReader.cjs.js.map +1 -1
  75. package/dist/entrypoints/urlReader/lib/BitbucketServerUrlReader.cjs.js.map +1 -1
  76. package/dist/entrypoints/urlReader/lib/BitbucketUrlReader.cjs.js.map +1 -1
  77. package/dist/entrypoints/urlReader/lib/FetchUrlReader.cjs.js.map +1 -1
  78. package/dist/entrypoints/urlReader/lib/GerritUrlReader.cjs.js.map +1 -1
  79. package/dist/entrypoints/urlReader/lib/GiteaUrlReader.cjs.js.map +1 -1
  80. package/dist/entrypoints/urlReader/lib/GithubUrlReader.cjs.js.map +1 -1
  81. package/dist/entrypoints/urlReader/lib/GitlabUrlReader.cjs.js.map +1 -1
  82. package/dist/entrypoints/urlReader/lib/GoogleGcsUrlReader.cjs.js.map +1 -1
  83. package/dist/entrypoints/urlReader/lib/HarnessUrlReader.cjs.js.map +1 -1
  84. package/dist/entrypoints/urlReader/lib/ReadUrlResponseFactory.cjs.js.map +1 -1
  85. package/dist/entrypoints/urlReader/lib/UrlReaderPredicateMux.cjs.js.map +1 -1
  86. package/dist/entrypoints/urlReader/lib/UrlReaders.cjs.js.map +1 -1
  87. package/dist/entrypoints/urlReader/lib/tree/ReadTreeResponseFactory.cjs.js.map +1 -1
  88. package/dist/entrypoints/urlReader/lib/tree/ReadableArrayResponse.cjs.js.map +1 -1
  89. package/dist/entrypoints/urlReader/lib/tree/TarArchiveResponse.cjs.js.map +1 -1
  90. package/dist/entrypoints/urlReader/lib/tree/ZipArchiveResponse.cjs.js.map +1 -1
  91. package/dist/entrypoints/urlReader/lib/tree/util.cjs.js.map +1 -1
  92. package/dist/entrypoints/urlReader/lib/util.cjs.js.map +1 -1
  93. package/dist/entrypoints/urlReader/urlReaderServiceFactory.cjs.js.map +1 -1
  94. package/dist/entrypoints/userInfo/DefaultUserInfoService.cjs.js.map +1 -1
  95. package/dist/entrypoints/userInfo/userInfoServiceFactory.cjs.js.map +1 -1
  96. package/dist/lib/escapeRegExp.cjs.js.map +1 -1
  97. package/dist/package.json.cjs.js +1 -1
  98. package/package.json +69 -28
  99. package/auth/package.json +0 -6
  100. package/cache/package.json +0 -6
  101. package/database/package.json +0 -6
  102. package/discovery/package.json +0 -6
  103. package/httpAuth/package.json +0 -6
  104. package/httpRouter/package.json +0 -6
  105. package/lifecycle/package.json +0 -6
  106. package/logger/package.json +0 -6
  107. package/permissions/package.json +0 -6
  108. package/rootConfig/package.json +0 -6
  109. package/rootHealth/package.json +0 -6
  110. package/rootHttpRouter/package.json +0 -6
  111. package/rootLifecycle/package.json +0 -6
  112. package/rootLogger/package.json +0 -6
  113. package/scheduler/package.json +0 -6
  114. package/urlReader/package.json +0 -6
  115. package/userInfo/package.json +0 -6
package/CHANGELOG.md CHANGED
@@ -1,5 +1,47 @@
1
1
  # @backstage/backend-defaults
2
2
 
3
+ ## 0.5.3-next.3
4
+
5
+ ### Patch Changes
6
+
7
+ - ecf6b39: Use `node-fetch` instead of native fetch, as per https://backstage.io/docs/architecture-decisions/adrs-adr013
8
+ - Updated dependencies
9
+ - @backstage/integration-aws-node@0.1.13-next.0
10
+ - @backstage/plugin-events-node@0.4.5-next.3
11
+ - @backstage/backend-app-api@1.0.2-next.2
12
+ - @backstage/backend-dev-utils@0.1.5
13
+ - @backstage/backend-plugin-api@1.0.2-next.2
14
+ - @backstage/cli-common@0.1.15-next.0
15
+ - @backstage/cli-node@0.2.10-next.0
16
+ - @backstage/config@1.2.0
17
+ - @backstage/config-loader@1.9.2-next.0
18
+ - @backstage/errors@1.2.4
19
+ - @backstage/integration@1.15.1
20
+ - @backstage/types@1.1.1
21
+ - @backstage/plugin-auth-node@0.5.4-next.2
22
+ - @backstage/plugin-permission-node@0.8.5-next.2
23
+
24
+ ## 0.5.3-next.2
25
+
26
+ ### Patch Changes
27
+
28
+ - e30bb46: Disabling database migrations now correctly uses the `backend.default.skipMigrations` config value.
29
+ - Updated dependencies
30
+ - @backstage/plugin-events-node@0.4.5-next.2
31
+ - @backstage/plugin-auth-node@0.5.4-next.2
32
+ - @backstage/backend-app-api@1.0.2-next.2
33
+ - @backstage/backend-dev-utils@0.1.5
34
+ - @backstage/backend-plugin-api@1.0.2-next.2
35
+ - @backstage/cli-common@0.1.15-next.0
36
+ - @backstage/cli-node@0.2.10-next.0
37
+ - @backstage/config@1.2.0
38
+ - @backstage/config-loader@1.9.2-next.0
39
+ - @backstage/errors@1.2.4
40
+ - @backstage/integration@1.15.1
41
+ - @backstage/integration-aws-node@0.1.12
42
+ - @backstage/types@1.1.1
43
+ - @backstage/plugin-permission-node@0.8.5-next.2
44
+
3
45
  ## 0.5.3-next.1
4
46
 
5
47
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"CreateBackend.cjs.js","sources":["../src/CreateBackend.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Backend, createSpecializedBackend } from '@backstage/backend-app-api';\nimport { authServiceFactory } from '@backstage/backend-defaults/auth';\nimport { cacheServiceFactory } from '@backstage/backend-defaults/cache';\nimport { databaseServiceFactory } from '@backstage/backend-defaults/database';\nimport { discoveryServiceFactory } from '@backstage/backend-defaults/discovery';\nimport { httpAuthServiceFactory } from '@backstage/backend-defaults/httpAuth';\nimport { httpRouterServiceFactory } from '@backstage/backend-defaults/httpRouter';\nimport { lifecycleServiceFactory } from '@backstage/backend-defaults/lifecycle';\nimport { loggerServiceFactory } from '@backstage/backend-defaults/logger';\nimport { permissionsServiceFactory } from '@backstage/backend-defaults/permissions';\nimport { rootConfigServiceFactory } from '@backstage/backend-defaults/rootConfig';\nimport { rootHealthServiceFactory } from '@backstage/backend-defaults/rootHealth';\nimport { rootHttpRouterServiceFactory } from '@backstage/backend-defaults/rootHttpRouter';\nimport { rootLifecycleServiceFactory } from '@backstage/backend-defaults/rootLifecycle';\nimport { rootLoggerServiceFactory } from '@backstage/backend-defaults/rootLogger';\nimport { schedulerServiceFactory } from '@backstage/backend-defaults/scheduler';\nimport { urlReaderServiceFactory } from '@backstage/backend-defaults/urlReader';\nimport { userInfoServiceFactory } from '@backstage/backend-defaults/userInfo';\nimport { eventsServiceFactory } from '@backstage/plugin-events-node';\n\nexport const defaultServiceFactories = [\n authServiceFactory,\n cacheServiceFactory,\n rootConfigServiceFactory,\n databaseServiceFactory,\n discoveryServiceFactory,\n httpAuthServiceFactory,\n httpRouterServiceFactory,\n lifecycleServiceFactory,\n loggerServiceFactory,\n permissionsServiceFactory,\n rootHealthServiceFactory,\n rootHttpRouterServiceFactory,\n rootLifecycleServiceFactory,\n rootLoggerServiceFactory,\n schedulerServiceFactory,\n userInfoServiceFactory,\n urlReaderServiceFactory,\n eventsServiceFactory,\n];\n\n/**\n * @public\n */\nexport function createBackend(): Backend {\n return createSpecializedBackend({ defaultServiceFactories });\n}\n"],"names":["authServiceFactory","cacheServiceFactory","rootConfigServiceFactory","databaseServiceFactory","discoveryServiceFactory","httpAuthServiceFactory","httpRouterServiceFactory","lifecycleServiceFactory","loggerServiceFactory","permissionsServiceFactory","rootHealthServiceFactory","rootHttpRouterServiceFactory","rootLifecycleServiceFactory","rootLoggerServiceFactory","schedulerServiceFactory","userInfoServiceFactory","urlReaderServiceFactory","eventsServiceFactory","createSpecializedBackend"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAoCO,MAAM,uBAA0B,GAAA;AAAA,EACrCA,uBAAA;AAAA,EACAC,yBAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,+BAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,+BAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,2BAAA;AAAA,EACAC,qCAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,2CAAA;AAAA,EACAC,yCAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,+BAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,qCAAA;AACF,EAAA;AAKO,SAAS,aAAyB,GAAA;AACvC,EAAO,OAAAC,sCAAA,CAAyB,EAAE,uBAAA,EAAyB,CAAA,CAAA;AAC7D;;;;;"}
1
+ {"version":3,"file":"CreateBackend.cjs.js","sources":["../src/CreateBackend.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Backend, createSpecializedBackend } from '@backstage/backend-app-api';\nimport { authServiceFactory } from '@backstage/backend-defaults/auth';\nimport { cacheServiceFactory } from '@backstage/backend-defaults/cache';\nimport { databaseServiceFactory } from '@backstage/backend-defaults/database';\nimport { discoveryServiceFactory } from '@backstage/backend-defaults/discovery';\nimport { httpAuthServiceFactory } from '@backstage/backend-defaults/httpAuth';\nimport { httpRouterServiceFactory } from '@backstage/backend-defaults/httpRouter';\nimport { lifecycleServiceFactory } from '@backstage/backend-defaults/lifecycle';\nimport { loggerServiceFactory } from '@backstage/backend-defaults/logger';\nimport { permissionsServiceFactory } from '@backstage/backend-defaults/permissions';\nimport { rootConfigServiceFactory } from '@backstage/backend-defaults/rootConfig';\nimport { rootHealthServiceFactory } from '@backstage/backend-defaults/rootHealth';\nimport { rootHttpRouterServiceFactory } from '@backstage/backend-defaults/rootHttpRouter';\nimport { rootLifecycleServiceFactory } from '@backstage/backend-defaults/rootLifecycle';\nimport { rootLoggerServiceFactory } from '@backstage/backend-defaults/rootLogger';\nimport { schedulerServiceFactory } from '@backstage/backend-defaults/scheduler';\nimport { urlReaderServiceFactory } from '@backstage/backend-defaults/urlReader';\nimport { userInfoServiceFactory } from '@backstage/backend-defaults/userInfo';\nimport { eventsServiceFactory } from '@backstage/plugin-events-node';\n\nexport const defaultServiceFactories = [\n authServiceFactory,\n cacheServiceFactory,\n rootConfigServiceFactory,\n databaseServiceFactory,\n discoveryServiceFactory,\n httpAuthServiceFactory,\n httpRouterServiceFactory,\n lifecycleServiceFactory,\n loggerServiceFactory,\n permissionsServiceFactory,\n rootHealthServiceFactory,\n rootHttpRouterServiceFactory,\n rootLifecycleServiceFactory,\n rootLoggerServiceFactory,\n schedulerServiceFactory,\n userInfoServiceFactory,\n urlReaderServiceFactory,\n eventsServiceFactory,\n];\n\n/**\n * @public\n */\nexport function createBackend(): Backend {\n return createSpecializedBackend({ defaultServiceFactories });\n}\n"],"names":["authServiceFactory","cacheServiceFactory","rootConfigServiceFactory","databaseServiceFactory","discoveryServiceFactory","httpAuthServiceFactory","httpRouterServiceFactory","lifecycleServiceFactory","loggerServiceFactory","permissionsServiceFactory","rootHealthServiceFactory","rootHttpRouterServiceFactory","rootLifecycleServiceFactory","rootLoggerServiceFactory","schedulerServiceFactory","userInfoServiceFactory","urlReaderServiceFactory","eventsServiceFactory","createSpecializedBackend"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAoCO,MAAM,uBAA0B,GAAA;AAAA,EACrCA,uBAAA;AAAA,EACAC,yBAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,+BAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,+BAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,2BAAA;AAAA,EACAC,qCAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,2CAAA;AAAA,EACAC,yCAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,+BAAA;AAAA,EACAC,iCAAA;AAAA,EACAC;AACF;AAKO,SAAS,aAAyB,GAAA;AACvC,EAAO,OAAAC,sCAAA,CAAyB,EAAE,uBAAA,EAAyB,CAAA;AAC7D;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"PackageDiscoveryService.cjs.js","sources":["../src/PackageDiscoveryService.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport fs from 'fs-extra';\nimport { resolve as resolvePath, dirname } from 'path';\n\nimport {\n BackendFeature,\n RootConfigService,\n RootLoggerService,\n} from '@backstage/backend-plugin-api';\nimport { FeatureDiscoveryService } from '@backstage/backend-plugin-api/alpha';\nimport { BackstagePackageJson } from '@backstage/cli-node';\n\nconst DETECTED_PACKAGE_ROLES = [\n 'node-library',\n 'backend',\n 'backend-plugin',\n 'backend-plugin-module',\n];\n\n/** @internal */\nfunction isBackendFeature(value: unknown): value is BackendFeature {\n return (\n !!value &&\n ['object', 'function'].includes(typeof value) &&\n (value as BackendFeature).$$type === '@backstage/BackendFeature'\n );\n}\n\n/** @internal */\nfunction isBackendFeatureFactory(\n value: unknown,\n): value is () => BackendFeature {\n return (\n !!value &&\n typeof value === 'function' &&\n (value as any).$$type === '@backstage/BackendFeatureFactory'\n );\n}\n\n/** @internal */\nasync function findClosestPackageDir(\n searchDir: string,\n): Promise<string | undefined> {\n let path = searchDir;\n\n // Some confidence check to avoid infinite loop\n for (let i = 0; i < 1000; i++) {\n const packagePath = resolvePath(path, 'package.json');\n const exists = await fs.pathExists(packagePath);\n if (exists) {\n return path;\n }\n\n const newPath = dirname(path);\n if (newPath === path) {\n return undefined;\n }\n path = newPath;\n }\n\n throw new Error(\n `Iteration limit reached when searching for root package.json at ${searchDir}`,\n );\n}\n\n/** @internal */\nexport class PackageDiscoveryService implements FeatureDiscoveryService {\n constructor(\n private readonly config: RootConfigService,\n private readonly logger: RootLoggerService,\n ) {}\n\n getDependencyNames(path: string) {\n const { dependencies } = require(path) as BackstagePackageJson;\n const packagesConfig = this.config.getOptional('backend.packages');\n\n const dependencyNames = Object.keys(dependencies || {});\n\n if (packagesConfig === 'all') {\n return dependencyNames;\n }\n\n const includedPackagesConfig = this.config.getOptionalStringArray(\n 'backend.packages.include',\n );\n\n const includedPackages = includedPackagesConfig\n ? new Set(includedPackagesConfig)\n : dependencyNames;\n const excludedPackagesSet = new Set(\n this.config.getOptionalStringArray('backend.packages.exclude'),\n );\n\n return [...includedPackages].filter(name => !excludedPackagesSet.has(name));\n }\n\n async getBackendFeatures(): Promise<{ features: Array<BackendFeature> }> {\n const packagesConfig = this.config.getOptional('backend.packages');\n if (!packagesConfig || Object.keys(packagesConfig).length === 0) {\n return { features: [] };\n }\n\n const packageDir = await findClosestPackageDir(process.argv[1]);\n if (!packageDir) {\n throw new Error('Package discovery failed to find package.json');\n }\n const dependencyNames = this.getDependencyNames(\n resolvePath(packageDir, 'package.json'),\n );\n\n const features: BackendFeature[] = [];\n\n for (const name of dependencyNames) {\n const depPkg = require(require.resolve(`${name}/package.json`, {\n paths: [packageDir],\n })) as BackstagePackageJson;\n if (\n !depPkg?.backstage?.role ||\n !DETECTED_PACKAGE_ROLES.includes(depPkg.backstage.role)\n ) {\n continue; // Not a backstage backend package, ignore\n }\n\n const exportedModulePaths = [\n require.resolve(name, {\n paths: [packageDir],\n }),\n ];\n\n // Find modules exported as alpha\n try {\n exportedModulePaths.push(\n require.resolve(`${name}/alpha`, { paths: [packageDir] }),\n );\n } catch {\n /* ignore */\n }\n\n for (const modulePath of exportedModulePaths) {\n const mod = require(modulePath);\n\n if (isBackendFeature(mod.default)) {\n this.logger.info(`Detected: ${name}`);\n features.push(mod.default);\n }\n if (isBackendFeatureFactory(mod.default)) {\n this.logger.info(`Detected: ${name}`);\n features.push(mod.default());\n }\n }\n }\n\n return { features };\n }\n}\n"],"names":["resolvePath","fs","dirname"],"mappings":"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"}
1
+ {"version":3,"file":"PackageDiscoveryService.cjs.js","sources":["../src/PackageDiscoveryService.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport fs from 'fs-extra';\nimport { resolve as resolvePath, dirname } from 'path';\n\nimport {\n BackendFeature,\n RootConfigService,\n RootLoggerService,\n} from '@backstage/backend-plugin-api';\nimport { FeatureDiscoveryService } from '@backstage/backend-plugin-api/alpha';\nimport { BackstagePackageJson } from '@backstage/cli-node';\n\nconst DETECTED_PACKAGE_ROLES = [\n 'node-library',\n 'backend',\n 'backend-plugin',\n 'backend-plugin-module',\n];\n\n/** @internal */\nfunction isBackendFeature(value: unknown): value is BackendFeature {\n return (\n !!value &&\n ['object', 'function'].includes(typeof value) &&\n (value as BackendFeature).$$type === '@backstage/BackendFeature'\n );\n}\n\n/** @internal */\nfunction isBackendFeatureFactory(\n value: unknown,\n): value is () => BackendFeature {\n return (\n !!value &&\n typeof value === 'function' &&\n (value as any).$$type === '@backstage/BackendFeatureFactory'\n );\n}\n\n/** @internal */\nasync function findClosestPackageDir(\n searchDir: string,\n): Promise<string | undefined> {\n let path = searchDir;\n\n // Some confidence check to avoid infinite loop\n for (let i = 0; i < 1000; i++) {\n const packagePath = resolvePath(path, 'package.json');\n const exists = await fs.pathExists(packagePath);\n if (exists) {\n return path;\n }\n\n const newPath = dirname(path);\n if (newPath === path) {\n return undefined;\n }\n path = newPath;\n }\n\n throw new Error(\n `Iteration limit reached when searching for root package.json at ${searchDir}`,\n );\n}\n\n/** @internal */\nexport class PackageDiscoveryService implements FeatureDiscoveryService {\n constructor(\n private readonly config: RootConfigService,\n private readonly logger: RootLoggerService,\n ) {}\n\n getDependencyNames(path: string) {\n const { dependencies } = require(path) as BackstagePackageJson;\n const packagesConfig = this.config.getOptional('backend.packages');\n\n const dependencyNames = Object.keys(dependencies || {});\n\n if (packagesConfig === 'all') {\n return dependencyNames;\n }\n\n const includedPackagesConfig = this.config.getOptionalStringArray(\n 'backend.packages.include',\n );\n\n const includedPackages = includedPackagesConfig\n ? new Set(includedPackagesConfig)\n : dependencyNames;\n const excludedPackagesSet = new Set(\n this.config.getOptionalStringArray('backend.packages.exclude'),\n );\n\n return [...includedPackages].filter(name => !excludedPackagesSet.has(name));\n }\n\n async getBackendFeatures(): Promise<{ features: Array<BackendFeature> }> {\n const packagesConfig = this.config.getOptional('backend.packages');\n if (!packagesConfig || Object.keys(packagesConfig).length === 0) {\n return { features: [] };\n }\n\n const packageDir = await findClosestPackageDir(process.argv[1]);\n if (!packageDir) {\n throw new Error('Package discovery failed to find package.json');\n }\n const dependencyNames = this.getDependencyNames(\n resolvePath(packageDir, 'package.json'),\n );\n\n const features: BackendFeature[] = [];\n\n for (const name of dependencyNames) {\n const depPkg = require(require.resolve(`${name}/package.json`, {\n paths: [packageDir],\n })) as BackstagePackageJson;\n if (\n !depPkg?.backstage?.role ||\n !DETECTED_PACKAGE_ROLES.includes(depPkg.backstage.role)\n ) {\n continue; // Not a backstage backend package, ignore\n }\n\n const exportedModulePaths = [\n require.resolve(name, {\n paths: [packageDir],\n }),\n ];\n\n // Find modules exported as alpha\n try {\n exportedModulePaths.push(\n require.resolve(`${name}/alpha`, { paths: [packageDir] }),\n );\n } catch {\n /* ignore */\n }\n\n for (const modulePath of exportedModulePaths) {\n const mod = require(modulePath);\n\n if (isBackendFeature(mod.default)) {\n this.logger.info(`Detected: ${name}`);\n features.push(mod.default);\n }\n if (isBackendFeatureFactory(mod.default)) {\n this.logger.info(`Detected: ${name}`);\n features.push(mod.default());\n }\n }\n }\n\n return { features };\n }\n}\n"],"names":["resolvePath","fs","dirname"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"discoveryFeatureLoader.cjs.js","sources":["../src/discoveryFeatureLoader.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n coreServices,\n createBackendFeatureLoader,\n} from '@backstage/backend-plugin-api';\nimport { PackageDiscoveryService } from './PackageDiscoveryService';\n\n/**\n * A loader that discovers backend features from the current package.json and its dependencies.\n *\n * @public\n *\n * @example\n * Using the `discoveryFeatureLoader` loader in a backend instance:\n * ```ts\n * //...\n * import { createBackend } from '@backstage/backend-defaults';\n * import { discoveryFeatureLoader } from '@backstage/backend-defaults';\n *\n * const backend = createBackend();\n * backend.add(discoveryFeatureLoader);\n * //...\n * backend.start();\n * ```\n */\nexport const discoveryFeatureLoader = createBackendFeatureLoader({\n deps: {\n config: coreServices.rootConfig,\n logger: coreServices.rootLogger,\n },\n async loader({ config, logger }) {\n const service = new PackageDiscoveryService(config, logger);\n const { features } = await service.getBackendFeatures();\n return features;\n },\n});\n"],"names":["createBackendFeatureLoader","coreServices","PackageDiscoveryService"],"mappings":";;;;;AAwCO,MAAM,yBAAyBA,2CAA2B,CAAA;AAAA,EAC/D,IAAM,EAAA;AAAA,IACJ,QAAQC,6BAAa,CAAA,UAAA;AAAA,IACrB,QAAQA,6BAAa,CAAA,UAAA;AAAA,GACvB;AAAA,EACA,MAAM,MAAA,CAAO,EAAE,MAAA,EAAQ,QAAU,EAAA;AAC/B,IAAA,MAAM,OAAU,GAAA,IAAIC,+CAAwB,CAAA,MAAA,EAAQ,MAAM,CAAA,CAAA;AAC1D,IAAA,MAAM,EAAE,QAAA,EAAa,GAAA,MAAM,QAAQ,kBAAmB,EAAA,CAAA;AACtD,IAAO,OAAA,QAAA,CAAA;AAAA,GACT;AACF,CAAC;;;;"}
1
+ {"version":3,"file":"discoveryFeatureLoader.cjs.js","sources":["../src/discoveryFeatureLoader.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n coreServices,\n createBackendFeatureLoader,\n} from '@backstage/backend-plugin-api';\nimport { PackageDiscoveryService } from './PackageDiscoveryService';\n\n/**\n * A loader that discovers backend features from the current package.json and its dependencies.\n *\n * @public\n *\n * @example\n * Using the `discoveryFeatureLoader` loader in a backend instance:\n * ```ts\n * //...\n * import { createBackend } from '@backstage/backend-defaults';\n * import { discoveryFeatureLoader } from '@backstage/backend-defaults';\n *\n * const backend = createBackend();\n * backend.add(discoveryFeatureLoader);\n * //...\n * backend.start();\n * ```\n */\nexport const discoveryFeatureLoader = createBackendFeatureLoader({\n deps: {\n config: coreServices.rootConfig,\n logger: coreServices.rootLogger,\n },\n async loader({ config, logger }) {\n const service = new PackageDiscoveryService(config, logger);\n const { features } = await service.getBackendFeatures();\n return features;\n },\n});\n"],"names":["createBackendFeatureLoader","coreServices","PackageDiscoveryService"],"mappings":";;;;;AAwCO,MAAM,yBAAyBA,2CAA2B,CAAA;AAAA,EAC/D,IAAM,EAAA;AAAA,IACJ,QAAQC,6BAAa,CAAA,UAAA;AAAA,IACrB,QAAQA,6BAAa,CAAA;AAAA,GACvB;AAAA,EACA,MAAM,MAAA,CAAO,EAAE,MAAA,EAAQ,QAAU,EAAA;AAC/B,IAAA,MAAM,OAAU,GAAA,IAAIC,+CAAwB,CAAA,MAAA,EAAQ,MAAM,CAAA;AAC1D,IAAA,MAAM,EAAE,QAAA,EAAa,GAAA,MAAM,QAAQ,kBAAmB,EAAA;AACtD,IAAO,OAAA,QAAA;AAAA;AAEX,CAAC;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"DefaultAuthService.cjs.js","sources":["../../../src/entrypoints/auth/DefaultAuthService.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AuthService,\n BackstageCredentials,\n BackstageNonePrincipal,\n BackstagePrincipalTypes,\n BackstageServicePrincipal,\n BackstageUserPrincipal,\n} from '@backstage/backend-plugin-api';\nimport { AuthenticationError } from '@backstage/errors';\nimport { JsonObject } from '@backstage/types';\nimport { decodeJwt } from 'jose';\nimport { ExternalTokenHandler } from './external/ExternalTokenHandler';\nimport {\n createCredentialsWithNonePrincipal,\n createCredentialsWithServicePrincipal,\n createCredentialsWithUserPrincipal,\n toInternalBackstageCredentials,\n} from './helpers';\nimport { PluginTokenHandler } from './plugin/PluginTokenHandler';\nimport { PluginKeySource } from './plugin/keys/types';\nimport { UserTokenHandler } from './user/UserTokenHandler';\n\n/** @internal */\nexport class DefaultAuthService implements AuthService {\n constructor(\n private readonly userTokenHandler: UserTokenHandler,\n private readonly pluginTokenHandler: PluginTokenHandler,\n private readonly externalTokenHandler: ExternalTokenHandler,\n private readonly pluginId: string,\n private readonly disableDefaultAuthPolicy: boolean,\n private readonly pluginKeySource: PluginKeySource,\n ) {}\n\n async authenticate(\n token: string,\n options?: {\n allowLimitedAccess?: boolean;\n },\n ): Promise<BackstageCredentials> {\n const pluginResult = await this.pluginTokenHandler.verifyToken(token);\n if (pluginResult) {\n if (pluginResult.limitedUserToken) {\n const userResult = await this.userTokenHandler.verifyToken(\n pluginResult.limitedUserToken,\n );\n if (!userResult) {\n throw new AuthenticationError(\n 'Invalid user token in plugin token obo claim',\n );\n }\n return createCredentialsWithUserPrincipal(\n userResult.userEntityRef,\n pluginResult.limitedUserToken,\n this.#getJwtExpiration(pluginResult.limitedUserToken),\n );\n }\n return createCredentialsWithServicePrincipal(pluginResult.subject);\n }\n\n const userResult = await this.userTokenHandler.verifyToken(token);\n if (userResult) {\n if (\n !options?.allowLimitedAccess &&\n this.userTokenHandler.isLimitedUserToken(token)\n ) {\n throw new AuthenticationError('Illegal limited user token');\n }\n\n return createCredentialsWithUserPrincipal(\n userResult.userEntityRef,\n token,\n this.#getJwtExpiration(token),\n );\n }\n\n const externalResult = await this.externalTokenHandler.verifyToken(token);\n if (externalResult) {\n return createCredentialsWithServicePrincipal(\n externalResult.subject,\n undefined,\n externalResult.accessRestrictions,\n );\n }\n\n throw new AuthenticationError('Illegal token');\n }\n\n isPrincipal<TType extends keyof BackstagePrincipalTypes>(\n credentials: BackstageCredentials,\n type: TType,\n ): credentials is BackstageCredentials<BackstagePrincipalTypes[TType]> {\n const principal = credentials.principal as\n | BackstageUserPrincipal\n | BackstageServicePrincipal;\n\n if (type === 'unknown') {\n return true;\n }\n\n if (principal.type !== type) {\n return false;\n }\n\n return true;\n }\n\n async getNoneCredentials(): Promise<\n BackstageCredentials<BackstageNonePrincipal>\n > {\n return createCredentialsWithNonePrincipal();\n }\n\n async getOwnServiceCredentials(): Promise<\n BackstageCredentials<BackstageServicePrincipal>\n > {\n return createCredentialsWithServicePrincipal(`plugin:${this.pluginId}`);\n }\n\n async getPluginRequestToken(options: {\n onBehalfOf: BackstageCredentials;\n targetPluginId: string;\n }): Promise<{ token: string }> {\n const { targetPluginId } = options;\n const internalForward = toInternalBackstageCredentials(options.onBehalfOf);\n const { type } = internalForward.principal;\n\n // Since disabling the default policy means we'll be allowing\n // unauthenticated requests through, we might have unauthenticated\n // credentials from service calls that reach this point. If that's the case,\n // we'll want to keep \"forwarding\" the unauthenticated credentials, which we\n // do by returning an empty token.\n if (type === 'none' && this.disableDefaultAuthPolicy) {\n return { token: '' };\n }\n\n // check whether a plugin support the new auth system\n // by checking the public keys endpoint existance.\n switch (type) {\n // TODO: Check whether the principal is ourselves\n case 'service':\n return this.pluginTokenHandler.issueToken({\n pluginId: this.pluginId,\n targetPluginId,\n });\n case 'user': {\n const { token } = internalForward;\n if (!token) {\n throw new Error('User credentials is unexpectedly missing token');\n }\n const onBehalfOf = await this.userTokenHandler.createLimitedUserToken(\n token,\n );\n return this.pluginTokenHandler.issueToken({\n pluginId: this.pluginId,\n targetPluginId,\n onBehalfOf,\n });\n }\n default:\n throw new AuthenticationError(\n `Refused to issue service token for credential type '${type}'`,\n );\n }\n }\n\n async getLimitedUserToken(\n credentials: BackstageCredentials<BackstageUserPrincipal>,\n ): Promise<{ token: string; expiresAt: Date }> {\n const { token: backstageToken } =\n toInternalBackstageCredentials(credentials);\n if (!backstageToken) {\n throw new AuthenticationError(\n 'User credentials is unexpectedly missing token',\n );\n }\n\n return this.userTokenHandler.createLimitedUserToken(backstageToken);\n }\n\n async listPublicServiceKeys(): Promise<{ keys: JsonObject[] }> {\n const { keys } = await this.pluginKeySource.listKeys();\n return { keys: keys.map(({ key }) => key) };\n }\n\n #getJwtExpiration(token: string) {\n const { exp } = decodeJwt(token);\n if (!exp) {\n throw new AuthenticationError('User token is missing expiration');\n }\n return new Date(exp * 1000);\n }\n}\n"],"names":["userResult","AuthenticationError","createCredentialsWithUserPrincipal","createCredentialsWithServicePrincipal","createCredentialsWithNonePrincipal","toInternalBackstageCredentials","decodeJwt"],"mappings":"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"}
1
+ {"version":3,"file":"DefaultAuthService.cjs.js","sources":["../../../src/entrypoints/auth/DefaultAuthService.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AuthService,\n BackstageCredentials,\n BackstageNonePrincipal,\n BackstagePrincipalTypes,\n BackstageServicePrincipal,\n BackstageUserPrincipal,\n} from '@backstage/backend-plugin-api';\nimport { AuthenticationError } from '@backstage/errors';\nimport { JsonObject } from '@backstage/types';\nimport { decodeJwt } from 'jose';\nimport { ExternalTokenHandler } from './external/ExternalTokenHandler';\nimport {\n createCredentialsWithNonePrincipal,\n createCredentialsWithServicePrincipal,\n createCredentialsWithUserPrincipal,\n toInternalBackstageCredentials,\n} from './helpers';\nimport { PluginTokenHandler } from './plugin/PluginTokenHandler';\nimport { PluginKeySource } from './plugin/keys/types';\nimport { UserTokenHandler } from './user/UserTokenHandler';\n\n/** @internal */\nexport class DefaultAuthService implements AuthService {\n constructor(\n private readonly userTokenHandler: UserTokenHandler,\n private readonly pluginTokenHandler: PluginTokenHandler,\n private readonly externalTokenHandler: ExternalTokenHandler,\n private readonly pluginId: string,\n private readonly disableDefaultAuthPolicy: boolean,\n private readonly pluginKeySource: PluginKeySource,\n ) {}\n\n async authenticate(\n token: string,\n options?: {\n allowLimitedAccess?: boolean;\n },\n ): Promise<BackstageCredentials> {\n const pluginResult = await this.pluginTokenHandler.verifyToken(token);\n if (pluginResult) {\n if (pluginResult.limitedUserToken) {\n const userResult = await this.userTokenHandler.verifyToken(\n pluginResult.limitedUserToken,\n );\n if (!userResult) {\n throw new AuthenticationError(\n 'Invalid user token in plugin token obo claim',\n );\n }\n return createCredentialsWithUserPrincipal(\n userResult.userEntityRef,\n pluginResult.limitedUserToken,\n this.#getJwtExpiration(pluginResult.limitedUserToken),\n );\n }\n return createCredentialsWithServicePrincipal(pluginResult.subject);\n }\n\n const userResult = await this.userTokenHandler.verifyToken(token);\n if (userResult) {\n if (\n !options?.allowLimitedAccess &&\n this.userTokenHandler.isLimitedUserToken(token)\n ) {\n throw new AuthenticationError('Illegal limited user token');\n }\n\n return createCredentialsWithUserPrincipal(\n userResult.userEntityRef,\n token,\n this.#getJwtExpiration(token),\n );\n }\n\n const externalResult = await this.externalTokenHandler.verifyToken(token);\n if (externalResult) {\n return createCredentialsWithServicePrincipal(\n externalResult.subject,\n undefined,\n externalResult.accessRestrictions,\n );\n }\n\n throw new AuthenticationError('Illegal token');\n }\n\n isPrincipal<TType extends keyof BackstagePrincipalTypes>(\n credentials: BackstageCredentials,\n type: TType,\n ): credentials is BackstageCredentials<BackstagePrincipalTypes[TType]> {\n const principal = credentials.principal as\n | BackstageUserPrincipal\n | BackstageServicePrincipal;\n\n if (type === 'unknown') {\n return true;\n }\n\n if (principal.type !== type) {\n return false;\n }\n\n return true;\n }\n\n async getNoneCredentials(): Promise<\n BackstageCredentials<BackstageNonePrincipal>\n > {\n return createCredentialsWithNonePrincipal();\n }\n\n async getOwnServiceCredentials(): Promise<\n BackstageCredentials<BackstageServicePrincipal>\n > {\n return createCredentialsWithServicePrincipal(`plugin:${this.pluginId}`);\n }\n\n async getPluginRequestToken(options: {\n onBehalfOf: BackstageCredentials;\n targetPluginId: string;\n }): Promise<{ token: string }> {\n const { targetPluginId } = options;\n const internalForward = toInternalBackstageCredentials(options.onBehalfOf);\n const { type } = internalForward.principal;\n\n // Since disabling the default policy means we'll be allowing\n // unauthenticated requests through, we might have unauthenticated\n // credentials from service calls that reach this point. If that's the case,\n // we'll want to keep \"forwarding\" the unauthenticated credentials, which we\n // do by returning an empty token.\n if (type === 'none' && this.disableDefaultAuthPolicy) {\n return { token: '' };\n }\n\n // check whether a plugin support the new auth system\n // by checking the public keys endpoint existance.\n switch (type) {\n // TODO: Check whether the principal is ourselves\n case 'service':\n return this.pluginTokenHandler.issueToken({\n pluginId: this.pluginId,\n targetPluginId,\n });\n case 'user': {\n const { token } = internalForward;\n if (!token) {\n throw new Error('User credentials is unexpectedly missing token');\n }\n const onBehalfOf = await this.userTokenHandler.createLimitedUserToken(\n token,\n );\n return this.pluginTokenHandler.issueToken({\n pluginId: this.pluginId,\n targetPluginId,\n onBehalfOf,\n });\n }\n default:\n throw new AuthenticationError(\n `Refused to issue service token for credential type '${type}'`,\n );\n }\n }\n\n async getLimitedUserToken(\n credentials: BackstageCredentials<BackstageUserPrincipal>,\n ): Promise<{ token: string; expiresAt: Date }> {\n const { token: backstageToken } =\n toInternalBackstageCredentials(credentials);\n if (!backstageToken) {\n throw new AuthenticationError(\n 'User credentials is unexpectedly missing token',\n );\n }\n\n return this.userTokenHandler.createLimitedUserToken(backstageToken);\n }\n\n async listPublicServiceKeys(): Promise<{ keys: JsonObject[] }> {\n const { keys } = await this.pluginKeySource.listKeys();\n return { keys: keys.map(({ key }) => key) };\n }\n\n #getJwtExpiration(token: string) {\n const { exp } = decodeJwt(token);\n if (!exp) {\n throw new AuthenticationError('User token is missing expiration');\n }\n return new Date(exp * 1000);\n }\n}\n"],"names":["userResult","AuthenticationError","createCredentialsWithUserPrincipal","createCredentialsWithServicePrincipal","createCredentialsWithNonePrincipal","toInternalBackstageCredentials","decodeJwt"],"mappings":";;;;;;AAuCO,MAAM,kBAA0C,CAAA;AAAA,EACrD,YACmB,gBACA,EAAA,kBAAA,EACA,oBACA,EAAA,QAAA,EACA,0BACA,eACjB,EAAA;AANiB,IAAA,IAAA,CAAA,gBAAA,GAAA,gBAAA;AACA,IAAA,IAAA,CAAA,kBAAA,GAAA,kBAAA;AACA,IAAA,IAAA,CAAA,oBAAA,GAAA,oBAAA;AACA,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AACA,IAAA,IAAA,CAAA,wBAAA,GAAA,wBAAA;AACA,IAAA,IAAA,CAAA,eAAA,GAAA,eAAA;AAAA;AAChB,EAEH,MAAM,YACJ,CAAA,KAAA,EACA,OAG+B,EAAA;AAC/B,IAAA,MAAM,YAAe,GAAA,MAAM,IAAK,CAAA,kBAAA,CAAmB,YAAY,KAAK,CAAA;AACpE,IAAA,IAAI,YAAc,EAAA;AAChB,MAAA,IAAI,aAAa,gBAAkB,EAAA;AACjC,QAAMA,MAAAA,WAAAA,GAAa,MAAM,IAAA,CAAK,gBAAiB,CAAA,WAAA;AAAA,UAC7C,YAAa,CAAA;AAAA,SACf;AACA,QAAA,IAAI,CAACA,WAAY,EAAA;AACf,UAAA,MAAM,IAAIC,0BAAA;AAAA,YACR;AAAA,WACF;AAAA;AAEF,QAAO,OAAAC,0CAAA;AAAA,UACLF,WAAW,CAAA,aAAA;AAAA,UACX,YAAa,CAAA,gBAAA;AAAA,UACb,IAAA,CAAK,iBAAkB,CAAA,YAAA,CAAa,gBAAgB;AAAA,SACtD;AAAA;AAEF,MAAO,OAAAG,6CAAA,CAAsC,aAAa,OAAO,CAAA;AAAA;AAGnE,IAAA,MAAM,UAAa,GAAA,MAAM,IAAK,CAAA,gBAAA,CAAiB,YAAY,KAAK,CAAA;AAChE,IAAA,IAAI,UAAY,EAAA;AACd,MAAA,IACE,CAAC,OAAS,EAAA,kBAAA,IACV,KAAK,gBAAiB,CAAA,kBAAA,CAAmB,KAAK,CAC9C,EAAA;AACA,QAAM,MAAA,IAAIF,2BAAoB,4BAA4B,CAAA;AAAA;AAG5D,MAAO,OAAAC,0CAAA;AAAA,QACL,UAAW,CAAA,aAAA;AAAA,QACX,KAAA;AAAA,QACA,IAAA,CAAK,kBAAkB,KAAK;AAAA,OAC9B;AAAA;AAGF,IAAA,MAAM,cAAiB,GAAA,MAAM,IAAK,CAAA,oBAAA,CAAqB,YAAY,KAAK,CAAA;AACxE,IAAA,IAAI,cAAgB,EAAA;AAClB,MAAO,OAAAC,6CAAA;AAAA,QACL,cAAe,CAAA,OAAA;AAAA,QACf,KAAA,CAAA;AAAA,QACA,cAAe,CAAA;AAAA,OACjB;AAAA;AAGF,IAAM,MAAA,IAAIF,2BAAoB,eAAe,CAAA;AAAA;AAC/C,EAEA,WAAA,CACE,aACA,IACqE,EAAA;AACrE,IAAA,MAAM,YAAY,WAAY,CAAA,SAAA;AAI9B,IAAA,IAAI,SAAS,SAAW,EAAA;AACtB,MAAO,OAAA,IAAA;AAAA;AAGT,IAAI,IAAA,SAAA,CAAU,SAAS,IAAM,EAAA;AAC3B,MAAO,OAAA,KAAA;AAAA;AAGT,IAAO,OAAA,IAAA;AAAA;AACT,EAEA,MAAM,kBAEJ,GAAA;AACA,IAAA,OAAOG,0CAAmC,EAAA;AAAA;AAC5C,EAEA,MAAM,wBAEJ,GAAA;AACA,IAAA,OAAOD,6CAAsC,CAAA,CAAA,OAAA,EAAU,IAAK,CAAA,QAAQ,CAAE,CAAA,CAAA;AAAA;AACxE,EAEA,MAAM,sBAAsB,OAGG,EAAA;AAC7B,IAAM,MAAA,EAAE,gBAAmB,GAAA,OAAA;AAC3B,IAAM,MAAA,eAAA,GAAkBE,sCAA+B,CAAA,OAAA,CAAQ,UAAU,CAAA;AACzE,IAAM,MAAA,EAAE,IAAK,EAAA,GAAI,eAAgB,CAAA,SAAA;AAOjC,IAAI,IAAA,IAAA,KAAS,MAAU,IAAA,IAAA,CAAK,wBAA0B,EAAA;AACpD,MAAO,OAAA,EAAE,OAAO,EAAG,EAAA;AAAA;AAKrB,IAAA,QAAQ,IAAM;AAAA;AAAA,MAEZ,KAAK,SAAA;AACH,QAAO,OAAA,IAAA,CAAK,mBAAmB,UAAW,CAAA;AAAA,UACxC,UAAU,IAAK,CAAA,QAAA;AAAA,UACf;AAAA,SACD,CAAA;AAAA,MACH,KAAK,MAAQ,EAAA;AACX,QAAM,MAAA,EAAE,OAAU,GAAA,eAAA;AAClB,QAAA,IAAI,CAAC,KAAO,EAAA;AACV,UAAM,MAAA,IAAI,MAAM,gDAAgD,CAAA;AAAA;AAElE,QAAM,MAAA,UAAA,GAAa,MAAM,IAAA,CAAK,gBAAiB,CAAA,sBAAA;AAAA,UAC7C;AAAA,SACF;AACA,QAAO,OAAA,IAAA,CAAK,mBAAmB,UAAW,CAAA;AAAA,UACxC,UAAU,IAAK,CAAA,QAAA;AAAA,UACf,cAAA;AAAA,UACA;AAAA,SACD,CAAA;AAAA;AACH,MACA;AACE,QAAA,MAAM,IAAIJ,0BAAA;AAAA,UACR,uDAAuD,IAAI,CAAA,CAAA;AAAA,SAC7D;AAAA;AACJ;AACF,EAEA,MAAM,oBACJ,WAC6C,EAAA;AAC7C,IAAA,MAAM,EAAE,KAAA,EAAO,cAAe,EAAA,GAC5BI,uCAA+B,WAAW,CAAA;AAC5C,IAAA,IAAI,CAAC,cAAgB,EAAA;AACnB,MAAA,MAAM,IAAIJ,0BAAA;AAAA,QACR;AAAA,OACF;AAAA;AAGF,IAAO,OAAA,IAAA,CAAK,gBAAiB,CAAA,sBAAA,CAAuB,cAAc,CAAA;AAAA;AACpE,EAEA,MAAM,qBAAyD,GAAA;AAC7D,IAAA,MAAM,EAAE,IAAK,EAAA,GAAI,MAAM,IAAA,CAAK,gBAAgB,QAAS,EAAA;AACrD,IAAO,OAAA,EAAE,MAAM,IAAK,CAAA,GAAA,CAAI,CAAC,EAAE,GAAA,EAAU,KAAA,GAAG,CAAE,EAAA;AAAA;AAC5C,EAEA,kBAAkB,KAAe,EAAA;AAC/B,IAAA,MAAM,EAAE,GAAA,EAAQ,GAAAK,cAAA,CAAU,KAAK,CAAA;AAC/B,IAAA,IAAI,CAAC,GAAK,EAAA;AACR,MAAM,MAAA,IAAIL,2BAAoB,kCAAkC,CAAA;AAAA;AAElE,IAAO,OAAA,IAAI,IAAK,CAAA,GAAA,GAAM,GAAI,CAAA;AAAA;AAE9B;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"JwksClient.cjs.js","sources":["../../../src/entrypoints/auth/JwksClient.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AuthenticationError } from '@backstage/errors';\nimport {\n createRemoteJWKSet,\n decodeJwt,\n decodeProtectedHeader,\n FlattenedJWSInput,\n JWSHeaderParameters,\n} from 'jose';\nimport { GetKeyFunction } from 'jose/dist/types/types';\n\nconst CLOCK_MARGIN_S = 10;\n\nexport class JwksClient {\n #keyStore?: GetKeyFunction<JWSHeaderParameters, FlattenedJWSInput>;\n #keyStoreUpdated: number = 0;\n\n constructor(private readonly getEndpoint: () => Promise<URL>) {}\n\n get getKey() {\n if (!this.#keyStore) {\n throw new AuthenticationError(\n 'refreshKeyStore must be called before jwksClient.getKey',\n );\n }\n return this.#keyStore;\n }\n\n /**\n * If the last keystore refresh is stale, update the keystore URL to the latest\n */\n async refreshKeyStore(rawJwtToken: string): Promise<void> {\n const payload = await decodeJwt(rawJwtToken);\n const header = await decodeProtectedHeader(rawJwtToken);\n\n // Refresh public keys if needed\n let keyStoreHasKey;\n try {\n if (this.#keyStore) {\n // Check if the key is present in the keystore\n const [_, rawPayload, rawSignature] = rawJwtToken.split('.');\n keyStoreHasKey = await this.#keyStore(header, {\n payload: rawPayload,\n signature: rawSignature,\n });\n }\n } catch (error) {\n keyStoreHasKey = false;\n }\n // Refresh public key URL if needed\n // Add a small margin in case clocks are out of sync\n const issuedAfterLastRefresh =\n payload?.iat && payload.iat > this.#keyStoreUpdated - CLOCK_MARGIN_S;\n if (!this.#keyStore || (!keyStoreHasKey && issuedAfterLastRefresh)) {\n const endpoint = await this.getEndpoint();\n this.#keyStore = createRemoteJWKSet(endpoint);\n this.#keyStoreUpdated = Date.now() / 1000;\n }\n }\n}\n"],"names":["AuthenticationError","decodeJwt","decodeProtectedHeader","createRemoteJWKSet"],"mappings":";;;;;AA0BA,MAAM,cAAiB,GAAA,EAAA,CAAA;AAEhB,MAAM,UAAW,CAAA;AAAA,EAItB,YAA6B,WAAiC,EAAA;AAAjC,IAAA,IAAA,CAAA,WAAA,GAAA,WAAA,CAAA;AAAA,GAAkC;AAAA,EAH/D,SAAA,CAAA;AAAA,EACA,gBAA2B,GAAA,CAAA,CAAA;AAAA,EAI3B,IAAI,MAAS,GAAA;AACX,IAAI,IAAA,CAAC,KAAK,SAAW,EAAA;AACnB,MAAA,MAAM,IAAIA,0BAAA;AAAA,QACR,yDAAA;AAAA,OACF,CAAA;AAAA,KACF;AACA,IAAA,OAAO,IAAK,CAAA,SAAA,CAAA;AAAA,GACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,gBAAgB,WAAoC,EAAA;AACxD,IAAM,MAAA,OAAA,GAAU,MAAMC,cAAA,CAAU,WAAW,CAAA,CAAA;AAC3C,IAAM,MAAA,MAAA,GAAS,MAAMC,0BAAA,CAAsB,WAAW,CAAA,CAAA;AAGtD,IAAI,IAAA,cAAA,CAAA;AACJ,IAAI,IAAA;AACF,MAAA,IAAI,KAAK,SAAW,EAAA;AAElB,QAAA,MAAM,CAAC,CAAG,EAAA,UAAA,EAAY,YAAY,CAAI,GAAA,WAAA,CAAY,MAAM,GAAG,CAAA,CAAA;AAC3D,QAAiB,cAAA,GAAA,MAAM,IAAK,CAAA,SAAA,CAAU,MAAQ,EAAA;AAAA,UAC5C,OAAS,EAAA,UAAA;AAAA,UACT,SAAW,EAAA,YAAA;AAAA,SACZ,CAAA,CAAA;AAAA,OACH;AAAA,aACO,KAAO,EAAA;AACd,MAAiB,cAAA,GAAA,KAAA,CAAA;AAAA,KACnB;AAGA,IAAA,MAAM,yBACJ,OAAS,EAAA,GAAA,IAAO,OAAQ,CAAA,GAAA,GAAM,KAAK,gBAAmB,GAAA,cAAA,CAAA;AACxD,IAAA,IAAI,CAAC,IAAA,CAAK,SAAc,IAAA,CAAC,kBAAkB,sBAAyB,EAAA;AAClE,MAAM,MAAA,QAAA,GAAW,MAAM,IAAA,CAAK,WAAY,EAAA,CAAA;AACxC,MAAK,IAAA,CAAA,SAAA,GAAYC,wBAAmB,QAAQ,CAAA,CAAA;AAC5C,MAAK,IAAA,CAAA,gBAAA,GAAmB,IAAK,CAAA,GAAA,EAAQ,GAAA,GAAA,CAAA;AAAA,KACvC;AAAA,GACF;AACF;;;;"}
1
+ {"version":3,"file":"JwksClient.cjs.js","sources":["../../../src/entrypoints/auth/JwksClient.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AuthenticationError } from '@backstage/errors';\nimport {\n createRemoteJWKSet,\n decodeJwt,\n decodeProtectedHeader,\n FlattenedJWSInput,\n JWSHeaderParameters,\n} from 'jose';\nimport { GetKeyFunction } from 'jose/dist/types/types';\n\nconst CLOCK_MARGIN_S = 10;\n\nexport class JwksClient {\n #keyStore?: GetKeyFunction<JWSHeaderParameters, FlattenedJWSInput>;\n #keyStoreUpdated: number = 0;\n\n constructor(private readonly getEndpoint: () => Promise<URL>) {}\n\n get getKey() {\n if (!this.#keyStore) {\n throw new AuthenticationError(\n 'refreshKeyStore must be called before jwksClient.getKey',\n );\n }\n return this.#keyStore;\n }\n\n /**\n * If the last keystore refresh is stale, update the keystore URL to the latest\n */\n async refreshKeyStore(rawJwtToken: string): Promise<void> {\n const payload = await decodeJwt(rawJwtToken);\n const header = await decodeProtectedHeader(rawJwtToken);\n\n // Refresh public keys if needed\n let keyStoreHasKey;\n try {\n if (this.#keyStore) {\n // Check if the key is present in the keystore\n const [_, rawPayload, rawSignature] = rawJwtToken.split('.');\n keyStoreHasKey = await this.#keyStore(header, {\n payload: rawPayload,\n signature: rawSignature,\n });\n }\n } catch (error) {\n keyStoreHasKey = false;\n }\n // Refresh public key URL if needed\n // Add a small margin in case clocks are out of sync\n const issuedAfterLastRefresh =\n payload?.iat && payload.iat > this.#keyStoreUpdated - CLOCK_MARGIN_S;\n if (!this.#keyStore || (!keyStoreHasKey && issuedAfterLastRefresh)) {\n const endpoint = await this.getEndpoint();\n this.#keyStore = createRemoteJWKSet(endpoint);\n this.#keyStoreUpdated = Date.now() / 1000;\n }\n }\n}\n"],"names":["AuthenticationError","decodeJwt","decodeProtectedHeader","createRemoteJWKSet"],"mappings":";;;;;AA0BA,MAAM,cAAiB,GAAA,EAAA;AAEhB,MAAM,UAAW,CAAA;AAAA,EAItB,YAA6B,WAAiC,EAAA;AAAjC,IAAA,IAAA,CAAA,WAAA,GAAA,WAAA;AAAA;AAAkC,EAH/D,SAAA;AAAA,EACA,gBAA2B,GAAA,CAAA;AAAA,EAI3B,IAAI,MAAS,GAAA;AACX,IAAI,IAAA,CAAC,KAAK,SAAW,EAAA;AACnB,MAAA,MAAM,IAAIA,0BAAA;AAAA,QACR;AAAA,OACF;AAAA;AAEF,IAAA,OAAO,IAAK,CAAA,SAAA;AAAA;AACd;AAAA;AAAA;AAAA,EAKA,MAAM,gBAAgB,WAAoC,EAAA;AACxD,IAAM,MAAA,OAAA,GAAU,MAAMC,cAAA,CAAU,WAAW,CAAA;AAC3C,IAAM,MAAA,MAAA,GAAS,MAAMC,0BAAA,CAAsB,WAAW,CAAA;AAGtD,IAAI,IAAA,cAAA;AACJ,IAAI,IAAA;AACF,MAAA,IAAI,KAAK,SAAW,EAAA;AAElB,QAAA,MAAM,CAAC,CAAG,EAAA,UAAA,EAAY,YAAY,CAAI,GAAA,WAAA,CAAY,MAAM,GAAG,CAAA;AAC3D,QAAiB,cAAA,GAAA,MAAM,IAAK,CAAA,SAAA,CAAU,MAAQ,EAAA;AAAA,UAC5C,OAAS,EAAA,UAAA;AAAA,UACT,SAAW,EAAA;AAAA,SACZ,CAAA;AAAA;AACH,aACO,KAAO,EAAA;AACd,MAAiB,cAAA,GAAA,KAAA;AAAA;AAInB,IAAA,MAAM,yBACJ,OAAS,EAAA,GAAA,IAAO,OAAQ,CAAA,GAAA,GAAM,KAAK,gBAAmB,GAAA,cAAA;AACxD,IAAA,IAAI,CAAC,IAAA,CAAK,SAAc,IAAA,CAAC,kBAAkB,sBAAyB,EAAA;AAClE,MAAM,MAAA,QAAA,GAAW,MAAM,IAAA,CAAK,WAAY,EAAA;AACxC,MAAK,IAAA,CAAA,SAAA,GAAYC,wBAAmB,QAAQ,CAAA;AAC5C,MAAK,IAAA,CAAA,gBAAA,GAAmB,IAAK,CAAA,GAAA,EAAQ,GAAA,GAAA;AAAA;AACvC;AAEJ;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"authServiceFactory.cjs.js","sources":["../../../src/entrypoints/auth/authServiceFactory.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n coreServices,\n createServiceFactory,\n} from '@backstage/backend-plugin-api';\nimport { DefaultAuthService } from './DefaultAuthService';\nimport { ExternalTokenHandler } from './external/ExternalTokenHandler';\nimport { PluginTokenHandler } from './plugin/PluginTokenHandler';\nimport { createPluginKeySource } from './plugin/keys/createPluginKeySource';\nimport { UserTokenHandler } from './user/UserTokenHandler';\n\n/**\n * Handles token authentication and credentials management.\n *\n * See {@link @backstage/code-plugin-api#AuthService}\n * and {@link https://backstage.io/docs/backend-system/core-services/auth | the service docs}\n * for more information.\n *\n * @public\n */\nexport const authServiceFactory = createServiceFactory({\n service: coreServices.auth,\n deps: {\n config: coreServices.rootConfig,\n logger: coreServices.rootLogger,\n discovery: coreServices.discovery,\n plugin: coreServices.pluginMetadata,\n database: coreServices.database,\n },\n async factory({ config, discovery, plugin, logger, database }) {\n const disableDefaultAuthPolicy =\n config.getOptionalBoolean(\n 'backend.auth.dangerouslyDisableDefaultAuthPolicy',\n ) ?? false;\n\n const keyDuration = { hours: 1 };\n\n const keySource = await createPluginKeySource({\n config,\n database,\n logger,\n keyDuration,\n });\n\n const userTokens = UserTokenHandler.create({\n discovery,\n });\n\n const pluginTokens = PluginTokenHandler.create({\n ownPluginId: plugin.getId(),\n logger,\n keySource,\n keyDuration,\n discovery,\n });\n\n const externalTokens = ExternalTokenHandler.create({\n ownPluginId: plugin.getId(),\n config,\n logger,\n });\n\n return new DefaultAuthService(\n userTokens,\n pluginTokens,\n externalTokens,\n plugin.getId(),\n disableDefaultAuthPolicy,\n keySource,\n );\n },\n});\n"],"names":["createServiceFactory","coreServices","createPluginKeySource","UserTokenHandler","PluginTokenHandler","ExternalTokenHandler","DefaultAuthService"],"mappings":";;;;;;;;;AAmCO,MAAM,qBAAqBA,qCAAqB,CAAA;AAAA,EACrD,SAASC,6BAAa,CAAA,IAAA;AAAA,EACtB,IAAM,EAAA;AAAA,IACJ,QAAQA,6BAAa,CAAA,UAAA;AAAA,IACrB,QAAQA,6BAAa,CAAA,UAAA;AAAA,IACrB,WAAWA,6BAAa,CAAA,SAAA;AAAA,IACxB,QAAQA,6BAAa,CAAA,cAAA;AAAA,IACrB,UAAUA,6BAAa,CAAA,QAAA;AAAA,GACzB;AAAA,EACA,MAAM,QAAQ,EAAE,MAAA,EAAQ,WAAW,MAAQ,EAAA,MAAA,EAAQ,UAAY,EAAA;AAC7D,IAAA,MAAM,2BACJ,MAAO,CAAA,kBAAA;AAAA,MACL,kDAAA;AAAA,KACG,IAAA,KAAA,CAAA;AAEP,IAAM,MAAA,WAAA,GAAc,EAAE,KAAA,EAAO,CAAE,EAAA,CAAA;AAE/B,IAAM,MAAA,SAAA,GAAY,MAAMC,2CAAsB,CAAA;AAAA,MAC5C,MAAA;AAAA,MACA,QAAA;AAAA,MACA,MAAA;AAAA,MACA,WAAA;AAAA,KACD,CAAA,CAAA;AAED,IAAM,MAAA,UAAA,GAAaC,kCAAiB,MAAO,CAAA;AAAA,MACzC,SAAA;AAAA,KACD,CAAA,CAAA;AAED,IAAM,MAAA,YAAA,GAAeC,sCAAmB,MAAO,CAAA;AAAA,MAC7C,WAAA,EAAa,OAAO,KAAM,EAAA;AAAA,MAC1B,MAAA;AAAA,MACA,SAAA;AAAA,MACA,WAAA;AAAA,MACA,SAAA;AAAA,KACD,CAAA,CAAA;AAED,IAAM,MAAA,cAAA,GAAiBC,0CAAqB,MAAO,CAAA;AAAA,MACjD,WAAA,EAAa,OAAO,KAAM,EAAA;AAAA,MAC1B,MAAA;AAAA,MACA,MAAA;AAAA,KACD,CAAA,CAAA;AAED,IAAA,OAAO,IAAIC,qCAAA;AAAA,MACT,UAAA;AAAA,MACA,YAAA;AAAA,MACA,cAAA;AAAA,MACA,OAAO,KAAM,EAAA;AAAA,MACb,wBAAA;AAAA,MACA,SAAA;AAAA,KACF,CAAA;AAAA,GACF;AACF,CAAC;;;;"}
1
+ {"version":3,"file":"authServiceFactory.cjs.js","sources":["../../../src/entrypoints/auth/authServiceFactory.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n coreServices,\n createServiceFactory,\n} from '@backstage/backend-plugin-api';\nimport { DefaultAuthService } from './DefaultAuthService';\nimport { ExternalTokenHandler } from './external/ExternalTokenHandler';\nimport { PluginTokenHandler } from './plugin/PluginTokenHandler';\nimport { createPluginKeySource } from './plugin/keys/createPluginKeySource';\nimport { UserTokenHandler } from './user/UserTokenHandler';\n\n/**\n * Handles token authentication and credentials management.\n *\n * See {@link @backstage/code-plugin-api#AuthService}\n * and {@link https://backstage.io/docs/backend-system/core-services/auth | the service docs}\n * for more information.\n *\n * @public\n */\nexport const authServiceFactory = createServiceFactory({\n service: coreServices.auth,\n deps: {\n config: coreServices.rootConfig,\n logger: coreServices.rootLogger,\n discovery: coreServices.discovery,\n plugin: coreServices.pluginMetadata,\n database: coreServices.database,\n },\n async factory({ config, discovery, plugin, logger, database }) {\n const disableDefaultAuthPolicy =\n config.getOptionalBoolean(\n 'backend.auth.dangerouslyDisableDefaultAuthPolicy',\n ) ?? false;\n\n const keyDuration = { hours: 1 };\n\n const keySource = await createPluginKeySource({\n config,\n database,\n logger,\n keyDuration,\n });\n\n const userTokens = UserTokenHandler.create({\n discovery,\n });\n\n const pluginTokens = PluginTokenHandler.create({\n ownPluginId: plugin.getId(),\n logger,\n keySource,\n keyDuration,\n discovery,\n });\n\n const externalTokens = ExternalTokenHandler.create({\n ownPluginId: plugin.getId(),\n config,\n logger,\n });\n\n return new DefaultAuthService(\n userTokens,\n pluginTokens,\n externalTokens,\n plugin.getId(),\n disableDefaultAuthPolicy,\n keySource,\n );\n },\n});\n"],"names":["createServiceFactory","coreServices","createPluginKeySource","UserTokenHandler","PluginTokenHandler","ExternalTokenHandler","DefaultAuthService"],"mappings":";;;;;;;;;AAmCO,MAAM,qBAAqBA,qCAAqB,CAAA;AAAA,EACrD,SAASC,6BAAa,CAAA,IAAA;AAAA,EACtB,IAAM,EAAA;AAAA,IACJ,QAAQA,6BAAa,CAAA,UAAA;AAAA,IACrB,QAAQA,6BAAa,CAAA,UAAA;AAAA,IACrB,WAAWA,6BAAa,CAAA,SAAA;AAAA,IACxB,QAAQA,6BAAa,CAAA,cAAA;AAAA,IACrB,UAAUA,6BAAa,CAAA;AAAA,GACzB;AAAA,EACA,MAAM,QAAQ,EAAE,MAAA,EAAQ,WAAW,MAAQ,EAAA,MAAA,EAAQ,UAAY,EAAA;AAC7D,IAAA,MAAM,2BACJ,MAAO,CAAA,kBAAA;AAAA,MACL;AAAA,KACG,IAAA,KAAA;AAEP,IAAM,MAAA,WAAA,GAAc,EAAE,KAAA,EAAO,CAAE,EAAA;AAE/B,IAAM,MAAA,SAAA,GAAY,MAAMC,2CAAsB,CAAA;AAAA,MAC5C,MAAA;AAAA,MACA,QAAA;AAAA,MACA,MAAA;AAAA,MACA;AAAA,KACD,CAAA;AAED,IAAM,MAAA,UAAA,GAAaC,kCAAiB,MAAO,CAAA;AAAA,MACzC;AAAA,KACD,CAAA;AAED,IAAM,MAAA,YAAA,GAAeC,sCAAmB,MAAO,CAAA;AAAA,MAC7C,WAAA,EAAa,OAAO,KAAM,EAAA;AAAA,MAC1B,MAAA;AAAA,MACA,SAAA;AAAA,MACA,WAAA;AAAA,MACA;AAAA,KACD,CAAA;AAED,IAAM,MAAA,cAAA,GAAiBC,0CAAqB,MAAO,CAAA;AAAA,MACjD,WAAA,EAAa,OAAO,KAAM,EAAA;AAAA,MAC1B,MAAA;AAAA,MACA;AAAA,KACD,CAAA;AAED,IAAA,OAAO,IAAIC,qCAAA;AAAA,MACT,UAAA;AAAA,MACA,YAAA;AAAA,MACA,cAAA;AAAA,MACA,OAAO,KAAM,EAAA;AAAA,MACb,wBAAA;AAAA,MACA;AAAA,KACF;AAAA;AAEJ,CAAC;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"ExternalTokenHandler.cjs.js","sources":["../../../../src/entrypoints/auth/external/ExternalTokenHandler.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n BackstagePrincipalAccessRestrictions,\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { NotAllowedError } from '@backstage/errors';\nimport { LegacyTokenHandler } from './legacy';\nimport { StaticTokenHandler } from './static';\nimport { JWKSHandler } from './jwks';\nimport { TokenHandler } from './types';\n\nconst NEW_CONFIG_KEY = 'backend.auth.externalAccess';\nconst OLD_CONFIG_KEY = 'backend.auth.keys';\nlet loggedDeprecationWarning = false;\n\n/**\n * Handles all types of external caller token types (i.e. not Backstage user\n * tokens, nor Backstage backend plugin tokens).\n *\n * @internal\n */\nexport class ExternalTokenHandler {\n static create(options: {\n ownPluginId: string;\n config: RootConfigService;\n logger: LoggerService;\n }): ExternalTokenHandler {\n const { ownPluginId, config, logger } = options;\n\n const staticHandler = new StaticTokenHandler();\n const legacyHandler = new LegacyTokenHandler();\n const jwksHandler = new JWKSHandler();\n const handlers: Record<string, TokenHandler> = {\n static: staticHandler,\n legacy: legacyHandler,\n jwks: jwksHandler,\n };\n\n // Load the new-style handlers\n const handlerConfigs = config.getOptionalConfigArray(NEW_CONFIG_KEY) ?? [];\n for (const handlerConfig of handlerConfigs) {\n const type = handlerConfig.getString('type');\n const handler = handlers[type];\n if (!handler) {\n const valid = Object.keys(handlers)\n .map(k => `'${k}'`)\n .join(', ');\n throw new Error(\n `Unknown type '${type}' in ${NEW_CONFIG_KEY}, expected one of ${valid}`,\n );\n }\n handler.add(handlerConfig);\n }\n\n // Load the old keys too\n const legacyConfigs = config.getOptionalConfigArray(OLD_CONFIG_KEY) ?? [];\n if (legacyConfigs.length && !loggedDeprecationWarning) {\n loggedDeprecationWarning = true;\n logger.warn(\n `DEPRECATION WARNING: The ${OLD_CONFIG_KEY} config has been replaced by ${NEW_CONFIG_KEY}, see https://backstage.io/docs/auth/service-to-service-auth`,\n );\n }\n for (const handlerConfig of legacyConfigs) {\n legacyHandler.addOld(handlerConfig);\n }\n\n return new ExternalTokenHandler(ownPluginId, Object.values(handlers));\n }\n\n constructor(\n private readonly ownPluginId: string,\n private readonly handlers: TokenHandler[],\n ) {}\n\n async verifyToken(token: string): Promise<\n | {\n subject: string;\n accessRestrictions?: BackstagePrincipalAccessRestrictions;\n }\n | undefined\n > {\n for (const handler of this.handlers) {\n const result = await handler.verifyToken(token);\n if (result) {\n const { allAccessRestrictions, ...rest } = result;\n if (allAccessRestrictions) {\n const accessRestrictions = allAccessRestrictions.get(\n this.ownPluginId,\n );\n if (!accessRestrictions) {\n const valid = [...allAccessRestrictions.keys()]\n .map(k => `'${k}'`)\n .join(', ');\n throw new NotAllowedError(\n `This token's access is restricted to plugin(s) ${valid}`,\n );\n }\n\n return {\n ...rest,\n accessRestrictions,\n };\n }\n\n return rest;\n }\n }\n\n return undefined;\n }\n}\n"],"names":["StaticTokenHandler","LegacyTokenHandler","JWKSHandler","NotAllowedError"],"mappings":"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"}
1
+ {"version":3,"file":"ExternalTokenHandler.cjs.js","sources":["../../../../src/entrypoints/auth/external/ExternalTokenHandler.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n BackstagePrincipalAccessRestrictions,\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { NotAllowedError } from '@backstage/errors';\nimport { LegacyTokenHandler } from './legacy';\nimport { StaticTokenHandler } from './static';\nimport { JWKSHandler } from './jwks';\nimport { TokenHandler } from './types';\n\nconst NEW_CONFIG_KEY = 'backend.auth.externalAccess';\nconst OLD_CONFIG_KEY = 'backend.auth.keys';\nlet loggedDeprecationWarning = false;\n\n/**\n * Handles all types of external caller token types (i.e. not Backstage user\n * tokens, nor Backstage backend plugin tokens).\n *\n * @internal\n */\nexport class ExternalTokenHandler {\n static create(options: {\n ownPluginId: string;\n config: RootConfigService;\n logger: LoggerService;\n }): ExternalTokenHandler {\n const { ownPluginId, config, logger } = options;\n\n const staticHandler = new StaticTokenHandler();\n const legacyHandler = new LegacyTokenHandler();\n const jwksHandler = new JWKSHandler();\n const handlers: Record<string, TokenHandler> = {\n static: staticHandler,\n legacy: legacyHandler,\n jwks: jwksHandler,\n };\n\n // Load the new-style handlers\n const handlerConfigs = config.getOptionalConfigArray(NEW_CONFIG_KEY) ?? [];\n for (const handlerConfig of handlerConfigs) {\n const type = handlerConfig.getString('type');\n const handler = handlers[type];\n if (!handler) {\n const valid = Object.keys(handlers)\n .map(k => `'${k}'`)\n .join(', ');\n throw new Error(\n `Unknown type '${type}' in ${NEW_CONFIG_KEY}, expected one of ${valid}`,\n );\n }\n handler.add(handlerConfig);\n }\n\n // Load the old keys too\n const legacyConfigs = config.getOptionalConfigArray(OLD_CONFIG_KEY) ?? [];\n if (legacyConfigs.length && !loggedDeprecationWarning) {\n loggedDeprecationWarning = true;\n logger.warn(\n `DEPRECATION WARNING: The ${OLD_CONFIG_KEY} config has been replaced by ${NEW_CONFIG_KEY}, see https://backstage.io/docs/auth/service-to-service-auth`,\n );\n }\n for (const handlerConfig of legacyConfigs) {\n legacyHandler.addOld(handlerConfig);\n }\n\n return new ExternalTokenHandler(ownPluginId, Object.values(handlers));\n }\n\n constructor(\n private readonly ownPluginId: string,\n private readonly handlers: TokenHandler[],\n ) {}\n\n async verifyToken(token: string): Promise<\n | {\n subject: string;\n accessRestrictions?: BackstagePrincipalAccessRestrictions;\n }\n | undefined\n > {\n for (const handler of this.handlers) {\n const result = await handler.verifyToken(token);\n if (result) {\n const { allAccessRestrictions, ...rest } = result;\n if (allAccessRestrictions) {\n const accessRestrictions = allAccessRestrictions.get(\n this.ownPluginId,\n );\n if (!accessRestrictions) {\n const valid = [...allAccessRestrictions.keys()]\n .map(k => `'${k}'`)\n .join(', ');\n throw new NotAllowedError(\n `This token's access is restricted to plugin(s) ${valid}`,\n );\n }\n\n return {\n ...rest,\n accessRestrictions,\n };\n }\n\n return rest;\n }\n }\n\n return undefined;\n }\n}\n"],"names":["StaticTokenHandler","LegacyTokenHandler","JWKSHandler","NotAllowedError"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.cjs.js","sources":["../../../../src/entrypoints/auth/external/helpers.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { AccessRestriptionsMap } from './types';\n\n/**\n * Parses and returns the `accessRestrictions` configuration from an\n * `externalAccess` entry, or undefined if there wasn't one.\n *\n * @internal\n */\nexport function readAccessRestrictionsFromConfig(\n externalAccessEntryConfig: Config,\n): AccessRestriptionsMap | undefined {\n const configs =\n externalAccessEntryConfig.getOptionalConfigArray('accessRestrictions') ??\n [];\n\n const result: AccessRestriptionsMap = new Map();\n for (const config of configs) {\n const validKeys = ['plugin', 'permission', 'permissionAttribute'];\n for (const key of config.keys()) {\n if (!validKeys.includes(key)) {\n const valid = validKeys.map(k => `'${k}'`).join(', ');\n throw new Error(\n `Invalid key '${key}' in 'accessRestrictions' config, expected one of ${valid}`,\n );\n }\n }\n\n const pluginId = config.getString('plugin');\n const permissionNames = readPermissionNames(config);\n const permissionAttributes = readPermissionAttributes(config);\n\n if (result.has(pluginId)) {\n throw new Error(\n `Attempted to declare 'accessRestrictions' twice for plugin '${pluginId}', which is not permitted`,\n );\n }\n\n result.set(pluginId, {\n ...(permissionNames ? { permissionNames } : {}),\n ...(permissionAttributes ? { permissionAttributes } : {}),\n });\n }\n\n return result.size ? result : undefined;\n}\n\n/**\n * Reads a config value as a string or an array of strings, and deduplicates and\n * splits by comma/space into a string array. Can also validate against a known\n * set of values. Returns undefined if the key didn't exist or if the array\n * would have ended up being empty.\n *\n * @internal\n */\nexport function readStringOrStringArrayFromConfig<T extends string>(\n root: Config,\n key: string,\n validValues?: readonly T[],\n): T[] | undefined {\n if (!root.has(key)) {\n return undefined;\n }\n\n const rawValues = Array.isArray(root.get(key))\n ? root.getStringArray(key)\n : [root.getString(key)];\n\n const values = [\n ...new Set(\n rawValues\n .map(v => v.split(/[ ,]/))\n .flat()\n .filter(Boolean),\n ),\n ];\n\n if (!values.length) {\n return undefined;\n }\n\n if (validValues?.length) {\n for (const value of values) {\n if (!validValues.includes(value as T)) {\n const valid = validValues.map(k => `'${k}'`).join(', ');\n throw new Error(\n `Invalid value '${value}' at '${key}' in 'permissionAttributes' config, valid values are ${valid}`,\n );\n }\n }\n }\n\n return values as T[];\n}\n\nfunction readPermissionNames(externalAccessEntryConfig: Config) {\n return readStringOrStringArrayFromConfig(\n externalAccessEntryConfig,\n 'permission',\n );\n}\n\nfunction readPermissionAttributes(externalAccessEntryConfig: Config) {\n const config = externalAccessEntryConfig.getOptionalConfig(\n 'permissionAttribute',\n );\n if (!config) {\n return undefined;\n }\n\n const validKeys = ['action'];\n for (const key of config.keys()) {\n if (!validKeys.includes(key)) {\n const valid = validKeys.map(k => `'${k}'`).join(', ');\n throw new Error(\n `Invalid key '${key}' in 'permissionAttribute' config, expected ${valid}`,\n );\n }\n }\n\n const action = readStringOrStringArrayFromConfig(config, 'action', [\n 'create',\n 'read',\n 'update',\n 'delete',\n ]);\n\n const result = {\n ...(action ? { action } : {}),\n };\n\n return Object.keys(result).length ? result : undefined;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"helpers.cjs.js","sources":["../../../../src/entrypoints/auth/external/helpers.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { AccessRestriptionsMap } from './types';\n\n/**\n * Parses and returns the `accessRestrictions` configuration from an\n * `externalAccess` entry, or undefined if there wasn't one.\n *\n * @internal\n */\nexport function readAccessRestrictionsFromConfig(\n externalAccessEntryConfig: Config,\n): AccessRestriptionsMap | undefined {\n const configs =\n externalAccessEntryConfig.getOptionalConfigArray('accessRestrictions') ??\n [];\n\n const result: AccessRestriptionsMap = new Map();\n for (const config of configs) {\n const validKeys = ['plugin', 'permission', 'permissionAttribute'];\n for (const key of config.keys()) {\n if (!validKeys.includes(key)) {\n const valid = validKeys.map(k => `'${k}'`).join(', ');\n throw new Error(\n `Invalid key '${key}' in 'accessRestrictions' config, expected one of ${valid}`,\n );\n }\n }\n\n const pluginId = config.getString('plugin');\n const permissionNames = readPermissionNames(config);\n const permissionAttributes = readPermissionAttributes(config);\n\n if (result.has(pluginId)) {\n throw new Error(\n `Attempted to declare 'accessRestrictions' twice for plugin '${pluginId}', which is not permitted`,\n );\n }\n\n result.set(pluginId, {\n ...(permissionNames ? { permissionNames } : {}),\n ...(permissionAttributes ? { permissionAttributes } : {}),\n });\n }\n\n return result.size ? result : undefined;\n}\n\n/**\n * Reads a config value as a string or an array of strings, and deduplicates and\n * splits by comma/space into a string array. Can also validate against a known\n * set of values. Returns undefined if the key didn't exist or if the array\n * would have ended up being empty.\n *\n * @internal\n */\nexport function readStringOrStringArrayFromConfig<T extends string>(\n root: Config,\n key: string,\n validValues?: readonly T[],\n): T[] | undefined {\n if (!root.has(key)) {\n return undefined;\n }\n\n const rawValues = Array.isArray(root.get(key))\n ? root.getStringArray(key)\n : [root.getString(key)];\n\n const values = [\n ...new Set(\n rawValues\n .map(v => v.split(/[ ,]/))\n .flat()\n .filter(Boolean),\n ),\n ];\n\n if (!values.length) {\n return undefined;\n }\n\n if (validValues?.length) {\n for (const value of values) {\n if (!validValues.includes(value as T)) {\n const valid = validValues.map(k => `'${k}'`).join(', ');\n throw new Error(\n `Invalid value '${value}' at '${key}' in 'permissionAttributes' config, valid values are ${valid}`,\n );\n }\n }\n }\n\n return values as T[];\n}\n\nfunction readPermissionNames(externalAccessEntryConfig: Config) {\n return readStringOrStringArrayFromConfig(\n externalAccessEntryConfig,\n 'permission',\n );\n}\n\nfunction readPermissionAttributes(externalAccessEntryConfig: Config) {\n const config = externalAccessEntryConfig.getOptionalConfig(\n 'permissionAttribute',\n );\n if (!config) {\n return undefined;\n }\n\n const validKeys = ['action'];\n for (const key of config.keys()) {\n if (!validKeys.includes(key)) {\n const valid = validKeys.map(k => `'${k}'`).join(', ');\n throw new Error(\n `Invalid key '${key}' in 'permissionAttribute' config, expected ${valid}`,\n );\n }\n }\n\n const action = readStringOrStringArrayFromConfig(config, 'action', [\n 'create',\n 'read',\n 'update',\n 'delete',\n ]);\n\n const result = {\n ...(action ? { action } : {}),\n };\n\n return Object.keys(result).length ? result : undefined;\n}\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"jwks.cjs.js","sources":["../../../../src/entrypoints/auth/external/jwks.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { jwtVerify, createRemoteJWKSet, JWTVerifyGetKey } from 'jose';\nimport { Config } from '@backstage/config';\nimport {\n readAccessRestrictionsFromConfig,\n readStringOrStringArrayFromConfig,\n} from './helpers';\nimport { AccessRestriptionsMap, TokenHandler } from './types';\n\n/**\n * Handles `type: jwks` access.\n *\n * @internal\n */\nexport class JWKSHandler implements TokenHandler {\n #entries: Array<{\n algorithms?: string[];\n audiences?: string[];\n issuers?: string[];\n subjectPrefix?: string;\n url: URL;\n jwks: JWTVerifyGetKey;\n allAccessRestrictions?: AccessRestriptionsMap;\n }> = [];\n\n add(config: Config) {\n if (!config.getString('options.url').match(/^\\S+$/)) {\n throw new Error(\n 'Illegal JWKS URL, must be a set of non-space characters',\n );\n }\n\n const algorithms = readStringOrStringArrayFromConfig(\n config,\n 'options.algorithm',\n );\n const issuers = readStringOrStringArrayFromConfig(config, 'options.issuer');\n const audiences = readStringOrStringArrayFromConfig(\n config,\n 'options.audience',\n );\n const subjectPrefix = config.getOptionalString('options.subjectPrefix');\n const url = new URL(config.getString('options.url'));\n const jwks = createRemoteJWKSet(url);\n const allAccessRestrictions = readAccessRestrictionsFromConfig(config);\n\n this.#entries.push({\n algorithms,\n audiences,\n issuers,\n jwks,\n subjectPrefix,\n url,\n allAccessRestrictions,\n });\n }\n\n async verifyToken(token: string) {\n for (const entry of this.#entries) {\n try {\n const {\n payload: { sub },\n } = await jwtVerify(token, entry.jwks, {\n algorithms: entry.algorithms,\n issuer: entry.issuers,\n audience: entry.audiences,\n });\n\n if (sub) {\n const prefix = entry.subjectPrefix\n ? `external:${entry.subjectPrefix}:`\n : 'external:';\n return {\n subject: `${prefix}${sub}`,\n allAccessRestrictions: entry.allAccessRestrictions,\n };\n }\n } catch {\n continue;\n }\n }\n return undefined;\n }\n}\n"],"names":["readStringOrStringArrayFromConfig","createRemoteJWKSet","readAccessRestrictionsFromConfig","jwtVerify"],"mappings":";;;;;AA6BO,MAAM,WAAoC,CAAA;AAAA,EAC/C,WAQK,EAAC,CAAA;AAAA,EAEN,IAAI,MAAgB,EAAA;AAClB,IAAA,IAAI,CAAC,MAAO,CAAA,SAAA,CAAU,aAAa,CAAE,CAAA,KAAA,CAAM,OAAO,CAAG,EAAA;AACnD,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,yDAAA;AAAA,OACF,CAAA;AAAA,KACF;AAEA,IAAA,MAAM,UAAa,GAAAA,yCAAA;AAAA,MACjB,MAAA;AAAA,MACA,mBAAA;AAAA,KACF,CAAA;AACA,IAAM,MAAA,OAAA,GAAUA,yCAAkC,CAAA,MAAA,EAAQ,gBAAgB,CAAA,CAAA;AAC1E,IAAA,MAAM,SAAY,GAAAA,yCAAA;AAAA,MAChB,MAAA;AAAA,MACA,kBAAA;AAAA,KACF,CAAA;AACA,IAAM,MAAA,aAAA,GAAgB,MAAO,CAAA,iBAAA,CAAkB,uBAAuB,CAAA,CAAA;AACtE,IAAA,MAAM,MAAM,IAAI,GAAA,CAAI,MAAO,CAAA,SAAA,CAAU,aAAa,CAAC,CAAA,CAAA;AACnD,IAAM,MAAA,IAAA,GAAOC,wBAAmB,GAAG,CAAA,CAAA;AACnC,IAAM,MAAA,qBAAA,GAAwBC,yCAAiC,MAAM,CAAA,CAAA;AAErE,IAAA,IAAA,CAAK,SAAS,IAAK,CAAA;AAAA,MACjB,UAAA;AAAA,MACA,SAAA;AAAA,MACA,OAAA;AAAA,MACA,IAAA;AAAA,MACA,aAAA;AAAA,MACA,GAAA;AAAA,MACA,qBAAA;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AAAA,EAEA,MAAM,YAAY,KAAe,EAAA;AAC/B,IAAW,KAAA,MAAA,KAAA,IAAS,KAAK,QAAU,EAAA;AACjC,MAAI,IAAA;AACF,QAAM,MAAA;AAAA,UACJ,OAAA,EAAS,EAAE,GAAI,EAAA;AAAA,SACb,GAAA,MAAMC,cAAU,CAAA,KAAA,EAAO,MAAM,IAAM,EAAA;AAAA,UACrC,YAAY,KAAM,CAAA,UAAA;AAAA,UAClB,QAAQ,KAAM,CAAA,OAAA;AAAA,UACd,UAAU,KAAM,CAAA,SAAA;AAAA,SACjB,CAAA,CAAA;AAED,QAAA,IAAI,GAAK,EAAA;AACP,UAAA,MAAM,SAAS,KAAM,CAAA,aAAA,GACjB,CAAY,SAAA,EAAA,KAAA,CAAM,aAAa,CAC/B,CAAA,CAAA,GAAA,WAAA,CAAA;AACJ,UAAO,OAAA;AAAA,YACL,OAAS,EAAA,CAAA,EAAG,MAAM,CAAA,EAAG,GAAG,CAAA,CAAA;AAAA,YACxB,uBAAuB,KAAM,CAAA,qBAAA;AAAA,WAC/B,CAAA;AAAA,SACF;AAAA,OACM,CAAA,MAAA;AACN,QAAA,SAAA;AAAA,OACF;AAAA,KACF;AACA,IAAO,OAAA,KAAA,CAAA,CAAA;AAAA,GACT;AACF;;;;"}
1
+ {"version":3,"file":"jwks.cjs.js","sources":["../../../../src/entrypoints/auth/external/jwks.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { jwtVerify, createRemoteJWKSet, JWTVerifyGetKey } from 'jose';\nimport { Config } from '@backstage/config';\nimport {\n readAccessRestrictionsFromConfig,\n readStringOrStringArrayFromConfig,\n} from './helpers';\nimport { AccessRestriptionsMap, TokenHandler } from './types';\n\n/**\n * Handles `type: jwks` access.\n *\n * @internal\n */\nexport class JWKSHandler implements TokenHandler {\n #entries: Array<{\n algorithms?: string[];\n audiences?: string[];\n issuers?: string[];\n subjectPrefix?: string;\n url: URL;\n jwks: JWTVerifyGetKey;\n allAccessRestrictions?: AccessRestriptionsMap;\n }> = [];\n\n add(config: Config) {\n if (!config.getString('options.url').match(/^\\S+$/)) {\n throw new Error(\n 'Illegal JWKS URL, must be a set of non-space characters',\n );\n }\n\n const algorithms = readStringOrStringArrayFromConfig(\n config,\n 'options.algorithm',\n );\n const issuers = readStringOrStringArrayFromConfig(config, 'options.issuer');\n const audiences = readStringOrStringArrayFromConfig(\n config,\n 'options.audience',\n );\n const subjectPrefix = config.getOptionalString('options.subjectPrefix');\n const url = new URL(config.getString('options.url'));\n const jwks = createRemoteJWKSet(url);\n const allAccessRestrictions = readAccessRestrictionsFromConfig(config);\n\n this.#entries.push({\n algorithms,\n audiences,\n issuers,\n jwks,\n subjectPrefix,\n url,\n allAccessRestrictions,\n });\n }\n\n async verifyToken(token: string) {\n for (const entry of this.#entries) {\n try {\n const {\n payload: { sub },\n } = await jwtVerify(token, entry.jwks, {\n algorithms: entry.algorithms,\n issuer: entry.issuers,\n audience: entry.audiences,\n });\n\n if (sub) {\n const prefix = entry.subjectPrefix\n ? `external:${entry.subjectPrefix}:`\n : 'external:';\n return {\n subject: `${prefix}${sub}`,\n allAccessRestrictions: entry.allAccessRestrictions,\n };\n }\n } catch {\n continue;\n }\n }\n return undefined;\n }\n}\n"],"names":["readStringOrStringArrayFromConfig","createRemoteJWKSet","readAccessRestrictionsFromConfig","jwtVerify"],"mappings":";;;;;AA6BO,MAAM,WAAoC,CAAA;AAAA,EAC/C,WAQK,EAAC;AAAA,EAEN,IAAI,MAAgB,EAAA;AAClB,IAAA,IAAI,CAAC,MAAO,CAAA,SAAA,CAAU,aAAa,CAAE,CAAA,KAAA,CAAM,OAAO,CAAG,EAAA;AACnD,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA;AAGF,IAAA,MAAM,UAAa,GAAAA,yCAAA;AAAA,MACjB,MAAA;AAAA,MACA;AAAA,KACF;AACA,IAAM,MAAA,OAAA,GAAUA,yCAAkC,CAAA,MAAA,EAAQ,gBAAgB,CAAA;AAC1E,IAAA,MAAM,SAAY,GAAAA,yCAAA;AAAA,MAChB,MAAA;AAAA,MACA;AAAA,KACF;AACA,IAAM,MAAA,aAAA,GAAgB,MAAO,CAAA,iBAAA,CAAkB,uBAAuB,CAAA;AACtE,IAAA,MAAM,MAAM,IAAI,GAAA,CAAI,MAAO,CAAA,SAAA,CAAU,aAAa,CAAC,CAAA;AACnD,IAAM,MAAA,IAAA,GAAOC,wBAAmB,GAAG,CAAA;AACnC,IAAM,MAAA,qBAAA,GAAwBC,yCAAiC,MAAM,CAAA;AAErE,IAAA,IAAA,CAAK,SAAS,IAAK,CAAA;AAAA,MACjB,UAAA;AAAA,MACA,SAAA;AAAA,MACA,OAAA;AAAA,MACA,IAAA;AAAA,MACA,aAAA;AAAA,MACA,GAAA;AAAA,MACA;AAAA,KACD,CAAA;AAAA;AACH,EAEA,MAAM,YAAY,KAAe,EAAA;AAC/B,IAAW,KAAA,MAAA,KAAA,IAAS,KAAK,QAAU,EAAA;AACjC,MAAI,IAAA;AACF,QAAM,MAAA;AAAA,UACJ,OAAA,EAAS,EAAE,GAAI;AAAA,SACb,GAAA,MAAMC,cAAU,CAAA,KAAA,EAAO,MAAM,IAAM,EAAA;AAAA,UACrC,YAAY,KAAM,CAAA,UAAA;AAAA,UAClB,QAAQ,KAAM,CAAA,OAAA;AAAA,UACd,UAAU,KAAM,CAAA;AAAA,SACjB,CAAA;AAED,QAAA,IAAI,GAAK,EAAA;AACP,UAAA,MAAM,SAAS,KAAM,CAAA,aAAA,GACjB,CAAY,SAAA,EAAA,KAAA,CAAM,aAAa,CAC/B,CAAA,CAAA,GAAA,WAAA;AACJ,UAAO,OAAA;AAAA,YACL,OAAS,EAAA,CAAA,EAAG,MAAM,CAAA,EAAG,GAAG,CAAA,CAAA;AAAA,YACxB,uBAAuB,KAAM,CAAA;AAAA,WAC/B;AAAA;AACF,OACM,CAAA,MAAA;AACN,QAAA;AAAA;AACF;AAEF,IAAO,OAAA,KAAA,CAAA;AAAA;AAEX;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"legacy.cjs.js","sources":["../../../../src/entrypoints/auth/external/legacy.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { base64url, decodeJwt, decodeProtectedHeader, jwtVerify } from 'jose';\nimport { readAccessRestrictionsFromConfig } from './helpers';\nimport { AccessRestriptionsMap, TokenHandler } from './types';\n\n/**\n * Handles `type: legacy` access.\n *\n * @internal\n */\nexport class LegacyTokenHandler implements TokenHandler {\n #entries = new Array<{\n key: Uint8Array;\n result: {\n subject: string;\n allAccessRestrictions?: AccessRestriptionsMap;\n };\n }>();\n\n add(config: Config) {\n const allAccessRestrictions = readAccessRestrictionsFromConfig(config);\n this.#doAdd(\n config.getString('options.secret'),\n config.getString('options.subject'),\n allAccessRestrictions,\n );\n }\n\n // used only for the old backend.auth.keys array\n addOld(config: Config) {\n // This choice of subject is for compatibility reasons\n this.#doAdd(config.getString('secret'), 'external:backstage-plugin');\n }\n\n #doAdd(\n secret: string,\n subject: string,\n allAccessRestrictions?: AccessRestriptionsMap,\n ) {\n if (!secret.match(/^\\S+$/)) {\n throw new Error('Illegal secret, must be a valid base64 string');\n } else if (!subject.match(/^\\S+$/)) {\n throw new Error('Illegal subject, must be a set of non-space characters');\n }\n\n let key: Uint8Array;\n try {\n key = base64url.decode(secret);\n } catch {\n throw new Error('Illegal secret, must be a valid base64 string');\n }\n\n if (this.#entries.some(e => e.key === key)) {\n throw new Error(\n 'Legacy externalAccess token was declared more than once',\n );\n }\n\n this.#entries.push({\n key,\n result: {\n subject,\n allAccessRestrictions,\n },\n });\n }\n\n async verifyToken(token: string) {\n // First do a duck typing check to see if it remotely looks like a legacy token\n try {\n // We do a fair amount of checking upfront here. Since we aren't certain\n // that it's even the right type of key that we're looking at, we can't\n // defer eg the alg check to jwtVerify, because it won't be possible to\n // discern different reasons for key verification failures from each other\n // easily\n const { alg } = decodeProtectedHeader(token);\n if (alg !== 'HS256') {\n return undefined;\n }\n const { sub, aud } = decodeJwt(token);\n if (sub !== 'backstage-server' || aud) {\n return undefined;\n }\n } catch (e) {\n // Doesn't look like a jwt at all\n return undefined;\n }\n\n for (const { key, result } of this.#entries) {\n try {\n await jwtVerify(token, key);\n return result;\n } catch (e) {\n if (e.code !== 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') {\n throw e;\n }\n // Otherwise continue to try the next key\n }\n }\n\n // None of the signing keys matched\n return undefined;\n }\n}\n"],"names":["readAccessRestrictionsFromConfig","base64url","decodeProtectedHeader","decodeJwt","jwtVerify"],"mappings":";;;;;AA0BO,MAAM,kBAA2C,CAAA;AAAA,EACtD,QAAA,GAAW,IAAI,KAMZ,EAAA,CAAA;AAAA,EAEH,IAAI,MAAgB,EAAA;AAClB,IAAM,MAAA,qBAAA,GAAwBA,yCAAiC,MAAM,CAAA,CAAA;AACrE,IAAK,IAAA,CAAA,MAAA;AAAA,MACH,MAAA,CAAO,UAAU,gBAAgB,CAAA;AAAA,MACjC,MAAA,CAAO,UAAU,iBAAiB,CAAA;AAAA,MAClC,qBAAA;AAAA,KACF,CAAA;AAAA,GACF;AAAA;AAAA,EAGA,OAAO,MAAgB,EAAA;AAErB,IAAA,IAAA,CAAK,MAAO,CAAA,MAAA,CAAO,SAAU,CAAA,QAAQ,GAAG,2BAA2B,CAAA,CAAA;AAAA,GACrE;AAAA,EAEA,MAAA,CACE,MACA,EAAA,OAAA,EACA,qBACA,EAAA;AACA,IAAA,IAAI,CAAC,MAAA,CAAO,KAAM,CAAA,OAAO,CAAG,EAAA;AAC1B,MAAM,MAAA,IAAI,MAAM,+CAA+C,CAAA,CAAA;AAAA,KACtD,MAAA,IAAA,CAAC,OAAQ,CAAA,KAAA,CAAM,OAAO,CAAG,EAAA;AAClC,MAAM,MAAA,IAAI,MAAM,wDAAwD,CAAA,CAAA;AAAA,KAC1E;AAEA,IAAI,IAAA,GAAA,CAAA;AACJ,IAAI,IAAA;AACF,MAAM,GAAA,GAAAC,cAAA,CAAU,OAAO,MAAM,CAAA,CAAA;AAAA,KACvB,CAAA,MAAA;AACN,MAAM,MAAA,IAAI,MAAM,+CAA+C,CAAA,CAAA;AAAA,KACjE;AAEA,IAAA,IAAI,KAAK,QAAS,CAAA,IAAA,CAAK,OAAK,CAAE,CAAA,GAAA,KAAQ,GAAG,CAAG,EAAA;AAC1C,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,yDAAA;AAAA,OACF,CAAA;AAAA,KACF;AAEA,IAAA,IAAA,CAAK,SAAS,IAAK,CAAA;AAAA,MACjB,GAAA;AAAA,MACA,MAAQ,EAAA;AAAA,QACN,OAAA;AAAA,QACA,qBAAA;AAAA,OACF;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AAAA,EAEA,MAAM,YAAY,KAAe,EAAA;AAE/B,IAAI,IAAA;AAMF,MAAA,MAAM,EAAE,GAAA,EAAQ,GAAAC,0BAAA,CAAsB,KAAK,CAAA,CAAA;AAC3C,MAAA,IAAI,QAAQ,OAAS,EAAA;AACnB,QAAO,OAAA,KAAA,CAAA,CAAA;AAAA,OACT;AACA,MAAA,MAAM,EAAE,GAAA,EAAK,GAAI,EAAA,GAAIC,eAAU,KAAK,CAAA,CAAA;AACpC,MAAI,IAAA,GAAA,KAAQ,sBAAsB,GAAK,EAAA;AACrC,QAAO,OAAA,KAAA,CAAA,CAAA;AAAA,OACT;AAAA,aACO,CAAG,EAAA;AAEV,MAAO,OAAA,KAAA,CAAA,CAAA;AAAA,KACT;AAEA,IAAA,KAAA,MAAW,EAAE,GAAA,EAAK,MAAO,EAAA,IAAK,KAAK,QAAU,EAAA;AAC3C,MAAI,IAAA;AACF,QAAM,MAAAC,cAAA,CAAU,OAAO,GAAG,CAAA,CAAA;AAC1B,QAAO,OAAA,MAAA,CAAA;AAAA,eACA,CAAG,EAAA;AACV,QAAI,IAAA,CAAA,CAAE,SAAS,uCAAyC,EAAA;AACtD,UAAM,MAAA,CAAA,CAAA;AAAA,SACR;AAAA,OAEF;AAAA,KACF;AAGA,IAAO,OAAA,KAAA,CAAA,CAAA;AAAA,GACT;AACF;;;;"}
1
+ {"version":3,"file":"legacy.cjs.js","sources":["../../../../src/entrypoints/auth/external/legacy.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { base64url, decodeJwt, decodeProtectedHeader, jwtVerify } from 'jose';\nimport { readAccessRestrictionsFromConfig } from './helpers';\nimport { AccessRestriptionsMap, TokenHandler } from './types';\n\n/**\n * Handles `type: legacy` access.\n *\n * @internal\n */\nexport class LegacyTokenHandler implements TokenHandler {\n #entries = new Array<{\n key: Uint8Array;\n result: {\n subject: string;\n allAccessRestrictions?: AccessRestriptionsMap;\n };\n }>();\n\n add(config: Config) {\n const allAccessRestrictions = readAccessRestrictionsFromConfig(config);\n this.#doAdd(\n config.getString('options.secret'),\n config.getString('options.subject'),\n allAccessRestrictions,\n );\n }\n\n // used only for the old backend.auth.keys array\n addOld(config: Config) {\n // This choice of subject is for compatibility reasons\n this.#doAdd(config.getString('secret'), 'external:backstage-plugin');\n }\n\n #doAdd(\n secret: string,\n subject: string,\n allAccessRestrictions?: AccessRestriptionsMap,\n ) {\n if (!secret.match(/^\\S+$/)) {\n throw new Error('Illegal secret, must be a valid base64 string');\n } else if (!subject.match(/^\\S+$/)) {\n throw new Error('Illegal subject, must be a set of non-space characters');\n }\n\n let key: Uint8Array;\n try {\n key = base64url.decode(secret);\n } catch {\n throw new Error('Illegal secret, must be a valid base64 string');\n }\n\n if (this.#entries.some(e => e.key === key)) {\n throw new Error(\n 'Legacy externalAccess token was declared more than once',\n );\n }\n\n this.#entries.push({\n key,\n result: {\n subject,\n allAccessRestrictions,\n },\n });\n }\n\n async verifyToken(token: string) {\n // First do a duck typing check to see if it remotely looks like a legacy token\n try {\n // We do a fair amount of checking upfront here. Since we aren't certain\n // that it's even the right type of key that we're looking at, we can't\n // defer eg the alg check to jwtVerify, because it won't be possible to\n // discern different reasons for key verification failures from each other\n // easily\n const { alg } = decodeProtectedHeader(token);\n if (alg !== 'HS256') {\n return undefined;\n }\n const { sub, aud } = decodeJwt(token);\n if (sub !== 'backstage-server' || aud) {\n return undefined;\n }\n } catch (e) {\n // Doesn't look like a jwt at all\n return undefined;\n }\n\n for (const { key, result } of this.#entries) {\n try {\n await jwtVerify(token, key);\n return result;\n } catch (e) {\n if (e.code !== 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') {\n throw e;\n }\n // Otherwise continue to try the next key\n }\n }\n\n // None of the signing keys matched\n return undefined;\n }\n}\n"],"names":["readAccessRestrictionsFromConfig","base64url","decodeProtectedHeader","decodeJwt","jwtVerify"],"mappings":";;;;;AA0BO,MAAM,kBAA2C,CAAA;AAAA,EACtD,QAAA,GAAW,IAAI,KAMZ,EAAA;AAAA,EAEH,IAAI,MAAgB,EAAA;AAClB,IAAM,MAAA,qBAAA,GAAwBA,yCAAiC,MAAM,CAAA;AACrE,IAAK,IAAA,CAAA,MAAA;AAAA,MACH,MAAA,CAAO,UAAU,gBAAgB,CAAA;AAAA,MACjC,MAAA,CAAO,UAAU,iBAAiB,CAAA;AAAA,MAClC;AAAA,KACF;AAAA;AACF;AAAA,EAGA,OAAO,MAAgB,EAAA;AAErB,IAAA,IAAA,CAAK,MAAO,CAAA,MAAA,CAAO,SAAU,CAAA,QAAQ,GAAG,2BAA2B,CAAA;AAAA;AACrE,EAEA,MAAA,CACE,MACA,EAAA,OAAA,EACA,qBACA,EAAA;AACA,IAAA,IAAI,CAAC,MAAA,CAAO,KAAM,CAAA,OAAO,CAAG,EAAA;AAC1B,MAAM,MAAA,IAAI,MAAM,+CAA+C,CAAA;AAAA,KACtD,MAAA,IAAA,CAAC,OAAQ,CAAA,KAAA,CAAM,OAAO,CAAG,EAAA;AAClC,MAAM,MAAA,IAAI,MAAM,wDAAwD,CAAA;AAAA;AAG1E,IAAI,IAAA,GAAA;AACJ,IAAI,IAAA;AACF,MAAM,GAAA,GAAAC,cAAA,CAAU,OAAO,MAAM,CAAA;AAAA,KACvB,CAAA,MAAA;AACN,MAAM,MAAA,IAAI,MAAM,+CAA+C,CAAA;AAAA;AAGjE,IAAA,IAAI,KAAK,QAAS,CAAA,IAAA,CAAK,OAAK,CAAE,CAAA,GAAA,KAAQ,GAAG,CAAG,EAAA;AAC1C,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA;AAGF,IAAA,IAAA,CAAK,SAAS,IAAK,CAAA;AAAA,MACjB,GAAA;AAAA,MACA,MAAQ,EAAA;AAAA,QACN,OAAA;AAAA,QACA;AAAA;AACF,KACD,CAAA;AAAA;AACH,EAEA,MAAM,YAAY,KAAe,EAAA;AAE/B,IAAI,IAAA;AAMF,MAAA,MAAM,EAAE,GAAA,EAAQ,GAAAC,0BAAA,CAAsB,KAAK,CAAA;AAC3C,MAAA,IAAI,QAAQ,OAAS,EAAA;AACnB,QAAO,OAAA,KAAA,CAAA;AAAA;AAET,MAAA,MAAM,EAAE,GAAA,EAAK,GAAI,EAAA,GAAIC,eAAU,KAAK,CAAA;AACpC,MAAI,IAAA,GAAA,KAAQ,sBAAsB,GAAK,EAAA;AACrC,QAAO,OAAA,KAAA,CAAA;AAAA;AACT,aACO,CAAG,EAAA;AAEV,MAAO,OAAA,KAAA,CAAA;AAAA;AAGT,IAAA,KAAA,MAAW,EAAE,GAAA,EAAK,MAAO,EAAA,IAAK,KAAK,QAAU,EAAA;AAC3C,MAAI,IAAA;AACF,QAAM,MAAAC,cAAA,CAAU,OAAO,GAAG,CAAA;AAC1B,QAAO,OAAA,MAAA;AAAA,eACA,CAAG,EAAA;AACV,QAAI,IAAA,CAAA,CAAE,SAAS,uCAAyC,EAAA;AACtD,UAAM,MAAA,CAAA;AAAA;AACR;AAEF;AAIF,IAAO,OAAA,KAAA,CAAA;AAAA;AAEX;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"static.cjs.js","sources":["../../../../src/entrypoints/auth/external/static.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { readAccessRestrictionsFromConfig } from './helpers';\nimport { AccessRestriptionsMap, TokenHandler } from './types';\n\nconst MIN_TOKEN_LENGTH = 8;\n\n/**\n * Handles `type: static` access.\n *\n * @internal\n */\nexport class StaticTokenHandler implements TokenHandler {\n #entries = new Map<\n string,\n {\n subject: string;\n allAccessRestrictions?: AccessRestriptionsMap;\n }\n >();\n\n add(config: Config) {\n const token = config.getString('options.token');\n const subject = config.getString('options.subject');\n const allAccessRestrictions = readAccessRestrictionsFromConfig(config);\n\n if (!token.match(/^\\S+$/)) {\n throw new Error('Illegal token, must be a set of non-space characters');\n } else if (token.length < MIN_TOKEN_LENGTH) {\n throw new Error(\n `Illegal token, must be at least ${MIN_TOKEN_LENGTH} characters length`,\n );\n } else if (!subject.match(/^\\S+$/)) {\n throw new Error('Illegal subject, must be a set of non-space characters');\n } else if (this.#entries.has(token)) {\n throw new Error(\n 'Static externalAccess token was declared more than once',\n );\n }\n\n this.#entries.set(token, { subject, allAccessRestrictions });\n }\n\n async verifyToken(token: string) {\n return this.#entries.get(token);\n }\n}\n"],"names":["readAccessRestrictionsFromConfig"],"mappings":";;;;AAoBA,MAAM,gBAAmB,GAAA,CAAA,CAAA;AAOlB,MAAM,kBAA2C,CAAA;AAAA,EACtD,QAAA,uBAAe,GAMb,EAAA,CAAA;AAAA,EAEF,IAAI,MAAgB,EAAA;AAClB,IAAM,MAAA,KAAA,GAAQ,MAAO,CAAA,SAAA,CAAU,eAAe,CAAA,CAAA;AAC9C,IAAM,MAAA,OAAA,GAAU,MAAO,CAAA,SAAA,CAAU,iBAAiB,CAAA,CAAA;AAClD,IAAM,MAAA,qBAAA,GAAwBA,yCAAiC,MAAM,CAAA,CAAA;AAErE,IAAA,IAAI,CAAC,KAAA,CAAM,KAAM,CAAA,OAAO,CAAG,EAAA;AACzB,MAAM,MAAA,IAAI,MAAM,sDAAsD,CAAA,CAAA;AAAA,KACxE,MAAA,IAAW,KAAM,CAAA,MAAA,GAAS,gBAAkB,EAAA;AAC1C,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,mCAAmC,gBAAgB,CAAA,kBAAA,CAAA;AAAA,OACrD,CAAA;AAAA,KACS,MAAA,IAAA,CAAC,OAAQ,CAAA,KAAA,CAAM,OAAO,CAAG,EAAA;AAClC,MAAM,MAAA,IAAI,MAAM,wDAAwD,CAAA,CAAA;AAAA,KAC/D,MAAA,IAAA,IAAA,CAAK,QAAS,CAAA,GAAA,CAAI,KAAK,CAAG,EAAA;AACnC,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,yDAAA;AAAA,OACF,CAAA;AAAA,KACF;AAEA,IAAA,IAAA,CAAK,SAAS,GAAI,CAAA,KAAA,EAAO,EAAE,OAAA,EAAS,uBAAuB,CAAA,CAAA;AAAA,GAC7D;AAAA,EAEA,MAAM,YAAY,KAAe,EAAA;AAC/B,IAAO,OAAA,IAAA,CAAK,QAAS,CAAA,GAAA,CAAI,KAAK,CAAA,CAAA;AAAA,GAChC;AACF;;;;"}
1
+ {"version":3,"file":"static.cjs.js","sources":["../../../../src/entrypoints/auth/external/static.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { readAccessRestrictionsFromConfig } from './helpers';\nimport { AccessRestriptionsMap, TokenHandler } from './types';\n\nconst MIN_TOKEN_LENGTH = 8;\n\n/**\n * Handles `type: static` access.\n *\n * @internal\n */\nexport class StaticTokenHandler implements TokenHandler {\n #entries = new Map<\n string,\n {\n subject: string;\n allAccessRestrictions?: AccessRestriptionsMap;\n }\n >();\n\n add(config: Config) {\n const token = config.getString('options.token');\n const subject = config.getString('options.subject');\n const allAccessRestrictions = readAccessRestrictionsFromConfig(config);\n\n if (!token.match(/^\\S+$/)) {\n throw new Error('Illegal token, must be a set of non-space characters');\n } else if (token.length < MIN_TOKEN_LENGTH) {\n throw new Error(\n `Illegal token, must be at least ${MIN_TOKEN_LENGTH} characters length`,\n );\n } else if (!subject.match(/^\\S+$/)) {\n throw new Error('Illegal subject, must be a set of non-space characters');\n } else if (this.#entries.has(token)) {\n throw new Error(\n 'Static externalAccess token was declared more than once',\n );\n }\n\n this.#entries.set(token, { subject, allAccessRestrictions });\n }\n\n async verifyToken(token: string) {\n return this.#entries.get(token);\n }\n}\n"],"names":["readAccessRestrictionsFromConfig"],"mappings":";;;;AAoBA,MAAM,gBAAmB,GAAA,CAAA;AAOlB,MAAM,kBAA2C,CAAA;AAAA,EACtD,QAAA,uBAAe,GAMb,EAAA;AAAA,EAEF,IAAI,MAAgB,EAAA;AAClB,IAAM,MAAA,KAAA,GAAQ,MAAO,CAAA,SAAA,CAAU,eAAe,CAAA;AAC9C,IAAM,MAAA,OAAA,GAAU,MAAO,CAAA,SAAA,CAAU,iBAAiB,CAAA;AAClD,IAAM,MAAA,qBAAA,GAAwBA,yCAAiC,MAAM,CAAA;AAErE,IAAA,IAAI,CAAC,KAAA,CAAM,KAAM,CAAA,OAAO,CAAG,EAAA;AACzB,MAAM,MAAA,IAAI,MAAM,sDAAsD,CAAA;AAAA,KACxE,MAAA,IAAW,KAAM,CAAA,MAAA,GAAS,gBAAkB,EAAA;AAC1C,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,mCAAmC,gBAAgB,CAAA,kBAAA;AAAA,OACrD;AAAA,KACS,MAAA,IAAA,CAAC,OAAQ,CAAA,KAAA,CAAM,OAAO,CAAG,EAAA;AAClC,MAAM,MAAA,IAAI,MAAM,wDAAwD,CAAA;AAAA,KAC/D,MAAA,IAAA,IAAA,CAAK,QAAS,CAAA,GAAA,CAAI,KAAK,CAAG,EAAA;AACnC,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA;AAGF,IAAA,IAAA,CAAK,SAAS,GAAI,CAAA,KAAA,EAAO,EAAE,OAAA,EAAS,uBAAuB,CAAA;AAAA;AAC7D,EAEA,MAAM,YAAY,KAAe,EAAA;AAC/B,IAAO,OAAA,IAAA,CAAK,QAAS,CAAA,GAAA,CAAI,KAAK,CAAA;AAAA;AAElC;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.cjs.js","sources":["../../../src/entrypoints/auth/helpers.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n BackstageCredentials,\n BackstageNonePrincipal,\n BackstagePrincipalAccessRestrictions,\n BackstageServicePrincipal,\n BackstageUserPrincipal,\n} from '@backstage/backend-plugin-api';\nimport { InternalBackstageCredentials } from './types';\n\nexport function createCredentialsWithServicePrincipal(\n sub: string,\n token?: string,\n accessRestrictions?: BackstagePrincipalAccessRestrictions,\n): InternalBackstageCredentials<BackstageServicePrincipal> {\n return Object.defineProperty(\n {\n $$type: '@backstage/BackstageCredentials',\n version: 'v1',\n principal: {\n type: 'service',\n subject: sub,\n accessRestrictions,\n },\n },\n 'token',\n {\n enumerable: false,\n configurable: true,\n value: token,\n },\n );\n}\n\nexport function createCredentialsWithUserPrincipal(\n sub: string,\n token: string,\n expiresAt?: Date,\n): InternalBackstageCredentials<BackstageUserPrincipal> {\n return Object.defineProperty(\n {\n $$type: '@backstage/BackstageCredentials',\n version: 'v1',\n expiresAt,\n principal: {\n type: 'user',\n userEntityRef: sub,\n },\n },\n 'token',\n {\n enumerable: false,\n configurable: true,\n value: token,\n },\n );\n}\n\nexport function createCredentialsWithNonePrincipal(): InternalBackstageCredentials<BackstageNonePrincipal> {\n return {\n $$type: '@backstage/BackstageCredentials',\n version: 'v1',\n principal: {\n type: 'none',\n },\n };\n}\n\nexport function toInternalBackstageCredentials(\n credentials: BackstageCredentials,\n): InternalBackstageCredentials<\n BackstageUserPrincipal | BackstageServicePrincipal | BackstageNonePrincipal\n> {\n if (credentials.$$type !== '@backstage/BackstageCredentials') {\n throw new Error('Invalid credential type');\n }\n\n const internalCredentials = credentials as InternalBackstageCredentials<\n BackstageUserPrincipal | BackstageServicePrincipal | BackstageNonePrincipal\n >;\n\n if (internalCredentials.version !== 'v1') {\n throw new Error(\n `Invalid credential version ${internalCredentials.version}`,\n );\n }\n\n return internalCredentials;\n}\n"],"names":[],"mappings":";;AAyBgB,SAAA,qCAAA,CACd,GACA,EAAA,KAAA,EACA,kBACyD,EAAA;AACzD,EAAA,OAAO,MAAO,CAAA,cAAA;AAAA,IACZ;AAAA,MACE,MAAQ,EAAA,iCAAA;AAAA,MACR,OAAS,EAAA,IAAA;AAAA,MACT,SAAW,EAAA;AAAA,QACT,IAAM,EAAA,SAAA;AAAA,QACN,OAAS,EAAA,GAAA;AAAA,QACT,kBAAA;AAAA,OACF;AAAA,KACF;AAAA,IACA,OAAA;AAAA,IACA;AAAA,MACE,UAAY,EAAA,KAAA;AAAA,MACZ,YAAc,EAAA,IAAA;AAAA,MACd,KAAO,EAAA,KAAA;AAAA,KACT;AAAA,GACF,CAAA;AACF,CAAA;AAEgB,SAAA,kCAAA,CACd,GACA,EAAA,KAAA,EACA,SACsD,EAAA;AACtD,EAAA,OAAO,MAAO,CAAA,cAAA;AAAA,IACZ;AAAA,MACE,MAAQ,EAAA,iCAAA;AAAA,MACR,OAAS,EAAA,IAAA;AAAA,MACT,SAAA;AAAA,MACA,SAAW,EAAA;AAAA,QACT,IAAM,EAAA,MAAA;AAAA,QACN,aAAe,EAAA,GAAA;AAAA,OACjB;AAAA,KACF;AAAA,IACA,OAAA;AAAA,IACA;AAAA,MACE,UAAY,EAAA,KAAA;AAAA,MACZ,YAAc,EAAA,IAAA;AAAA,MACd,KAAO,EAAA,KAAA;AAAA,KACT;AAAA,GACF,CAAA;AACF,CAAA;AAEO,SAAS,kCAA2F,GAAA;AACzG,EAAO,OAAA;AAAA,IACL,MAAQ,EAAA,iCAAA;AAAA,IACR,OAAS,EAAA,IAAA;AAAA,IACT,SAAW,EAAA;AAAA,MACT,IAAM,EAAA,MAAA;AAAA,KACR;AAAA,GACF,CAAA;AACF,CAAA;AAEO,SAAS,+BACd,WAGA,EAAA;AACA,EAAI,IAAA,WAAA,CAAY,WAAW,iCAAmC,EAAA;AAC5D,IAAM,MAAA,IAAI,MAAM,yBAAyB,CAAA,CAAA;AAAA,GAC3C;AAEA,EAAA,MAAM,mBAAsB,GAAA,WAAA,CAAA;AAI5B,EAAI,IAAA,mBAAA,CAAoB,YAAY,IAAM,EAAA;AACxC,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,2BAAA,EAA8B,oBAAoB,OAAO,CAAA,CAAA;AAAA,KAC3D,CAAA;AAAA,GACF;AAEA,EAAO,OAAA,mBAAA,CAAA;AACT;;;;;;;"}
1
+ {"version":3,"file":"helpers.cjs.js","sources":["../../../src/entrypoints/auth/helpers.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n BackstageCredentials,\n BackstageNonePrincipal,\n BackstagePrincipalAccessRestrictions,\n BackstageServicePrincipal,\n BackstageUserPrincipal,\n} from '@backstage/backend-plugin-api';\nimport { InternalBackstageCredentials } from './types';\n\nexport function createCredentialsWithServicePrincipal(\n sub: string,\n token?: string,\n accessRestrictions?: BackstagePrincipalAccessRestrictions,\n): InternalBackstageCredentials<BackstageServicePrincipal> {\n return Object.defineProperty(\n {\n $$type: '@backstage/BackstageCredentials',\n version: 'v1',\n principal: {\n type: 'service',\n subject: sub,\n accessRestrictions,\n },\n },\n 'token',\n {\n enumerable: false,\n configurable: true,\n value: token,\n },\n );\n}\n\nexport function createCredentialsWithUserPrincipal(\n sub: string,\n token: string,\n expiresAt?: Date,\n): InternalBackstageCredentials<BackstageUserPrincipal> {\n return Object.defineProperty(\n {\n $$type: '@backstage/BackstageCredentials',\n version: 'v1',\n expiresAt,\n principal: {\n type: 'user',\n userEntityRef: sub,\n },\n },\n 'token',\n {\n enumerable: false,\n configurable: true,\n value: token,\n },\n );\n}\n\nexport function createCredentialsWithNonePrincipal(): InternalBackstageCredentials<BackstageNonePrincipal> {\n return {\n $$type: '@backstage/BackstageCredentials',\n version: 'v1',\n principal: {\n type: 'none',\n },\n };\n}\n\nexport function toInternalBackstageCredentials(\n credentials: BackstageCredentials,\n): InternalBackstageCredentials<\n BackstageUserPrincipal | BackstageServicePrincipal | BackstageNonePrincipal\n> {\n if (credentials.$$type !== '@backstage/BackstageCredentials') {\n throw new Error('Invalid credential type');\n }\n\n const internalCredentials = credentials as InternalBackstageCredentials<\n BackstageUserPrincipal | BackstageServicePrincipal | BackstageNonePrincipal\n >;\n\n if (internalCredentials.version !== 'v1') {\n throw new Error(\n `Invalid credential version ${internalCredentials.version}`,\n );\n }\n\n return internalCredentials;\n}\n"],"names":[],"mappings":";;AAyBgB,SAAA,qCAAA,CACd,GACA,EAAA,KAAA,EACA,kBACyD,EAAA;AACzD,EAAA,OAAO,MAAO,CAAA,cAAA;AAAA,IACZ;AAAA,MACE,MAAQ,EAAA,iCAAA;AAAA,MACR,OAAS,EAAA,IAAA;AAAA,MACT,SAAW,EAAA;AAAA,QACT,IAAM,EAAA,SAAA;AAAA,QACN,OAAS,EAAA,GAAA;AAAA,QACT;AAAA;AACF,KACF;AAAA,IACA,OAAA;AAAA,IACA;AAAA,MACE,UAAY,EAAA,KAAA;AAAA,MACZ,YAAc,EAAA,IAAA;AAAA,MACd,KAAO,EAAA;AAAA;AACT,GACF;AACF;AAEgB,SAAA,kCAAA,CACd,GACA,EAAA,KAAA,EACA,SACsD,EAAA;AACtD,EAAA,OAAO,MAAO,CAAA,cAAA;AAAA,IACZ;AAAA,MACE,MAAQ,EAAA,iCAAA;AAAA,MACR,OAAS,EAAA,IAAA;AAAA,MACT,SAAA;AAAA,MACA,SAAW,EAAA;AAAA,QACT,IAAM,EAAA,MAAA;AAAA,QACN,aAAe,EAAA;AAAA;AACjB,KACF;AAAA,IACA,OAAA;AAAA,IACA;AAAA,MACE,UAAY,EAAA,KAAA;AAAA,MACZ,YAAc,EAAA,IAAA;AAAA,MACd,KAAO,EAAA;AAAA;AACT,GACF;AACF;AAEO,SAAS,kCAA2F,GAAA;AACzG,EAAO,OAAA;AAAA,IACL,MAAQ,EAAA,iCAAA;AAAA,IACR,OAAS,EAAA,IAAA;AAAA,IACT,SAAW,EAAA;AAAA,MACT,IAAM,EAAA;AAAA;AACR,GACF;AACF;AAEO,SAAS,+BACd,WAGA,EAAA;AACA,EAAI,IAAA,WAAA,CAAY,WAAW,iCAAmC,EAAA;AAC5D,IAAM,MAAA,IAAI,MAAM,yBAAyB,CAAA;AAAA;AAG3C,EAAA,MAAM,mBAAsB,GAAA,WAAA;AAI5B,EAAI,IAAA,mBAAA,CAAoB,YAAY,IAAM,EAAA;AACxC,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,2BAAA,EAA8B,oBAAoB,OAAO,CAAA;AAAA,KAC3D;AAAA;AAGF,EAAO,OAAA,mBAAA;AACT;;;;;;;"}
@@ -5,6 +5,11 @@ var errors = require('@backstage/errors');
5
5
  var pluginAuthNode = require('@backstage/plugin-auth-node');
6
6
  var JwksClient = require('../JwksClient.cjs.js');
7
7
  var types = require('@backstage/types');
8
+ var fetch = require('node-fetch');
9
+
10
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
11
+
12
+ var fetch__default = /*#__PURE__*/_interopDefaultCompat(fetch);
8
13
 
9
14
  const SECONDS_IN_MS = 1e3;
10
15
  const ALLOWED_PLUGIN_ID_PATTERN = /^[a-z0-9_-]+$/i;
@@ -93,7 +98,7 @@ class PluginTokenHandler {
93
98
  }
94
99
  const doCheck = async () => {
95
100
  try {
96
- const res = await fetch(
101
+ const res = await fetch__default.default(
97
102
  `${await this.discovery.getBaseUrl(
98
103
  targetPluginId
99
104
  )}/.backstage/auth/v1/jwks.json`
@@ -1 +1 @@
1
- {"version":3,"file":"PluginTokenHandler.cjs.js","sources":["../../../../src/entrypoints/auth/plugin/PluginTokenHandler.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DiscoveryService, LoggerService } from '@backstage/backend-plugin-api';\nimport { decodeJwt, importJWK, SignJWT, decodeProtectedHeader } from 'jose';\nimport { AuthenticationError } from '@backstage/errors';\nimport { jwtVerify } from 'jose';\nimport { tokenTypes } from '@backstage/plugin-auth-node';\nimport { JwksClient } from '../JwksClient';\nimport { HumanDuration, durationToMilliseconds } from '@backstage/types';\nimport { PluginKeySource } from './keys/types';\n\nconst SECONDS_IN_MS = 1000;\n\nconst ALLOWED_PLUGIN_ID_PATTERN = /^[a-z0-9_-]+$/i;\n\ntype Options = {\n ownPluginId: string;\n keyDuration: HumanDuration;\n keySource: PluginKeySource;\n discovery: DiscoveryService;\n logger: LoggerService;\n /**\n * JWS \"alg\" (Algorithm) Header Parameter value. Defaults to ES256.\n * Must match one of the algorithms defined for IdentityClient.\n * When setting a different algorithm, check if the `key` field\n * of the `signing_keys` table can fit the length of the generated keys.\n * If not, add a knex migration file in the migrations folder.\n * More info on supported algorithms: https://github.com/panva/jose\n */\n algorithm?: string;\n};\n\nexport class PluginTokenHandler {\n private jwksMap = new Map<string, JwksClient>();\n\n // Tracking state for isTargetPluginSupported\n private supportedTargetPlugins = new Set<string>();\n private targetPluginInflightChecks = new Map<string, Promise<boolean>>();\n\n static create(options: Options) {\n return new PluginTokenHandler(\n options.logger,\n options.ownPluginId,\n options.keySource,\n options.algorithm ?? 'ES256',\n Math.round(durationToMilliseconds(options.keyDuration) / 1000),\n options.discovery,\n );\n }\n\n private constructor(\n private readonly logger: LoggerService,\n private readonly ownPluginId: string,\n private readonly keySource: PluginKeySource,\n private readonly algorithm: string,\n private readonly keyDurationSeconds: number,\n private readonly discovery: DiscoveryService,\n ) {}\n\n async verifyToken(\n token: string,\n ): Promise<{ subject: string; limitedUserToken?: string } | undefined> {\n try {\n const { typ } = decodeProtectedHeader(token);\n if (typ !== tokenTypes.plugin.typParam) {\n return undefined;\n }\n } catch {\n return undefined;\n }\n\n const pluginId = String(decodeJwt(token).sub);\n if (!pluginId) {\n throw new AuthenticationError('Invalid plugin token: missing subject');\n }\n if (!ALLOWED_PLUGIN_ID_PATTERN.test(pluginId)) {\n throw new AuthenticationError(\n 'Invalid plugin token: forbidden subject format',\n );\n }\n\n const jwksClient = await this.getJwksClient(pluginId);\n await jwksClient.refreshKeyStore(token); // TODO(Rugvip): Refactor so that this isn't needed\n\n const { payload } = await jwtVerify<{ sub: string; obo?: string }>(\n token,\n jwksClient.getKey,\n {\n typ: tokenTypes.plugin.typParam,\n audience: this.ownPluginId,\n requiredClaims: ['iat', 'exp', 'sub', 'aud'],\n },\n ).catch(e => {\n throw new AuthenticationError('Invalid plugin token', e);\n });\n\n return { subject: `plugin:${payload.sub}`, limitedUserToken: payload.obo };\n }\n\n async issueToken(options: {\n pluginId: string;\n targetPluginId: string;\n onBehalfOf?: { token: string; expiresAt: Date };\n }): Promise<{ token: string }> {\n const { pluginId, targetPluginId, onBehalfOf } = options;\n const key = await this.keySource.getPrivateSigningKey();\n\n const sub = pluginId;\n const aud = targetPluginId;\n const iat = Math.floor(Date.now() / SECONDS_IN_MS);\n const ourExp = iat + this.keyDurationSeconds;\n const exp = onBehalfOf\n ? Math.min(\n ourExp,\n Math.floor(onBehalfOf.expiresAt.getTime() / SECONDS_IN_MS),\n )\n : ourExp;\n\n const claims = { sub, aud, iat, exp, obo: onBehalfOf?.token };\n const token = await new SignJWT(claims)\n .setProtectedHeader({\n typ: tokenTypes.plugin.typParam,\n alg: this.algorithm,\n kid: key.kid,\n })\n .setAudience(aud)\n .setSubject(sub)\n .setIssuedAt(iat)\n .setExpirationTime(exp)\n .sign(await importJWK(key));\n\n return { token };\n }\n\n private async isTargetPluginSupported(\n targetPluginId: string,\n ): Promise<boolean> {\n if (this.supportedTargetPlugins.has(targetPluginId)) {\n return true;\n }\n const inFlight = this.targetPluginInflightChecks.get(targetPluginId);\n if (inFlight) {\n return inFlight;\n }\n\n const doCheck = async () => {\n try {\n const res = await fetch(\n `${await this.discovery.getBaseUrl(\n targetPluginId,\n )}/.backstage/auth/v1/jwks.json`,\n );\n if (res.status === 404) {\n return false;\n }\n\n if (!res.ok) {\n throw new Error(`Failed to fetch jwks.json, ${res.status}`);\n }\n\n const data = await res.json();\n if (!data.keys) {\n throw new Error(`Invalid jwks.json response, missing keys`);\n }\n\n this.supportedTargetPlugins.add(targetPluginId);\n return true;\n } catch (error) {\n this.logger.error('Unexpected failure for target JWKS check', error);\n return false;\n } finally {\n this.targetPluginInflightChecks.delete(targetPluginId);\n }\n };\n\n const check = doCheck();\n this.targetPluginInflightChecks.set(targetPluginId, check);\n return check;\n }\n\n private async getJwksClient(pluginId: string) {\n const client = this.jwksMap.get(pluginId);\n if (client) {\n return client;\n }\n\n // Double check that the target plugin has a valid JWKS endpoint, otherwise avoid creating a remote key set\n if (!(await this.isTargetPluginSupported(pluginId))) {\n throw new AuthenticationError(\n `Received a plugin token where the source '${pluginId}' plugin unexpectedly does not have a JWKS endpoint. ` +\n 'The target plugin needs to be migrated to be installed in an app using the new backend system.',\n );\n }\n\n const newClient = new JwksClient(async () => {\n return new URL(\n `${await this.discovery.getBaseUrl(\n pluginId,\n )}/.backstage/auth/v1/jwks.json`,\n );\n });\n\n this.jwksMap.set(pluginId, newClient);\n return newClient;\n }\n}\n"],"names":["durationToMilliseconds","decodeProtectedHeader","tokenTypes","decodeJwt","AuthenticationError","jwtVerify","SignJWT","importJWK","JwksClient"],"mappings":";;;;;;;;AAyBA,MAAM,aAAgB,GAAA,GAAA,CAAA;AAEtB,MAAM,yBAA4B,GAAA,gBAAA,CAAA;AAmB3B,MAAM,kBAAmB,CAAA;AAAA,EAkBtB,YACW,MACA,EAAA,WAAA,EACA,SACA,EAAA,SAAA,EACA,oBACA,SACjB,EAAA;AANiB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA,CAAA;AACA,IAAA,IAAA,CAAA,WAAA,GAAA,WAAA,CAAA;AACA,IAAA,IAAA,CAAA,SAAA,GAAA,SAAA,CAAA;AACA,IAAA,IAAA,CAAA,SAAA,GAAA,SAAA,CAAA;AACA,IAAA,IAAA,CAAA,kBAAA,GAAA,kBAAA,CAAA;AACA,IAAA,IAAA,CAAA,SAAA,GAAA,SAAA,CAAA;AAAA,GAChB;AAAA,EAxBK,OAAA,uBAAc,GAAwB,EAAA,CAAA;AAAA;AAAA,EAGtC,sBAAA,uBAA6B,GAAY,EAAA,CAAA;AAAA,EACzC,0BAAA,uBAAiC,GAA8B,EAAA,CAAA;AAAA,EAEvE,OAAO,OAAO,OAAkB,EAAA;AAC9B,IAAA,OAAO,IAAI,kBAAA;AAAA,MACT,OAAQ,CAAA,MAAA;AAAA,MACR,OAAQ,CAAA,WAAA;AAAA,MACR,OAAQ,CAAA,SAAA;AAAA,MACR,QAAQ,SAAa,IAAA,OAAA;AAAA,MACrB,KAAK,KAAM,CAAAA,4BAAA,CAAuB,OAAQ,CAAA,WAAW,IAAI,GAAI,CAAA;AAAA,MAC7D,OAAQ,CAAA,SAAA;AAAA,KACV,CAAA;AAAA,GACF;AAAA,EAWA,MAAM,YACJ,KACqE,EAAA;AACrE,IAAI,IAAA;AACF,MAAA,MAAM,EAAE,GAAA,EAAQ,GAAAC,0BAAA,CAAsB,KAAK,CAAA,CAAA;AAC3C,MAAI,IAAA,GAAA,KAAQC,yBAAW,CAAA,MAAA,CAAO,QAAU,EAAA;AACtC,QAAO,OAAA,KAAA,CAAA,CAAA;AAAA,OACT;AAAA,KACM,CAAA,MAAA;AACN,MAAO,OAAA,KAAA,CAAA,CAAA;AAAA,KACT;AAEA,IAAA,MAAM,QAAW,GAAA,MAAA,CAAOC,cAAU,CAAA,KAAK,EAAE,GAAG,CAAA,CAAA;AAC5C,IAAA,IAAI,CAAC,QAAU,EAAA;AACb,MAAM,MAAA,IAAIC,2BAAoB,uCAAuC,CAAA,CAAA;AAAA,KACvE;AACA,IAAA,IAAI,CAAC,yBAAA,CAA0B,IAAK,CAAA,QAAQ,CAAG,EAAA;AAC7C,MAAA,MAAM,IAAIA,0BAAA;AAAA,QACR,gDAAA;AAAA,OACF,CAAA;AAAA,KACF;AAEA,IAAA,MAAM,UAAa,GAAA,MAAM,IAAK,CAAA,aAAA,CAAc,QAAQ,CAAA,CAAA;AACpD,IAAM,MAAA,UAAA,CAAW,gBAAgB,KAAK,CAAA,CAAA;AAEtC,IAAM,MAAA,EAAE,OAAQ,EAAA,GAAI,MAAMC,cAAA;AAAA,MACxB,KAAA;AAAA,MACA,UAAW,CAAA,MAAA;AAAA,MACX;AAAA,QACE,GAAA,EAAKH,0BAAW,MAAO,CAAA,QAAA;AAAA,QACvB,UAAU,IAAK,CAAA,WAAA;AAAA,QACf,cAAgB,EAAA,CAAC,KAAO,EAAA,KAAA,EAAO,OAAO,KAAK,CAAA;AAAA,OAC7C;AAAA,KACF,CAAE,MAAM,CAAK,CAAA,KAAA;AACX,MAAM,MAAA,IAAIE,0BAAoB,CAAA,sBAAA,EAAwB,CAAC,CAAA,CAAA;AAAA,KACxD,CAAA,CAAA;AAED,IAAO,OAAA,EAAE,SAAS,CAAU,OAAA,EAAA,OAAA,CAAQ,GAAG,CAAI,CAAA,EAAA,gBAAA,EAAkB,QAAQ,GAAI,EAAA,CAAA;AAAA,GAC3E;AAAA,EAEA,MAAM,WAAW,OAIc,EAAA;AAC7B,IAAA,MAAM,EAAE,QAAA,EAAU,cAAgB,EAAA,UAAA,EAAe,GAAA,OAAA,CAAA;AACjD,IAAA,MAAM,GAAM,GAAA,MAAM,IAAK,CAAA,SAAA,CAAU,oBAAqB,EAAA,CAAA;AAEtD,IAAA,MAAM,GAAM,GAAA,QAAA,CAAA;AACZ,IAAA,MAAM,GAAM,GAAA,cAAA,CAAA;AACZ,IAAA,MAAM,MAAM,IAAK,CAAA,KAAA,CAAM,IAAK,CAAA,GAAA,KAAQ,aAAa,CAAA,CAAA;AACjD,IAAM,MAAA,MAAA,GAAS,MAAM,IAAK,CAAA,kBAAA,CAAA;AAC1B,IAAM,MAAA,GAAA,GAAM,aACR,IAAK,CAAA,GAAA;AAAA,MACH,MAAA;AAAA,MACA,KAAK,KAAM,CAAA,UAAA,CAAW,SAAU,CAAA,OAAA,KAAY,aAAa,CAAA;AAAA,KAE3D,GAAA,MAAA,CAAA;AAEJ,IAAM,MAAA,MAAA,GAAS,EAAE,GAAK,EAAA,GAAA,EAAK,KAAK,GAAK,EAAA,GAAA,EAAK,YAAY,KAAM,EAAA,CAAA;AAC5D,IAAA,MAAM,QAAQ,MAAM,IAAIE,YAAQ,CAAA,MAAM,EACnC,kBAAmB,CAAA;AAAA,MAClB,GAAA,EAAKJ,0BAAW,MAAO,CAAA,QAAA;AAAA,MACvB,KAAK,IAAK,CAAA,SAAA;AAAA,MACV,KAAK,GAAI,CAAA,GAAA;AAAA,KACV,CACA,CAAA,WAAA,CAAY,GAAG,CACf,CAAA,UAAA,CAAW,GAAG,CACd,CAAA,WAAA,CAAY,GAAG,CAAA,CACf,kBAAkB,GAAG,CAAA,CACrB,KAAK,MAAMK,cAAA,CAAU,GAAG,CAAC,CAAA,CAAA;AAE5B,IAAA,OAAO,EAAE,KAAM,EAAA,CAAA;AAAA,GACjB;AAAA,EAEA,MAAc,wBACZ,cACkB,EAAA;AAClB,IAAA,IAAI,IAAK,CAAA,sBAAA,CAAuB,GAAI,CAAA,cAAc,CAAG,EAAA;AACnD,MAAO,OAAA,IAAA,CAAA;AAAA,KACT;AACA,IAAA,MAAM,QAAW,GAAA,IAAA,CAAK,0BAA2B,CAAA,GAAA,CAAI,cAAc,CAAA,CAAA;AACnE,IAAA,IAAI,QAAU,EAAA;AACZ,MAAO,OAAA,QAAA,CAAA;AAAA,KACT;AAEA,IAAA,MAAM,UAAU,YAAY;AAC1B,MAAI,IAAA;AACF,QAAA,MAAM,MAAM,MAAM,KAAA;AAAA,UAChB,CAAA,EAAG,MAAM,IAAA,CAAK,SAAU,CAAA,UAAA;AAAA,YACtB,cAAA;AAAA,WACD,CAAA,6BAAA,CAAA;AAAA,SACH,CAAA;AACA,QAAI,IAAA,GAAA,CAAI,WAAW,GAAK,EAAA;AACtB,UAAO,OAAA,KAAA,CAAA;AAAA,SACT;AAEA,QAAI,IAAA,CAAC,IAAI,EAAI,EAAA;AACX,UAAA,MAAM,IAAI,KAAA,CAAM,CAA8B,2BAAA,EAAA,GAAA,CAAI,MAAM,CAAE,CAAA,CAAA,CAAA;AAAA,SAC5D;AAEA,QAAM,MAAA,IAAA,GAAO,MAAM,GAAA,CAAI,IAAK,EAAA,CAAA;AAC5B,QAAI,IAAA,CAAC,KAAK,IAAM,EAAA;AACd,UAAM,MAAA,IAAI,MAAM,CAA0C,wCAAA,CAAA,CAAA,CAAA;AAAA,SAC5D;AAEA,QAAK,IAAA,CAAA,sBAAA,CAAuB,IAAI,cAAc,CAAA,CAAA;AAC9C,QAAO,OAAA,IAAA,CAAA;AAAA,eACA,KAAO,EAAA;AACd,QAAK,IAAA,CAAA,MAAA,CAAO,KAAM,CAAA,0CAAA,EAA4C,KAAK,CAAA,CAAA;AACnE,QAAO,OAAA,KAAA,CAAA;AAAA,OACP,SAAA;AACA,QAAK,IAAA,CAAA,0BAAA,CAA2B,OAAO,cAAc,CAAA,CAAA;AAAA,OACvD;AAAA,KACF,CAAA;AAEA,IAAA,MAAM,QAAQ,OAAQ,EAAA,CAAA;AACtB,IAAK,IAAA,CAAA,0BAAA,CAA2B,GAAI,CAAA,cAAA,EAAgB,KAAK,CAAA,CAAA;AACzD,IAAO,OAAA,KAAA,CAAA;AAAA,GACT;AAAA,EAEA,MAAc,cAAc,QAAkB,EAAA;AAC5C,IAAA,MAAM,MAAS,GAAA,IAAA,CAAK,OAAQ,CAAA,GAAA,CAAI,QAAQ,CAAA,CAAA;AACxC,IAAA,IAAI,MAAQ,EAAA;AACV,MAAO,OAAA,MAAA,CAAA;AAAA,KACT;AAGA,IAAA,IAAI,CAAE,MAAM,IAAK,CAAA,uBAAA,CAAwB,QAAQ,CAAI,EAAA;AACnD,MAAA,MAAM,IAAIH,0BAAA;AAAA,QACR,6CAA6C,QAAQ,CAAA,mJAAA,CAAA;AAAA,OAEvD,CAAA;AAAA,KACF;AAEA,IAAM,MAAA,SAAA,GAAY,IAAII,qBAAA,CAAW,YAAY;AAC3C,MAAA,OAAO,IAAI,GAAA;AAAA,QACT,CAAA,EAAG,MAAM,IAAA,CAAK,SAAU,CAAA,UAAA;AAAA,UACtB,QAAA;AAAA,SACD,CAAA,6BAAA,CAAA;AAAA,OACH,CAAA;AAAA,KACD,CAAA,CAAA;AAED,IAAK,IAAA,CAAA,OAAA,CAAQ,GAAI,CAAA,QAAA,EAAU,SAAS,CAAA,CAAA;AACpC,IAAO,OAAA,SAAA,CAAA;AAAA,GACT;AACF;;;;"}
1
+ {"version":3,"file":"PluginTokenHandler.cjs.js","sources":["../../../../src/entrypoints/auth/plugin/PluginTokenHandler.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DiscoveryService, LoggerService } from '@backstage/backend-plugin-api';\nimport { decodeJwt, importJWK, SignJWT, decodeProtectedHeader } from 'jose';\nimport { AuthenticationError } from '@backstage/errors';\nimport { jwtVerify } from 'jose';\nimport { tokenTypes } from '@backstage/plugin-auth-node';\nimport { JwksClient } from '../JwksClient';\nimport { HumanDuration, durationToMilliseconds } from '@backstage/types';\nimport { PluginKeySource } from './keys/types';\nimport fetch from 'node-fetch';\n\nconst SECONDS_IN_MS = 1000;\n\nconst ALLOWED_PLUGIN_ID_PATTERN = /^[a-z0-9_-]+$/i;\n\ntype Options = {\n ownPluginId: string;\n keyDuration: HumanDuration;\n keySource: PluginKeySource;\n discovery: DiscoveryService;\n logger: LoggerService;\n /**\n * JWS \"alg\" (Algorithm) Header Parameter value. Defaults to ES256.\n * Must match one of the algorithms defined for IdentityClient.\n * When setting a different algorithm, check if the `key` field\n * of the `signing_keys` table can fit the length of the generated keys.\n * If not, add a knex migration file in the migrations folder.\n * More info on supported algorithms: https://github.com/panva/jose\n */\n algorithm?: string;\n};\n\nexport class PluginTokenHandler {\n private jwksMap = new Map<string, JwksClient>();\n\n // Tracking state for isTargetPluginSupported\n private supportedTargetPlugins = new Set<string>();\n private targetPluginInflightChecks = new Map<string, Promise<boolean>>();\n\n static create(options: Options) {\n return new PluginTokenHandler(\n options.logger,\n options.ownPluginId,\n options.keySource,\n options.algorithm ?? 'ES256',\n Math.round(durationToMilliseconds(options.keyDuration) / 1000),\n options.discovery,\n );\n }\n\n private constructor(\n private readonly logger: LoggerService,\n private readonly ownPluginId: string,\n private readonly keySource: PluginKeySource,\n private readonly algorithm: string,\n private readonly keyDurationSeconds: number,\n private readonly discovery: DiscoveryService,\n ) {}\n\n async verifyToken(\n token: string,\n ): Promise<{ subject: string; limitedUserToken?: string } | undefined> {\n try {\n const { typ } = decodeProtectedHeader(token);\n if (typ !== tokenTypes.plugin.typParam) {\n return undefined;\n }\n } catch {\n return undefined;\n }\n\n const pluginId = String(decodeJwt(token).sub);\n if (!pluginId) {\n throw new AuthenticationError('Invalid plugin token: missing subject');\n }\n if (!ALLOWED_PLUGIN_ID_PATTERN.test(pluginId)) {\n throw new AuthenticationError(\n 'Invalid plugin token: forbidden subject format',\n );\n }\n\n const jwksClient = await this.getJwksClient(pluginId);\n await jwksClient.refreshKeyStore(token); // TODO(Rugvip): Refactor so that this isn't needed\n\n const { payload } = await jwtVerify<{ sub: string; obo?: string }>(\n token,\n jwksClient.getKey,\n {\n typ: tokenTypes.plugin.typParam,\n audience: this.ownPluginId,\n requiredClaims: ['iat', 'exp', 'sub', 'aud'],\n },\n ).catch(e => {\n throw new AuthenticationError('Invalid plugin token', e);\n });\n\n return { subject: `plugin:${payload.sub}`, limitedUserToken: payload.obo };\n }\n\n async issueToken(options: {\n pluginId: string;\n targetPluginId: string;\n onBehalfOf?: { token: string; expiresAt: Date };\n }): Promise<{ token: string }> {\n const { pluginId, targetPluginId, onBehalfOf } = options;\n const key = await this.keySource.getPrivateSigningKey();\n\n const sub = pluginId;\n const aud = targetPluginId;\n const iat = Math.floor(Date.now() / SECONDS_IN_MS);\n const ourExp = iat + this.keyDurationSeconds;\n const exp = onBehalfOf\n ? Math.min(\n ourExp,\n Math.floor(onBehalfOf.expiresAt.getTime() / SECONDS_IN_MS),\n )\n : ourExp;\n\n const claims = { sub, aud, iat, exp, obo: onBehalfOf?.token };\n const token = await new SignJWT(claims)\n .setProtectedHeader({\n typ: tokenTypes.plugin.typParam,\n alg: this.algorithm,\n kid: key.kid,\n })\n .setAudience(aud)\n .setSubject(sub)\n .setIssuedAt(iat)\n .setExpirationTime(exp)\n .sign(await importJWK(key));\n\n return { token };\n }\n\n private async isTargetPluginSupported(\n targetPluginId: string,\n ): Promise<boolean> {\n if (this.supportedTargetPlugins.has(targetPluginId)) {\n return true;\n }\n const inFlight = this.targetPluginInflightChecks.get(targetPluginId);\n if (inFlight) {\n return inFlight;\n }\n\n const doCheck = async () => {\n try {\n const res = await fetch(\n `${await this.discovery.getBaseUrl(\n targetPluginId,\n )}/.backstage/auth/v1/jwks.json`,\n );\n if (res.status === 404) {\n return false;\n }\n\n if (!res.ok) {\n throw new Error(`Failed to fetch jwks.json, ${res.status}`);\n }\n\n const data = await res.json();\n if (!data.keys) {\n throw new Error(`Invalid jwks.json response, missing keys`);\n }\n\n this.supportedTargetPlugins.add(targetPluginId);\n return true;\n } catch (error) {\n this.logger.error('Unexpected failure for target JWKS check', error);\n return false;\n } finally {\n this.targetPluginInflightChecks.delete(targetPluginId);\n }\n };\n\n const check = doCheck();\n this.targetPluginInflightChecks.set(targetPluginId, check);\n return check;\n }\n\n private async getJwksClient(pluginId: string) {\n const client = this.jwksMap.get(pluginId);\n if (client) {\n return client;\n }\n\n // Double check that the target plugin has a valid JWKS endpoint, otherwise avoid creating a remote key set\n if (!(await this.isTargetPluginSupported(pluginId))) {\n throw new AuthenticationError(\n `Received a plugin token where the source '${pluginId}' plugin unexpectedly does not have a JWKS endpoint. ` +\n 'The target plugin needs to be migrated to be installed in an app using the new backend system.',\n );\n }\n\n const newClient = new JwksClient(async () => {\n return new URL(\n `${await this.discovery.getBaseUrl(\n pluginId,\n )}/.backstage/auth/v1/jwks.json`,\n );\n });\n\n this.jwksMap.set(pluginId, newClient);\n return newClient;\n }\n}\n"],"names":["durationToMilliseconds","decodeProtectedHeader","tokenTypes","decodeJwt","AuthenticationError","jwtVerify","SignJWT","importJWK","fetch","JwksClient"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"DatabaseKeyStore.cjs.js","sources":["../../../../../src/entrypoints/auth/plugin/keys/DatabaseKeyStore.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n DatabaseService,\n LoggerService,\n resolvePackagePath,\n} from '@backstage/backend-plugin-api';\nimport { JsonObject } from '@backstage/types';\nimport { Knex } from 'knex';\nimport { DateTime } from 'luxon';\nimport { KeyStore } from './types';\n\nconst MIGRATIONS_TABLE = 'backstage_backend_public_keys__knex_migrations';\n\n/** @internal */\nexport const TABLE = 'backstage_backend_public_keys__keys';\n\ntype Row = {\n id: string;\n key: string;\n expires_at: string;\n};\n\nexport function applyDatabaseMigrations(knex: Knex): Promise<void> {\n const migrationsDir = resolvePackagePath(\n '@backstage/backend-defaults',\n 'migrations/auth',\n );\n\n return knex.migrate.latest({\n directory: migrationsDir,\n tableName: MIGRATIONS_TABLE,\n });\n}\n\n/** @internal */\nexport class DatabaseKeyStore implements KeyStore {\n static async create(options: {\n database: DatabaseService;\n logger: LoggerService;\n }) {\n const { database, logger } = options;\n\n const client = await database.getClient();\n if (!database.migrations?.skip) {\n await applyDatabaseMigrations(client);\n }\n return new DatabaseKeyStore(client, logger);\n }\n\n private constructor(\n private readonly client: Knex,\n private readonly logger: LoggerService,\n ) {}\n\n async addKey(options: {\n id: string;\n key: JsonObject & { kid: string };\n expiresAt: Date;\n }) {\n await this.client<Row>(TABLE).insert({\n id: options.key.kid,\n key: JSON.stringify(options.key),\n expires_at: options.expiresAt.toISOString(),\n });\n }\n\n async listKeys() {\n const rows = await this.client<Row>(TABLE).select();\n const keys = rows.map(row => ({\n id: row.id,\n key: JSON.parse(row.key),\n expiresAt: new Date(row.expires_at),\n }));\n\n const validKeys = [];\n const expiredKeys = [];\n\n for (const key of keys) {\n if (DateTime.fromJSDate(key.expiresAt) < DateTime.local()) {\n expiredKeys.push(key);\n } else {\n validKeys.push(key);\n }\n }\n\n // Lazily prune expired keys. This may cause duplicate removals if we have concurrent callers, but w/e\n if (expiredKeys.length > 0) {\n const kids = expiredKeys.map(({ key }) => key.kid);\n\n this.logger.info(\n `Removing expired plugin service keys, '${kids.join(\"', '\")}'`,\n );\n\n // We don't await this, just let it run in the background\n this.client<Row>(TABLE)\n .delete()\n .whereIn('id', kids)\n .catch(error => {\n this.logger.error(\n 'Failed to remove expired plugin service keys',\n error,\n );\n });\n }\n\n return { keys: validKeys };\n }\n}\n"],"names":["resolvePackagePath","DateTime"],"mappings":"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"}
1
+ {"version":3,"file":"DatabaseKeyStore.cjs.js","sources":["../../../../../src/entrypoints/auth/plugin/keys/DatabaseKeyStore.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n DatabaseService,\n LoggerService,\n resolvePackagePath,\n} from '@backstage/backend-plugin-api';\nimport { JsonObject } from '@backstage/types';\nimport { Knex } from 'knex';\nimport { DateTime } from 'luxon';\nimport { KeyStore } from './types';\n\nconst MIGRATIONS_TABLE = 'backstage_backend_public_keys__knex_migrations';\n\n/** @internal */\nexport const TABLE = 'backstage_backend_public_keys__keys';\n\ntype Row = {\n id: string;\n key: string;\n expires_at: string;\n};\n\nexport function applyDatabaseMigrations(knex: Knex): Promise<void> {\n const migrationsDir = resolvePackagePath(\n '@backstage/backend-defaults',\n 'migrations/auth',\n );\n\n return knex.migrate.latest({\n directory: migrationsDir,\n tableName: MIGRATIONS_TABLE,\n });\n}\n\n/** @internal */\nexport class DatabaseKeyStore implements KeyStore {\n static async create(options: {\n database: DatabaseService;\n logger: LoggerService;\n }) {\n const { database, logger } = options;\n\n const client = await database.getClient();\n if (!database.migrations?.skip) {\n await applyDatabaseMigrations(client);\n }\n return new DatabaseKeyStore(client, logger);\n }\n\n private constructor(\n private readonly client: Knex,\n private readonly logger: LoggerService,\n ) {}\n\n async addKey(options: {\n id: string;\n key: JsonObject & { kid: string };\n expiresAt: Date;\n }) {\n await this.client<Row>(TABLE).insert({\n id: options.key.kid,\n key: JSON.stringify(options.key),\n expires_at: options.expiresAt.toISOString(),\n });\n }\n\n async listKeys() {\n const rows = await this.client<Row>(TABLE).select();\n const keys = rows.map(row => ({\n id: row.id,\n key: JSON.parse(row.key),\n expiresAt: new Date(row.expires_at),\n }));\n\n const validKeys = [];\n const expiredKeys = [];\n\n for (const key of keys) {\n if (DateTime.fromJSDate(key.expiresAt) < DateTime.local()) {\n expiredKeys.push(key);\n } else {\n validKeys.push(key);\n }\n }\n\n // Lazily prune expired keys. This may cause duplicate removals if we have concurrent callers, but w/e\n if (expiredKeys.length > 0) {\n const kids = expiredKeys.map(({ key }) => key.kid);\n\n this.logger.info(\n `Removing expired plugin service keys, '${kids.join(\"', '\")}'`,\n );\n\n // We don't await this, just let it run in the background\n this.client<Row>(TABLE)\n .delete()\n .whereIn('id', kids)\n .catch(error => {\n this.logger.error(\n 'Failed to remove expired plugin service keys',\n error,\n );\n });\n }\n\n return { keys: validKeys };\n }\n}\n"],"names":["resolvePackagePath","DateTime"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"DatabasePluginKeySource.cjs.js","sources":["../../../../../src/entrypoints/auth/plugin/keys/DatabasePluginKeySource.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DatabaseService, LoggerService } from '@backstage/backend-plugin-api';\nimport { HumanDuration, durationToMilliseconds } from '@backstage/types';\nimport { JWK, exportJWK, generateKeyPair } from 'jose';\nimport { v4 as uuid } from 'uuid';\nimport { DatabaseKeyStore } from './DatabaseKeyStore';\nimport { InternalKey, KeyPayload, KeyStore } from './types';\nimport { PluginKeySource } from './types';\n\nconst SECONDS_IN_MS = 1000;\n\n/**\n * The margin for how many times longer we make the public key available\n * compared to how long we use the private key to sign new tokens.\n */\nconst KEY_EXPIRATION_MARGIN_FACTOR = 3;\n\nexport class DatabasePluginKeySource implements PluginKeySource {\n private privateKeyPromise?: Promise<JWK>;\n private keyExpiry?: Date;\n\n constructor(\n private readonly keyStore: KeyStore,\n private readonly logger: LoggerService,\n private readonly keyDurationSeconds: number,\n private readonly algorithm: string,\n ) {}\n\n public static async create(options: {\n logger: LoggerService;\n database: DatabaseService;\n keyDuration: HumanDuration;\n algorithm?: string;\n }): Promise<PluginKeySource> {\n const keyStore = await DatabaseKeyStore.create({\n database: options.database,\n logger: options.logger,\n });\n\n return new DatabasePluginKeySource(\n keyStore,\n options.logger,\n Math.round(durationToMilliseconds(options.keyDuration) / 1000),\n options.algorithm ?? 'ES256',\n );\n }\n\n async getPrivateSigningKey(): Promise<JWK> {\n // Make sure that we only generate one key at a time\n if (this.privateKeyPromise) {\n if (this.keyExpiry && this.keyExpiry.getTime() > Date.now()) {\n return this.privateKeyPromise;\n }\n this.logger.info(`Signing key has expired, generating new key`);\n delete this.privateKeyPromise;\n }\n\n this.keyExpiry = new Date(\n Date.now() + this.keyDurationSeconds * SECONDS_IN_MS,\n );\n\n const promise = (async () => {\n // This generates a new signing key to be used to sign tokens until the next key rotation\n const kid = uuid();\n const key = await generateKeyPair(this.algorithm);\n const publicKey = await exportJWK(key.publicKey);\n const privateKey = await exportJWK(key.privateKey);\n publicKey.kid = privateKey.kid = kid;\n publicKey.alg = privateKey.alg = this.algorithm;\n\n // We're not allowed to use the key until it has been successfully stored\n // TODO: some token verification implementations aggressively cache the list of keys, and\n // don't attempt to fetch new ones even if they encounter an unknown kid. Therefore we\n // may want to keep using the existing key for some period of time until we switch to\n // the new one. This also needs to be implemented cross-service though, meaning new services\n // that boot up need to be able to grab an existing key to use for signing.\n this.logger.info(`Created new signing key ${kid}`);\n\n await this.keyStore.addKey({\n id: kid,\n key: publicKey as InternalKey,\n expiresAt: new Date(\n Date.now() +\n this.keyDurationSeconds *\n SECONDS_IN_MS *\n KEY_EXPIRATION_MARGIN_FACTOR,\n ),\n });\n\n // At this point we are allowed to start using the new key\n return privateKey;\n })();\n\n this.privateKeyPromise = promise;\n\n try {\n // If we fail to generate a new key, we need to clear the state so that\n // the next caller will try to generate another key.\n await promise;\n } catch (error) {\n this.logger.error(`Failed to generate new signing key, ${error}`);\n delete this.keyExpiry;\n delete this.privateKeyPromise;\n }\n\n return promise;\n }\n\n listKeys(): Promise<{ keys: KeyPayload[] }> {\n return this.keyStore.listKeys();\n }\n}\n"],"names":["DatabaseKeyStore","durationToMilliseconds","uuid","generateKeyPair","exportJWK"],"mappings":"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"}
1
+ {"version":3,"file":"DatabasePluginKeySource.cjs.js","sources":["../../../../../src/entrypoints/auth/plugin/keys/DatabasePluginKeySource.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { DatabaseService, LoggerService } from '@backstage/backend-plugin-api';\nimport { HumanDuration, durationToMilliseconds } from '@backstage/types';\nimport { JWK, exportJWK, generateKeyPair } from 'jose';\nimport { v4 as uuid } from 'uuid';\nimport { DatabaseKeyStore } from './DatabaseKeyStore';\nimport { InternalKey, KeyPayload, KeyStore } from './types';\nimport { PluginKeySource } from './types';\n\nconst SECONDS_IN_MS = 1000;\n\n/**\n * The margin for how many times longer we make the public key available\n * compared to how long we use the private key to sign new tokens.\n */\nconst KEY_EXPIRATION_MARGIN_FACTOR = 3;\n\nexport class DatabasePluginKeySource implements PluginKeySource {\n private privateKeyPromise?: Promise<JWK>;\n private keyExpiry?: Date;\n\n constructor(\n private readonly keyStore: KeyStore,\n private readonly logger: LoggerService,\n private readonly keyDurationSeconds: number,\n private readonly algorithm: string,\n ) {}\n\n public static async create(options: {\n logger: LoggerService;\n database: DatabaseService;\n keyDuration: HumanDuration;\n algorithm?: string;\n }): Promise<PluginKeySource> {\n const keyStore = await DatabaseKeyStore.create({\n database: options.database,\n logger: options.logger,\n });\n\n return new DatabasePluginKeySource(\n keyStore,\n options.logger,\n Math.round(durationToMilliseconds(options.keyDuration) / 1000),\n options.algorithm ?? 'ES256',\n );\n }\n\n async getPrivateSigningKey(): Promise<JWK> {\n // Make sure that we only generate one key at a time\n if (this.privateKeyPromise) {\n if (this.keyExpiry && this.keyExpiry.getTime() > Date.now()) {\n return this.privateKeyPromise;\n }\n this.logger.info(`Signing key has expired, generating new key`);\n delete this.privateKeyPromise;\n }\n\n this.keyExpiry = new Date(\n Date.now() + this.keyDurationSeconds * SECONDS_IN_MS,\n );\n\n const promise = (async () => {\n // This generates a new signing key to be used to sign tokens until the next key rotation\n const kid = uuid();\n const key = await generateKeyPair(this.algorithm);\n const publicKey = await exportJWK(key.publicKey);\n const privateKey = await exportJWK(key.privateKey);\n publicKey.kid = privateKey.kid = kid;\n publicKey.alg = privateKey.alg = this.algorithm;\n\n // We're not allowed to use the key until it has been successfully stored\n // TODO: some token verification implementations aggressively cache the list of keys, and\n // don't attempt to fetch new ones even if they encounter an unknown kid. Therefore we\n // may want to keep using the existing key for some period of time until we switch to\n // the new one. This also needs to be implemented cross-service though, meaning new services\n // that boot up need to be able to grab an existing key to use for signing.\n this.logger.info(`Created new signing key ${kid}`);\n\n await this.keyStore.addKey({\n id: kid,\n key: publicKey as InternalKey,\n expiresAt: new Date(\n Date.now() +\n this.keyDurationSeconds *\n SECONDS_IN_MS *\n KEY_EXPIRATION_MARGIN_FACTOR,\n ),\n });\n\n // At this point we are allowed to start using the new key\n return privateKey;\n })();\n\n this.privateKeyPromise = promise;\n\n try {\n // If we fail to generate a new key, we need to clear the state so that\n // the next caller will try to generate another key.\n await promise;\n } catch (error) {\n this.logger.error(`Failed to generate new signing key, ${error}`);\n delete this.keyExpiry;\n delete this.privateKeyPromise;\n }\n\n return promise;\n }\n\n listKeys(): Promise<{ keys: KeyPayload[] }> {\n return this.keyStore.listKeys();\n }\n}\n"],"names":["DatabaseKeyStore","durationToMilliseconds","uuid","generateKeyPair","exportJWK"],"mappings":";;;;;;;AAwBA,MAAM,aAAgB,GAAA,GAAA;AAMtB,MAAM,4BAA+B,GAAA,CAAA;AAE9B,MAAM,uBAAmD,CAAA;AAAA,EAI9D,WACmB,CAAA,QAAA,EACA,MACA,EAAA,kBAAA,EACA,SACjB,EAAA;AAJiB,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AACA,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AACA,IAAA,IAAA,CAAA,kBAAA,GAAA,kBAAA;AACA,IAAA,IAAA,CAAA,SAAA,GAAA,SAAA;AAAA;AAChB,EARK,iBAAA;AAAA,EACA,SAAA;AAAA,EASR,aAAoB,OAAO,OAKE,EAAA;AAC3B,IAAM,MAAA,QAAA,GAAW,MAAMA,iCAAA,CAAiB,MAAO,CAAA;AAAA,MAC7C,UAAU,OAAQ,CAAA,QAAA;AAAA,MAClB,QAAQ,OAAQ,CAAA;AAAA,KACjB,CAAA;AAED,IAAA,OAAO,IAAI,uBAAA;AAAA,MACT,QAAA;AAAA,MACA,OAAQ,CAAA,MAAA;AAAA,MACR,KAAK,KAAM,CAAAC,4BAAA,CAAuB,OAAQ,CAAA,WAAW,IAAI,GAAI,CAAA;AAAA,MAC7D,QAAQ,SAAa,IAAA;AAAA,KACvB;AAAA;AACF,EAEA,MAAM,oBAAqC,GAAA;AAEzC,IAAA,IAAI,KAAK,iBAAmB,EAAA;AAC1B,MAAI,IAAA,IAAA,CAAK,aAAa,IAAK,CAAA,SAAA,CAAU,SAAY,GAAA,IAAA,CAAK,KAAO,EAAA;AAC3D,QAAA,OAAO,IAAK,CAAA,iBAAA;AAAA;AAEd,MAAK,IAAA,CAAA,MAAA,CAAO,KAAK,CAA6C,2CAAA,CAAA,CAAA;AAC9D,MAAA,OAAO,IAAK,CAAA,iBAAA;AAAA;AAGd,IAAA,IAAA,CAAK,YAAY,IAAI,IAAA;AAAA,MACnB,IAAK,CAAA,GAAA,EAAQ,GAAA,IAAA,CAAK,kBAAqB,GAAA;AAAA,KACzC;AAEA,IAAA,MAAM,WAAW,YAAY;AAE3B,MAAA,MAAM,MAAMC,OAAK,EAAA;AACjB,MAAA,MAAM,GAAM,GAAA,MAAMC,oBAAgB,CAAA,IAAA,CAAK,SAAS,CAAA;AAChD,MAAA,MAAM,SAAY,GAAA,MAAMC,cAAU,CAAA,GAAA,CAAI,SAAS,CAAA;AAC/C,MAAA,MAAM,UAAa,GAAA,MAAMA,cAAU,CAAA,GAAA,CAAI,UAAU,CAAA;AACjD,MAAU,SAAA,CAAA,GAAA,GAAM,WAAW,GAAM,GAAA,GAAA;AACjC,MAAU,SAAA,CAAA,GAAA,GAAM,UAAW,CAAA,GAAA,GAAM,IAAK,CAAA,SAAA;AAQtC,MAAA,IAAA,CAAK,MAAO,CAAA,IAAA,CAAK,CAA2B,wBAAA,EAAA,GAAG,CAAE,CAAA,CAAA;AAEjD,MAAM,MAAA,IAAA,CAAK,SAAS,MAAO,CAAA;AAAA,QACzB,EAAI,EAAA,GAAA;AAAA,QACJ,GAAK,EAAA,SAAA;AAAA,QACL,WAAW,IAAI,IAAA;AAAA,UACb,IAAK,CAAA,GAAA,EACH,GAAA,IAAA,CAAK,qBACH,aACA,GAAA;AAAA;AACN,OACD,CAAA;AAGD,MAAO,OAAA,UAAA;AAAA,KACN,GAAA;AAEH,IAAA,IAAA,CAAK,iBAAoB,GAAA,OAAA;AAEzB,IAAI,IAAA;AAGF,MAAM,MAAA,OAAA;AAAA,aACC,KAAO,EAAA;AACd,MAAA,IAAA,CAAK,MAAO,CAAA,KAAA,CAAM,CAAuC,oCAAA,EAAA,KAAK,CAAE,CAAA,CAAA;AAChE,MAAA,OAAO,IAAK,CAAA,SAAA;AACZ,MAAA,OAAO,IAAK,CAAA,iBAAA;AAAA;AAGd,IAAO,OAAA,OAAA;AAAA;AACT,EAEA,QAA4C,GAAA;AAC1C,IAAO,OAAA,IAAA,CAAK,SAAS,QAAS,EAAA;AAAA;AAElC;;;;"}