passwd-sso-cli 0.4.57 → 0.4.58
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/dist/commands/agent-decrypt.js +2 -1
- package/dist/commands/agent.js +5 -3
- package/dist/commands/decrypt.js +2 -1
- package/dist/commands/export.js +2 -1
- package/dist/commands/list.js +2 -1
- package/dist/commands/totp.js +2 -1
- package/dist/lib/api-paths.d.ts +3 -0
- package/dist/lib/api-paths.js +4 -0
- package/dist/lib/clipboard.js +2 -1
- package/dist/lib/crypto.js +3 -2
- package/dist/lib/oauth.js +11 -6
- package/dist/lib/time.d.ts +10 -0
- package/dist/lib/time.js +10 -0
- package/package.json +3 -2
|
@@ -16,6 +16,7 @@ import { buildPersonalEntryAAD, VAULT_TYPE } from "../lib/crypto-aad.js";
|
|
|
16
16
|
import { getEncryptionKey, getUserId, getSecretKeyBytes, setEncryptionKey } from "../lib/vault-state.js";
|
|
17
17
|
import { readPassphrase, unlockWithPassphrase } from "./unlock.js";
|
|
18
18
|
import * as output from "../lib/output.js";
|
|
19
|
+
import { AGENT_CHILD_TIMEOUT_MS } from "../lib/time.js";
|
|
19
20
|
// ─── Input Validation Schema ───────────────────────────────────
|
|
20
21
|
export const DecryptRequestSchema = z.object({
|
|
21
22
|
entryId: z.string().regex(/^[a-zA-Z0-9_-]{1,100}$/),
|
|
@@ -249,7 +250,7 @@ async function forkDaemon(socketPath) {
|
|
|
249
250
|
process.stderr.write("Error: Agent child did not respond within 10s.\n");
|
|
250
251
|
child.kill();
|
|
251
252
|
process.exit(1);
|
|
252
|
-
},
|
|
253
|
+
}, AGENT_CHILD_TIMEOUT_MS);
|
|
253
254
|
}
|
|
254
255
|
/**
|
|
255
256
|
* Internal: runs as the daemon child process (forked by --eval).
|
package/dist/commands/agent.js
CHANGED
|
@@ -23,6 +23,8 @@ import { authorizeSign } from "../lib/ssh-sign-authorizer.js";
|
|
|
23
23
|
import { confirmSign } from "../lib/ssh-confirm.js";
|
|
24
24
|
import { decryptAgentCommand } from "./agent-decrypt.js";
|
|
25
25
|
import * as output from "../lib/output.js";
|
|
26
|
+
import { AGENT_CHILD_TIMEOUT_MS, VAULT_LOCK_POLL_INTERVAL_MS } from "../lib/time.js";
|
|
27
|
+
import { CLI_API_PATH } from "../lib/api-paths.js";
|
|
26
28
|
/** Env flag marking the forked daemon child process. */
|
|
27
29
|
const SSH_DAEMON_ENV = "_PSSO_SSH_DAEMON";
|
|
28
30
|
/**
|
|
@@ -52,7 +54,7 @@ async function loadSshKeys() {
|
|
|
52
54
|
process.exit(1);
|
|
53
55
|
}
|
|
54
56
|
const userId = getUserId();
|
|
55
|
-
const res = await apiRequest(
|
|
57
|
+
const res = await apiRequest(`${CLI_API_PATH.PASSWORDS}?type=SSH_KEY&include=blob`);
|
|
56
58
|
if (!res.ok) {
|
|
57
59
|
output.error(`Failed to fetch SSH keys: HTTP ${res.status}`);
|
|
58
60
|
process.exit(1);
|
|
@@ -133,7 +135,7 @@ export async function agentCommand(opts) {
|
|
|
133
135
|
clearInterval(checkLock);
|
|
134
136
|
process.exit(0);
|
|
135
137
|
}
|
|
136
|
-
},
|
|
138
|
+
}, VAULT_LOCK_POLL_INTERVAL_MS);
|
|
137
139
|
// Wait forever (signal handlers in ssh-agent-socket.ts handle cleanup)
|
|
138
140
|
await new Promise(() => {
|
|
139
141
|
// Never resolves — process exits via signal handlers
|
|
@@ -184,7 +186,7 @@ async function forkDaemon() {
|
|
|
184
186
|
process.stderr.write("Error: Agent child did not respond within 10s.\n");
|
|
185
187
|
child.kill();
|
|
186
188
|
process.exit(1);
|
|
187
|
-
},
|
|
189
|
+
}, AGENT_CHILD_TIMEOUT_MS);
|
|
188
190
|
}
|
|
189
191
|
/**
|
|
190
192
|
* Internal daemon child: receives the vault secret via IPC, derives the
|
package/dist/commands/decrypt.js
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* --json outputs all decrypted fields as JSON (ignores --field).
|
|
12
12
|
*/
|
|
13
13
|
import { createConnection } from "node:net";
|
|
14
|
+
import { AGENT_CHILD_TIMEOUT_MS } from "../lib/time.js";
|
|
14
15
|
export async function decryptCommand(id, options) {
|
|
15
16
|
const socketPath = process.env.PSSO_AGENT_SOCK;
|
|
16
17
|
if (!socketPath) {
|
|
@@ -95,7 +96,7 @@ export async function decryptCommand(id, options) {
|
|
|
95
96
|
process.exitCode = 1;
|
|
96
97
|
finish(err);
|
|
97
98
|
});
|
|
98
|
-
socket.setTimeout(
|
|
99
|
+
socket.setTimeout(AGENT_CHILD_TIMEOUT_MS);
|
|
99
100
|
socket.on("timeout", () => {
|
|
100
101
|
process.stderr.write("Error: Agent request timed out\n");
|
|
101
102
|
process.exitCode = 1;
|
package/dist/commands/export.js
CHANGED
|
@@ -9,6 +9,7 @@ import { decryptData } from "../lib/crypto.js";
|
|
|
9
9
|
import { buildPersonalEntryAAD, VAULT_TYPE } from "../lib/crypto-aad.js";
|
|
10
10
|
import { writeSecretFile } from "../lib/secure-file.js";
|
|
11
11
|
import * as output from "../lib/output.js";
|
|
12
|
+
import { CLI_API_PATH } from "../lib/api-paths.js";
|
|
12
13
|
function escapeCSV(value) {
|
|
13
14
|
if (value.includes(",") || value.includes('"') || value.includes("\n")) {
|
|
14
15
|
return `"${value.replace(/"/g, '""')}"`;
|
|
@@ -26,7 +27,7 @@ export async function exportCommand(options) {
|
|
|
26
27
|
output.error("Format must be 'json' or 'csv'.");
|
|
27
28
|
return;
|
|
28
29
|
}
|
|
29
|
-
const res = await apiRequest(
|
|
30
|
+
const res = await apiRequest(`${CLI_API_PATH.PASSWORDS}?include=blob`);
|
|
30
31
|
if (!res.ok) {
|
|
31
32
|
output.error(`Failed to fetch entries: ${res.status}`);
|
|
32
33
|
return;
|
package/dist/commands/list.js
CHANGED
|
@@ -6,6 +6,7 @@ import { getEncryptionKey, getUserId } from "../lib/vault-state.js";
|
|
|
6
6
|
import { decryptData } from "../lib/crypto.js";
|
|
7
7
|
import { buildPersonalEntryAAD, VAULT_TYPE } from "../lib/crypto-aad.js";
|
|
8
8
|
import * as output from "../lib/output.js";
|
|
9
|
+
import { CLI_API_PATH } from "../lib/api-paths.js";
|
|
9
10
|
export async function listCommand(options) {
|
|
10
11
|
const key = getEncryptionKey();
|
|
11
12
|
if (!key) {
|
|
@@ -13,7 +14,7 @@ export async function listCommand(options) {
|
|
|
13
14
|
return;
|
|
14
15
|
}
|
|
15
16
|
const userId = getUserId();
|
|
16
|
-
const res = await apiRequest(
|
|
17
|
+
const res = await apiRequest(CLI_API_PATH.PASSWORDS);
|
|
17
18
|
if (!res.ok) {
|
|
18
19
|
output.error(`Failed to fetch entries: ${res.status}`);
|
|
19
20
|
return;
|
package/dist/commands/totp.js
CHANGED
|
@@ -8,6 +8,7 @@ import { buildPersonalEntryAAD, VAULT_TYPE } from "../lib/crypto-aad.js";
|
|
|
8
8
|
import { generateTOTPCode } from "../lib/totp.js";
|
|
9
9
|
import { copyToClipboard } from "../lib/clipboard.js";
|
|
10
10
|
import * as output from "../lib/output.js";
|
|
11
|
+
import { MS_PER_SECOND } from "../lib/time.js";
|
|
11
12
|
export async function totpCommand(id, options) {
|
|
12
13
|
const key = getEncryptionKey();
|
|
13
14
|
if (!key) {
|
|
@@ -37,7 +38,7 @@ export async function totpCommand(id, options) {
|
|
|
37
38
|
period: blob.totp.period,
|
|
38
39
|
});
|
|
39
40
|
const period = blob.totp.period ?? 30;
|
|
40
|
-
const remaining = period - (Math.floor(Date.now() /
|
|
41
|
+
const remaining = period - (Math.floor(Date.now() / MS_PER_SECOND) % period);
|
|
41
42
|
if (options.json) {
|
|
42
43
|
output.json({ code, remaining, period });
|
|
43
44
|
return;
|
package/dist/lib/clipboard.js
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { createHash } from "node:crypto";
|
|
8
8
|
import { execSync } from "node:child_process";
|
|
9
|
-
|
|
9
|
+
import { MS_PER_SECOND } from "./time.js";
|
|
10
|
+
const CLEAR_TIMEOUT_MS = 30 * MS_PER_SECOND;
|
|
10
11
|
let clearTimer = null;
|
|
11
12
|
let copiedHash = null;
|
|
12
13
|
function hashContent(content) {
|
package/dist/lib/crypto.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
const PBKDF2_ITERATIONS = 600_000;
|
|
8
8
|
const AES_KEY_LENGTH = 256;
|
|
9
9
|
const IV_LENGTH = 12;
|
|
10
|
+
const GCM_TAG_LENGTH = 16;
|
|
10
11
|
const HKDF_ENC_INFO = "passwd-sso-enc-v1";
|
|
11
12
|
const HKDF_AUTH_INFO = "passwd-sso-auth-v1";
|
|
12
13
|
const VERIFICATION_PLAINTEXT = "passwd-sso-vault-verification-v1";
|
|
@@ -109,8 +110,8 @@ export async function encryptData(plaintext, key, aad) {
|
|
|
109
110
|
params.additionalData = toArrayBuffer(aad);
|
|
110
111
|
const encrypted = await crypto.subtle.encrypt(params, key, textEncode(plaintext));
|
|
111
112
|
const encryptedBytes = new Uint8Array(encrypted);
|
|
112
|
-
const ciphertext = encryptedBytes.slice(0, encryptedBytes.length -
|
|
113
|
-
const authTag = encryptedBytes.slice(encryptedBytes.length -
|
|
113
|
+
const ciphertext = encryptedBytes.slice(0, encryptedBytes.length - GCM_TAG_LENGTH);
|
|
114
|
+
const authTag = encryptedBytes.slice(encryptedBytes.length - GCM_TAG_LENGTH);
|
|
114
115
|
return {
|
|
115
116
|
ciphertext: hexEncode(ciphertext),
|
|
116
117
|
iv: hexEncode(iv),
|
package/dist/lib/oauth.js
CHANGED
|
@@ -7,10 +7,15 @@
|
|
|
7
7
|
import { randomBytes, createHash, timingSafeEqual } from "node:crypto";
|
|
8
8
|
import { createServer, } from "node:http";
|
|
9
9
|
import { spawn } from "node:child_process";
|
|
10
|
-
|
|
10
|
+
import { MS_PER_MINUTE, MS_PER_SECOND, SEC_PER_HOUR } from "./time.js";
|
|
11
|
+
const CALLBACK_TIMEOUT_MS = 2 * MS_PER_MINUTE;
|
|
11
12
|
const CLI_CLIENT_NAME = "passwd-sso-cli";
|
|
12
13
|
const CLI_SCOPES = "credentials:list credentials:use vault:status vault:unlock-data passwords:read passwords:write ssh:sign";
|
|
14
|
+
const OAUTH_DEFAULT_EXPIRES_IN_SEC = SEC_PER_HOUR;
|
|
13
15
|
const MCP_TOKEN_ENDPOINT = "/api/mcp/token";
|
|
16
|
+
const MCP_REGISTER_ENDPOINT = "/api/mcp/register";
|
|
17
|
+
const MCP_REVOKE_ENDPOINT = "/api/mcp/revoke";
|
|
18
|
+
const MCP_AUTHORIZE_ENDPOINT = "/api/mcp/authorize";
|
|
14
19
|
// ─── PKCE helpers ─────────────────────────────────────────────────────────────
|
|
15
20
|
export function generateCodeVerifier() {
|
|
16
21
|
return randomBytes(32).toString("base64url");
|
|
@@ -117,7 +122,7 @@ export async function startCallbackServer(expectedState) {
|
|
|
117
122
|
return new Promise((res, rej) => {
|
|
118
123
|
const timer = setTimeout(() => {
|
|
119
124
|
server.close();
|
|
120
|
-
rej(new Error(`Timed out waiting for OAuth callback after ${CALLBACK_TIMEOUT_MS /
|
|
125
|
+
rej(new Error(`Timed out waiting for OAuth callback after ${CALLBACK_TIMEOUT_MS / MS_PER_SECOND}s`));
|
|
121
126
|
}, CALLBACK_TIMEOUT_MS);
|
|
122
127
|
callbackPromise
|
|
123
128
|
.then((result) => { clearTimeout(timer); server.close(); res(result); })
|
|
@@ -129,7 +134,7 @@ export async function startCallbackServer(expectedState) {
|
|
|
129
134
|
// ─── DCR client registration ──────────────────────────────────────────────────
|
|
130
135
|
/** Register a new public OAuth client via Dynamic Client Registration (RFC 7591). */
|
|
131
136
|
export async function registerClient(serverUrl, redirectUri) {
|
|
132
|
-
const response = await fetch(`${serverUrl}
|
|
137
|
+
const response = await fetch(`${serverUrl}${MCP_REGISTER_ENDPOINT}`, {
|
|
133
138
|
method: "POST",
|
|
134
139
|
headers: { "Content-Type": "application/json" },
|
|
135
140
|
body: JSON.stringify({
|
|
@@ -172,7 +177,7 @@ function parseTokenResponse(data) {
|
|
|
172
177
|
return {
|
|
173
178
|
accessToken,
|
|
174
179
|
refreshToken,
|
|
175
|
-
expiresIn: typeof expiresIn === "number" ? expiresIn :
|
|
180
|
+
expiresIn: typeof expiresIn === "number" ? expiresIn : OAUTH_DEFAULT_EXPIRES_IN_SEC,
|
|
176
181
|
scope: typeof scope === "string" ? scope : CLI_SCOPES,
|
|
177
182
|
};
|
|
178
183
|
}
|
|
@@ -220,7 +225,7 @@ export async function revokeTokenRequest(serverUrl, token, clientId, tokenTypeHi
|
|
|
220
225
|
if (tokenTypeHint)
|
|
221
226
|
params.token_type_hint = tokenTypeHint;
|
|
222
227
|
try {
|
|
223
|
-
await fetch(`${serverUrl}
|
|
228
|
+
await fetch(`${serverUrl}${MCP_REVOKE_ENDPOINT}`, {
|
|
224
229
|
method: "POST",
|
|
225
230
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
226
231
|
body: new URLSearchParams(params).toString(),
|
|
@@ -298,7 +303,7 @@ export async function runOAuthFlow(serverUrl) {
|
|
|
298
303
|
const { port, waitForCallback } = await startCallbackServer(state);
|
|
299
304
|
const redirectUri = `http://127.0.0.1:${port}/callback`;
|
|
300
305
|
const { clientId } = await registerClient(serverUrl, redirectUri);
|
|
301
|
-
const authUrl = new URL(`${serverUrl}
|
|
306
|
+
const authUrl = new URL(`${serverUrl}${MCP_AUTHORIZE_ENDPOINT}`);
|
|
302
307
|
authUrl.searchParams.set("response_type", "code");
|
|
303
308
|
authUrl.searchParams.set("client_id", clientId);
|
|
304
309
|
authUrl.searchParams.set("redirect_uri", redirectUri);
|
package/dist/lib/time.d.ts
CHANGED
|
@@ -6,3 +6,13 @@ export declare const MS_PER_MINUTE: number;
|
|
|
6
6
|
export declare const MS_PER_HOUR: number;
|
|
7
7
|
/** Milliseconds per day. */
|
|
8
8
|
export declare const MS_PER_DAY: number;
|
|
9
|
+
/** Seconds per minute. */
|
|
10
|
+
export declare const SEC_PER_MINUTE = 60;
|
|
11
|
+
/** Seconds per hour. */
|
|
12
|
+
export declare const SEC_PER_HOUR: number;
|
|
13
|
+
/** Seconds per day. */
|
|
14
|
+
export declare const SEC_PER_DAY: number;
|
|
15
|
+
/** Timeout for agent child process acknowledgement (agent-decrypt, agent, decrypt). */
|
|
16
|
+
export declare const AGENT_CHILD_TIMEOUT_MS: number;
|
|
17
|
+
/** Polling interval for vault lock check in agent foreground mode. */
|
|
18
|
+
export declare const VAULT_LOCK_POLL_INTERVAL_MS: number;
|
package/dist/lib/time.js
CHANGED
|
@@ -6,4 +6,14 @@ export const MS_PER_MINUTE = 60 * MS_PER_SECOND;
|
|
|
6
6
|
export const MS_PER_HOUR = 60 * MS_PER_MINUTE;
|
|
7
7
|
/** Milliseconds per day. */
|
|
8
8
|
export const MS_PER_DAY = 24 * MS_PER_HOUR;
|
|
9
|
+
/** Seconds per minute. */
|
|
10
|
+
export const SEC_PER_MINUTE = 60;
|
|
11
|
+
/** Seconds per hour. */
|
|
12
|
+
export const SEC_PER_HOUR = 60 * SEC_PER_MINUTE;
|
|
13
|
+
/** Seconds per day. */
|
|
14
|
+
export const SEC_PER_DAY = 24 * SEC_PER_HOUR;
|
|
15
|
+
/** Timeout for agent child process acknowledgement (agent-decrypt, agent, decrypt). */
|
|
16
|
+
export const AGENT_CHILD_TIMEOUT_MS = 10 * MS_PER_SECOND;
|
|
17
|
+
/** Polling interval for vault lock check in agent foreground mode. */
|
|
18
|
+
export const VAULT_LOCK_POLL_INTERVAL_MS = 5 * MS_PER_SECOND;
|
|
9
19
|
//# sourceMappingURL=time.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "passwd-sso-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.58",
|
|
4
4
|
"description": "CLI for passwd-sso password manager",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"vitest": "^4.1.8"
|
|
52
52
|
},
|
|
53
53
|
"overrides": {
|
|
54
|
-
"postcss": ">=8.5.10"
|
|
54
|
+
"postcss": ">=8.5.10",
|
|
55
|
+
"esbuild": ">=0.28.1"
|
|
55
56
|
}
|
|
56
57
|
}
|