@backstage/cli-module-auth 0.0.0-nightly-20260317031259
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 +13 -0
- package/README.md +19 -0
- package/bin/backstage-cli-module-auth +32 -0
- package/dist/commands/list.cjs.js +23 -0
- package/dist/commands/list.cjs.js.map +1 -0
- package/dist/commands/login.cjs.js +316 -0
- package/dist/commands/login.cjs.js.map +1 -0
- package/dist/commands/logout.cjs.js +55 -0
- package/dist/commands/logout.cjs.js.map +1 -0
- package/dist/commands/printToken.cjs.js +41 -0
- package/dist/commands/printToken.cjs.js.map +1 -0
- package/dist/commands/select.cjs.js +32 -0
- package/dist/commands/select.cjs.js.map +1 -0
- package/dist/commands/show.cjs.js +59 -0
- package/dist/commands/show.cjs.js.map +1 -0
- package/dist/index.cjs.js +45 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/lib/auth.cjs.js +60 -0
- package/dist/lib/auth.cjs.js.map +1 -0
- package/dist/lib/http.cjs.js +26 -0
- package/dist/lib/http.cjs.js.map +1 -0
- package/dist/lib/localServer.cjs.js +80 -0
- package/dist/lib/localServer.cjs.js.map +1 -0
- package/dist/lib/pkce.cjs.js +23 -0
- package/dist/lib/pkce.cjs.js.map +1 -0
- package/dist/lib/prompt.cjs.js +44 -0
- package/dist/lib/prompt.cjs.js.map +1 -0
- package/dist/lib/secretStore.cjs.js +81 -0
- package/dist/lib/secretStore.cjs.js.map +1 -0
- package/dist/lib/storage.cjs.js +152 -0
- package/dist/lib/storage.cjs.js.map +1 -0
- package/dist/package.json.cjs.js +95 -0
- package/dist/package.json.cjs.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var cleye = require('cleye');
|
|
6
|
+
var http = require('../lib/http.cjs.js');
|
|
7
|
+
var storage = require('../lib/storage.cjs.js');
|
|
8
|
+
var auth = require('../lib/auth.cjs.js');
|
|
9
|
+
var secretStore = require('../lib/secretStore.cjs.js');
|
|
10
|
+
|
|
11
|
+
var show = async ({ args, info }) => {
|
|
12
|
+
const {
|
|
13
|
+
flags: { instance: instanceFlag }
|
|
14
|
+
} = cleye.cli(
|
|
15
|
+
{
|
|
16
|
+
help: info,
|
|
17
|
+
flags: {
|
|
18
|
+
instance: {
|
|
19
|
+
type: String,
|
|
20
|
+
description: "Name of the instance to show"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
void 0,
|
|
25
|
+
args
|
|
26
|
+
);
|
|
27
|
+
let instance = await storage.getSelectedInstance(instanceFlag);
|
|
28
|
+
if (auth.accessTokenNeedsRefresh(instance)) {
|
|
29
|
+
process.stdout.write("Refreshing access token...\n");
|
|
30
|
+
instance = await auth.refreshAccessToken(instance.name);
|
|
31
|
+
}
|
|
32
|
+
const authBase = new URL("/api/auth", instance.baseUrl).toString().replace(/\/$/, "");
|
|
33
|
+
const secretStore$1 = await secretStore.getSecretStore();
|
|
34
|
+
const service = `backstage-cli:auth-instance:${instance.name}`;
|
|
35
|
+
const accessToken = await secretStore$1.get(service, "accessToken");
|
|
36
|
+
if (!accessToken) {
|
|
37
|
+
throw new Error('No access token found. Run "auth login" to authenticate.');
|
|
38
|
+
}
|
|
39
|
+
const userinfo = await http.httpJson(
|
|
40
|
+
`${authBase}/v1/userinfo`,
|
|
41
|
+
{
|
|
42
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
43
|
+
signal: AbortSignal.timeout(3e4)
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
process.stdout.write(`User: ${userinfo.claims.sub}
|
|
47
|
+
`);
|
|
48
|
+
process.stdout.write(`
|
|
49
|
+
`);
|
|
50
|
+
process.stdout.write(`Ownership:
|
|
51
|
+
`);
|
|
52
|
+
for (const ent of userinfo.claims.ent ?? []) {
|
|
53
|
+
process.stdout.write(` - ${ent}
|
|
54
|
+
`);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
exports.default = show;
|
|
59
|
+
//# sourceMappingURL=show.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"show.cjs.js","sources":["../../src/commands/show.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { cli } from 'cleye';\nimport type { CliCommandContext } from '@backstage/cli-node';\nimport { httpJson } from '../lib/http';\nimport { getSelectedInstance } from '../lib/storage';\nimport { accessTokenNeedsRefresh, refreshAccessToken } from '../lib/auth';\nimport { getSecretStore } from '../lib/secretStore';\n\nexport default async ({ args, info }: CliCommandContext) => {\n const {\n flags: { instance: instanceFlag },\n } = cli(\n {\n help: info,\n flags: {\n instance: {\n type: String,\n description: 'Name of the instance to show',\n },\n },\n },\n undefined,\n args,\n );\n\n let instance = await getSelectedInstance(instanceFlag);\n\n if (accessTokenNeedsRefresh(instance)) {\n process.stdout.write('Refreshing access token...\\n');\n instance = await refreshAccessToken(instance.name);\n }\n const authBase = new URL('/api/auth', instance.baseUrl)\n .toString()\n .replace(/\\/$/, '');\n\n const secretStore = await getSecretStore();\n const service = `backstage-cli:auth-instance:${instance.name}`;\n const accessToken = await secretStore.get(service, 'accessToken');\n if (!accessToken) {\n throw new Error('No access token found. Run \"auth login\" to authenticate.');\n }\n\n const userinfo = await httpJson<{ claims: { sub: string; ent: string[] } }>(\n `${authBase}/v1/userinfo`,\n {\n headers: { Authorization: `Bearer ${accessToken}` },\n signal: AbortSignal.timeout(30_000),\n },\n );\n\n process.stdout.write(`User: ${userinfo.claims.sub}\\n`);\n process.stdout.write(`\\n`);\n process.stdout.write(`Ownership:\\n`);\n for (const ent of userinfo.claims.ent ?? []) {\n process.stdout.write(` - ${ent}\\n`);\n }\n};\n"],"names":["cli","getSelectedInstance","accessTokenNeedsRefresh","refreshAccessToken","secretStore","getSecretStore","httpJson"],"mappings":";;;;;;;;;;AAuBA,WAAe,OAAO,EAAE,IAAA,EAAM,IAAA,EAAK,KAAyB;AAC1D,EAAA,MAAM;AAAA,IACJ,KAAA,EAAO,EAAE,QAAA,EAAU,YAAA;AAAa,GAClC,GAAIA,SAAA;AAAA,IACF;AAAA,MACE,IAAA,EAAM,IAAA;AAAA,MACN,KAAA,EAAO;AAAA,QACL,QAAA,EAAU;AAAA,UACR,IAAA,EAAM,MAAA;AAAA,UACN,WAAA,EAAa;AAAA;AACf;AACF,KACF;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,IAAI,QAAA,GAAW,MAAMC,2BAAA,CAAoB,YAAY,CAAA;AAErD,EAAA,IAAIC,4BAAA,CAAwB,QAAQ,CAAA,EAAG;AACrC,IAAA,OAAA,CAAQ,MAAA,CAAO,MAAM,8BAA8B,CAAA;AACnD,IAAA,QAAA,GAAW,MAAMC,uBAAA,CAAmB,QAAA,CAAS,IAAI,CAAA;AAAA,EACnD;AACA,EAAA,MAAM,QAAA,GAAW,IAAI,GAAA,CAAI,WAAA,EAAa,QAAA,CAAS,OAAO,CAAA,CACnD,QAAA,EAAS,CACT,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAEpB,EAAA,MAAMC,aAAA,GAAc,MAAMC,0BAAA,EAAe;AACzC,EAAA,MAAM,OAAA,GAAU,CAAA,4BAAA,EAA+B,QAAA,CAAS,IAAI,CAAA,CAAA;AAC5D,EAAA,MAAM,WAAA,GAAc,MAAMD,aAAA,CAAY,GAAA,CAAI,SAAS,aAAa,CAAA;AAChE,EAAA,IAAI,CAAC,WAAA,EAAa;AAChB,IAAA,MAAM,IAAI,MAAM,0DAA0D,CAAA;AAAA,EAC5E;AAEA,EAAA,MAAM,WAAW,MAAME,aAAA;AAAA,IACrB,GAAG,QAAQ,CAAA,YAAA,CAAA;AAAA,IACX;AAAA,MACE,OAAA,EAAS,EAAE,aAAA,EAAe,CAAA,OAAA,EAAU,WAAW,CAAA,CAAA,EAAG;AAAA,MAClD,MAAA,EAAQ,WAAA,CAAY,OAAA,CAAQ,GAAM;AAAA;AACpC,GACF;AAEA,EAAA,OAAA,CAAQ,MAAA,CAAO,KAAA,CAAM,CAAA,MAAA,EAAS,QAAA,CAAS,OAAO,GAAG;AAAA,CAAI,CAAA;AACrD,EAAA,OAAA,CAAQ,OAAO,KAAA,CAAM;AAAA,CAAI,CAAA;AACzB,EAAA,OAAA,CAAQ,OAAO,KAAA,CAAM,CAAA;AAAA,CAAc,CAAA;AACnC,EAAA,KAAA,MAAW,GAAA,IAAO,QAAA,CAAS,MAAA,CAAO,GAAA,IAAO,EAAC,EAAG;AAC3C,IAAA,OAAA,CAAQ,MAAA,CAAO,KAAA,CAAM,CAAA,IAAA,EAAO,GAAG;AAAA,CAAI,CAAA;AAAA,EACrC;AACF,CAAA;;;;"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var cliNode = require('@backstage/cli-node');
|
|
6
|
+
var _package = require('./package.json.cjs.js');
|
|
7
|
+
|
|
8
|
+
var index = cliNode.createCliModule({
|
|
9
|
+
packageJson: _package.default,
|
|
10
|
+
init: async (reg) => {
|
|
11
|
+
reg.addCommand({
|
|
12
|
+
path: ["auth", "login"],
|
|
13
|
+
description: "Log in the CLI to a Backstage instance",
|
|
14
|
+
execute: { loader: () => import('./commands/login.cjs.js') }
|
|
15
|
+
});
|
|
16
|
+
reg.addCommand({
|
|
17
|
+
path: ["auth", "logout"],
|
|
18
|
+
description: "Log out the CLI and clear stored credentials",
|
|
19
|
+
execute: { loader: () => import('./commands/logout.cjs.js') }
|
|
20
|
+
});
|
|
21
|
+
reg.addCommand({
|
|
22
|
+
path: ["auth", "show"],
|
|
23
|
+
description: "Show details of an authenticated instance",
|
|
24
|
+
execute: { loader: () => import('./commands/show.cjs.js') }
|
|
25
|
+
});
|
|
26
|
+
reg.addCommand({
|
|
27
|
+
path: ["auth", "list"],
|
|
28
|
+
description: "List authenticated instances",
|
|
29
|
+
execute: { loader: () => import('./commands/list.cjs.js') }
|
|
30
|
+
});
|
|
31
|
+
reg.addCommand({
|
|
32
|
+
path: ["auth", "print-token"],
|
|
33
|
+
description: "Print an access token to stdout (auto-refresh if needed)",
|
|
34
|
+
execute: { loader: () => import('./commands/printToken.cjs.js') }
|
|
35
|
+
});
|
|
36
|
+
reg.addCommand({
|
|
37
|
+
path: ["auth", "select"],
|
|
38
|
+
description: "Select the default instance",
|
|
39
|
+
execute: { loader: () => import('./commands/select.cjs.js') }
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
exports.default = index;
|
|
45
|
+
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../src/index.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { createCliModule } from '@backstage/cli-node';\nimport packageJson from '../package.json';\n\nexport default createCliModule({\n packageJson,\n init: async reg => {\n reg.addCommand({\n path: ['auth', 'login'],\n description: 'Log in the CLI to a Backstage instance',\n execute: { loader: () => import('./commands/login') },\n });\n reg.addCommand({\n path: ['auth', 'logout'],\n description: 'Log out the CLI and clear stored credentials',\n execute: { loader: () => import('./commands/logout') },\n });\n reg.addCommand({\n path: ['auth', 'show'],\n description: 'Show details of an authenticated instance',\n execute: { loader: () => import('./commands/show') },\n });\n reg.addCommand({\n path: ['auth', 'list'],\n description: 'List authenticated instances',\n execute: { loader: () => import('./commands/list') },\n });\n reg.addCommand({\n path: ['auth', 'print-token'],\n description: 'Print an access token to stdout (auto-refresh if needed)',\n execute: { loader: () => import('./commands/printToken') },\n });\n reg.addCommand({\n path: ['auth', 'select'],\n description: 'Select the default instance',\n execute: { loader: () => import('./commands/select') },\n });\n },\n});\n"],"names":["createCliModule","packageJson"],"mappings":";;;;;;;AAmBA,YAAeA,uBAAA,CAAgB;AAAA,eAC7BC,gBAAA;AAAA,EACA,IAAA,EAAM,OAAM,GAAA,KAAO;AACjB,IAAA,GAAA,CAAI,UAAA,CAAW;AAAA,MACb,IAAA,EAAM,CAAC,MAAA,EAAQ,OAAO,CAAA;AAAA,MACtB,WAAA,EAAa,wCAAA;AAAA,MACb,SAAS,EAAE,MAAA,EAAQ,MAAM,OAAO,yBAAkB,CAAA;AAAE,KACrD,CAAA;AACD,IAAA,GAAA,CAAI,UAAA,CAAW;AAAA,MACb,IAAA,EAAM,CAAC,MAAA,EAAQ,QAAQ,CAAA;AAAA,MACvB,WAAA,EAAa,8CAAA;AAAA,MACb,SAAS,EAAE,MAAA,EAAQ,MAAM,OAAO,0BAAmB,CAAA;AAAE,KACtD,CAAA;AACD,IAAA,GAAA,CAAI,UAAA,CAAW;AAAA,MACb,IAAA,EAAM,CAAC,MAAA,EAAQ,MAAM,CAAA;AAAA,MACrB,WAAA,EAAa,2CAAA;AAAA,MACb,SAAS,EAAE,MAAA,EAAQ,MAAM,OAAO,wBAAiB,CAAA;AAAE,KACpD,CAAA;AACD,IAAA,GAAA,CAAI,UAAA,CAAW;AAAA,MACb,IAAA,EAAM,CAAC,MAAA,EAAQ,MAAM,CAAA;AAAA,MACrB,WAAA,EAAa,8BAAA;AAAA,MACb,SAAS,EAAE,MAAA,EAAQ,MAAM,OAAO,wBAAiB,CAAA;AAAE,KACpD,CAAA;AACD,IAAA,GAAA,CAAI,UAAA,CAAW;AAAA,MACb,IAAA,EAAM,CAAC,MAAA,EAAQ,aAAa,CAAA;AAAA,MAC5B,WAAA,EAAa,0DAAA;AAAA,MACb,SAAS,EAAE,MAAA,EAAQ,MAAM,OAAO,8BAAuB,CAAA;AAAE,KAC1D,CAAA;AACD,IAAA,GAAA,CAAI,UAAA,CAAW;AAAA,MACb,IAAA,EAAM,CAAC,MAAA,EAAQ,QAAQ,CAAA;AAAA,MACvB,WAAA,EAAa,6BAAA;AAAA,MACb,SAAS,EAAE,MAAA,EAAQ,MAAM,OAAO,0BAAmB,CAAA;AAAE,KACtD,CAAA;AAAA,EACH;AACF,CAAC,CAAA;;;;"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var zod = require('zod');
|
|
4
|
+
var storage = require('./storage.cjs.js');
|
|
5
|
+
var secretStore = require('./secretStore.cjs.js');
|
|
6
|
+
var http = require('./http.cjs.js');
|
|
7
|
+
|
|
8
|
+
const TokenResponseSchema = zod.z.object({
|
|
9
|
+
access_token: zod.z.string().min(1),
|
|
10
|
+
token_type: zod.z.string().min(1),
|
|
11
|
+
expires_in: zod.z.number().positive().finite(),
|
|
12
|
+
refresh_token: zod.z.string().min(1).optional()
|
|
13
|
+
});
|
|
14
|
+
function accessTokenNeedsRefresh(instance) {
|
|
15
|
+
return instance.accessTokenExpiresAt <= Date.now() + 2 * 6e4;
|
|
16
|
+
}
|
|
17
|
+
async function refreshAccessToken(instanceName) {
|
|
18
|
+
const secretStore$1 = await secretStore.getSecretStore();
|
|
19
|
+
return storage.withMetadataLock(async () => {
|
|
20
|
+
const instance = await storage.getInstanceByName(instanceName);
|
|
21
|
+
const service = `backstage-cli:auth-instance:${instanceName}`;
|
|
22
|
+
const refreshToken = await secretStore$1.get(service, "refreshToken") ?? "";
|
|
23
|
+
if (!refreshToken) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
"Access token is expired and no refresh token is available"
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
const response = await http.httpJson(
|
|
29
|
+
`${instance.baseUrl}/api/auth/v1/token`,
|
|
30
|
+
{
|
|
31
|
+
method: "POST",
|
|
32
|
+
body: {
|
|
33
|
+
grant_type: "refresh_token",
|
|
34
|
+
refresh_token: refreshToken
|
|
35
|
+
},
|
|
36
|
+
signal: AbortSignal.timeout(3e4)
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
const parsed = TokenResponseSchema.safeParse(response);
|
|
40
|
+
if (!parsed.success) {
|
|
41
|
+
throw new Error(`Invalid token response: ${parsed.error.message}`);
|
|
42
|
+
}
|
|
43
|
+
const token = parsed.data;
|
|
44
|
+
await secretStore$1.set(service, "accessToken", token.access_token);
|
|
45
|
+
if (token.refresh_token) {
|
|
46
|
+
await secretStore$1.set(service, "refreshToken", token.refresh_token);
|
|
47
|
+
}
|
|
48
|
+
const newInstance = {
|
|
49
|
+
...instance,
|
|
50
|
+
issuedAt: Date.now(),
|
|
51
|
+
accessTokenExpiresAt: Date.now() + token.expires_in * 1e3
|
|
52
|
+
};
|
|
53
|
+
await storage.upsertInstance(newInstance);
|
|
54
|
+
return newInstance;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
exports.accessTokenNeedsRefresh = accessTokenNeedsRefresh;
|
|
59
|
+
exports.refreshAccessToken = refreshAccessToken;
|
|
60
|
+
//# sourceMappingURL=auth.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.cjs.js","sources":["../../src/lib/auth.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { z } from 'zod';\nimport {\n StoredInstance,\n upsertInstance,\n withMetadataLock,\n getInstanceByName,\n} from './storage';\nimport { getSecretStore } from './secretStore';\nimport { httpJson } from './http';\n\nconst TokenResponseSchema = z.object({\n access_token: z.string().min(1),\n token_type: z.string().min(1),\n expires_in: z.number().positive().finite(),\n refresh_token: z.string().min(1).optional(),\n});\n\nexport function accessTokenNeedsRefresh(instance: StoredInstance): boolean {\n return instance.accessTokenExpiresAt <= Date.now() + 2 * 60_000; // 2 minutes before expiration\n}\n\nexport async function refreshAccessToken(\n instanceName: string,\n): Promise<StoredInstance> {\n const secretStore = await getSecretStore();\n\n return withMetadataLock(async () => {\n const instance = await getInstanceByName(instanceName);\n\n const service = `backstage-cli:auth-instance:${instanceName}`;\n const refreshToken = (await secretStore.get(service, 'refreshToken')) ?? '';\n if (!refreshToken) {\n throw new Error(\n 'Access token is expired and no refresh token is available',\n );\n }\n\n const response = await httpJson<unknown>(\n `${instance.baseUrl}/api/auth/v1/token`,\n {\n method: 'POST',\n body: {\n grant_type: 'refresh_token',\n refresh_token: refreshToken,\n },\n signal: AbortSignal.timeout(30_000),\n },\n );\n\n const parsed = TokenResponseSchema.safeParse(response);\n if (!parsed.success) {\n throw new Error(`Invalid token response: ${parsed.error.message}`);\n }\n const token = parsed.data;\n\n await secretStore.set(service, 'accessToken', token.access_token);\n if (token.refresh_token) {\n await secretStore.set(service, 'refreshToken', token.refresh_token);\n }\n const newInstance = {\n ...instance,\n issuedAt: Date.now(),\n accessTokenExpiresAt: Date.now() + token.expires_in * 1000,\n };\n await upsertInstance(newInstance);\n return newInstance;\n });\n}\n"],"names":["z","secretStore","getSecretStore","withMetadataLock","getInstanceByName","httpJson","upsertInstance"],"mappings":";;;;;;;AA0BA,MAAM,mBAAA,GAAsBA,MAAE,MAAA,CAAO;AAAA,EACnC,YAAA,EAAcA,KAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA;AAAA,EAC9B,UAAA,EAAYA,KAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA;AAAA,EAC5B,YAAYA,KAAA,CAAE,MAAA,EAAO,CAAE,QAAA,GAAW,MAAA,EAAO;AAAA,EACzC,eAAeA,KAAA,CAAE,MAAA,GAAS,GAAA,CAAI,CAAC,EAAE,QAAA;AACnC,CAAC,CAAA;AAEM,SAAS,wBAAwB,QAAA,EAAmC;AACzE,EAAA,OAAO,QAAA,CAAS,oBAAA,IAAwB,IAAA,CAAK,GAAA,KAAQ,CAAA,GAAI,GAAA;AAC3D;AAEA,eAAsB,mBACpB,YAAA,EACyB;AACzB,EAAA,MAAMC,aAAA,GAAc,MAAMC,0BAAA,EAAe;AAEzC,EAAA,OAAOC,yBAAiB,YAAY;AAClC,IAAA,MAAM,QAAA,GAAW,MAAMC,yBAAA,CAAkB,YAAY,CAAA;AAErD,IAAA,MAAM,OAAA,GAAU,+BAA+B,YAAY,CAAA,CAAA;AAC3D,IAAA,MAAM,eAAgB,MAAMH,aAAA,CAAY,GAAA,CAAI,OAAA,EAAS,cAAc,CAAA,IAAM,EAAA;AACzE,IAAA,IAAI,CAAC,YAAA,EAAc;AACjB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AAEA,IAAA,MAAM,WAAW,MAAMI,aAAA;AAAA,MACrB,CAAA,EAAG,SAAS,OAAO,CAAA,kBAAA,CAAA;AAAA,MACnB;AAAA,QACE,MAAA,EAAQ,MAAA;AAAA,QACR,IAAA,EAAM;AAAA,UACJ,UAAA,EAAY,eAAA;AAAA,UACZ,aAAA,EAAe;AAAA,SACjB;AAAA,QACA,MAAA,EAAQ,WAAA,CAAY,OAAA,CAAQ,GAAM;AAAA;AACpC,KACF;AAEA,IAAA,MAAM,MAAA,GAAS,mBAAA,CAAoB,SAAA,CAAU,QAAQ,CAAA;AACrD,IAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,wBAAA,EAA2B,MAAA,CAAO,KAAA,CAAM,OAAO,CAAA,CAAE,CAAA;AAAA,IACnE;AACA,IAAA,MAAM,QAAQ,MAAA,CAAO,IAAA;AAErB,IAAA,MAAMJ,aAAA,CAAY,GAAA,CAAI,OAAA,EAAS,aAAA,EAAe,MAAM,YAAY,CAAA;AAChE,IAAA,IAAI,MAAM,aAAA,EAAe;AACvB,MAAA,MAAMA,aAAA,CAAY,GAAA,CAAI,OAAA,EAAS,cAAA,EAAgB,MAAM,aAAa,CAAA;AAAA,IACpE;AACA,IAAA,MAAM,WAAA,GAAc;AAAA,MAClB,GAAG,QAAA;AAAA,MACH,QAAA,EAAU,KAAK,GAAA,EAAI;AAAA,MACnB,oBAAA,EAAsB,IAAA,CAAK,GAAA,EAAI,GAAI,MAAM,UAAA,GAAa;AAAA,KACxD;AACA,IAAA,MAAMK,uBAAe,WAAW,CAAA;AAChC,IAAA,OAAO,WAAA;AAAA,EACT,CAAC,CAAA;AACH;;;;;"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var fetch = require('cross-fetch');
|
|
4
|
+
var errors = require('@backstage/errors');
|
|
5
|
+
|
|
6
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
7
|
+
|
|
8
|
+
var fetch__default = /*#__PURE__*/_interopDefaultCompat(fetch);
|
|
9
|
+
|
|
10
|
+
async function httpJson(url, init) {
|
|
11
|
+
const res = await fetch__default.default(url, {
|
|
12
|
+
...init,
|
|
13
|
+
body: init?.body ? JSON.stringify(init.body) : void 0,
|
|
14
|
+
headers: {
|
|
15
|
+
...init?.body ? { "Content-Type": "application/json" } : {},
|
|
16
|
+
...init?.headers
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
if (!res.ok) {
|
|
20
|
+
throw await errors.ResponseError.fromResponse(res);
|
|
21
|
+
}
|
|
22
|
+
return await res.json();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
exports.httpJson = httpJson;
|
|
26
|
+
//# sourceMappingURL=http.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.cjs.js","sources":["../../src/lib/http.ts"],"sourcesContent":["/*\n * Copyright 2025 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 fetch from 'cross-fetch';\nimport { ResponseError } from '@backstage/errors';\n\ntype HttpInit = {\n headers?: Record<string, string>;\n method?: string;\n body?: any;\n signal?: AbortSignal;\n};\n\nexport async function httpJson<T>(url: string, init?: HttpInit): Promise<T> {\n const res = await fetch(url, {\n ...init,\n body: init?.body ? JSON.stringify(init.body) : undefined,\n headers: {\n ...(init?.body ? { 'Content-Type': 'application/json' } : {}),\n ...init?.headers,\n },\n });\n if (!res.ok) {\n throw await ResponseError.fromResponse(res);\n }\n return (await res.json()) as T;\n}\n"],"names":["fetch","ResponseError"],"mappings":";;;;;;;;;AA0BA,eAAsB,QAAA,CAAY,KAAa,IAAA,EAA6B;AAC1E,EAAA,MAAM,GAAA,GAAM,MAAMA,sBAAA,CAAM,GAAA,EAAK;AAAA,IAC3B,GAAG,IAAA;AAAA,IACH,MAAM,IAAA,EAAM,IAAA,GAAO,KAAK,SAAA,CAAU,IAAA,CAAK,IAAI,CAAA,GAAI,MAAA;AAAA,IAC/C,OAAA,EAAS;AAAA,MACP,GAAI,IAAA,EAAM,IAAA,GAAO,EAAE,cAAA,EAAgB,kBAAA,KAAuB,EAAC;AAAA,MAC3D,GAAG,IAAA,EAAM;AAAA;AACX,GACD,CAAA;AACD,EAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,IAAA,MAAM,MAAMC,oBAAA,CAAc,YAAA,CAAa,GAAG,CAAA;AAAA,EAC5C;AACA,EAAA,OAAQ,MAAM,IAAI,IAAA,EAAK;AACzB;;;;"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var http = require('node:http');
|
|
4
|
+
var node_url = require('node:url');
|
|
5
|
+
|
|
6
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
7
|
+
|
|
8
|
+
var http__default = /*#__PURE__*/_interopDefaultCompat(http);
|
|
9
|
+
|
|
10
|
+
const CALLBACK_PORT = 8055;
|
|
11
|
+
async function startCallbackServer(options) {
|
|
12
|
+
const server = http__default.default.createServer();
|
|
13
|
+
let resolveResult;
|
|
14
|
+
const resultPromise = new Promise(
|
|
15
|
+
(resolve) => {
|
|
16
|
+
resolveResult = resolve;
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
server.on("request", (req, res) => {
|
|
20
|
+
if (!req.url) {
|
|
21
|
+
res.statusCode = 400;
|
|
22
|
+
res.end("Bad Request");
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const u = new node_url.URL(req.url, "http://127.0.0.1");
|
|
26
|
+
if (u.pathname !== "/callback") {
|
|
27
|
+
res.statusCode = 404;
|
|
28
|
+
res.end("Not Found");
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const code = u.searchParams.get("code") ?? void 0;
|
|
32
|
+
const state = u.searchParams.get("state") ?? void 0;
|
|
33
|
+
if (!code) {
|
|
34
|
+
res.statusCode = 400;
|
|
35
|
+
res.end("Missing code");
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (state !== options.state) {
|
|
39
|
+
res.statusCode = 400;
|
|
40
|
+
res.end("State mismatch");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
res.statusCode = 200;
|
|
44
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
45
|
+
res.end("You may now close this window.");
|
|
46
|
+
resolveResult?.({ code, state });
|
|
47
|
+
});
|
|
48
|
+
const port = await new Promise((resolve, reject) => {
|
|
49
|
+
server.on("error", (err) => {
|
|
50
|
+
if (err.code === "EADDRINUSE") {
|
|
51
|
+
reject(
|
|
52
|
+
new Error(
|
|
53
|
+
`Port ${CALLBACK_PORT} is already in use. Close the application using it and try again.`
|
|
54
|
+
)
|
|
55
|
+
);
|
|
56
|
+
} else {
|
|
57
|
+
reject(err);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
server.listen(CALLBACK_PORT, "127.0.0.1", () => {
|
|
61
|
+
const address = server.address();
|
|
62
|
+
if (typeof address === "object" && address && "port" in address) {
|
|
63
|
+
resolve(address.port);
|
|
64
|
+
} else {
|
|
65
|
+
reject(new Error("Failed to bind local server"));
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
url: `http://127.0.0.1:${port}/callback`,
|
|
71
|
+
waitForCode: () => resultPromise,
|
|
72
|
+
close: async () => {
|
|
73
|
+
server.closeAllConnections();
|
|
74
|
+
return new Promise((resolve) => server.close(() => resolve()));
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
exports.startCallbackServer = startCallbackServer;
|
|
80
|
+
//# sourceMappingURL=localServer.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localServer.cjs.js","sources":["../../src/lib/localServer.ts"],"sourcesContent":["/*\n * Copyright 2025 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 http from 'node:http';\nimport { URL } from 'node:url';\n\nconst CALLBACK_PORT = 8055;\n\nexport async function startCallbackServer(options: { state: string }): Promise<{\n url: string;\n waitForCode: () => Promise<{ code: string; state?: string }>;\n close: () => Promise<void>;\n}> {\n const server = http.createServer();\n\n let resolveResult:\n | ((v: { code: string; state?: string }) => void)\n | undefined;\n const resultPromise = new Promise<{ code: string; state?: string }>(\n resolve => {\n resolveResult = resolve;\n },\n );\n\n server.on('request', (req, res) => {\n if (!req.url) {\n res.statusCode = 400;\n res.end('Bad Request');\n return;\n }\n const u = new URL(req.url, 'http://127.0.0.1');\n if (u.pathname !== '/callback') {\n res.statusCode = 404;\n res.end('Not Found');\n return;\n }\n const code = u.searchParams.get('code') ?? undefined;\n const state = u.searchParams.get('state') ?? undefined;\n if (!code) {\n res.statusCode = 400;\n res.end('Missing code');\n return;\n }\n if (state !== options.state) {\n res.statusCode = 400;\n res.end('State mismatch');\n return;\n }\n res.statusCode = 200;\n res.setHeader('Content-Type', 'text/plain; charset=utf-8');\n res.end('You may now close this window.');\n resolveResult?.({ code, state });\n });\n\n const port = await new Promise<number>((resolve, reject) => {\n server.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n reject(\n new Error(\n `Port ${CALLBACK_PORT} is already in use. Close the application using it and try again.`,\n ),\n );\n } else {\n reject(err);\n }\n });\n server.listen(CALLBACK_PORT, '127.0.0.1', () => {\n const address = server.address();\n if (typeof address === 'object' && address && 'port' in address) {\n resolve(address.port);\n } else {\n reject(new Error('Failed to bind local server'));\n }\n });\n });\n\n return {\n url: `http://127.0.0.1:${port}/callback`,\n waitForCode: () => resultPromise,\n close: async () => {\n server.closeAllConnections();\n return new Promise<void>(resolve => server.close(() => resolve()));\n },\n };\n}\n"],"names":["http","URL"],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var crypto = require('node:crypto');
|
|
4
|
+
|
|
5
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
6
|
+
|
|
7
|
+
var crypto__default = /*#__PURE__*/_interopDefaultCompat(crypto);
|
|
8
|
+
|
|
9
|
+
function base64url(input) {
|
|
10
|
+
return input.toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
11
|
+
}
|
|
12
|
+
function generateVerifier(length = 64) {
|
|
13
|
+
const bytes = crypto__default.default.randomBytes(Math.max(32, Math.min(96, length)));
|
|
14
|
+
return base64url(bytes);
|
|
15
|
+
}
|
|
16
|
+
function challengeFromVerifier(verifier) {
|
|
17
|
+
const hash = crypto__default.default.createHash("sha256").update(verifier).digest();
|
|
18
|
+
return base64url(hash);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
exports.challengeFromVerifier = challengeFromVerifier;
|
|
22
|
+
exports.generateVerifier = generateVerifier;
|
|
23
|
+
//# sourceMappingURL=pkce.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkce.cjs.js","sources":["../../src/lib/pkce.ts"],"sourcesContent":["/*\n * Copyright 2025 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 crypto from 'node:crypto';\n\nfunction base64url(input: Buffer): string {\n return input\n .toString('base64')\n .replace(/=/g, '')\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_');\n}\n\nexport function generateVerifier(length = 64): string {\n // length in bytes ~ 48 results in 64 base64url chars; keep within 43..128 chars\n const bytes = crypto.randomBytes(Math.max(32, Math.min(96, length)));\n return base64url(bytes);\n}\n\nexport function challengeFromVerifier(verifier: string): string {\n const hash = crypto.createHash('sha256').update(verifier).digest();\n return base64url(hash);\n}\n"],"names":["crypto"],"mappings":";;;;;;;;AAkBA,SAAS,UAAU,KAAA,EAAuB;AACxC,EAAA,OAAO,KAAA,CACJ,QAAA,CAAS,QAAQ,CAAA,CACjB,QAAQ,IAAA,EAAM,EAAE,CAAA,CAChB,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA,CAClB,OAAA,CAAQ,OAAO,GAAG,CAAA;AACvB;AAEO,SAAS,gBAAA,CAAiB,SAAS,EAAA,EAAY;AAEpD,EAAA,MAAM,KAAA,GAAQA,uBAAA,CAAO,WAAA,CAAY,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,MAAM,CAAC,CAAC,CAAA;AACnE,EAAA,OAAO,UAAU,KAAK,CAAA;AACxB;AAEO,SAAS,sBAAsB,QAAA,EAA0B;AAC9D,EAAA,MAAM,IAAA,GAAOA,wBAAO,UAAA,CAAW,QAAQ,EAAE,MAAA,CAAO,QAAQ,EAAE,MAAA,EAAO;AACjE,EAAA,OAAO,UAAU,IAAI,CAAA;AACvB;;;;;"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var inquirer = require('inquirer');
|
|
4
|
+
var storage = require('./storage.cjs.js');
|
|
5
|
+
|
|
6
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
7
|
+
|
|
8
|
+
var inquirer__default = /*#__PURE__*/_interopDefaultCompat(inquirer);
|
|
9
|
+
|
|
10
|
+
async function pickInstance(name) {
|
|
11
|
+
if (name) {
|
|
12
|
+
return storage.getInstanceByName(name);
|
|
13
|
+
}
|
|
14
|
+
const { instances, selected } = await storage.getAllInstances();
|
|
15
|
+
if (instances.length === 0) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
'No instances found. Run "auth login" to authenticate first.'
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
return await promptForInstance(instances, selected);
|
|
21
|
+
}
|
|
22
|
+
async function promptForInstance(instances, selected) {
|
|
23
|
+
const choices = instances.map((i) => ({
|
|
24
|
+
name: `${i.name === selected?.name ? "* " : " "}${i.name} (${i.baseUrl})`,
|
|
25
|
+
value: i.name
|
|
26
|
+
}));
|
|
27
|
+
const { choice } = await inquirer__default.default.prompt([
|
|
28
|
+
{
|
|
29
|
+
type: "list",
|
|
30
|
+
name: "choice",
|
|
31
|
+
message: "Select instance:",
|
|
32
|
+
choices,
|
|
33
|
+
default: selected?.name
|
|
34
|
+
}
|
|
35
|
+
]);
|
|
36
|
+
const instance = instances.find((i) => i.name === choice);
|
|
37
|
+
if (!instance) {
|
|
38
|
+
throw new Error(`Instance '${choice}' not found`);
|
|
39
|
+
}
|
|
40
|
+
return instance;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
exports.pickInstance = pickInstance;
|
|
44
|
+
//# sourceMappingURL=prompt.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.cjs.js","sources":["../../src/lib/prompt.ts"],"sourcesContent":["/*\n * Copyright 2025 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 inquirer from 'inquirer';\nimport { getInstanceByName, getAllInstances, StoredInstance } from './storage';\n\nexport async function pickInstance(name?: string): Promise<StoredInstance> {\n if (name) {\n return getInstanceByName(name);\n }\n\n const { instances, selected } = await getAllInstances();\n if (instances.length === 0) {\n throw new Error(\n 'No instances found. Run \"auth login\" to authenticate first.',\n );\n }\n return await promptForInstance(instances, selected);\n}\n\nasync function promptForInstance(\n instances: StoredInstance[],\n selected: StoredInstance | undefined,\n): Promise<StoredInstance> {\n const choices = instances.map(i => ({\n name: `${i.name === selected?.name ? '* ' : ' '}${i.name} (${i.baseUrl})`,\n value: i.name,\n }));\n\n const { choice } = await inquirer.prompt<{ choice: string }>([\n {\n type: 'list',\n name: 'choice',\n message: 'Select instance:',\n choices,\n default: selected?.name,\n },\n ]);\n\n const instance = instances.find(i => i.name === choice);\n if (!instance) {\n throw new Error(`Instance '${choice}' not found`);\n }\n return instance;\n}\n"],"names":["getInstanceByName","getAllInstances","inquirer"],"mappings":";;;;;;;;;AAmBA,eAAsB,aAAa,IAAA,EAAwC;AACzE,EAAA,IAAI,IAAA,EAAM;AACR,IAAA,OAAOA,0BAAkB,IAAI,CAAA;AAAA,EAC/B;AAEA,EAAA,MAAM,EAAE,SAAA,EAAW,QAAA,EAAS,GAAI,MAAMC,uBAAA,EAAgB;AACtD,EAAA,IAAI,SAAA,CAAU,WAAW,CAAA,EAAG;AAC1B,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AACA,EAAA,OAAO,MAAM,iBAAA,CAAkB,SAAA,EAAW,QAAQ,CAAA;AACpD;AAEA,eAAe,iBAAA,CACb,WACA,QAAA,EACyB;AACzB,EAAA,MAAM,OAAA,GAAU,SAAA,CAAU,GAAA,CAAI,CAAA,CAAA,MAAM;AAAA,IAClC,IAAA,EAAM,CAAA,EAAG,CAAA,CAAE,IAAA,KAAS,QAAA,EAAU,IAAA,GAAO,IAAA,GAAO,IAAI,CAAA,EAAG,CAAA,CAAE,IAAI,CAAA,EAAA,EAAK,EAAE,OAAO,CAAA,CAAA,CAAA;AAAA,IACvE,OAAO,CAAA,CAAE;AAAA,GACX,CAAE,CAAA;AAEF,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,MAAMC,0BAAS,MAAA,CAA2B;AAAA,IAC3D;AAAA,MACE,IAAA,EAAM,MAAA;AAAA,MACN,IAAA,EAAM,QAAA;AAAA,MACN,OAAA,EAAS,kBAAA;AAAA,MACT,OAAA;AAAA,MACA,SAAS,QAAA,EAAU;AAAA;AACrB,GACD,CAAA;AAED,EAAA,MAAM,WAAW,SAAA,CAAU,IAAA,CAAK,CAAA,CAAA,KAAK,CAAA,CAAE,SAAS,MAAM,CAAA;AACtD,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,UAAA,EAAa,MAAM,CAAA,WAAA,CAAa,CAAA;AAAA,EAClD;AACA,EAAA,OAAO,QAAA;AACT;;;;"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var fs = require('fs-extra');
|
|
4
|
+
var os = require('node:os');
|
|
5
|
+
var path = require('node:path');
|
|
6
|
+
|
|
7
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
8
|
+
|
|
9
|
+
var fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
|
|
10
|
+
var os__default = /*#__PURE__*/_interopDefaultCompat(os);
|
|
11
|
+
var path__default = /*#__PURE__*/_interopDefaultCompat(path);
|
|
12
|
+
|
|
13
|
+
async function loadKeytar() {
|
|
14
|
+
try {
|
|
15
|
+
const keytar = require("keytar");
|
|
16
|
+
if (keytar && typeof keytar.getPassword === "function") {
|
|
17
|
+
return keytar;
|
|
18
|
+
}
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
return void 0;
|
|
22
|
+
}
|
|
23
|
+
class KeytarSecretStore {
|
|
24
|
+
keytar;
|
|
25
|
+
constructor(keytar) {
|
|
26
|
+
this.keytar = keytar;
|
|
27
|
+
}
|
|
28
|
+
async get(service, account) {
|
|
29
|
+
const result = await this.keytar.getPassword(service, account);
|
|
30
|
+
return result ?? void 0;
|
|
31
|
+
}
|
|
32
|
+
async set(service, account, secret) {
|
|
33
|
+
await this.keytar.setPassword(service, account, secret);
|
|
34
|
+
}
|
|
35
|
+
async delete(service, account) {
|
|
36
|
+
await this.keytar.deletePassword(service, account);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
class FileSecretStore {
|
|
40
|
+
baseDir;
|
|
41
|
+
constructor() {
|
|
42
|
+
const root = process.env.XDG_DATA_HOME || (process.platform === "win32" ? process.env.APPDATA || path__default.default.join(os__default.default.homedir(), "AppData", "Roaming") : path__default.default.join(os__default.default.homedir(), ".local", "share"));
|
|
43
|
+
this.baseDir = path__default.default.join(root, "backstage-cli", "auth-secrets");
|
|
44
|
+
}
|
|
45
|
+
filePath(service, account) {
|
|
46
|
+
return path__default.default.join(
|
|
47
|
+
this.baseDir,
|
|
48
|
+
encodeURIComponent(service),
|
|
49
|
+
`${encodeURIComponent(account)}.secret`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
async get(service, account) {
|
|
53
|
+
const file = this.filePath(service, account);
|
|
54
|
+
if (!await fs__default.default.pathExists(file)) return void 0;
|
|
55
|
+
return await fs__default.default.readFile(file, "utf8");
|
|
56
|
+
}
|
|
57
|
+
async set(service, account, secret) {
|
|
58
|
+
const file = this.filePath(service, account);
|
|
59
|
+
await fs__default.default.ensureDir(path__default.default.dirname(file));
|
|
60
|
+
await fs__default.default.writeFile(file, secret, { encoding: "utf8", mode: 384 });
|
|
61
|
+
}
|
|
62
|
+
async delete(service, account) {
|
|
63
|
+
const file = this.filePath(service, account);
|
|
64
|
+
await fs__default.default.remove(file);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
let singleton;
|
|
68
|
+
async function getSecretStore() {
|
|
69
|
+
if (!singleton) {
|
|
70
|
+
const keytar = await loadKeytar();
|
|
71
|
+
if (keytar) {
|
|
72
|
+
singleton = new KeytarSecretStore(keytar);
|
|
73
|
+
} else {
|
|
74
|
+
singleton = new FileSecretStore();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return singleton;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
exports.getSecretStore = getSecretStore;
|
|
81
|
+
//# sourceMappingURL=secretStore.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secretStore.cjs.js","sources":["../../src/lib/secretStore.ts"],"sourcesContent":["/*\n * Copyright 2025 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 os from 'node:os';\nimport path from 'node:path';\n\ntype SecretStore = {\n get(service: string, account: string): Promise<string | undefined>;\n set(service: string, account: string, secret: string): Promise<void>;\n delete(service: string, account: string): Promise<void>;\n};\n\nasync function loadKeytar(): Promise<typeof import('keytar') | undefined> {\n try {\n // eslint-disable-next-line import/no-extraneous-dependencies, @backstage/no-undeclared-imports\n const keytar = require('keytar') as typeof import('keytar');\n if (keytar && typeof keytar.getPassword === 'function') {\n return keytar;\n }\n } catch {\n // keytar not available\n }\n return undefined;\n}\n\nclass KeytarSecretStore implements SecretStore {\n private readonly keytar: typeof import('keytar');\n constructor(keytar: typeof import('keytar')) {\n this.keytar = keytar;\n }\n async get(service: string, account: string): Promise<string | undefined> {\n const result = await this.keytar.getPassword(service, account);\n return result ?? undefined;\n }\n async set(service: string, account: string, secret: string): Promise<void> {\n await this.keytar.setPassword(service, account, secret);\n }\n async delete(service: string, account: string): Promise<void> {\n await this.keytar.deletePassword(service, account);\n }\n}\n\nclass FileSecretStore implements SecretStore {\n private readonly baseDir: string;\n constructor() {\n const root =\n process.env.XDG_DATA_HOME ||\n (process.platform === 'win32'\n ? process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming')\n : path.join(os.homedir(), '.local', 'share'));\n this.baseDir = path.join(root, 'backstage-cli', 'auth-secrets');\n }\n private filePath(service: string, account: string): string {\n return path.join(\n this.baseDir,\n encodeURIComponent(service),\n `${encodeURIComponent(account)}.secret`,\n );\n }\n async get(service: string, account: string): Promise<string | undefined> {\n const file = this.filePath(service, account);\n if (!(await fs.pathExists(file))) return undefined;\n return await fs.readFile(file, 'utf8');\n }\n async set(service: string, account: string, secret: string): Promise<void> {\n const file = this.filePath(service, account);\n await fs.ensureDir(path.dirname(file));\n await fs.writeFile(file, secret, { encoding: 'utf8', mode: 0o600 });\n }\n async delete(service: string, account: string): Promise<void> {\n const file = this.filePath(service, account);\n await fs.remove(file);\n }\n}\n\nlet singleton: SecretStore | undefined;\n\nexport async function getSecretStore(): Promise<SecretStore> {\n if (!singleton) {\n const keytar = await loadKeytar();\n if (keytar) {\n singleton = new KeytarSecretStore(keytar);\n } else {\n singleton = new FileSecretStore();\n }\n }\n return singleton;\n}\n\n/**\n * Reset the singleton instance (for testing purposes only)\n * @internal\n */\nexport function resetSecretStore(): void {\n singleton = undefined;\n}\n"],"names":["path","os","fs"],"mappings":";;;;;;;;;;;;AA0BA,eAAe,UAAA,GAA2D;AACxE,EAAA,IAAI;AAEF,IAAA,MAAM,MAAA,GAAS,QAAQ,QAAQ,CAAA;AAC/B,IAAA,IAAI,MAAA,IAAU,OAAO,MAAA,CAAO,WAAA,KAAgB,UAAA,EAAY;AACtD,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,OAAO,MAAA;AACT;AAEA,MAAM,iBAAA,CAAyC;AAAA,EAC5B,MAAA;AAAA,EACjB,YAAY,MAAA,EAAiC;AAC3C,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAAA,EAChB;AAAA,EACA,MAAM,GAAA,CAAI,OAAA,EAAiB,OAAA,EAA8C;AACvE,IAAA,MAAM,SAAS,MAAM,IAAA,CAAK,MAAA,CAAO,WAAA,CAAY,SAAS,OAAO,CAAA;AAC7D,IAAA,OAAO,MAAA,IAAU,MAAA;AAAA,EACnB;AAAA,EACA,MAAM,GAAA,CAAI,OAAA,EAAiB,OAAA,EAAiB,MAAA,EAA+B;AACzE,IAAA,MAAM,IAAA,CAAK,MAAA,CAAO,WAAA,CAAY,OAAA,EAAS,SAAS,MAAM,CAAA;AAAA,EACxD;AAAA,EACA,MAAM,MAAA,CAAO,OAAA,EAAiB,OAAA,EAAgC;AAC5D,IAAA,MAAM,IAAA,CAAK,MAAA,CAAO,cAAA,CAAe,OAAA,EAAS,OAAO,CAAA;AAAA,EACnD;AACF;AAEA,MAAM,eAAA,CAAuC;AAAA,EAC1B,OAAA;AAAA,EACjB,WAAA,GAAc;AACZ,IAAA,MAAM,IAAA,GACJ,OAAA,CAAQ,GAAA,CAAI,aAAA,KACX,OAAA,CAAQ,aAAa,OAAA,GAClB,OAAA,CAAQ,GAAA,CAAI,OAAA,IAAWA,qBAAA,CAAK,IAAA,CAAKC,oBAAG,OAAA,EAAQ,EAAG,SAAA,EAAW,SAAS,CAAA,GACnED,qBAAA,CAAK,KAAKC,mBAAA,CAAG,OAAA,EAAQ,EAAG,QAAA,EAAU,OAAO,CAAA,CAAA;AAC/C,IAAA,IAAA,CAAK,OAAA,GAAUD,qBAAA,CAAK,IAAA,CAAK,IAAA,EAAM,iBAAiB,cAAc,CAAA;AAAA,EAChE;AAAA,EACQ,QAAA,CAAS,SAAiB,OAAA,EAAyB;AACzD,IAAA,OAAOA,qBAAA,CAAK,IAAA;AAAA,MACV,IAAA,CAAK,OAAA;AAAA,MACL,mBAAmB,OAAO,CAAA;AAAA,MAC1B,CAAA,EAAG,kBAAA,CAAmB,OAAO,CAAC,CAAA,OAAA;AAAA,KAChC;AAAA,EACF;AAAA,EACA,MAAM,GAAA,CAAI,OAAA,EAAiB,OAAA,EAA8C;AACvE,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,QAAA,CAAS,OAAA,EAAS,OAAO,CAAA;AAC3C,IAAA,IAAI,CAAE,MAAME,mBAAA,CAAG,UAAA,CAAW,IAAI,GAAI,OAAO,MAAA;AACzC,IAAA,OAAO,MAAMA,mBAAA,CAAG,QAAA,CAAS,IAAA,EAAM,MAAM,CAAA;AAAA,EACvC;AAAA,EACA,MAAM,GAAA,CAAI,OAAA,EAAiB,OAAA,EAAiB,MAAA,EAA+B;AACzE,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,QAAA,CAAS,OAAA,EAAS,OAAO,CAAA;AAC3C,IAAA,MAAMA,mBAAA,CAAG,SAAA,CAAUF,qBAAA,CAAK,OAAA,CAAQ,IAAI,CAAC,CAAA;AACrC,IAAA,MAAME,mBAAA,CAAG,UAAU,IAAA,EAAM,MAAA,EAAQ,EAAE,QAAA,EAAU,MAAA,EAAQ,IAAA,EAAM,GAAA,EAAO,CAAA;AAAA,EACpE;AAAA,EACA,MAAM,MAAA,CAAO,OAAA,EAAiB,OAAA,EAAgC;AAC5D,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,QAAA,CAAS,OAAA,EAAS,OAAO,CAAA;AAC3C,IAAA,MAAMA,mBAAA,CAAG,OAAO,IAAI,CAAA;AAAA,EACtB;AACF;AAEA,IAAI,SAAA;AAEJ,eAAsB,cAAA,GAAuC;AAC3D,EAAA,IAAI,CAAC,SAAA,EAAW;AACd,IAAA,MAAM,MAAA,GAAS,MAAM,UAAA,EAAW;AAChC,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,SAAA,GAAY,IAAI,kBAAkB,MAAM,CAAA;AAAA,IAC1C,CAAA,MAAO;AACL,MAAA,SAAA,GAAY,IAAI,eAAA,EAAgB;AAAA,IAClC;AAAA,EACF;AACA,EAAA,OAAO,SAAA;AACT;;;;"}
|