@backstage/plugin-auth-backend 0.23.0 → 0.23.1-next.1
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.
- package/CHANGELOG.md +63 -0
- package/dist/authPlugin.cjs.js +75 -0
- package/dist/authPlugin.cjs.js.map +1 -0
- package/dist/database/AuthDatabase.cjs.js +51 -0
- package/dist/database/AuthDatabase.cjs.js.map +1 -0
- package/dist/identity/DatabaseKeyStore.cjs.js +40 -0
- package/dist/identity/DatabaseKeyStore.cjs.js.map +1 -0
- package/dist/identity/FirestoreKeyStore.cjs.js +90 -0
- package/dist/identity/FirestoreKeyStore.cjs.js.map +1 -0
- package/dist/identity/KeyStores.cjs.js +54 -0
- package/dist/identity/KeyStores.cjs.js.map +1 -0
- package/dist/identity/MemoryKeyStore.cjs.js +29 -0
- package/dist/identity/MemoryKeyStore.cjs.js.map +1 -0
- package/dist/identity/StaticKeyStore.cjs.js +91 -0
- package/dist/identity/StaticKeyStore.cjs.js.map +1 -0
- package/dist/identity/StaticTokenIssuer.cjs.js +53 -0
- package/dist/identity/StaticTokenIssuer.cjs.js.map +1 -0
- package/dist/identity/TokenFactory.cjs.js +164 -0
- package/dist/identity/TokenFactory.cjs.js.map +1 -0
- package/dist/identity/UserInfoDatabaseHandler.cjs.js +30 -0
- package/dist/identity/UserInfoDatabaseHandler.cjs.js.map +1 -0
- package/dist/identity/router.cjs.js +77 -0
- package/dist/identity/router.cjs.js.map +1 -0
- package/dist/index.cjs.js +31 -1981
- package/dist/index.cjs.js.map +1 -1
- package/dist/lib/catalog/CatalogIdentityClient.cjs.js +94 -0
- package/dist/lib/catalog/CatalogIdentityClient.cjs.js.map +1 -0
- package/dist/lib/flow/authFlowHelpers.cjs.js +43 -0
- package/dist/lib/flow/authFlowHelpers.cjs.js.map +1 -0
- package/dist/lib/legacy/adaptLegacyOAuthHandler.cjs.js +20 -0
- package/dist/lib/legacy/adaptLegacyOAuthHandler.cjs.js.map +1 -0
- package/dist/lib/legacy/adaptLegacyOAuthSignInResolver.cjs.js +24 -0
- package/dist/lib/legacy/adaptLegacyOAuthSignInResolver.cjs.js.map +1 -0
- package/dist/lib/legacy/adaptOAuthSignInResolverToLegacy.cjs.js +29 -0
- package/dist/lib/legacy/adaptOAuthSignInResolverToLegacy.cjs.js.map +1 -0
- package/dist/lib/oauth/OAuthAdapter.cjs.js +220 -0
- package/dist/lib/oauth/OAuthAdapter.cjs.js.map +1 -0
- package/dist/lib/oauth/OAuthEnvironmentHandler.cjs.js +8 -0
- package/dist/lib/oauth/OAuthEnvironmentHandler.cjs.js.map +1 -0
- package/dist/lib/oauth/helpers.cjs.js +40 -0
- package/dist/lib/oauth/helpers.cjs.js.map +1 -0
- package/dist/lib/passport/PassportStrategyHelper.cjs.js +48 -0
- package/dist/lib/passport/PassportStrategyHelper.cjs.js.map +1 -0
- package/dist/lib/resolvers/CatalogAuthResolverContext.cjs.js +116 -0
- package/dist/lib/resolvers/CatalogAuthResolverContext.cjs.js.map +1 -0
- package/dist/providers/atlassian/provider.cjs.js +20 -0
- package/dist/providers/atlassian/provider.cjs.js.map +1 -0
- package/dist/providers/auth0/provider.cjs.js +20 -0
- package/dist/providers/auth0/provider.cjs.js.map +1 -0
- package/dist/providers/aws-alb/provider.cjs.js +18 -0
- package/dist/providers/aws-alb/provider.cjs.js.map +1 -0
- package/dist/providers/azure-easyauth/provider.cjs.js +18 -0
- package/dist/providers/azure-easyauth/provider.cjs.js.map +1 -0
- package/dist/providers/bitbucket/provider.cjs.js +25 -0
- package/dist/providers/bitbucket/provider.cjs.js.map +1 -0
- package/dist/providers/bitbucketServer/provider.cjs.js +46 -0
- package/dist/providers/bitbucketServer/provider.cjs.js.map +1 -0
- package/dist/providers/cloudflare-access/provider.cjs.js +22 -0
- package/dist/providers/cloudflare-access/provider.cjs.js.map +1 -0
- package/dist/providers/createAuthProviderIntegration.cjs.js +11 -0
- package/dist/providers/createAuthProviderIntegration.cjs.js.map +1 -0
- package/dist/providers/gcp-iap/provider.cjs.js +18 -0
- package/dist/providers/gcp-iap/provider.cjs.js.map +1 -0
- package/dist/providers/github/provider.cjs.js +61 -0
- package/dist/providers/github/provider.cjs.js.map +1 -0
- package/dist/providers/gitlab/provider.cjs.js +20 -0
- package/dist/providers/gitlab/provider.cjs.js.map +1 -0
- package/dist/providers/google/provider.cjs.js +26 -0
- package/dist/providers/google/provider.cjs.js.map +1 -0
- package/dist/providers/microsoft/provider.cjs.js +27 -0
- package/dist/providers/microsoft/provider.cjs.js.map +1 -0
- package/dist/providers/oauth2/provider.cjs.js +20 -0
- package/dist/providers/oauth2/provider.cjs.js.map +1 -0
- package/dist/providers/oauth2-proxy/provider.cjs.js +18 -0
- package/dist/providers/oauth2-proxy/provider.cjs.js.map +1 -0
- package/dist/providers/oidc/provider.cjs.js +37 -0
- package/dist/providers/oidc/provider.cjs.js.map +1 -0
- package/dist/providers/okta/provider.cjs.js +47 -0
- package/dist/providers/okta/provider.cjs.js.map +1 -0
- package/dist/providers/onelogin/provider.cjs.js +20 -0
- package/dist/providers/onelogin/provider.cjs.js.map +1 -0
- package/dist/providers/prepareBackstageIdentityResponse.cjs.js +8 -0
- package/dist/providers/prepareBackstageIdentityResponse.cjs.js.map +1 -0
- package/dist/providers/providers.cjs.js +62 -0
- package/dist/providers/providers.cjs.js.map +1 -0
- package/dist/providers/resolvers.cjs.js +27 -0
- package/dist/providers/resolvers.cjs.js.map +1 -0
- package/dist/providers/router.cjs.js +111 -0
- package/dist/providers/router.cjs.js.map +1 -0
- package/dist/providers/saml/provider.cjs.js +121 -0
- package/dist/providers/saml/provider.cjs.js.map +1 -0
- package/dist/service/readBackstageTokenExpiration.cjs.js +27 -0
- package/dist/service/readBackstageTokenExpiration.cjs.js.map +1 -0
- package/dist/service/router.cjs.js +127 -0
- package/dist/service/router.cjs.js.map +1 -0
- package/package.json +29 -29
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,68 @@
|
|
|
1
1
|
# @backstage/plugin-auth-backend
|
|
2
2
|
|
|
3
|
+
## 0.23.1-next.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
- @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.3.1-next.1
|
|
9
|
+
- @backstage/plugin-auth-backend-module-atlassian-provider@0.3.1-next.1
|
|
10
|
+
- @backstage/plugin-auth-backend-module-bitbucket-provider@0.2.1-next.1
|
|
11
|
+
- @backstage/plugin-auth-backend-module-microsoft-provider@0.2.1-next.1
|
|
12
|
+
- @backstage/plugin-auth-backend-module-onelogin-provider@0.2.1-next.1
|
|
13
|
+
- @backstage/plugin-auth-backend-module-aws-alb-provider@0.2.1-next.1
|
|
14
|
+
- @backstage/plugin-auth-backend-module-gcp-iap-provider@0.3.1-next.1
|
|
15
|
+
- @backstage/plugin-auth-backend-module-github-provider@0.2.1-next.1
|
|
16
|
+
- @backstage/plugin-auth-backend-module-gitlab-provider@0.2.1-next.1
|
|
17
|
+
- @backstage/plugin-auth-backend-module-google-provider@0.2.1-next.1
|
|
18
|
+
- @backstage/plugin-auth-backend-module-oauth2-provider@0.3.1-next.1
|
|
19
|
+
- @backstage/plugin-auth-backend-module-oidc-provider@0.3.1-next.1
|
|
20
|
+
- @backstage/plugin-auth-backend-module-okta-provider@0.1.1-next.1
|
|
21
|
+
- @backstage/plugin-auth-node@0.5.3-next.1
|
|
22
|
+
- @backstage/plugin-catalog-node@1.13.1-next.1
|
|
23
|
+
- @backstage/catalog-client@1.7.1-next.0
|
|
24
|
+
- @backstage/backend-plugin-api@1.0.1-next.1
|
|
25
|
+
- @backstage/catalog-model@1.7.0
|
|
26
|
+
- @backstage/config@1.2.0
|
|
27
|
+
- @backstage/errors@1.2.4
|
|
28
|
+
- @backstage/types@1.1.1
|
|
29
|
+
- @backstage/plugin-auth-backend-module-auth0-provider@0.1.1-next.1
|
|
30
|
+
- @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.2.1-next.1
|
|
31
|
+
- @backstage/plugin-auth-backend-module-bitbucket-server-provider@0.1.1-next.1
|
|
32
|
+
- @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.2.1-next.1
|
|
33
|
+
|
|
34
|
+
## 0.23.1-next.0
|
|
35
|
+
|
|
36
|
+
### Patch Changes
|
|
37
|
+
|
|
38
|
+
- 094eaa3: Remove references to in-repo backend-common
|
|
39
|
+
- Updated dependencies
|
|
40
|
+
- @backstage/plugin-auth-backend-module-aws-alb-provider@0.2.1-next.0
|
|
41
|
+
- @backstage/plugin-auth-backend-module-oidc-provider@0.3.1-next.0
|
|
42
|
+
- @backstage/plugin-auth-node@0.5.3-next.0
|
|
43
|
+
- @backstage/backend-plugin-api@1.0.1-next.0
|
|
44
|
+
- @backstage/catalog-client@1.7.0
|
|
45
|
+
- @backstage/catalog-model@1.7.0
|
|
46
|
+
- @backstage/config@1.2.0
|
|
47
|
+
- @backstage/errors@1.2.4
|
|
48
|
+
- @backstage/types@1.1.1
|
|
49
|
+
- @backstage/plugin-auth-backend-module-atlassian-provider@0.3.1-next.0
|
|
50
|
+
- @backstage/plugin-auth-backend-module-auth0-provider@0.1.1-next.0
|
|
51
|
+
- @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.2.1-next.0
|
|
52
|
+
- @backstage/plugin-auth-backend-module-bitbucket-provider@0.2.1-next.0
|
|
53
|
+
- @backstage/plugin-auth-backend-module-bitbucket-server-provider@0.1.1-next.0
|
|
54
|
+
- @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.3.1-next.0
|
|
55
|
+
- @backstage/plugin-auth-backend-module-gcp-iap-provider@0.3.1-next.0
|
|
56
|
+
- @backstage/plugin-auth-backend-module-github-provider@0.2.1-next.0
|
|
57
|
+
- @backstage/plugin-auth-backend-module-gitlab-provider@0.2.1-next.0
|
|
58
|
+
- @backstage/plugin-auth-backend-module-google-provider@0.2.1-next.0
|
|
59
|
+
- @backstage/plugin-auth-backend-module-microsoft-provider@0.2.1-next.0
|
|
60
|
+
- @backstage/plugin-auth-backend-module-oauth2-provider@0.3.1-next.0
|
|
61
|
+
- @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.2.1-next.0
|
|
62
|
+
- @backstage/plugin-auth-backend-module-okta-provider@0.1.1-next.0
|
|
63
|
+
- @backstage/plugin-auth-backend-module-onelogin-provider@0.2.1-next.0
|
|
64
|
+
- @backstage/plugin-catalog-node@1.13.1-next.0
|
|
65
|
+
|
|
3
66
|
## 0.23.0
|
|
4
67
|
|
|
5
68
|
### Minor Changes
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
4
|
+
var pluginAuthNode = require('@backstage/plugin-auth-node');
|
|
5
|
+
var alpha = require('@backstage/plugin-catalog-node/alpha');
|
|
6
|
+
var router = require('./service/router.cjs.js');
|
|
7
|
+
|
|
8
|
+
const authPlugin = backendPluginApi.createBackendPlugin({
|
|
9
|
+
pluginId: "auth",
|
|
10
|
+
register(reg) {
|
|
11
|
+
const providers = /* @__PURE__ */ new Map();
|
|
12
|
+
let ownershipResolver = void 0;
|
|
13
|
+
reg.registerExtensionPoint(pluginAuthNode.authProvidersExtensionPoint, {
|
|
14
|
+
registerProvider({ providerId, factory }) {
|
|
15
|
+
if (providers.has(providerId)) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
`Auth provider '${providerId}' was already registered`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
providers.set(providerId, factory);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
reg.registerExtensionPoint(pluginAuthNode.authOwnershipResolutionExtensionPoint, {
|
|
24
|
+
setAuthOwnershipResolver(resolver) {
|
|
25
|
+
if (ownershipResolver) {
|
|
26
|
+
throw new Error("Auth ownership resolver is already set");
|
|
27
|
+
}
|
|
28
|
+
ownershipResolver = resolver;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
reg.registerInit({
|
|
32
|
+
deps: {
|
|
33
|
+
httpRouter: backendPluginApi.coreServices.httpRouter,
|
|
34
|
+
logger: backendPluginApi.coreServices.logger,
|
|
35
|
+
config: backendPluginApi.coreServices.rootConfig,
|
|
36
|
+
database: backendPluginApi.coreServices.database,
|
|
37
|
+
discovery: backendPluginApi.coreServices.discovery,
|
|
38
|
+
auth: backendPluginApi.coreServices.auth,
|
|
39
|
+
httpAuth: backendPluginApi.coreServices.httpAuth,
|
|
40
|
+
catalogApi: alpha.catalogServiceRef
|
|
41
|
+
},
|
|
42
|
+
async init({
|
|
43
|
+
httpRouter,
|
|
44
|
+
logger,
|
|
45
|
+
config,
|
|
46
|
+
database,
|
|
47
|
+
discovery,
|
|
48
|
+
auth,
|
|
49
|
+
httpAuth,
|
|
50
|
+
catalogApi
|
|
51
|
+
}) {
|
|
52
|
+
const router$1 = await router.createRouter({
|
|
53
|
+
logger,
|
|
54
|
+
config,
|
|
55
|
+
database,
|
|
56
|
+
discovery,
|
|
57
|
+
auth,
|
|
58
|
+
httpAuth,
|
|
59
|
+
catalogApi,
|
|
60
|
+
providerFactories: Object.fromEntries(providers),
|
|
61
|
+
disableDefaultProviderFactories: true,
|
|
62
|
+
ownershipResolver
|
|
63
|
+
});
|
|
64
|
+
httpRouter.addAuthPolicy({
|
|
65
|
+
path: "/",
|
|
66
|
+
allow: "unauthenticated"
|
|
67
|
+
});
|
|
68
|
+
httpRouter.use(router$1);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
exports.authPlugin = authPlugin;
|
|
75
|
+
//# sourceMappingURL=authPlugin.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authPlugin.cjs.js","sources":["../src/authPlugin.ts"],"sourcesContent":["/*\n * Copyright 2023 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 createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport {\n authOwnershipResolutionExtensionPoint,\n AuthOwnershipResolver,\n AuthProviderFactory,\n authProvidersExtensionPoint,\n} from '@backstage/plugin-auth-node';\nimport { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha';\nimport { createRouter } from './service/router';\n\n/**\n * Auth plugin\n *\n * @public\n */\nexport const authPlugin = createBackendPlugin({\n pluginId: 'auth',\n register(reg) {\n const providers = new Map<string, AuthProviderFactory>();\n let ownershipResolver: AuthOwnershipResolver | undefined = undefined;\n\n reg.registerExtensionPoint(authProvidersExtensionPoint, {\n registerProvider({ providerId, factory }) {\n if (providers.has(providerId)) {\n throw new Error(\n `Auth provider '${providerId}' was already registered`,\n );\n }\n providers.set(providerId, factory);\n },\n });\n\n reg.registerExtensionPoint(authOwnershipResolutionExtensionPoint, {\n setAuthOwnershipResolver(resolver) {\n if (ownershipResolver) {\n throw new Error('Auth ownership resolver is already set');\n }\n ownershipResolver = resolver;\n },\n });\n\n reg.registerInit({\n deps: {\n httpRouter: coreServices.httpRouter,\n logger: coreServices.logger,\n config: coreServices.rootConfig,\n database: coreServices.database,\n discovery: coreServices.discovery,\n auth: coreServices.auth,\n httpAuth: coreServices.httpAuth,\n catalogApi: catalogServiceRef,\n },\n async init({\n httpRouter,\n logger,\n config,\n database,\n discovery,\n auth,\n httpAuth,\n catalogApi,\n }) {\n const router = await createRouter({\n logger,\n config,\n database,\n discovery,\n auth,\n httpAuth,\n catalogApi,\n providerFactories: Object.fromEntries(providers),\n disableDefaultProviderFactories: true,\n ownershipResolver,\n });\n httpRouter.addAuthPolicy({\n path: '/',\n allow: 'unauthenticated',\n });\n httpRouter.use(router);\n },\n });\n },\n});\n"],"names":["createBackendPlugin","authProvidersExtensionPoint","authOwnershipResolutionExtensionPoint","coreServices","catalogServiceRef","router","createRouter"],"mappings":";;;;;;;AAkCO,MAAM,aAAaA,oCAAoB,CAAA;AAAA,EAC5C,QAAU,EAAA,MAAA;AAAA,EACV,SAAS,GAAK,EAAA;AACZ,IAAM,MAAA,SAAA,uBAAgB,GAAiC,EAAA,CAAA;AACvD,IAAA,IAAI,iBAAuD,GAAA,KAAA,CAAA,CAAA;AAE3D,IAAA,GAAA,CAAI,uBAAuBC,0CAA6B,EAAA;AAAA,MACtD,gBAAiB,CAAA,EAAE,UAAY,EAAA,OAAA,EAAW,EAAA;AACxC,QAAI,IAAA,SAAA,CAAU,GAAI,CAAA,UAAU,CAAG,EAAA;AAC7B,UAAA,MAAM,IAAI,KAAA;AAAA,YACR,kBAAkB,UAAU,CAAA,wBAAA,CAAA;AAAA,WAC9B,CAAA;AAAA,SACF;AACA,QAAU,SAAA,CAAA,GAAA,CAAI,YAAY,OAAO,CAAA,CAAA;AAAA,OACnC;AAAA,KACD,CAAA,CAAA;AAED,IAAA,GAAA,CAAI,uBAAuBC,oDAAuC,EAAA;AAAA,MAChE,yBAAyB,QAAU,EAAA;AACjC,QAAA,IAAI,iBAAmB,EAAA;AACrB,UAAM,MAAA,IAAI,MAAM,wCAAwC,CAAA,CAAA;AAAA,SAC1D;AACA,QAAoB,iBAAA,GAAA,QAAA,CAAA;AAAA,OACtB;AAAA,KACD,CAAA,CAAA;AAED,IAAA,GAAA,CAAI,YAAa,CAAA;AAAA,MACf,IAAM,EAAA;AAAA,QACJ,YAAYC,6BAAa,CAAA,UAAA;AAAA,QACzB,QAAQA,6BAAa,CAAA,MAAA;AAAA,QACrB,QAAQA,6BAAa,CAAA,UAAA;AAAA,QACrB,UAAUA,6BAAa,CAAA,QAAA;AAAA,QACvB,WAAWA,6BAAa,CAAA,SAAA;AAAA,QACxB,MAAMA,6BAAa,CAAA,IAAA;AAAA,QACnB,UAAUA,6BAAa,CAAA,QAAA;AAAA,QACvB,UAAY,EAAAC,uBAAA;AAAA,OACd;AAAA,MACA,MAAM,IAAK,CAAA;AAAA,QACT,UAAA;AAAA,QACA,MAAA;AAAA,QACA,MAAA;AAAA,QACA,QAAA;AAAA,QACA,SAAA;AAAA,QACA,IAAA;AAAA,QACA,QAAA;AAAA,QACA,UAAA;AAAA,OACC,EAAA;AACD,QAAM,MAAAC,QAAA,GAAS,MAAMC,mBAAa,CAAA;AAAA,UAChC,MAAA;AAAA,UACA,MAAA;AAAA,UACA,QAAA;AAAA,UACA,SAAA;AAAA,UACA,IAAA;AAAA,UACA,QAAA;AAAA,UACA,UAAA;AAAA,UACA,iBAAA,EAAmB,MAAO,CAAA,WAAA,CAAY,SAAS,CAAA;AAAA,UAC/C,+BAAiC,EAAA,IAAA;AAAA,UACjC,iBAAA;AAAA,SACD,CAAA,CAAA;AACD,QAAA,UAAA,CAAW,aAAc,CAAA;AAAA,UACvB,IAAM,EAAA,GAAA;AAAA,UACN,KAAO,EAAA,iBAAA;AAAA,SACR,CAAA,CAAA;AACD,QAAA,UAAA,CAAW,IAAID,QAAM,CAAA,CAAA;AAAA,OACvB;AAAA,KACD,CAAA,CAAA;AAAA,GACH;AACF,CAAC;;;;"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var backendCommon = require('@backstage/backend-common');
|
|
4
|
+
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
5
|
+
var config = require('@backstage/config');
|
|
6
|
+
|
|
7
|
+
const migrationsDir = backendPluginApi.resolvePackagePath(
|
|
8
|
+
"@backstage/plugin-auth-backend",
|
|
9
|
+
"migrations"
|
|
10
|
+
);
|
|
11
|
+
class AuthDatabase {
|
|
12
|
+
#database;
|
|
13
|
+
#promise;
|
|
14
|
+
static create(database) {
|
|
15
|
+
return new AuthDatabase(database);
|
|
16
|
+
}
|
|
17
|
+
/** @internal */
|
|
18
|
+
static forTesting() {
|
|
19
|
+
const config$1 = new config.ConfigReader({
|
|
20
|
+
backend: {
|
|
21
|
+
database: {
|
|
22
|
+
client: "better-sqlite3",
|
|
23
|
+
connection: ":memory:",
|
|
24
|
+
useNullAsDefault: true
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
const database = backendCommon.DatabaseManager.fromConfig(config$1).forPlugin("auth");
|
|
29
|
+
return new AuthDatabase(database);
|
|
30
|
+
}
|
|
31
|
+
static async runMigrations(knex) {
|
|
32
|
+
await knex.migrate.latest({
|
|
33
|
+
directory: migrationsDir
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
constructor(database) {
|
|
37
|
+
this.#database = database;
|
|
38
|
+
}
|
|
39
|
+
get() {
|
|
40
|
+
this.#promise ??= this.#database.getClient().then(async (client) => {
|
|
41
|
+
if (!this.#database.migrations?.skip) {
|
|
42
|
+
await AuthDatabase.runMigrations(client);
|
|
43
|
+
}
|
|
44
|
+
return client;
|
|
45
|
+
});
|
|
46
|
+
return this.#promise;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
exports.AuthDatabase = AuthDatabase;
|
|
51
|
+
//# sourceMappingURL=AuthDatabase.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthDatabase.cjs.js","sources":["../../src/database/AuthDatabase.ts"],"sourcesContent":["/*\n * Copyright 2023 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 DatabaseManager,\n PluginDatabaseManager,\n} from '@backstage/backend-common';\nimport { resolvePackagePath } from '@backstage/backend-plugin-api';\nimport { ConfigReader } from '@backstage/config';\nimport { Knex } from 'knex';\n\nconst migrationsDir = resolvePackagePath(\n '@backstage/plugin-auth-backend',\n 'migrations',\n);\n\n/**\n * Ensures that a database connection is established exactly once and only when\n * asked for, and runs migrations.\n */\nexport class AuthDatabase {\n readonly #database: PluginDatabaseManager;\n #promise: Promise<Knex> | undefined;\n\n static create(database: PluginDatabaseManager): AuthDatabase {\n return new AuthDatabase(database);\n }\n\n /** @internal */\n static forTesting(): AuthDatabase {\n const config = new ConfigReader({\n backend: {\n database: {\n client: 'better-sqlite3',\n connection: ':memory:',\n useNullAsDefault: true,\n },\n },\n });\n const database = DatabaseManager.fromConfig(config).forPlugin('auth');\n return new AuthDatabase(database);\n }\n\n static async runMigrations(knex: Knex): Promise<void> {\n await knex.migrate.latest({\n directory: migrationsDir,\n });\n }\n\n private constructor(database: PluginDatabaseManager) {\n this.#database = database;\n }\n\n get(): Promise<Knex> {\n this.#promise ??= this.#database.getClient().then(async client => {\n if (!this.#database.migrations?.skip) {\n await AuthDatabase.runMigrations(client);\n }\n return client;\n });\n\n return this.#promise;\n }\n}\n"],"names":["resolvePackagePath","config","ConfigReader","DatabaseManager"],"mappings":";;;;;;AAwBA,MAAM,aAAgB,GAAAA,mCAAA;AAAA,EACpB,gCAAA;AAAA,EACA,YAAA;AACF,CAAA,CAAA;AAMO,MAAM,YAAa,CAAA;AAAA,EACf,SAAA,CAAA;AAAA,EACT,QAAA,CAAA;AAAA,EAEA,OAAO,OAAO,QAA+C,EAAA;AAC3D,IAAO,OAAA,IAAI,aAAa,QAAQ,CAAA,CAAA;AAAA,GAClC;AAAA;AAAA,EAGA,OAAO,UAA2B,GAAA;AAChC,IAAM,MAAAC,QAAA,GAAS,IAAIC,mBAAa,CAAA;AAAA,MAC9B,OAAS,EAAA;AAAA,QACP,QAAU,EAAA;AAAA,UACR,MAAQ,EAAA,gBAAA;AAAA,UACR,UAAY,EAAA,UAAA;AAAA,UACZ,gBAAkB,EAAA,IAAA;AAAA,SACpB;AAAA,OACF;AAAA,KACD,CAAA,CAAA;AACD,IAAA,MAAM,WAAWC,6BAAgB,CAAA,UAAA,CAAWF,QAAM,CAAA,CAAE,UAAU,MAAM,CAAA,CAAA;AACpE,IAAO,OAAA,IAAI,aAAa,QAAQ,CAAA,CAAA;AAAA,GAClC;AAAA,EAEA,aAAa,cAAc,IAA2B,EAAA;AACpD,IAAM,MAAA,IAAA,CAAK,QAAQ,MAAO,CAAA;AAAA,MACxB,SAAW,EAAA,aAAA;AAAA,KACZ,CAAA,CAAA;AAAA,GACH;AAAA,EAEQ,YAAY,QAAiC,EAAA;AACnD,IAAA,IAAA,CAAK,SAAY,GAAA,QAAA,CAAA;AAAA,GACnB;AAAA,EAEA,GAAqB,GAAA;AACnB,IAAA,IAAA,CAAK,aAAa,IAAK,CAAA,SAAA,CAAU,WAAY,CAAA,IAAA,CAAK,OAAM,MAAU,KAAA;AAChE,MAAA,IAAI,CAAC,IAAA,CAAK,SAAU,CAAA,UAAA,EAAY,IAAM,EAAA;AACpC,QAAM,MAAA,YAAA,CAAa,cAAc,MAAM,CAAA,CAAA;AAAA,OACzC;AACA,MAAO,OAAA,MAAA,CAAA;AAAA,KACR,CAAA,CAAA;AAED,IAAA,OAAO,IAAK,CAAA,QAAA,CAAA;AAAA,GACd;AACF;;;;"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var luxon = require('luxon');
|
|
4
|
+
|
|
5
|
+
const TABLE = "signing_keys";
|
|
6
|
+
const parseDate = (date) => {
|
|
7
|
+
const parsedDate = typeof date === "string" ? luxon.DateTime.fromSQL(date, { zone: "UTC" }) : luxon.DateTime.fromJSDate(date);
|
|
8
|
+
if (!parsedDate.isValid) {
|
|
9
|
+
throw new Error(
|
|
10
|
+
`Failed to parse date, reason: ${parsedDate.invalidReason}, explanation: ${parsedDate.invalidExplanation}`
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
return parsedDate.toJSDate();
|
|
14
|
+
};
|
|
15
|
+
class DatabaseKeyStore {
|
|
16
|
+
constructor(client) {
|
|
17
|
+
this.client = client;
|
|
18
|
+
}
|
|
19
|
+
async addKey(key) {
|
|
20
|
+
await this.client(TABLE).insert({
|
|
21
|
+
kid: key.kid,
|
|
22
|
+
key: JSON.stringify(key)
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
async listKeys() {
|
|
26
|
+
const rows = await this.client(TABLE).select();
|
|
27
|
+
return {
|
|
28
|
+
items: rows.map((row) => ({
|
|
29
|
+
key: JSON.parse(row.key),
|
|
30
|
+
createdAt: parseDate(row.created_at)
|
|
31
|
+
}))
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
async removeKeys(kids) {
|
|
35
|
+
await this.client(TABLE).delete().whereIn("kid", kids);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
exports.DatabaseKeyStore = DatabaseKeyStore;
|
|
40
|
+
//# sourceMappingURL=DatabaseKeyStore.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DatabaseKeyStore.cjs.js","sources":["../../src/identity/DatabaseKeyStore.ts"],"sourcesContent":["/*\n * Copyright 2020 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 { Knex } from 'knex';\nimport { DateTime } from 'luxon';\nimport { AnyJWK, KeyStore, StoredKey } from './types';\n\nconst TABLE = 'signing_keys';\n\ntype Row = {\n created_at: Date; // row.created_at is a string after being returned from the database\n kid: string;\n key: string;\n};\n\nconst parseDate = (date: string | Date) => {\n const parsedDate =\n typeof date === 'string'\n ? DateTime.fromSQL(date, { zone: 'UTC' })\n : DateTime.fromJSDate(date);\n\n if (!parsedDate.isValid) {\n throw new Error(\n `Failed to parse date, reason: ${parsedDate.invalidReason}, explanation: ${parsedDate.invalidExplanation}`,\n );\n }\n\n return parsedDate.toJSDate();\n};\n\nexport class DatabaseKeyStore implements KeyStore {\n constructor(private readonly client: Knex) {}\n\n async addKey(key: AnyJWK): Promise<void> {\n await this.client<Row>(TABLE).insert({\n kid: key.kid,\n key: JSON.stringify(key),\n });\n }\n\n async listKeys(): Promise<{ items: StoredKey[] }> {\n const rows = await this.client<Row>(TABLE).select();\n\n return {\n items: rows.map(row => ({\n key: JSON.parse(row.key),\n createdAt: parseDate(row.created_at),\n })),\n };\n }\n\n async removeKeys(kids: string[]): Promise<void> {\n await this.client(TABLE).delete().whereIn('kid', kids);\n }\n}\n"],"names":["DateTime"],"mappings":";;;;AAoBA,MAAM,KAAQ,GAAA,cAAA,CAAA;AAQd,MAAM,SAAA,GAAY,CAAC,IAAwB,KAAA;AACzC,EAAA,MAAM,UACJ,GAAA,OAAO,IAAS,KAAA,QAAA,GACZA,eAAS,OAAQ,CAAA,IAAA,EAAM,EAAE,IAAA,EAAM,KAAM,EAAC,CACtC,GAAAA,cAAA,CAAS,WAAW,IAAI,CAAA,CAAA;AAE9B,EAAI,IAAA,CAAC,WAAW,OAAS,EAAA;AACvB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAiC,8BAAA,EAAA,UAAA,CAAW,aAAa,CAAA,eAAA,EAAkB,WAAW,kBAAkB,CAAA,CAAA;AAAA,KAC1G,CAAA;AAAA,GACF;AAEA,EAAA,OAAO,WAAW,QAAS,EAAA,CAAA;AAC7B,CAAA,CAAA;AAEO,MAAM,gBAAqC,CAAA;AAAA,EAChD,YAA6B,MAAc,EAAA;AAAd,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA,CAAA;AAAA,GAAe;AAAA,EAE5C,MAAM,OAAO,GAA4B,EAAA;AACvC,IAAA,MAAM,IAAK,CAAA,MAAA,CAAY,KAAK,CAAA,CAAE,MAAO,CAAA;AAAA,MACnC,KAAK,GAAI,CAAA,GAAA;AAAA,MACT,GAAA,EAAK,IAAK,CAAA,SAAA,CAAU,GAAG,CAAA;AAAA,KACxB,CAAA,CAAA;AAAA,GACH;AAAA,EAEA,MAAM,QAA4C,GAAA;AAChD,IAAA,MAAM,OAAO,MAAM,IAAA,CAAK,MAAY,CAAA,KAAK,EAAE,MAAO,EAAA,CAAA;AAElD,IAAO,OAAA;AAAA,MACL,KAAA,EAAO,IAAK,CAAA,GAAA,CAAI,CAAQ,GAAA,MAAA;AAAA,QACtB,GAAK,EAAA,IAAA,CAAK,KAAM,CAAA,GAAA,CAAI,GAAG,CAAA;AAAA,QACvB,SAAA,EAAW,SAAU,CAAA,GAAA,CAAI,UAAU,CAAA;AAAA,OACnC,CAAA,CAAA;AAAA,KACJ,CAAA;AAAA,GACF;AAAA,EAEA,MAAM,WAAW,IAA+B,EAAA;AAC9C,IAAM,MAAA,IAAA,CAAK,OAAO,KAAK,CAAA,CAAE,QAAS,CAAA,OAAA,CAAQ,OAAO,IAAI,CAAA,CAAA;AAAA,GACvD;AACF;;;;"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var firestore = require('@google-cloud/firestore');
|
|
4
|
+
|
|
5
|
+
const DEFAULT_TIMEOUT_MS = 1e4;
|
|
6
|
+
const DEFAULT_DOCUMENT_PATH = "sessions";
|
|
7
|
+
class FirestoreKeyStore {
|
|
8
|
+
constructor(database, path, timeout) {
|
|
9
|
+
this.database = database;
|
|
10
|
+
this.path = path;
|
|
11
|
+
this.timeout = timeout;
|
|
12
|
+
}
|
|
13
|
+
static async create(settings) {
|
|
14
|
+
const { path, timeout, ...firestoreSettings } = settings ?? {};
|
|
15
|
+
const database = new firestore.Firestore(firestoreSettings);
|
|
16
|
+
return new FirestoreKeyStore(
|
|
17
|
+
database,
|
|
18
|
+
path ?? DEFAULT_DOCUMENT_PATH,
|
|
19
|
+
timeout ?? DEFAULT_TIMEOUT_MS
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
static async verifyConnection(keyStore, logger) {
|
|
23
|
+
try {
|
|
24
|
+
await keyStore.verify();
|
|
25
|
+
} catch (error) {
|
|
26
|
+
if (process.env.NODE_ENV !== "development") {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`Failed to connect to database: ${error.message}`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
logger?.warn(
|
|
32
|
+
`Failed to connect to database: ${error.message}`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async addKey(key) {
|
|
37
|
+
await this.withTimeout(
|
|
38
|
+
this.database.collection(this.path).doc(key.kid).set({
|
|
39
|
+
kid: key.kid,
|
|
40
|
+
key: JSON.stringify(key)
|
|
41
|
+
})
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
async listKeys() {
|
|
45
|
+
const keys = await this.withTimeout(
|
|
46
|
+
this.database.collection(this.path).get()
|
|
47
|
+
);
|
|
48
|
+
return {
|
|
49
|
+
items: keys.docs.map((key) => ({
|
|
50
|
+
key: key.data(),
|
|
51
|
+
createdAt: key.createTime.toDate()
|
|
52
|
+
}))
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
async removeKeys(kids) {
|
|
56
|
+
for (const kid of kids) {
|
|
57
|
+
await this.withTimeout(
|
|
58
|
+
this.database.collection(this.path).doc(kid).delete()
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Helper function to allow us to modify the timeout used when
|
|
64
|
+
* performing Firestore database operations.
|
|
65
|
+
*
|
|
66
|
+
* The reason for this is that it seems that there's no other
|
|
67
|
+
* practical solution to change the default timeout of 10mins
|
|
68
|
+
* that Firestore has.
|
|
69
|
+
*
|
|
70
|
+
*/
|
|
71
|
+
async withTimeout(operation) {
|
|
72
|
+
const timer = new Promise(
|
|
73
|
+
(_, reject) => setTimeout(() => {
|
|
74
|
+
reject(new Error(`Operation timed out after ${this.timeout}ms`));
|
|
75
|
+
}, this.timeout)
|
|
76
|
+
);
|
|
77
|
+
return Promise.race([operation, timer]);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Used to verify that the database is reachable.
|
|
81
|
+
*/
|
|
82
|
+
async verify() {
|
|
83
|
+
await this.withTimeout(this.database.collection(this.path).limit(1).get());
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
exports.DEFAULT_DOCUMENT_PATH = DEFAULT_DOCUMENT_PATH;
|
|
88
|
+
exports.DEFAULT_TIMEOUT_MS = DEFAULT_TIMEOUT_MS;
|
|
89
|
+
exports.FirestoreKeyStore = FirestoreKeyStore;
|
|
90
|
+
//# sourceMappingURL=FirestoreKeyStore.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FirestoreKeyStore.cjs.js","sources":["../../src/identity/FirestoreKeyStore.ts"],"sourcesContent":["/*\n * Copyright 2021 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 { LoggerService } from '@backstage/backend-plugin-api';\nimport {\n DocumentData,\n Firestore,\n QuerySnapshot,\n Settings,\n WriteResult,\n} from '@google-cloud/firestore';\n\nimport { AnyJWK, KeyStore, StoredKey } from './types';\n\nexport type FirestoreKeyStoreSettings = Settings & Options;\n\ntype Options = {\n path?: string;\n timeout?: number;\n};\n\nexport const DEFAULT_TIMEOUT_MS = 10000;\nexport const DEFAULT_DOCUMENT_PATH = 'sessions';\n\nexport class FirestoreKeyStore implements KeyStore {\n static async create(\n settings?: FirestoreKeyStoreSettings,\n ): Promise<FirestoreKeyStore> {\n const { path, timeout, ...firestoreSettings } = settings ?? {};\n const database = new Firestore(firestoreSettings);\n\n return new FirestoreKeyStore(\n database,\n path ?? DEFAULT_DOCUMENT_PATH,\n timeout ?? DEFAULT_TIMEOUT_MS,\n );\n }\n\n private constructor(\n private readonly database: Firestore,\n private readonly path: string,\n private readonly timeout: number,\n ) {}\n\n static async verifyConnection(\n keyStore: FirestoreKeyStore,\n logger?: LoggerService,\n ): Promise<void> {\n try {\n await keyStore.verify();\n } catch (error) {\n if (process.env.NODE_ENV !== 'development') {\n throw new Error(\n `Failed to connect to database: ${(error as Error).message}`,\n );\n }\n logger?.warn(\n `Failed to connect to database: ${(error as Error).message}`,\n );\n }\n }\n\n async addKey(key: AnyJWK): Promise<void> {\n await this.withTimeout<WriteResult>(\n this.database\n .collection(this.path)\n .doc(key.kid)\n .set({\n kid: key.kid,\n key: JSON.stringify(key),\n }),\n );\n }\n\n async listKeys(): Promise<{ items: StoredKey[] }> {\n const keys = await this.withTimeout<QuerySnapshot<DocumentData>>(\n this.database.collection(this.path).get(),\n );\n\n return {\n items: keys.docs.map(key => ({\n key: key.data() as AnyJWK,\n createdAt: key.createTime.toDate(),\n })),\n };\n }\n\n async removeKeys(kids: string[]): Promise<void> {\n // This is probably really slow, but it's done async in the background\n for (const kid of kids) {\n await this.withTimeout<WriteResult>(\n this.database.collection(this.path).doc(kid).delete(),\n );\n }\n\n /**\n * This could be achieved with batching but there's a couple of limitations with that:\n *\n * - A batched write can contain a maximum of 500 operations\n * https://firebase.google.com/docs/firestore/manage-data/transactions#batched-writes\n *\n * - The \"in\" operator can combine a maximum of 10 equality clauses\n * https://firebase.google.com/docs/firestore/query-data/queries#in_not-in_and_array-contains-any\n *\n * Example:\n *\n * const batch = this.database.batch();\n * const docs = await this.database\n * .collection(this.path)\n * .where('kid', 'in', kids)\n * .get();\n * docs.forEach(doc => {\n * batch.delete(doc.ref);\n * });\n * await batch.commit();\n *\n */\n }\n\n /**\n * Helper function to allow us to modify the timeout used when\n * performing Firestore database operations.\n *\n * The reason for this is that it seems that there's no other\n * practical solution to change the default timeout of 10mins\n * that Firestore has.\n *\n */\n private async withTimeout<T>(operation: Promise<T>): Promise<T> {\n const timer = new Promise<never>((_, reject) =>\n setTimeout(() => {\n reject(new Error(`Operation timed out after ${this.timeout}ms`));\n }, this.timeout),\n );\n return Promise.race<T>([operation, timer]);\n }\n\n /**\n * Used to verify that the database is reachable.\n */\n private async verify(): Promise<void> {\n await this.withTimeout(this.database.collection(this.path).limit(1).get());\n }\n}\n"],"names":["Firestore"],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var lodash = require('lodash');
|
|
4
|
+
var DatabaseKeyStore = require('./DatabaseKeyStore.cjs.js');
|
|
5
|
+
var FirestoreKeyStore = require('./FirestoreKeyStore.cjs.js');
|
|
6
|
+
var MemoryKeyStore = require('./MemoryKeyStore.cjs.js');
|
|
7
|
+
var StaticKeyStore = require('./StaticKeyStore.cjs.js');
|
|
8
|
+
|
|
9
|
+
class KeyStores {
|
|
10
|
+
/**
|
|
11
|
+
* Looks at the `auth.keyStore` section in the application configuration
|
|
12
|
+
* and returns a KeyStore store. Defaults to `database`
|
|
13
|
+
*
|
|
14
|
+
* @returns a KeyStore store
|
|
15
|
+
*/
|
|
16
|
+
static async fromConfig(config, options) {
|
|
17
|
+
const { logger, database } = options;
|
|
18
|
+
const ks = config.getOptionalConfig("auth.keyStore");
|
|
19
|
+
const provider = ks?.getOptionalString("provider") ?? "database";
|
|
20
|
+
logger.info(`Configuring "${provider}" as KeyStore provider`);
|
|
21
|
+
if (provider === "database") {
|
|
22
|
+
return new DatabaseKeyStore.DatabaseKeyStore(await database.get());
|
|
23
|
+
}
|
|
24
|
+
if (provider === "memory") {
|
|
25
|
+
return new MemoryKeyStore.MemoryKeyStore();
|
|
26
|
+
}
|
|
27
|
+
if (provider === "firestore") {
|
|
28
|
+
const settings = ks?.getConfig(provider);
|
|
29
|
+
const keyStore = await FirestoreKeyStore.FirestoreKeyStore.create(
|
|
30
|
+
lodash.pickBy(
|
|
31
|
+
{
|
|
32
|
+
projectId: settings?.getOptionalString("projectId"),
|
|
33
|
+
keyFilename: settings?.getOptionalString("keyFilename"),
|
|
34
|
+
host: settings?.getOptionalString("host"),
|
|
35
|
+
port: settings?.getOptionalNumber("port"),
|
|
36
|
+
ssl: settings?.getOptionalBoolean("ssl"),
|
|
37
|
+
path: settings?.getOptionalString("path"),
|
|
38
|
+
timeout: settings?.getOptionalNumber("timeout")
|
|
39
|
+
},
|
|
40
|
+
(value) => value !== void 0
|
|
41
|
+
)
|
|
42
|
+
);
|
|
43
|
+
await FirestoreKeyStore.FirestoreKeyStore.verifyConnection(keyStore, logger);
|
|
44
|
+
return keyStore;
|
|
45
|
+
}
|
|
46
|
+
if (provider === "static") {
|
|
47
|
+
return await StaticKeyStore.StaticKeyStore.fromConfig(config);
|
|
48
|
+
}
|
|
49
|
+
throw new Error(`Unknown KeyStore provider: ${provider}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
exports.KeyStores = KeyStores;
|
|
54
|
+
//# sourceMappingURL=KeyStores.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KeyStores.cjs.js","sources":["../../src/identity/KeyStores.ts"],"sourcesContent":["/*\n * Copyright 2021 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 { pickBy } from 'lodash';\nimport { LoggerService } from '@backstage/backend-plugin-api';\n\nimport { Config } from '@backstage/config';\nimport { AuthDatabase } from '../database/AuthDatabase';\nimport { DatabaseKeyStore } from './DatabaseKeyStore';\nimport { FirestoreKeyStore } from './FirestoreKeyStore';\nimport { MemoryKeyStore } from './MemoryKeyStore';\nimport { KeyStore } from './types';\nimport { StaticKeyStore } from './StaticKeyStore';\n\ntype Options = {\n logger: LoggerService;\n database: AuthDatabase;\n};\n\nexport class KeyStores {\n /**\n * Looks at the `auth.keyStore` section in the application configuration\n * and returns a KeyStore store. Defaults to `database`\n *\n * @returns a KeyStore store\n */\n static async fromConfig(config: Config, options: Options): Promise<KeyStore> {\n const { logger, database } = options;\n\n const ks = config.getOptionalConfig('auth.keyStore');\n const provider = ks?.getOptionalString('provider') ?? 'database';\n\n logger.info(`Configuring \"${provider}\" as KeyStore provider`);\n\n if (provider === 'database') {\n return new DatabaseKeyStore(await database.get());\n }\n\n if (provider === 'memory') {\n return new MemoryKeyStore();\n }\n\n if (provider === 'firestore') {\n const settings = ks?.getConfig(provider);\n\n const keyStore = await FirestoreKeyStore.create(\n pickBy(\n {\n projectId: settings?.getOptionalString('projectId'),\n keyFilename: settings?.getOptionalString('keyFilename'),\n host: settings?.getOptionalString('host'),\n port: settings?.getOptionalNumber('port'),\n ssl: settings?.getOptionalBoolean('ssl'),\n path: settings?.getOptionalString('path'),\n timeout: settings?.getOptionalNumber('timeout'),\n },\n value => value !== undefined,\n ),\n );\n await FirestoreKeyStore.verifyConnection(keyStore, logger);\n\n return keyStore;\n }\n\n if (provider === 'static') {\n return await StaticKeyStore.fromConfig(config);\n }\n\n throw new Error(`Unknown KeyStore provider: ${provider}`);\n }\n}\n"],"names":["DatabaseKeyStore","MemoryKeyStore","FirestoreKeyStore","pickBy","StaticKeyStore"],"mappings":";;;;;;;;AAgCO,MAAM,SAAU,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOrB,aAAa,UAAW,CAAA,MAAA,EAAgB,OAAqC,EAAA;AAC3E,IAAM,MAAA,EAAE,MAAQ,EAAA,QAAA,EAAa,GAAA,OAAA,CAAA;AAE7B,IAAM,MAAA,EAAA,GAAK,MAAO,CAAA,iBAAA,CAAkB,eAAe,CAAA,CAAA;AACnD,IAAA,MAAM,QAAW,GAAA,EAAA,EAAI,iBAAkB,CAAA,UAAU,CAAK,IAAA,UAAA,CAAA;AAEtD,IAAO,MAAA,CAAA,IAAA,CAAK,CAAgB,aAAA,EAAA,QAAQ,CAAwB,sBAAA,CAAA,CAAA,CAAA;AAE5D,IAAA,IAAI,aAAa,UAAY,EAAA;AAC3B,MAAA,OAAO,IAAIA,iCAAA,CAAiB,MAAM,QAAA,CAAS,KAAK,CAAA,CAAA;AAAA,KAClD;AAEA,IAAA,IAAI,aAAa,QAAU,EAAA;AACzB,MAAA,OAAO,IAAIC,6BAAe,EAAA,CAAA;AAAA,KAC5B;AAEA,IAAA,IAAI,aAAa,WAAa,EAAA;AAC5B,MAAM,MAAA,QAAA,GAAW,EAAI,EAAA,SAAA,CAAU,QAAQ,CAAA,CAAA;AAEvC,MAAM,MAAA,QAAA,GAAW,MAAMC,mCAAkB,CAAA,MAAA;AAAA,QACvCC,aAAA;AAAA,UACE;AAAA,YACE,SAAA,EAAW,QAAU,EAAA,iBAAA,CAAkB,WAAW,CAAA;AAAA,YAClD,WAAA,EAAa,QAAU,EAAA,iBAAA,CAAkB,aAAa,CAAA;AAAA,YACtD,IAAA,EAAM,QAAU,EAAA,iBAAA,CAAkB,MAAM,CAAA;AAAA,YACxC,IAAA,EAAM,QAAU,EAAA,iBAAA,CAAkB,MAAM,CAAA;AAAA,YACxC,GAAA,EAAK,QAAU,EAAA,kBAAA,CAAmB,KAAK,CAAA;AAAA,YACvC,IAAA,EAAM,QAAU,EAAA,iBAAA,CAAkB,MAAM,CAAA;AAAA,YACxC,OAAA,EAAS,QAAU,EAAA,iBAAA,CAAkB,SAAS,CAAA;AAAA,WAChD;AAAA,UACA,WAAS,KAAU,KAAA,KAAA,CAAA;AAAA,SACrB;AAAA,OACF,CAAA;AACA,MAAM,MAAAD,mCAAA,CAAkB,gBAAiB,CAAA,QAAA,EAAU,MAAM,CAAA,CAAA;AAEzD,MAAO,OAAA,QAAA,CAAA;AAAA,KACT;AAEA,IAAA,IAAI,aAAa,QAAU,EAAA;AACzB,MAAO,OAAA,MAAME,6BAAe,CAAA,UAAA,CAAW,MAAM,CAAA,CAAA;AAAA,KAC/C;AAEA,IAAA,MAAM,IAAI,KAAA,CAAM,CAA8B,2BAAA,EAAA,QAAQ,CAAE,CAAA,CAAA,CAAA;AAAA,GAC1D;AACF;;;;"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var luxon = require('luxon');
|
|
4
|
+
|
|
5
|
+
class MemoryKeyStore {
|
|
6
|
+
keys = /* @__PURE__ */ new Map();
|
|
7
|
+
async addKey(key) {
|
|
8
|
+
this.keys.set(key.kid, {
|
|
9
|
+
createdAt: luxon.DateTime.utc().toJSDate(),
|
|
10
|
+
key: JSON.stringify(key)
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
async removeKeys(kids) {
|
|
14
|
+
for (const kid of kids) {
|
|
15
|
+
this.keys.delete(kid);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async listKeys() {
|
|
19
|
+
return {
|
|
20
|
+
items: Array.from(this.keys).map(([, { createdAt, key: keyStr }]) => ({
|
|
21
|
+
createdAt,
|
|
22
|
+
key: JSON.parse(keyStr)
|
|
23
|
+
}))
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
exports.MemoryKeyStore = MemoryKeyStore;
|
|
29
|
+
//# sourceMappingURL=MemoryKeyStore.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MemoryKeyStore.cjs.js","sources":["../../src/identity/MemoryKeyStore.ts"],"sourcesContent":["/*\n * Copyright 2020 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 { KeyStore, AnyJWK, StoredKey } from './types';\nimport { DateTime } from 'luxon';\n\nexport class MemoryKeyStore implements KeyStore {\n private readonly keys = new Map<string, { createdAt: Date; key: string }>();\n\n async addKey(key: AnyJWK): Promise<void> {\n this.keys.set(key.kid, {\n createdAt: DateTime.utc().toJSDate(),\n key: JSON.stringify(key),\n });\n }\n\n async removeKeys(kids: string[]): Promise<void> {\n for (const kid of kids) {\n this.keys.delete(kid);\n }\n }\n\n async listKeys(): Promise<{ items: StoredKey[] }> {\n return {\n items: Array.from(this.keys).map(([, { createdAt, key: keyStr }]) => ({\n createdAt,\n key: JSON.parse(keyStr),\n })),\n };\n }\n}\n"],"names":["DateTime"],"mappings":";;;;AAmBO,MAAM,cAAmC,CAAA;AAAA,EAC7B,IAAA,uBAAW,GAA8C,EAAA,CAAA;AAAA,EAE1E,MAAM,OAAO,GAA4B,EAAA;AACvC,IAAK,IAAA,CAAA,IAAA,CAAK,GAAI,CAAA,GAAA,CAAI,GAAK,EAAA;AAAA,MACrB,SAAW,EAAAA,cAAA,CAAS,GAAI,EAAA,CAAE,QAAS,EAAA;AAAA,MACnC,GAAA,EAAK,IAAK,CAAA,SAAA,CAAU,GAAG,CAAA;AAAA,KACxB,CAAA,CAAA;AAAA,GACH;AAAA,EAEA,MAAM,WAAW,IAA+B,EAAA;AAC9C,IAAA,KAAA,MAAW,OAAO,IAAM,EAAA;AACtB,MAAK,IAAA,CAAA,IAAA,CAAK,OAAO,GAAG,CAAA,CAAA;AAAA,KACtB;AAAA,GACF;AAAA,EAEA,MAAM,QAA4C,GAAA;AAChD,IAAO,OAAA;AAAA,MACL,KAAO,EAAA,KAAA,CAAM,IAAK,CAAA,IAAA,CAAK,IAAI,CAAE,CAAA,GAAA,CAAI,CAAC,GAAG,EAAE,SAAA,EAAW,GAAK,EAAA,MAAA,EAAQ,CAAO,MAAA;AAAA,QACpE,SAAA;AAAA,QACA,GAAA,EAAK,IAAK,CAAA,KAAA,CAAM,MAAM,CAAA;AAAA,OACtB,CAAA,CAAA;AAAA,KACJ,CAAA;AAAA,GACF;AACF;;;;"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jose = require('jose');
|
|
4
|
+
var fs = require('fs');
|
|
5
|
+
|
|
6
|
+
const DEFAULT_ALGORITHM = "ES256";
|
|
7
|
+
class StaticKeyStore {
|
|
8
|
+
keyPairs;
|
|
9
|
+
createdAt;
|
|
10
|
+
constructor(keyPairs) {
|
|
11
|
+
if (keyPairs.length === 0) {
|
|
12
|
+
throw new Error("Should provide at least one key pair");
|
|
13
|
+
}
|
|
14
|
+
this.keyPairs = keyPairs;
|
|
15
|
+
this.createdAt = /* @__PURE__ */ new Date();
|
|
16
|
+
}
|
|
17
|
+
static async fromConfig(config) {
|
|
18
|
+
const keyConfigs = config.getConfigArray("auth.keyStore.static.keys").map((c) => {
|
|
19
|
+
const staticKeyConfig = {
|
|
20
|
+
publicKeyFile: c.getString("publicKeyFile"),
|
|
21
|
+
privateKeyFile: c.getString("privateKeyFile"),
|
|
22
|
+
keyId: c.getString("keyId"),
|
|
23
|
+
algorithm: c.getOptionalString("algorithm") ?? DEFAULT_ALGORITHM
|
|
24
|
+
};
|
|
25
|
+
return staticKeyConfig;
|
|
26
|
+
});
|
|
27
|
+
const keyPairs = await Promise.all(
|
|
28
|
+
keyConfigs.map(async (k) => await this.loadKeyPair(k))
|
|
29
|
+
);
|
|
30
|
+
return new StaticKeyStore(keyPairs);
|
|
31
|
+
}
|
|
32
|
+
addKey(_key) {
|
|
33
|
+
throw new Error("Cannot add keys to the static key store");
|
|
34
|
+
}
|
|
35
|
+
listKeys() {
|
|
36
|
+
const keys = this.keyPairs.map((k) => this.keyPairToStoredKey(k));
|
|
37
|
+
return Promise.resolve({ items: keys });
|
|
38
|
+
}
|
|
39
|
+
getPrivateKey(keyId) {
|
|
40
|
+
const keyPair = this.keyPairs.find((k) => k.publicKey.kid === keyId);
|
|
41
|
+
if (keyPair === void 0) {
|
|
42
|
+
throw new Error(`Could not find key with keyId: ${keyId}`);
|
|
43
|
+
}
|
|
44
|
+
return keyPair.privateKey;
|
|
45
|
+
}
|
|
46
|
+
removeKeys(_kids) {
|
|
47
|
+
throw new Error("Cannot remove keys from the static key store");
|
|
48
|
+
}
|
|
49
|
+
keyPairToStoredKey(keyPair) {
|
|
50
|
+
const publicKey = {
|
|
51
|
+
...keyPair.publicKey,
|
|
52
|
+
use: "sig"
|
|
53
|
+
};
|
|
54
|
+
return {
|
|
55
|
+
key: publicKey,
|
|
56
|
+
createdAt: this.createdAt
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
static async loadKeyPair(options) {
|
|
60
|
+
const algorithm = options.algorithm;
|
|
61
|
+
const keyId = options.keyId;
|
|
62
|
+
const publicKey = await this.loadPublicKeyFromFile(
|
|
63
|
+
options.publicKeyFile,
|
|
64
|
+
keyId,
|
|
65
|
+
algorithm
|
|
66
|
+
);
|
|
67
|
+
const privateKey = await this.loadPrivateKeyFromFile(
|
|
68
|
+
options.privateKeyFile,
|
|
69
|
+
keyId,
|
|
70
|
+
algorithm
|
|
71
|
+
);
|
|
72
|
+
return { publicKey, privateKey };
|
|
73
|
+
}
|
|
74
|
+
static async loadPublicKeyFromFile(path, keyId, algorithm) {
|
|
75
|
+
return this.loadKeyFromFile(path, keyId, algorithm, jose.importSPKI);
|
|
76
|
+
}
|
|
77
|
+
static async loadPrivateKeyFromFile(path, keyId, algorithm) {
|
|
78
|
+
return this.loadKeyFromFile(path, keyId, algorithm, jose.importPKCS8);
|
|
79
|
+
}
|
|
80
|
+
static async loadKeyFromFile(path, keyId, algorithm, importer) {
|
|
81
|
+
const content = await fs.promises.readFile(path, { encoding: "utf8", flag: "r" });
|
|
82
|
+
const key = await importer(content, algorithm);
|
|
83
|
+
const jwk = await jose.exportJWK(key);
|
|
84
|
+
jwk.kid = keyId;
|
|
85
|
+
jwk.alg = algorithm;
|
|
86
|
+
return jwk;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
exports.StaticKeyStore = StaticKeyStore;
|
|
91
|
+
//# sourceMappingURL=StaticKeyStore.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StaticKeyStore.cjs.js","sources":["../../src/identity/StaticKeyStore.ts"],"sourcesContent":["/*\n * Copyright 2023 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 */\nimport { AnyJWK, KeyStore, StoredKey } from './types';\nimport { exportJWK, importPKCS8, importSPKI, JWK } from 'jose';\nimport { KeyLike } from 'jose/dist/types/types';\nimport { promises as fs } from 'fs';\nimport { Config } from '@backstage/config';\n\nexport type KeyPair = {\n publicKey: JWK;\n privateKey: JWK;\n};\n\nexport type StaticKeyConfig = {\n publicKeyFile: string;\n privateKeyFile: string;\n keyId: string;\n algorithm: string;\n};\n\nconst DEFAULT_ALGORITHM = 'ES256';\n\n/**\n * Key store that loads predefined public/private key pairs from disk\n *\n * The private key should be represented using the PKCS#8 format,\n * while the public key should be in the SPKI format.\n *\n * @remarks\n *\n * You can generate a public and private key pair, using\n * openssl:\n *\n * Generate a private key using the ES256 algorithm\n * ```sh\n * openssl ecparam -name prime256v1 -genkey -out private.ec.key\n * ```\n * Convert it to PKCS#8 format\n * ```sh\n * openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in private.ec.key -out private.key\n * ```\n * Extract the public key\n * ```sh\n * openssl ec -inform PEM -outform PEM -pubout -in private.key -out public.key\n * ```\n *\n * Provide the paths to private.key and public.key as the respective\n * private and public key paths in the StaticKeyStore.create(...) method.\n */\nexport class StaticKeyStore implements KeyStore {\n private readonly keyPairs: KeyPair[];\n private readonly createdAt: Date;\n\n private constructor(keyPairs: KeyPair[]) {\n if (keyPairs.length === 0) {\n throw new Error('Should provide at least one key pair');\n }\n\n this.keyPairs = keyPairs;\n this.createdAt = new Date();\n }\n\n public static async fromConfig(config: Config): Promise<StaticKeyStore> {\n const keyConfigs = config\n .getConfigArray('auth.keyStore.static.keys')\n .map(c => {\n const staticKeyConfig: StaticKeyConfig = {\n publicKeyFile: c.getString('publicKeyFile'),\n privateKeyFile: c.getString('privateKeyFile'),\n keyId: c.getString('keyId'),\n algorithm: c.getOptionalString('algorithm') ?? DEFAULT_ALGORITHM,\n };\n\n return staticKeyConfig;\n });\n\n const keyPairs = await Promise.all(\n keyConfigs.map(async k => await this.loadKeyPair(k)),\n );\n\n return new StaticKeyStore(keyPairs);\n }\n\n addKey(_key: AnyJWK): Promise<void> {\n throw new Error('Cannot add keys to the static key store');\n }\n\n listKeys(): Promise<{ items: StoredKey[] }> {\n const keys = this.keyPairs.map(k => this.keyPairToStoredKey(k));\n return Promise.resolve({ items: keys });\n }\n\n getPrivateKey(keyId: string): JWK {\n const keyPair = this.keyPairs.find(k => k.publicKey.kid === keyId);\n if (keyPair === undefined) {\n throw new Error(`Could not find key with keyId: ${keyId}`);\n }\n\n return keyPair.privateKey;\n }\n\n removeKeys(_kids: string[]): Promise<void> {\n throw new Error('Cannot remove keys from the static key store');\n }\n\n private keyPairToStoredKey(keyPair: KeyPair): StoredKey {\n const publicKey = {\n ...keyPair.publicKey,\n use: 'sig',\n };\n\n return {\n key: publicKey as AnyJWK,\n createdAt: this.createdAt,\n };\n }\n\n private static async loadKeyPair(options: StaticKeyConfig): Promise<KeyPair> {\n const algorithm = options.algorithm;\n const keyId = options.keyId;\n const publicKey = await this.loadPublicKeyFromFile(\n options.publicKeyFile,\n keyId,\n algorithm,\n );\n const privateKey = await this.loadPrivateKeyFromFile(\n options.privateKeyFile,\n keyId,\n algorithm,\n );\n\n return { publicKey, privateKey };\n }\n\n private static async loadPublicKeyFromFile(\n path: string,\n keyId: string,\n algorithm: string,\n ): Promise<JWK> {\n return this.loadKeyFromFile(path, keyId, algorithm, importSPKI);\n }\n\n private static async loadPrivateKeyFromFile(\n path: string,\n keyId: string,\n algorithm: string,\n ): Promise<JWK> {\n return this.loadKeyFromFile(path, keyId, algorithm, importPKCS8);\n }\n\n private static async loadKeyFromFile(\n path: string,\n keyId: string,\n algorithm: string,\n importer: (content: string, algorithm: string) => Promise<KeyLike>,\n ): Promise<JWK> {\n const content = await fs.readFile(path, { encoding: 'utf8', flag: 'r' });\n const key = await importer(content, algorithm);\n const jwk = await exportJWK(key);\n jwk.kid = keyId;\n jwk.alg = algorithm;\n\n return jwk;\n }\n}\n"],"names":["importSPKI","importPKCS8","fs","exportJWK"],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jose = require('jose');
|
|
4
|
+
var catalogModel = require('@backstage/catalog-model');
|
|
5
|
+
var errors = require('@backstage/errors');
|
|
6
|
+
|
|
7
|
+
const MS_IN_S = 1e3;
|
|
8
|
+
class StaticTokenIssuer {
|
|
9
|
+
issuer;
|
|
10
|
+
logger;
|
|
11
|
+
keyStore;
|
|
12
|
+
sessionExpirationSeconds;
|
|
13
|
+
constructor(options, keyStore) {
|
|
14
|
+
this.issuer = options.issuer;
|
|
15
|
+
this.logger = options.logger;
|
|
16
|
+
this.sessionExpirationSeconds = options.sessionExpirationSeconds;
|
|
17
|
+
this.keyStore = keyStore;
|
|
18
|
+
}
|
|
19
|
+
async issueToken(params) {
|
|
20
|
+
const key = await this.getSigningKey();
|
|
21
|
+
const iss = this.issuer;
|
|
22
|
+
const { sub, ent, ...additionalClaims } = params.claims;
|
|
23
|
+
const aud = "backstage";
|
|
24
|
+
const iat = Math.floor(Date.now() / MS_IN_S);
|
|
25
|
+
const exp = iat + this.sessionExpirationSeconds;
|
|
26
|
+
try {
|
|
27
|
+
catalogModel.parseEntityRef(sub);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
'"sub" claim provided by the auth resolver is not a valid EntityRef.'
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
this.logger.info(`Issuing token for ${sub}, with entities ${ent ?? []}`);
|
|
34
|
+
if (!key.alg) {
|
|
35
|
+
throw new errors.AuthenticationError("No algorithm was provided in the key");
|
|
36
|
+
}
|
|
37
|
+
return new jose.SignJWT({ ...additionalClaims, iss, sub, ent, aud, iat, exp }).setProtectedHeader({ alg: key.alg, kid: key.kid }).setIssuer(iss).setAudience(aud).setSubject(sub).setIssuedAt(iat).setExpirationTime(exp).sign(await jose.importJWK(key));
|
|
38
|
+
}
|
|
39
|
+
async getSigningKey() {
|
|
40
|
+
const { items: keys } = await this.keyStore.listKeys();
|
|
41
|
+
if (keys.length >= 1) {
|
|
42
|
+
return this.keyStore.getPrivateKey(keys[0].key.kid);
|
|
43
|
+
}
|
|
44
|
+
throw new Error("Keystore should hold at least 1 key");
|
|
45
|
+
}
|
|
46
|
+
async listPublicKeys() {
|
|
47
|
+
const { items: keys } = await this.keyStore.listKeys();
|
|
48
|
+
return { keys: keys.map(({ key }) => key) };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
exports.StaticTokenIssuer = StaticTokenIssuer;
|
|
53
|
+
//# sourceMappingURL=StaticTokenIssuer.cjs.js.map
|