@zivis/mcp 0.1.8 → 0.1.10
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/LICENSE +201 -0
- package/README.md +19 -0
- package/dist/api-client.d.ts +2 -24
- package/dist/api-client.js +32 -52
- package/dist/auth/index.d.ts +1 -22
- package/dist/auth/index.js +17 -38
- package/dist/auth/jwt.d.ts +0 -16
- package/dist/auth/jwt.js +0 -13
- package/dist/auth/keychain.d.ts +6 -32
- package/dist/auth/keychain.js +83 -114
- package/dist/auth/oauth.d.ts +3 -32
- package/dist/auth/oauth.js +67 -61
- package/dist/auth/sessions.d.ts +5 -25
- package/dist/auth/sessions.js +20 -39
- package/dist/auth/token-refresh.d.ts +2 -22
- package/dist/auth/token-refresh.js +33 -36
- package/dist/cli.d.ts +1 -18
- package/dist/cli.js +30 -908
- package/dist/index.d.ts +0 -6
- package/dist/index.js +0 -7
- package/dist/lib/inspect-cache.d.ts +10 -0
- package/dist/lib/inspect-cache.js +75 -0
- package/dist/lib/next-steps.d.ts +27 -0
- package/dist/lib/next-steps.js +21 -0
- package/dist/matcher/ast-eval.d.ts +8 -0
- package/dist/matcher/ast-eval.js +95 -0
- package/dist/matcher/finding.d.ts +55 -0
- package/dist/matcher/finding.js +1 -0
- package/dist/matcher/index.d.ts +8 -0
- package/dist/matcher/index.js +202 -0
- package/dist/matcher/inference-candidates.d.ts +26 -0
- package/dist/matcher/inference-candidates.js +81 -0
- package/dist/matcher/walker.d.ts +7 -0
- package/dist/matcher/walker.js +71 -0
- package/dist/pattern-pack/index.d.ts +125 -0
- package/dist/pattern-pack/index.js +189 -0
- package/dist/pattern-packs/zivis-public-0.2.0/capsules/agents/privilege-separation.yaml +224 -0
- package/dist/pattern-packs/zivis-public-0.2.0/capsules/prompting/self-consistency-no-isolation.yaml +237 -0
- package/dist/pattern-packs/zivis-public-0.2.0/capsules/retrieval/context-injection-no-validator.yaml +348 -0
- package/dist/pattern-packs/zivis-public-0.2.0/capsules/security/late-org-filter.yaml +266 -0
- package/dist/pattern-packs/zivis-public-0.2.0/controls/external_validator_on_facts.yaml +37 -0
- package/dist/pattern-packs/zivis-public-0.2.0/controls/fact_validator_present.yaml +68 -0
- package/dist/pattern-packs/zivis-public-0.2.0/controls/human_approval_gate.yaml +44 -0
- package/dist/pattern-packs/zivis-public-0.2.0/controls/langgraph_state_machine_with_per_node_tools.yaml +35 -0
- package/dist/pattern-packs/zivis-public-0.2.0/controls/per_sample_retrieval_diversification.yaml +38 -0
- package/dist/pattern-packs/zivis-public-0.2.0/controls/prisma_extension_or_middleware_attaching_org_filter.yaml +31 -0
- package/dist/pattern-packs/zivis-public-0.2.0/controls/row_level_security_policy.yaml +37 -0
- package/dist/pattern-packs/zivis-public-0.2.0/controls/source_attribution_present.yaml +58 -0
- package/dist/pattern-packs/zivis-public-0.2.0/manifest.json +167 -0
- package/dist/pattern-packs/zivis-public-0.2.0/prompts/agents/privilege-separation/identify.md +54 -0
- package/dist/pattern-packs/zivis-public-0.2.0/prompts/prompting/self-consistency-no-isolation/identify.md +60 -0
- package/dist/pattern-packs/zivis-public-0.2.0/prompts/retrieval/context-injection-no-validator/identify.md +109 -0
- package/dist/pattern-packs/zivis-public-0.2.0/prompts/security/late-org-filter/identify.md +78 -0
- package/dist/project-binding.d.ts +2 -19
- package/dist/project-binding.js +38 -21
- package/dist/prompts/audit-dependencies.d.ts +2 -0
- package/dist/prompts/audit-dependencies.js +47 -0
- package/dist/prompts/getting-started.d.ts +2 -0
- package/dist/prompts/getting-started.js +37 -0
- package/dist/prompts/index.d.ts +2 -0
- package/dist/prompts/index.js +6 -0
- package/dist/resolve-application-id.d.ts +0 -4
- package/dist/resolve-application-id.js +0 -5
- package/dist/sanitize.d.ts +1 -21
- package/dist/sanitize.js +7 -41
- package/dist/server.d.ts +1 -13
- package/dist/server.js +142 -265
- package/dist/tools/artifacts.d.ts +76 -0
- package/dist/tools/artifacts.js +178 -0
- package/dist/tools/check-project.d.ts +5 -18
- package/dist/tools/check-project.js +122 -110
- package/dist/tools/check-repo-trust.d.ts +4 -19
- package/dist/tools/check-repo-trust.js +56 -47
- package/dist/tools/create-diagram.d.ts +0 -5
- package/dist/tools/create-diagram.js +0 -10
- package/dist/tools/create-document.d.ts +0 -5
- package/dist/tools/create-document.js +3 -9
- package/dist/tools/create-finding.d.ts +85 -0
- package/dist/tools/create-finding.js +136 -0
- package/dist/tools/delete-finding.d.ts +19 -0
- package/dist/tools/delete-finding.js +36 -0
- package/dist/tools/devx-run.d.ts +88 -0
- package/dist/tools/devx-run.js +175 -0
- package/dist/tools/discover-local-infra.d.ts +0 -10
- package/dist/tools/discover-local-infra.js +3 -73
- package/dist/tools/enterprise/get-org-zat.d.ts +0 -5
- package/dist/tools/enterprise/get-org-zat.js +0 -6
- package/dist/tools/explain-signal-for-diff.d.ts +34 -0
- package/dist/tools/explain-signal-for-diff.js +75 -0
- package/dist/tools/generate-diagram.d.ts +0 -6
- package/dist/tools/generate-diagram.js +21 -13
- package/dist/tools/get-application-overview.d.ts +0 -5
- package/dist/tools/get-application-overview.js +0 -6
- package/dist/tools/get-application.d.ts +0 -5
- package/dist/tools/get-application.js +0 -6
- package/dist/tools/get-diagram.d.ts +0 -5
- package/dist/tools/get-diagram.js +1 -11
- package/dist/tools/get-document.d.ts +0 -5
- package/dist/tools/get-document.js +0 -6
- package/dist/tools/get-oss-zat.d.ts +0 -5
- package/dist/tools/get-oss-zat.js +0 -7
- package/dist/tools/get-signal.d.ts +24 -0
- package/dist/tools/get-signal.js +75 -0
- package/dist/tools/get-started.d.ts +18 -0
- package/dist/tools/get-started.js +470 -0
- package/dist/tools/get-trust-keys.d.ts +0 -5
- package/dist/tools/get-trust-keys.js +0 -6
- package/dist/tools/import-openapi-endpoints.d.ts +0 -5
- package/dist/tools/import-openapi-endpoints.js +0 -6
- package/dist/tools/inspect-zat.d.ts +0 -5
- package/dist/tools/inspect-zat.js +0 -19
- package/dist/tools/inspect.d.ts +21 -0
- package/dist/tools/inspect.js +222 -0
- package/dist/tools/list-applications.d.ts +1 -6
- package/dist/tools/list-applications.js +0 -6
- package/dist/tools/list-diagrams.d.ts +0 -5
- package/dist/tools/list-diagrams.js +0 -6
- package/dist/tools/list-documents.d.ts +0 -5
- package/dist/tools/list-documents.js +0 -6
- package/dist/tools/list-endpoints.d.ts +1 -6
- package/dist/tools/list-endpoints.js +0 -6
- package/dist/tools/list-signals.d.ts +37 -0
- package/dist/tools/list-signals.js +83 -0
- package/dist/tools/manage-application.d.ts +64 -0
- package/dist/tools/manage-application.js +129 -0
- package/dist/tools/manage-diagram.d.ts +0 -5
- package/dist/tools/manage-diagram.js +0 -16
- package/dist/tools/manage-endpoint-lifecycle.d.ts +80 -0
- package/dist/tools/manage-endpoint-lifecycle.js +180 -0
- package/dist/tools/open-in-ide.d.ts +2 -14
- package/dist/tools/open-in-ide.js +0 -13
- package/dist/tools/review-change.d.ts +34 -0
- package/dist/tools/review-change.js +93 -0
- package/dist/tools/security-memory.d.ts +76 -0
- package/dist/tools/security-memory.js +202 -0
- package/dist/tools/security-review.d.ts +1 -21
- package/dist/tools/security-review.js +8 -108
- package/dist/tools/threat-get-capsule.d.ts +15 -0
- package/dist/tools/threat-get-capsule.js +53 -0
- package/dist/tools/threat-get-inference-prompt.d.ts +15 -0
- package/dist/tools/threat-get-inference-prompt.js +73 -0
- package/dist/tools/threat-list-relevant-capsules.d.ts +17 -0
- package/dist/tools/threat-list-relevant-capsules.js +158 -0
- package/dist/tools/threat-run-matcher.d.ts +17 -0
- package/dist/tools/threat-run-matcher.js +145 -0
- package/dist/tools/update-document.d.ts +0 -5
- package/dist/tools/update-document.js +3 -9
- package/dist/tools/update-endpoint.d.ts +1 -6
- package/dist/tools/update-endpoint.js +0 -7
- package/dist/tools/update-finding.d.ts +61 -0
- package/dist/tools/update-finding.js +80 -0
- package/dist/tools/update-mermaid-source.d.ts +0 -13
- package/dist/tools/update-mermaid-source.js +0 -14
- package/dist/tools/verify-trust-mark.d.ts +0 -5
- package/dist/tools/verify-trust-mark.js +0 -6
- package/dist/types.d.ts +14 -14
- package/dist/types.js +22 -28
- package/package.json +38 -15
- package/dist/api-client.js.map +0 -1
- package/dist/auth/index.js.map +0 -1
- package/dist/auth/jwt.js.map +0 -1
- package/dist/auth/keychain.js.map +0 -1
- package/dist/auth/oauth.js.map +0 -1
- package/dist/auth/sessions.js.map +0 -1
- package/dist/auth/token-refresh.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/project-binding.js.map +0 -1
- package/dist/resolve-application-id.js.map +0 -1
- package/dist/sanitize.js.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/tools/check-project.js.map +0 -1
- package/dist/tools/check-repo-trust.js.map +0 -1
- package/dist/tools/create-diagram.js.map +0 -1
- package/dist/tools/create-document.js.map +0 -1
- package/dist/tools/discover-local-infra.js.map +0 -1
- package/dist/tools/enterprise/capture-evidence.d.ts +0 -54
- package/dist/tools/enterprise/capture-evidence.js +0 -72
- package/dist/tools/enterprise/capture-evidence.js.map +0 -1
- package/dist/tools/enterprise/create-report-ticket.d.ts +0 -34
- package/dist/tools/enterprise/create-report-ticket.js +0 -44
- package/dist/tools/enterprise/create-report-ticket.js.map +0 -1
- package/dist/tools/enterprise/end-pentest-session.d.ts +0 -33
- package/dist/tools/enterprise/end-pentest-session.js +0 -42
- package/dist/tools/enterprise/end-pentest-session.js.map +0 -1
- package/dist/tools/enterprise/generate-report.d.ts +0 -55
- package/dist/tools/enterprise/generate-report.js +0 -111
- package/dist/tools/enterprise/generate-report.js.map +0 -1
- package/dist/tools/enterprise/get-org-zat.js.map +0 -1
- package/dist/tools/enterprise/get-report-status.d.ts +0 -27
- package/dist/tools/enterprise/get-report-status.js +0 -38
- package/dist/tools/enterprise/get-report-status.js.map +0 -1
- package/dist/tools/enterprise/get-threat-model.d.ts +0 -29
- package/dist/tools/enterprise/get-threat-model.js +0 -83
- package/dist/tools/enterprise/get-threat-model.js.map +0 -1
- package/dist/tools/enterprise/list-agents.d.ts +0 -42
- package/dist/tools/enterprise/list-agents.js +0 -95
- package/dist/tools/enterprise/list-agents.js.map +0 -1
- package/dist/tools/enterprise/list-report-issues.d.ts +0 -36
- package/dist/tools/enterprise/list-report-issues.js +0 -50
- package/dist/tools/enterprise/list-report-issues.js.map +0 -1
- package/dist/tools/enterprise/list-threat-models.d.ts +0 -36
- package/dist/tools/enterprise/list-threat-models.js +0 -82
- package/dist/tools/enterprise/list-threat-models.js.map +0 -1
- package/dist/tools/enterprise/manage-actor.d.ts +0 -58
- package/dist/tools/enterprise/manage-actor.js +0 -99
- package/dist/tools/enterprise/manage-actor.js.map +0 -1
- package/dist/tools/enterprise/manage-attack-scenario.d.ts +0 -70
- package/dist/tools/enterprise/manage-attack-scenario.js +0 -89
- package/dist/tools/enterprise/manage-attack-scenario.js.map +0 -1
- package/dist/tools/enterprise/manage-component.d.ts +0 -58
- package/dist/tools/enterprise/manage-component.js +0 -90
- package/dist/tools/enterprise/manage-component.js.map +0 -1
- package/dist/tools/enterprise/manage-data-asset.d.ts +0 -53
- package/dist/tools/enterprise/manage-data-asset.js +0 -90
- package/dist/tools/enterprise/manage-data-asset.js.map +0 -1
- package/dist/tools/enterprise/manage-data-flow.d.ts +0 -57
- package/dist/tools/enterprise/manage-data-flow.js +0 -98
- package/dist/tools/enterprise/manage-data-flow.js.map +0 -1
- package/dist/tools/enterprise/manage-finding.d.ts +0 -69
- package/dist/tools/enterprise/manage-finding.js +0 -106
- package/dist/tools/enterprise/manage-finding.js.map +0 -1
- package/dist/tools/enterprise/manage-security-control.d.ts +0 -59
- package/dist/tools/enterprise/manage-security-control.js +0 -96
- package/dist/tools/enterprise/manage-security-control.js.map +0 -1
- package/dist/tools/enterprise/manage-test-case.d.ts +0 -88
- package/dist/tools/enterprise/manage-test-case.js +0 -119
- package/dist/tools/enterprise/manage-test-case.js.map +0 -1
- package/dist/tools/enterprise/promote-finding-to-issue.d.ts +0 -36
- package/dist/tools/enterprise/promote-finding-to-issue.js +0 -65
- package/dist/tools/enterprise/promote-finding-to-issue.js.map +0 -1
- package/dist/tools/enterprise/publish-report-version.d.ts +0 -34
- package/dist/tools/enterprise/publish-report-version.js +0 -75
- package/dist/tools/enterprise/publish-report-version.js.map +0 -1
- package/dist/tools/enterprise/publish-test-to-library.d.ts +0 -31
- package/dist/tools/enterprise/publish-test-to-library.js +0 -40
- package/dist/tools/enterprise/publish-test-to-library.js.map +0 -1
- package/dist/tools/enterprise/record-test-result.d.ts +0 -67
- package/dist/tools/enterprise/record-test-result.js +0 -75
- package/dist/tools/enterprise/record-test-result.js.map +0 -1
- package/dist/tools/enterprise/start-pentest-session.d.ts +0 -35
- package/dist/tools/enterprise/start-pentest-session.js +0 -50
- package/dist/tools/enterprise/start-pentest-session.js.map +0 -1
- package/dist/tools/enterprise/sync-report-section.d.ts +0 -41
- package/dist/tools/enterprise/sync-report-section.js +0 -79
- package/dist/tools/enterprise/sync-report-section.js.map +0 -1
- package/dist/tools/enterprise/update-report-issue.d.ts +0 -55
- package/dist/tools/enterprise/update-report-issue.js +0 -69
- package/dist/tools/enterprise/update-report-issue.js.map +0 -1
- package/dist/tools/generate-diagram.js.map +0 -1
- package/dist/tools/get-agent-test-results.d.ts +0 -41
- package/dist/tools/get-agent-test-results.js +0 -86
- package/dist/tools/get-agent-test-results.js.map +0 -1
- package/dist/tools/get-application-overview.js.map +0 -1
- package/dist/tools/get-application.js.map +0 -1
- package/dist/tools/get-artifact-content.d.ts +0 -29
- package/dist/tools/get-artifact-content.js +0 -86
- package/dist/tools/get-artifact-content.js.map +0 -1
- package/dist/tools/get-diagram.js.map +0 -1
- package/dist/tools/get-document.js.map +0 -1
- package/dist/tools/get-finding-details.d.ts +0 -28
- package/dist/tools/get-finding-details.js +0 -43
- package/dist/tools/get-finding-details.js.map +0 -1
- package/dist/tools/get-findings.d.ts +0 -57
- package/dist/tools/get-findings.js +0 -103
- package/dist/tools/get-findings.js.map +0 -1
- package/dist/tools/get-oss-zat.js.map +0 -1
- package/dist/tools/get-recon-scan.d.ts +0 -27
- package/dist/tools/get-recon-scan.js +0 -46
- package/dist/tools/get-recon-scan.js.map +0 -1
- package/dist/tools/get-scan-output.d.ts +0 -27
- package/dist/tools/get-scan-output.js +0 -64
- package/dist/tools/get-scan-output.js.map +0 -1
- package/dist/tools/get-scenario-details.d.ts +0 -27
- package/dist/tools/get-scenario-details.js +0 -46
- package/dist/tools/get-scenario-details.js.map +0 -1
- package/dist/tools/get-test-case-results.d.ts +0 -37
- package/dist/tools/get-test-case-results.js +0 -66
- package/dist/tools/get-test-case-results.js.map +0 -1
- package/dist/tools/get-test-details.d.ts +0 -29
- package/dist/tools/get-test-details.js +0 -49
- package/dist/tools/get-test-details.js.map +0 -1
- package/dist/tools/get-trust-keys.js.map +0 -1
- package/dist/tools/import-openapi-endpoints.js.map +0 -1
- package/dist/tools/inspect-zat.js.map +0 -1
- package/dist/tools/list-agent-test-cases.d.ts +0 -41
- package/dist/tools/list-agent-test-cases.js +0 -73
- package/dist/tools/list-agent-test-cases.js.map +0 -1
- package/dist/tools/list-applications.js.map +0 -1
- package/dist/tools/list-diagrams.js.map +0 -1
- package/dist/tools/list-documents.js.map +0 -1
- package/dist/tools/list-endpoints.js.map +0 -1
- package/dist/tools/list-recon-scans.d.ts +0 -36
- package/dist/tools/list-recon-scans.js +0 -74
- package/dist/tools/list-recon-scans.js.map +0 -1
- package/dist/tools/list-scan-artifacts.d.ts +0 -41
- package/dist/tools/list-scan-artifacts.js +0 -63
- package/dist/tools/list-scan-artifacts.js.map +0 -1
- package/dist/tools/list-scans.d.ts +0 -44
- package/dist/tools/list-scans.js +0 -91
- package/dist/tools/list-scans.js.map +0 -1
- package/dist/tools/list-test-library.d.ts +0 -44
- package/dist/tools/list-test-library.js +0 -83
- package/dist/tools/list-test-library.js.map +0 -1
- package/dist/tools/list-test-scenarios.d.ts +0 -31
- package/dist/tools/list-test-scenarios.js +0 -66
- package/dist/tools/list-test-scenarios.js.map +0 -1
- package/dist/tools/local-scan.d.ts +0 -40
- package/dist/tools/local-scan.js +0 -261
- package/dist/tools/local-scan.js.map +0 -1
- package/dist/tools/manage-diagram.js.map +0 -1
- package/dist/tools/open-in-ide.js.map +0 -1
- package/dist/tools/run-test-scenario.d.ts +0 -37
- package/dist/tools/run-test-scenario.js +0 -70
- package/dist/tools/run-test-scenario.js.map +0 -1
- package/dist/tools/scan-pr.d.ts +0 -27
- package/dist/tools/scan-pr.js +0 -51
- package/dist/tools/scan-pr.js.map +0 -1
- package/dist/tools/security-review.js.map +0 -1
- package/dist/tools/setup-red-team-target.d.ts +0 -79
- package/dist/tools/setup-red-team-target.js +0 -430
- package/dist/tools/setup-red-team-target.js.map +0 -1
- package/dist/tools/triage-finding.d.ts +0 -47
- package/dist/tools/triage-finding.js +0 -116
- package/dist/tools/triage-finding.js.map +0 -1
- package/dist/tools/update-document.js.map +0 -1
- package/dist/tools/update-endpoint.js.map +0 -1
- package/dist/tools/update-mermaid-source.js.map +0 -1
- package/dist/tools/verify-trust-mark.js.map +0 -1
- package/dist/types.js.map +0 -1
package/dist/auth/keychain.js
CHANGED
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OS Keychain credential storage
|
|
3
|
-
*
|
|
4
|
-
* Uses native OS CLI commands to store/retrieve credentials securely.
|
|
5
|
-
* No native npm dependencies (no keytar).
|
|
6
|
-
*
|
|
7
|
-
* - macOS: Keychain Services via `security` CLI
|
|
8
|
-
* - Linux: Secret Service API via `secret-tool` CLI
|
|
9
|
-
* - Windows: DPAPI via PowerShell
|
|
10
|
-
*
|
|
11
|
-
* Falls back to encrypted file if keychain CLI is unavailable.
|
|
12
|
-
*/
|
|
13
1
|
import { execFile } from "node:child_process";
|
|
14
2
|
import { promisify } from "node:util";
|
|
15
3
|
import * as fs from "node:fs/promises";
|
|
@@ -18,48 +6,49 @@ import * as os from "node:os";
|
|
|
18
6
|
import * as crypto from "node:crypto";
|
|
19
7
|
import { ENV_CONFIG } from "../types.js";
|
|
20
8
|
const execFileAsync = promisify(execFile);
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
*/
|
|
9
|
+
function resolvedStorage(storage) {
|
|
10
|
+
return (storage ?? {
|
|
11
|
+
keychainPrefix: ENV_CONFIG.keychainPrefix,
|
|
12
|
+
configDir: ENV_CONFIG.configDir,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
28
15
|
export function getSessionAccount(session) {
|
|
29
16
|
return session ? `oauth.${session}` : "oauth";
|
|
30
17
|
}
|
|
31
|
-
function getServiceName(account) {
|
|
32
|
-
return `${
|
|
18
|
+
function getServiceName(account, keychainPrefix) {
|
|
19
|
+
return `${keychainPrefix}.${account}`;
|
|
33
20
|
}
|
|
34
|
-
function getFallbackDir() {
|
|
35
|
-
// Use environment-aware config dir so staging and production are isolated
|
|
36
|
-
const configDir = ENV_CONFIG.configDir;
|
|
21
|
+
function getFallbackDir(configDir) {
|
|
37
22
|
if (process.platform === "win32") {
|
|
38
23
|
return path.join(process.env.APPDATA || os.homedir(), configDir);
|
|
39
24
|
}
|
|
40
25
|
return path.join(os.homedir(), ".config", configDir);
|
|
41
26
|
}
|
|
42
|
-
function getFallbackPath(account) {
|
|
43
|
-
return path.join(getFallbackDir(), `${account}.enc`);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
// Entry didn't exist, that's fine
|
|
27
|
+
function getFallbackPath(account, storage) {
|
|
28
|
+
return path.join(getFallbackDir(storage.configDir), `${account}.enc`);
|
|
29
|
+
}
|
|
30
|
+
async function macosDeleteAll(service) {
|
|
31
|
+
for (let i = 0; i < 100; i++) {
|
|
32
|
+
try {
|
|
33
|
+
await execFileAsync("security", [
|
|
34
|
+
"delete-generic-password",
|
|
35
|
+
"-a",
|
|
36
|
+
"zivis-mcp",
|
|
37
|
+
"-s",
|
|
38
|
+
service,
|
|
39
|
+
]);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
60
44
|
}
|
|
45
|
+
}
|
|
46
|
+
async function macosStore(account, value, keychainPrefix) {
|
|
47
|
+
const service = getServiceName(account, keychainPrefix);
|
|
48
|
+
await macosDeleteAll(service);
|
|
61
49
|
await execFileAsync("security", [
|
|
62
50
|
"add-generic-password",
|
|
51
|
+
"-U",
|
|
63
52
|
"-a",
|
|
64
53
|
"zivis-mcp",
|
|
65
54
|
"-s",
|
|
@@ -68,8 +57,8 @@ async function macosStore(account, value) {
|
|
|
68
57
|
value,
|
|
69
58
|
]);
|
|
70
59
|
}
|
|
71
|
-
async function macosRead(account) {
|
|
72
|
-
const service = getServiceName(account);
|
|
60
|
+
async function macosRead(account, keychainPrefix) {
|
|
61
|
+
const service = getServiceName(account, keychainPrefix);
|
|
73
62
|
try {
|
|
74
63
|
const { stdout } = await execFileAsync("security", [
|
|
75
64
|
"find-generic-password",
|
|
@@ -85,43 +74,29 @@ async function macosRead(account) {
|
|
|
85
74
|
return null;
|
|
86
75
|
}
|
|
87
76
|
}
|
|
88
|
-
async function macosDelete(account) {
|
|
89
|
-
|
|
90
|
-
try {
|
|
91
|
-
await execFileAsync("security", [
|
|
92
|
-
"delete-generic-password",
|
|
93
|
-
"-a",
|
|
94
|
-
"zivis-mcp",
|
|
95
|
-
"-s",
|
|
96
|
-
service,
|
|
97
|
-
]);
|
|
98
|
-
}
|
|
99
|
-
catch {
|
|
100
|
-
// Entry didn't exist
|
|
101
|
-
}
|
|
77
|
+
async function macosDelete(account, keychainPrefix) {
|
|
78
|
+
await macosDeleteAll(getServiceName(account, keychainPrefix));
|
|
102
79
|
}
|
|
103
|
-
|
|
104
|
-
async function linuxStore(account, value) {
|
|
80
|
+
async function linuxStore(account, value, keychainPrefix) {
|
|
105
81
|
const child = execFileAsync("secret-tool", [
|
|
106
82
|
"store",
|
|
107
83
|
"--label",
|
|
108
84
|
`ZIVIS MCP ${account}`,
|
|
109
85
|
"service",
|
|
110
|
-
|
|
86
|
+
keychainPrefix,
|
|
111
87
|
"account",
|
|
112
88
|
account,
|
|
113
89
|
]);
|
|
114
|
-
// secret-tool reads value from stdin
|
|
115
90
|
child.child.stdin?.write(value);
|
|
116
91
|
child.child.stdin?.end();
|
|
117
92
|
await child;
|
|
118
93
|
}
|
|
119
|
-
async function linuxRead(account) {
|
|
94
|
+
async function linuxRead(account, keychainPrefix) {
|
|
120
95
|
try {
|
|
121
96
|
const { stdout } = await execFileAsync("secret-tool", [
|
|
122
97
|
"lookup",
|
|
123
98
|
"service",
|
|
124
|
-
|
|
99
|
+
keychainPrefix,
|
|
125
100
|
"account",
|
|
126
101
|
account,
|
|
127
102
|
]);
|
|
@@ -131,44 +106,39 @@ async function linuxRead(account) {
|
|
|
131
106
|
return null;
|
|
132
107
|
}
|
|
133
108
|
}
|
|
134
|
-
async function linuxDelete(account) {
|
|
109
|
+
async function linuxDelete(account, keychainPrefix) {
|
|
135
110
|
try {
|
|
136
111
|
await execFileAsync("secret-tool", [
|
|
137
112
|
"clear",
|
|
138
113
|
"service",
|
|
139
|
-
|
|
114
|
+
keychainPrefix,
|
|
140
115
|
"account",
|
|
141
116
|
account,
|
|
142
117
|
]);
|
|
143
118
|
}
|
|
144
119
|
catch {
|
|
145
|
-
// Entry didn't exist
|
|
146
120
|
}
|
|
147
121
|
}
|
|
148
|
-
// ── Encrypted File Fallback (Windows + fallback for all platforms) ──
|
|
149
122
|
function deriveKey() {
|
|
150
|
-
// Use machine-specific data as encryption key seed
|
|
151
123
|
const seed = `zivis-mcp:${os.hostname()}:${os.userInfo().username}`;
|
|
152
124
|
return crypto.createHash("sha256").update(seed).digest();
|
|
153
125
|
}
|
|
154
|
-
async function fileStore(account, value) {
|
|
126
|
+
async function fileStore(account, value, storage) {
|
|
155
127
|
const key = deriveKey();
|
|
156
128
|
const iv = crypto.randomBytes(16);
|
|
157
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
158
129
|
const cipher = crypto.createCipheriv("aes-256-cbc", key, iv);
|
|
159
130
|
let encrypted = cipher.update(value, "utf8", "hex");
|
|
160
131
|
encrypted += cipher.final("hex");
|
|
161
132
|
const data = JSON.stringify({ iv: iv.toString("hex"), data: encrypted });
|
|
162
|
-
const dir = getFallbackDir();
|
|
133
|
+
const dir = getFallbackDir(storage.configDir);
|
|
163
134
|
await fs.mkdir(dir, { recursive: true });
|
|
164
|
-
await fs.writeFile(getFallbackPath(account), data, { mode: 0o600 });
|
|
135
|
+
await fs.writeFile(getFallbackPath(account, storage), data, { mode: 0o600 });
|
|
165
136
|
}
|
|
166
|
-
async function fileRead(account) {
|
|
137
|
+
async function fileRead(account, storage) {
|
|
167
138
|
try {
|
|
168
|
-
const raw = await fs.readFile(getFallbackPath(account), "utf8");
|
|
139
|
+
const raw = await fs.readFile(getFallbackPath(account, storage), "utf8");
|
|
169
140
|
const { iv, data } = JSON.parse(raw);
|
|
170
141
|
const key = deriveKey();
|
|
171
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
172
142
|
const decipher = crypto.createDecipheriv("aes-256-cbc", key, Buffer.from(iv, "hex"));
|
|
173
143
|
let decrypted = decipher.update(data, "hex", "utf8");
|
|
174
144
|
decrypted += decipher.final("utf8");
|
|
@@ -178,15 +148,13 @@ async function fileRead(account) {
|
|
|
178
148
|
return null;
|
|
179
149
|
}
|
|
180
150
|
}
|
|
181
|
-
async function fileDelete(account) {
|
|
151
|
+
async function fileDelete(account, storage) {
|
|
182
152
|
try {
|
|
183
|
-
await fs.unlink(getFallbackPath(account));
|
|
153
|
+
await fs.unlink(getFallbackPath(account, storage));
|
|
184
154
|
}
|
|
185
155
|
catch {
|
|
186
|
-
// File didn't exist
|
|
187
156
|
}
|
|
188
157
|
}
|
|
189
|
-
// ── Platform Detection & Dispatch ──
|
|
190
158
|
async function isKeychainAvailable() {
|
|
191
159
|
try {
|
|
192
160
|
if (process.platform === "darwin") {
|
|
@@ -210,56 +178,58 @@ async function useKeychain() {
|
|
|
210
178
|
}
|
|
211
179
|
return keychainAvailable;
|
|
212
180
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
// may not be available, e.g., MCP servers spawned by IDEs with different $PATH)
|
|
217
|
-
await fileStore(account, value);
|
|
218
|
-
// Also store to native keychain if available
|
|
181
|
+
export async function keychainStore(account, value, storage) {
|
|
182
|
+
const st = resolvedStorage(storage);
|
|
183
|
+
await fileStore(account, value, st);
|
|
219
184
|
if (await useKeychain()) {
|
|
220
185
|
if (process.platform === "darwin") {
|
|
221
|
-
await macosStore(account, value);
|
|
186
|
+
await macosStore(account, value, st.keychainPrefix);
|
|
222
187
|
}
|
|
223
188
|
else if (process.platform === "linux") {
|
|
224
|
-
await linuxStore(account, value);
|
|
189
|
+
await linuxStore(account, value, st.keychainPrefix);
|
|
225
190
|
}
|
|
226
191
|
}
|
|
227
192
|
}
|
|
228
|
-
|
|
193
|
+
const LEGACY_KEYCHAIN_PREFIX = "io.zivis.mcp";
|
|
194
|
+
async function readFromBucket(account, storage) {
|
|
229
195
|
if (await useKeychain()) {
|
|
230
196
|
if (process.platform === "darwin")
|
|
231
|
-
return macosRead(account);
|
|
197
|
+
return macosRead(account, storage.keychainPrefix);
|
|
232
198
|
if (process.platform === "linux")
|
|
233
|
-
return linuxRead(account);
|
|
234
|
-
}
|
|
235
|
-
return fileRead(account);
|
|
236
|
-
}
|
|
237
|
-
export async function
|
|
199
|
+
return linuxRead(account, storage.keychainPrefix);
|
|
200
|
+
}
|
|
201
|
+
return fileRead(account, storage);
|
|
202
|
+
}
|
|
203
|
+
export async function keychainRead(account, storage) {
|
|
204
|
+
const st = resolvedStorage(storage);
|
|
205
|
+
const primary = await readFromBucket(account, st);
|
|
206
|
+
if (primary !== null)
|
|
207
|
+
return primary;
|
|
208
|
+
if (st.keychainPrefix !== LEGACY_KEYCHAIN_PREFIX) {
|
|
209
|
+
return await readFromBucket(account, {
|
|
210
|
+
...st,
|
|
211
|
+
keychainPrefix: LEGACY_KEYCHAIN_PREFIX,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
export async function keychainDelete(account, storage) {
|
|
217
|
+
const st = resolvedStorage(storage);
|
|
218
|
+
await fileDelete(account, st);
|
|
238
219
|
if (await useKeychain()) {
|
|
239
220
|
if (process.platform === "darwin")
|
|
240
|
-
return macosDelete(account);
|
|
221
|
+
return macosDelete(account, st.keychainPrefix);
|
|
241
222
|
if (process.platform === "linux")
|
|
242
|
-
return linuxDelete(account);
|
|
223
|
+
return linuxDelete(account, st.keychainPrefix);
|
|
243
224
|
}
|
|
244
|
-
return fileDelete(account);
|
|
245
225
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
*/
|
|
249
|
-
export async function keychainClearSession(session) {
|
|
250
|
-
await keychainDelete(getSessionAccount(session));
|
|
226
|
+
export async function keychainClearSession(session, storage) {
|
|
227
|
+
await keychainDelete(getSessionAccount(session), storage);
|
|
251
228
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
*/
|
|
256
|
-
export async function keychainClearAll() {
|
|
257
|
-
await keychainDelete("oauth");
|
|
258
|
-
await keychainDelete("apikey");
|
|
229
|
+
export async function keychainClearAll(storage) {
|
|
230
|
+
await keychainDelete("oauth", storage);
|
|
231
|
+
await keychainDelete("apikey", storage);
|
|
259
232
|
}
|
|
260
|
-
/**
|
|
261
|
-
* Returns the storage mechanism name for display purposes
|
|
262
|
-
*/
|
|
263
233
|
export function getStorageName() {
|
|
264
234
|
if (process.platform === "darwin")
|
|
265
235
|
return "macOS Keychain";
|
|
@@ -269,4 +239,3 @@ export function getStorageName() {
|
|
|
269
239
|
return "encrypted file";
|
|
270
240
|
return "encrypted file";
|
|
271
241
|
}
|
|
272
|
-
//# sourceMappingURL=keychain.js.map
|
package/dist/auth/oauth.d.ts
CHANGED
|
@@ -1,38 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OAuth PKCE Authentication Flow
|
|
3
|
-
*
|
|
4
|
-
* Authorization Code Flow with Proof Key for Code Exchange (PKCE)
|
|
5
|
-
* for native/CLI applications. Uses WorkOS as the identity provider.
|
|
6
|
-
*
|
|
7
|
-
* Flow:
|
|
8
|
-
* 1. Generate PKCE code_verifier + code_challenge
|
|
9
|
-
* 2. Start localhost callback server
|
|
10
|
-
* 3. Open browser to WorkOS authorization URL
|
|
11
|
-
* 4. Receive authorization code via callback
|
|
12
|
-
* 5. Exchange code + verifier for tokens
|
|
13
|
-
* 6. Store tokens in OS keychain
|
|
14
|
-
*/
|
|
15
1
|
import type { OAuthTokens, ZivisConfig } from "../types.js";
|
|
16
|
-
/**
|
|
17
|
-
* Run the full OAuth PKCE login flow.
|
|
18
|
-
*
|
|
19
|
-
* 1. Generate PKCE pair + state
|
|
20
|
-
* 2. Start localhost callback server
|
|
21
|
-
* 3. Open browser to WorkOS
|
|
22
|
-
* 4. Wait for callback
|
|
23
|
-
* 5. Exchange code for tokens
|
|
24
|
-
* 6. Store tokens in keychain (under session-qualified key)
|
|
25
|
-
*
|
|
26
|
-
* @param config - Server configuration
|
|
27
|
-
* @param session - Optional named session for multi-tenant isolation
|
|
28
|
-
* @param organizationId - Optional WorkOS org ID to scope the login to a specific org
|
|
29
|
-
* @param opts - Display and confirmation options
|
|
30
|
-
* @returns The OAuth tokens
|
|
31
|
-
*/
|
|
32
2
|
export interface LoginOptions {
|
|
33
|
-
/** Human-readable org name, shown in the pre-browser confirmation prompt. */
|
|
34
3
|
orgName?: string;
|
|
35
|
-
|
|
4
|
+
sessionLabel?: string;
|
|
36
5
|
skipConfirm?: boolean;
|
|
6
|
+
printUrlOnly?: boolean;
|
|
7
|
+
incognito?: boolean;
|
|
37
8
|
}
|
|
38
9
|
export declare function login(config?: ZivisConfig, session?: string, organizationId?: string, opts?: LoginOptions): Promise<OAuthTokens>;
|
package/dist/auth/oauth.js
CHANGED
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OAuth PKCE Authentication Flow
|
|
3
|
-
*
|
|
4
|
-
* Authorization Code Flow with Proof Key for Code Exchange (PKCE)
|
|
5
|
-
* for native/CLI applications. Uses WorkOS as the identity provider.
|
|
6
|
-
*
|
|
7
|
-
* Flow:
|
|
8
|
-
* 1. Generate PKCE code_verifier + code_challenge
|
|
9
|
-
* 2. Start localhost callback server
|
|
10
|
-
* 3. Open browser to WorkOS authorization URL
|
|
11
|
-
* 4. Receive authorization code via callback
|
|
12
|
-
* 5. Exchange code + verifier for tokens
|
|
13
|
-
* 6. Store tokens in OS keychain
|
|
14
|
-
*/
|
|
15
1
|
import * as crypto from "node:crypto";
|
|
16
2
|
import * as http from "node:http";
|
|
17
3
|
import * as readline from "node:readline/promises";
|
|
@@ -19,8 +5,8 @@ import { URL } from "node:url";
|
|
|
19
5
|
import open from "open";
|
|
20
6
|
import { keychainStore, getSessionAccount } from "./keychain.js";
|
|
21
7
|
import { decodeJwtPayload } from "./jwt.js";
|
|
22
|
-
import { DEFAULT_CONFIG } from "../types.js";
|
|
23
|
-
const LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
8
|
+
import { DEFAULT_CONFIG, credentialStorageFromConfig } from "../types.js";
|
|
9
|
+
const LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
24
10
|
function generatePKCE() {
|
|
25
11
|
const verifier = crypto.randomBytes(32).toString("base64url");
|
|
26
12
|
const challenge = crypto
|
|
@@ -42,29 +28,15 @@ function buildAuthUrl(config, codeChallenge, state, organizationId) {
|
|
|
42
28
|
state,
|
|
43
29
|
scope: "openid profile email offline_access",
|
|
44
30
|
});
|
|
45
|
-
// Scope the OAuth session to a specific org so the JWT has the correct org_id
|
|
46
31
|
if (organizationId) {
|
|
47
32
|
params.set("organization_id", organizationId);
|
|
48
33
|
}
|
|
49
|
-
// Force the WorkOS login screen even when an active session exists.
|
|
50
|
-
// Why: users in multiple orgs must visually confirm which identity/org they're using —
|
|
51
|
-
// silent SSO could let them accidentally bind the wrong org to a project.
|
|
52
34
|
params.set("prompt", "login");
|
|
53
|
-
|
|
54
|
-
const isAuthKit = config.oauthApiUrl.includes("authkit.app");
|
|
35
|
+
const isAuthKit = !config.oauthApiUrl.includes("workos.com");
|
|
55
36
|
const authPath = isAuthKit ? "/oauth2/authorize" : "/authorize";
|
|
56
37
|
const authUrl = `${config.oauthApiUrl}${authPath}?${params.toString()}`;
|
|
57
|
-
// We used to chain custom AuthKit hosts through /logout?return_to=... so
|
|
58
|
-
// browser cookies did not override organization_id. AuthKit OIDC metadata
|
|
59
|
-
// does not advertise end_session_endpoint, and *.authkit.app/logout returns
|
|
60
|
-
// 404 — so that redirect is unusable. Rely on prompt=login plus
|
|
61
|
-
// organization_id for org-scoped flows instead.
|
|
62
38
|
return authUrl;
|
|
63
39
|
}
|
|
64
|
-
/**
|
|
65
|
-
* Start a localhost HTTP server to receive the OAuth callback.
|
|
66
|
-
* Returns the authorization code on success.
|
|
67
|
-
*/
|
|
68
40
|
function startCallbackServer(port, expectedState) {
|
|
69
41
|
return new Promise((resolve, reject) => {
|
|
70
42
|
const server = http.createServer((req, res) => {
|
|
@@ -74,6 +46,11 @@ function startCallbackServer(port, expectedState) {
|
|
|
74
46
|
const state = parsedUrl.searchParams.get("state");
|
|
75
47
|
const error = parsedUrl.searchParams.get("error");
|
|
76
48
|
const errorDesc = parsedUrl.searchParams.get("error_description");
|
|
49
|
+
if (state !== expectedState) {
|
|
50
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
51
|
+
res.end(callbackHtml(false, "This is a stale authentication tab. You can close it — your login is continuing in the original window."));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
77
54
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
78
55
|
if (error) {
|
|
79
56
|
res.end(callbackHtml(false, errorDesc || error));
|
|
@@ -81,12 +58,6 @@ function startCallbackServer(port, expectedState) {
|
|
|
81
58
|
reject(new Error(errorDesc || error));
|
|
82
59
|
return;
|
|
83
60
|
}
|
|
84
|
-
if (state !== expectedState) {
|
|
85
|
-
res.end(callbackHtml(false, "State mismatch — possible CSRF attack"));
|
|
86
|
-
server.close();
|
|
87
|
-
reject(new Error("State mismatch"));
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
61
|
if (!code) {
|
|
91
62
|
res.end(callbackHtml(false, "No authorization code received"));
|
|
92
63
|
server.close();
|
|
@@ -98,14 +69,13 @@ function startCallbackServer(port, expectedState) {
|
|
|
98
69
|
resolve(code);
|
|
99
70
|
}
|
|
100
71
|
else {
|
|
101
|
-
// 404 for any other path
|
|
102
72
|
res.writeHead(404, { "Content-Type": "text/html" });
|
|
103
73
|
res.end(`<!DOCTYPE html>
|
|
104
74
|
<html>
|
|
105
75
|
<head><title>Not Found</title></head>
|
|
106
76
|
<body style="font-family:system-ui;text-align:center;padding:50px;background:#0f0f1a;color:#fff;">
|
|
107
77
|
<h1>404 - Not Found</h1>
|
|
108
|
-
<p>Expected callback at /callback, got ${parsedUrl.pathname}</p>
|
|
78
|
+
<p>Expected callback at /callback, got ${escapeHtml(parsedUrl.pathname)}</p>
|
|
109
79
|
</body>
|
|
110
80
|
</html>`);
|
|
111
81
|
}
|
|
@@ -114,13 +84,20 @@ function startCallbackServer(port, expectedState) {
|
|
|
114
84
|
reject(new Error(`Failed to start callback server on port ${port}: ${err.message}`));
|
|
115
85
|
});
|
|
116
86
|
server.listen(port, "127.0.0.1");
|
|
117
|
-
// Timeout
|
|
118
87
|
setTimeout(() => {
|
|
119
88
|
server.close();
|
|
120
89
|
reject(new Error("Authentication timed out after 5 minutes"));
|
|
121
90
|
}, LOGIN_TIMEOUT_MS);
|
|
122
91
|
});
|
|
123
92
|
}
|
|
93
|
+
function escapeHtml(s) {
|
|
94
|
+
return s
|
|
95
|
+
.replace(/&/g, "&")
|
|
96
|
+
.replace(/</g, "<")
|
|
97
|
+
.replace(/>/g, ">")
|
|
98
|
+
.replace(/"/g, """)
|
|
99
|
+
.replace(/'/g, "'");
|
|
100
|
+
}
|
|
124
101
|
function callbackHtml(success, errorMessage) {
|
|
125
102
|
if (success) {
|
|
126
103
|
return `<!DOCTYPE html>
|
|
@@ -138,17 +115,13 @@ function callbackHtml(success, errorMessage) {
|
|
|
138
115
|
<head><title>Authentication Failed</title></head>
|
|
139
116
|
<body style="font-family:system-ui;text-align:center;padding:50px;background:#0f0f1a;color:#fff;">
|
|
140
117
|
<h1 style="color:#ff5252;">Authentication Failed</h1>
|
|
141
|
-
<p>${errorMessage || "Unknown error"}</p>
|
|
118
|
+
<p>${escapeHtml(errorMessage || "Unknown error")}</p>
|
|
142
119
|
<p>You can close this window.</p>
|
|
143
120
|
</body>
|
|
144
121
|
</html>`;
|
|
145
122
|
}
|
|
146
|
-
/**
|
|
147
|
-
* Exchange authorization code for tokens via WorkOS token endpoint
|
|
148
|
-
*/
|
|
149
123
|
async function exchangeCodeForTokens(config, code, codeVerifier) {
|
|
150
|
-
|
|
151
|
-
const tokenPath = config.oauthApiUrl.includes("authkit.app") ? "/oauth2/token" : "/oauth/token";
|
|
124
|
+
const tokenPath = config.oauthApiUrl.includes("workos.com") ? "/oauth/token" : "/oauth2/token";
|
|
152
125
|
const tokenUrl = `${config.oauthApiUrl}${tokenPath}`;
|
|
153
126
|
const response = await fetch(tokenUrl, {
|
|
154
127
|
method: "POST",
|
|
@@ -169,6 +142,14 @@ async function exchangeCodeForTokens(config, code, codeVerifier) {
|
|
|
169
142
|
throw new Error(errorMsg);
|
|
170
143
|
}
|
|
171
144
|
const data = (await response.json());
|
|
145
|
+
if (!data.refresh_token) {
|
|
146
|
+
console.error("[zivis] WARNING: WorkOS did not return a refresh_token. " +
|
|
147
|
+
"Verify the OAuth client has Refresh Tokens / offline_access enabled in the WorkOS dashboard. " +
|
|
148
|
+
"Without this, sessions will expire and require manual re-login.");
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
console.error("[zivis] Token exchange complete — refresh_token received, session will auto-renew.");
|
|
152
|
+
}
|
|
172
153
|
return {
|
|
173
154
|
access_token: data.access_token,
|
|
174
155
|
refresh_token: data.refresh_token,
|
|
@@ -183,13 +164,12 @@ export async function login(config = DEFAULT_CONFIG, session, organizationId, op
|
|
|
183
164
|
const { verifier, challenge } = generatePKCE();
|
|
184
165
|
const state = generateState();
|
|
185
166
|
const authUrl = buildAuthUrl(config, challenge, state, organizationId);
|
|
186
|
-
// Pre-browser confirmation: only when an org is scoped AND the terminal is interactive.
|
|
187
|
-
// Protects users in multiple orgs from accidentally authenticating to the wrong tenant.
|
|
188
167
|
if (organizationId && !opts.skipConfirm && process.stderr.isTTY && process.stdin.isTTY) {
|
|
189
168
|
const orgLabel = opts.orgName
|
|
190
169
|
? `${opts.orgName} (${organizationId})`
|
|
191
170
|
: organizationId;
|
|
192
|
-
const
|
|
171
|
+
const displaySession = opts.sessionLabel ?? session;
|
|
172
|
+
const sessionLabel = displaySession ? ` as session "${displaySession}"` : "";
|
|
193
173
|
console.error("");
|
|
194
174
|
console.error(`[zivis] About to authenticate to org: ${orgLabel}${sessionLabel}`);
|
|
195
175
|
const rl = readline.createInterface({
|
|
@@ -209,18 +189,46 @@ export async function login(config = DEFAULT_CONFIG, session, organizationId, op
|
|
|
209
189
|
else if (organizationId) {
|
|
210
190
|
console.error(`[zivis] Login scoped to organization: ${organizationId}`);
|
|
211
191
|
}
|
|
212
|
-
// Start callback server before opening browser
|
|
213
192
|
const codePromise = startCallbackServer(config.callbackPort, state);
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
193
|
+
if (opts.printUrlOnly) {
|
|
194
|
+
console.error("[zivis] Open this URL in a private/incognito browser window:");
|
|
195
|
+
console.error("");
|
|
196
|
+
console.error(` ${authUrl}`);
|
|
197
|
+
console.error("");
|
|
198
|
+
console.error("[zivis] Waiting for login...");
|
|
199
|
+
}
|
|
200
|
+
else if (opts.incognito) {
|
|
201
|
+
let opened = false;
|
|
202
|
+
for (const app of [
|
|
203
|
+
{ name: "google chrome", args: ["--incognito"] },
|
|
204
|
+
{ name: "chromium", args: ["--incognito"] },
|
|
205
|
+
{ name: "firefox", args: ["-private-window"] },
|
|
206
|
+
]) {
|
|
207
|
+
try {
|
|
208
|
+
await open(authUrl, { app: { name: app.name, arguments: [authUrl, ...app.args] } });
|
|
209
|
+
console.error(`[zivis] Opened private window in ${app.name} — waiting for login...`);
|
|
210
|
+
opened = true;
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (!opened) {
|
|
217
|
+
console.error("[zivis] Could not open an incognito window automatically.");
|
|
218
|
+
console.error("[zivis] Paste this URL into a private/incognito browser window:");
|
|
219
|
+
console.error("");
|
|
220
|
+
console.error(` ${authUrl}`);
|
|
221
|
+
console.error("");
|
|
222
|
+
console.error("[zivis] Waiting for login...");
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
console.error("[zivis] Opening browser for authentication...");
|
|
227
|
+
await open(authUrl);
|
|
228
|
+
console.error("[zivis] Waiting for login...");
|
|
229
|
+
}
|
|
218
230
|
const code = await codePromise;
|
|
219
|
-
// Exchange code for tokens
|
|
220
231
|
const tokens = await exchangeCodeForTokens(config, code, verifier);
|
|
221
|
-
// Hard verification: if we requested a specific org, the returned JWT
|
|
222
|
-
// MUST be scoped to that org. Refuse to store mismatched tokens — this is
|
|
223
|
-
// the last-ditch guard against silently binding the wrong tenant.
|
|
224
232
|
if (organizationId) {
|
|
225
233
|
const claims = decodeJwtPayload(tokens.access_token);
|
|
226
234
|
const jwtOrgId = claims?.org_id;
|
|
@@ -234,8 +242,6 @@ export async function login(config = DEFAULT_CONFIG, session, organizationId, op
|
|
|
234
242
|
}
|
|
235
243
|
console.error(`[zivis] \u2713 JWT org_id verified: ${jwtOrgId}`);
|
|
236
244
|
}
|
|
237
|
-
|
|
238
|
-
await keychainStore(getSessionAccount(session), JSON.stringify(tokens));
|
|
245
|
+
await keychainStore(getSessionAccount(session), JSON.stringify(tokens), credentialStorageFromConfig(config));
|
|
239
246
|
return tokens;
|
|
240
247
|
}
|
|
241
|
-
//# sourceMappingURL=oauth.js.map
|
package/dist/auth/sessions.d.ts
CHANGED
|
@@ -1,28 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Session Index Management
|
|
3
|
-
*
|
|
4
|
-
* Tracks named OAuth sessions in a JSON file so users can list
|
|
5
|
-
* and manage multiple tenant sessions without enumerating keychain entries.
|
|
6
|
-
*
|
|
7
|
-
* Session index file: ~/.config/{configDir}/sessions.json
|
|
8
|
-
*/
|
|
9
1
|
import type { SessionIndex, SessionInfo } from "../types.js";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
export declare function
|
|
15
|
-
/**
|
|
16
|
-
* Add or update a session entry in the index.
|
|
17
|
-
*/
|
|
18
|
-
export declare function saveSessionEntry(name: string, email: string): Promise<void>;
|
|
19
|
-
/**
|
|
20
|
-
* Remove a session entry from the index.
|
|
21
|
-
*/
|
|
22
|
-
export declare function removeSessionEntry(name: string): Promise<void>;
|
|
23
|
-
/**
|
|
24
|
-
* List all sessions with their metadata.
|
|
25
|
-
*/
|
|
26
|
-
export declare function listSessions(): Promise<Array<{
|
|
2
|
+
import { type CredentialStorage } from "../types.js";
|
|
3
|
+
export declare function loadSessionIndex(storage?: CredentialStorage): Promise<SessionIndex>;
|
|
4
|
+
export declare function saveSessionEntry(name: string, email: string, storage?: CredentialStorage): Promise<void>;
|
|
5
|
+
export declare function removeSessionEntry(name: string, storage?: CredentialStorage): Promise<void>;
|
|
6
|
+
export declare function listSessions(storage?: CredentialStorage): Promise<Array<{
|
|
27
7
|
name: string;
|
|
28
8
|
} & SessionInfo>>;
|