@attest-it/cli 0.6.1 → 0.7.0
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/bin/attest-it.js +197 -4
- package/dist/bin/attest-it.js.map +1 -1
- package/dist/index.cjs +196 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +197 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1665,17 +1665,28 @@ function getKeyRefFromIdentity(identity) {
|
|
|
1665
1665
|
}
|
|
1666
1666
|
}
|
|
1667
1667
|
function KeygenInteractive(props) {
|
|
1668
|
-
const { onComplete, onError } = props;
|
|
1668
|
+
const { onComplete, onCancel, onError } = props;
|
|
1669
1669
|
const [step, setStep] = React7.useState("checking-providers");
|
|
1670
1670
|
const [opAvailable, setOpAvailable] = React7.useState(false);
|
|
1671
1671
|
const [keychainAvailable, setKeychainAvailable] = React7.useState(false);
|
|
1672
|
+
const [yubiKeyAvailable, setYubiKeyAvailable] = React7.useState(false);
|
|
1672
1673
|
const [accounts, setAccounts] = React7.useState([]);
|
|
1673
1674
|
const [vaults, setVaults] = React7.useState([]);
|
|
1675
|
+
const [yubiKeyDevices, setYubiKeyDevices] = React7.useState([]);
|
|
1674
1676
|
const [_selectedProvider, setSelectedProvider] = React7.useState();
|
|
1675
1677
|
const [selectedAccount, setSelectedAccount] = React7.useState();
|
|
1676
1678
|
const [selectedVault, setSelectedVault] = React7.useState();
|
|
1677
1679
|
const [itemName, setItemName] = React7.useState("attest-it-private-key");
|
|
1678
1680
|
const [keychainItemName, setKeychainItemName] = React7.useState("attest-it-private-key");
|
|
1681
|
+
const [selectedYubiKeySerial, setSelectedYubiKeySerial] = React7.useState();
|
|
1682
|
+
const [selectedYubiKeySlot, setSelectedYubiKeySlot] = React7.useState(2);
|
|
1683
|
+
const [slot1Configured, setSlot1Configured] = React7.useState(false);
|
|
1684
|
+
const [slot2Configured, setSlot2Configured] = React7.useState(false);
|
|
1685
|
+
ink.useInput((_input, key) => {
|
|
1686
|
+
if (key.escape) {
|
|
1687
|
+
onCancel();
|
|
1688
|
+
}
|
|
1689
|
+
});
|
|
1679
1690
|
React7.useEffect(() => {
|
|
1680
1691
|
const checkProviders = async () => {
|
|
1681
1692
|
try {
|
|
@@ -1690,6 +1701,19 @@ function KeygenInteractive(props) {
|
|
|
1690
1701
|
}
|
|
1691
1702
|
const isKeychainAvailable = core.MacOSKeychainKeyProvider.isAvailable();
|
|
1692
1703
|
setKeychainAvailable(isKeychainAvailable);
|
|
1704
|
+
try {
|
|
1705
|
+
const isInstalled = await core.YubiKeyProvider.isInstalled();
|
|
1706
|
+
if (isInstalled) {
|
|
1707
|
+
const isConnected = await core.YubiKeyProvider.isConnected();
|
|
1708
|
+
if (isConnected) {
|
|
1709
|
+
const devices = await core.YubiKeyProvider.listDevices();
|
|
1710
|
+
setYubiKeyDevices(devices);
|
|
1711
|
+
setYubiKeyAvailable(devices.length > 0);
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
} catch {
|
|
1715
|
+
setYubiKeyAvailable(false);
|
|
1716
|
+
}
|
|
1693
1717
|
setStep("select-provider");
|
|
1694
1718
|
};
|
|
1695
1719
|
void checkProviders();
|
|
@@ -1707,6 +1731,27 @@ function KeygenInteractive(props) {
|
|
|
1707
1731
|
void fetchVaults();
|
|
1708
1732
|
}
|
|
1709
1733
|
}, [step, selectedAccount, onError]);
|
|
1734
|
+
const checkYubiKeySlots = async (serial) => {
|
|
1735
|
+
try {
|
|
1736
|
+
const slot1 = await core.YubiKeyProvider.isChallengeResponseConfigured(1, serial);
|
|
1737
|
+
const slot2 = await core.YubiKeyProvider.isChallengeResponseConfigured(2, serial);
|
|
1738
|
+
setSlot1Configured(slot1);
|
|
1739
|
+
setSlot2Configured(slot2);
|
|
1740
|
+
if (slot1 && slot2) {
|
|
1741
|
+
setStep("select-yubikey-slot");
|
|
1742
|
+
} else if (slot2) {
|
|
1743
|
+
setSelectedYubiKeySlot(2);
|
|
1744
|
+
void generateKeys("yubikey");
|
|
1745
|
+
} else if (slot1) {
|
|
1746
|
+
setSelectedYubiKeySlot(1);
|
|
1747
|
+
void generateKeys("yubikey");
|
|
1748
|
+
} else {
|
|
1749
|
+
setStep("yubikey-offer-setup");
|
|
1750
|
+
}
|
|
1751
|
+
} catch (err) {
|
|
1752
|
+
onError(err instanceof Error ? err : new Error("Failed to check YubiKey slots"));
|
|
1753
|
+
}
|
|
1754
|
+
};
|
|
1710
1755
|
const handleProviderSelect = (value) => {
|
|
1711
1756
|
if (value === "filesystem") {
|
|
1712
1757
|
setSelectedProvider("filesystem");
|
|
@@ -1722,6 +1767,14 @@ function KeygenInteractive(props) {
|
|
|
1722
1767
|
} else if (value === "macos-keychain") {
|
|
1723
1768
|
setSelectedProvider("macos-keychain");
|
|
1724
1769
|
setStep("enter-keychain-item-name");
|
|
1770
|
+
} else if (value === "yubikey") {
|
|
1771
|
+
setSelectedProvider("yubikey");
|
|
1772
|
+
if (yubiKeyDevices.length > 1) {
|
|
1773
|
+
setStep("select-yubikey-device");
|
|
1774
|
+
} else if (yubiKeyDevices.length === 1 && yubiKeyDevices[0]) {
|
|
1775
|
+
setSelectedYubiKeySerial(yubiKeyDevices[0].serial);
|
|
1776
|
+
void checkYubiKeySlots(yubiKeyDevices[0].serial);
|
|
1777
|
+
}
|
|
1725
1778
|
}
|
|
1726
1779
|
};
|
|
1727
1780
|
const handleAccountSelect = (value) => {
|
|
@@ -1740,6 +1793,47 @@ function KeygenInteractive(props) {
|
|
|
1740
1793
|
setKeychainItemName(value);
|
|
1741
1794
|
void generateKeys("macos-keychain");
|
|
1742
1795
|
};
|
|
1796
|
+
const handleYubiKeyDeviceSelect = (value) => {
|
|
1797
|
+
setSelectedYubiKeySerial(value);
|
|
1798
|
+
void checkYubiKeySlots(value);
|
|
1799
|
+
};
|
|
1800
|
+
const handleYubiKeySlotSelect = (value) => {
|
|
1801
|
+
const slot = value === "1" ? 1 : 2;
|
|
1802
|
+
setSelectedYubiKeySlot(slot);
|
|
1803
|
+
void generateKeys("yubikey");
|
|
1804
|
+
};
|
|
1805
|
+
const handleYubiKeySetupConfirm = (value) => {
|
|
1806
|
+
if (value === "yes") {
|
|
1807
|
+
void setupYubiKeySlot();
|
|
1808
|
+
} else {
|
|
1809
|
+
onError(new Error("YubiKey setup cancelled"));
|
|
1810
|
+
}
|
|
1811
|
+
};
|
|
1812
|
+
const setupYubiKeySlot = async () => {
|
|
1813
|
+
setStep("yubikey-configuring");
|
|
1814
|
+
try {
|
|
1815
|
+
const { spawn: spawn3 } = await import('child_process');
|
|
1816
|
+
const args = ["otp", "chalresp", "--touch", "--generate", "2"];
|
|
1817
|
+
if (selectedYubiKeySerial) {
|
|
1818
|
+
args.unshift("--device", selectedYubiKeySerial);
|
|
1819
|
+
}
|
|
1820
|
+
await new Promise((resolve2, reject) => {
|
|
1821
|
+
const proc = spawn3("ykman", args, { stdio: "inherit" });
|
|
1822
|
+
proc.on("close", (code) => {
|
|
1823
|
+
if (code === 0) {
|
|
1824
|
+
resolve2();
|
|
1825
|
+
} else {
|
|
1826
|
+
reject(new Error(`ykman exited with code ${String(code)}`));
|
|
1827
|
+
}
|
|
1828
|
+
});
|
|
1829
|
+
proc.on("error", reject);
|
|
1830
|
+
});
|
|
1831
|
+
setSelectedYubiKeySlot(2);
|
|
1832
|
+
void generateKeys("yubikey");
|
|
1833
|
+
} catch (err) {
|
|
1834
|
+
onError(err instanceof Error ? err : new Error("Failed to configure YubiKey"));
|
|
1835
|
+
}
|
|
1836
|
+
};
|
|
1743
1837
|
const generateKeys = async (provider) => {
|
|
1744
1838
|
setStep("generating");
|
|
1745
1839
|
try {
|
|
@@ -1790,7 +1884,7 @@ function KeygenInteractive(props) {
|
|
|
1790
1884
|
completionResult.account = selectedAccount;
|
|
1791
1885
|
}
|
|
1792
1886
|
onComplete(completionResult);
|
|
1793
|
-
} else {
|
|
1887
|
+
} else if (provider === "macos-keychain") {
|
|
1794
1888
|
if (!keychainItemName) {
|
|
1795
1889
|
throw new Error("Item name is required for macOS Keychain");
|
|
1796
1890
|
}
|
|
@@ -1809,6 +1903,33 @@ function KeygenInteractive(props) {
|
|
|
1809
1903
|
storageDescription: result.storageDescription,
|
|
1810
1904
|
itemName: keychainItemName
|
|
1811
1905
|
});
|
|
1906
|
+
} else {
|
|
1907
|
+
const encryptedKeyPath = core.getDefaultYubiKeyEncryptedKeyPath();
|
|
1908
|
+
const providerOptions = {
|
|
1909
|
+
encryptedKeyPath,
|
|
1910
|
+
slot: selectedYubiKeySlot
|
|
1911
|
+
};
|
|
1912
|
+
if (selectedYubiKeySerial !== void 0) {
|
|
1913
|
+
providerOptions.serial = selectedYubiKeySerial;
|
|
1914
|
+
}
|
|
1915
|
+
const ykProvider = new core.YubiKeyProvider(providerOptions);
|
|
1916
|
+
const genOptions = { publicKeyPath };
|
|
1917
|
+
if (props.force !== void 0) {
|
|
1918
|
+
genOptions.force = props.force;
|
|
1919
|
+
}
|
|
1920
|
+
const result = await ykProvider.generateKeyPair(genOptions);
|
|
1921
|
+
const completionResult = {
|
|
1922
|
+
provider: "yubikey",
|
|
1923
|
+
publicKeyPath: result.publicKeyPath,
|
|
1924
|
+
privateKeyRef: result.privateKeyRef,
|
|
1925
|
+
storageDescription: result.storageDescription,
|
|
1926
|
+
slot: selectedYubiKeySlot,
|
|
1927
|
+
encryptedKeyPath
|
|
1928
|
+
};
|
|
1929
|
+
if (selectedYubiKeySerial !== void 0) {
|
|
1930
|
+
completionResult.serial = selectedYubiKeySerial;
|
|
1931
|
+
}
|
|
1932
|
+
onComplete(completionResult);
|
|
1812
1933
|
}
|
|
1813
1934
|
setStep("done");
|
|
1814
1935
|
} catch (err) {
|
|
@@ -1834,6 +1955,12 @@ function KeygenInteractive(props) {
|
|
|
1834
1955
|
value: "macos-keychain"
|
|
1835
1956
|
});
|
|
1836
1957
|
}
|
|
1958
|
+
if (yubiKeyAvailable) {
|
|
1959
|
+
options.push({
|
|
1960
|
+
label: "YubiKey (hardware security key)",
|
|
1961
|
+
value: "yubikey"
|
|
1962
|
+
});
|
|
1963
|
+
}
|
|
1837
1964
|
if (opAvailable) {
|
|
1838
1965
|
options.push({
|
|
1839
1966
|
label: "1Password (requires op CLI)",
|
|
@@ -1848,7 +1975,7 @@ function KeygenInteractive(props) {
|
|
|
1848
1975
|
}
|
|
1849
1976
|
if (step === "select-account") {
|
|
1850
1977
|
const options = accounts.map((account) => ({
|
|
1851
|
-
label: account.email,
|
|
1978
|
+
label: account.name ? `${account.name} (${account.email})` : account.email,
|
|
1852
1979
|
value: account.user_uuid
|
|
1853
1980
|
}));
|
|
1854
1981
|
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", children: [
|
|
@@ -1890,6 +2017,65 @@ function KeygenInteractive(props) {
|
|
|
1890
2017
|
/* @__PURE__ */ jsxRuntime.jsx(ui.TextInput, { defaultValue: keychainItemName, onSubmit: handleKeychainItemNameSubmit })
|
|
1891
2018
|
] });
|
|
1892
2019
|
}
|
|
2020
|
+
if (step === "select-yubikey-device") {
|
|
2021
|
+
const options = yubiKeyDevices.map((device) => ({
|
|
2022
|
+
label: `${device.type} (Serial: ${device.serial})`,
|
|
2023
|
+
value: device.serial
|
|
2024
|
+
}));
|
|
2025
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", children: [
|
|
2026
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Select YubiKey device:" }),
|
|
2027
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "" }),
|
|
2028
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select, { options, onChange: handleYubiKeyDeviceSelect })
|
|
2029
|
+
] });
|
|
2030
|
+
}
|
|
2031
|
+
if (step === "select-yubikey-slot") {
|
|
2032
|
+
const options = [];
|
|
2033
|
+
if (slot2Configured) {
|
|
2034
|
+
options.push({
|
|
2035
|
+
label: "Slot 2 (recommended)",
|
|
2036
|
+
value: "2"
|
|
2037
|
+
});
|
|
2038
|
+
}
|
|
2039
|
+
if (slot1Configured) {
|
|
2040
|
+
options.push({
|
|
2041
|
+
label: "Slot 1",
|
|
2042
|
+
value: "1"
|
|
2043
|
+
});
|
|
2044
|
+
}
|
|
2045
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", children: [
|
|
2046
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Select YubiKey slot for challenge-response:" }),
|
|
2047
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "" }),
|
|
2048
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select, { options, onChange: handleYubiKeySlotSelect })
|
|
2049
|
+
] });
|
|
2050
|
+
}
|
|
2051
|
+
if (step === "yubikey-offer-setup") {
|
|
2052
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", children: [
|
|
2053
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { bold: true, children: "Your YubiKey is not configured for challenge-response." }),
|
|
2054
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "" }),
|
|
2055
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: "Would you like to configure slot 2 for challenge-response now?" }),
|
|
2056
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "This will enable touch-to-sign functionality." }),
|
|
2057
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "" }),
|
|
2058
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2059
|
+
ui.Select,
|
|
2060
|
+
{
|
|
2061
|
+
options: [
|
|
2062
|
+
{ label: "Yes, configure my YubiKey", value: "yes" },
|
|
2063
|
+
{ label: "No, cancel", value: "no" }
|
|
2064
|
+
],
|
|
2065
|
+
onChange: handleYubiKeySetupConfirm
|
|
2066
|
+
}
|
|
2067
|
+
)
|
|
2068
|
+
] });
|
|
2069
|
+
}
|
|
2070
|
+
if (step === "yubikey-configuring") {
|
|
2071
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "column", children: [
|
|
2072
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "row", gap: 1, children: [
|
|
2073
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, {}),
|
|
2074
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { children: "Configuring YubiKey slot 2 for challenge-response..." })
|
|
2075
|
+
] }),
|
|
2076
|
+
/* @__PURE__ */ jsxRuntime.jsx(ink.Text, { dimColor: true, children: "Touch your YubiKey when it flashes." })
|
|
2077
|
+
] });
|
|
2078
|
+
}
|
|
1893
2079
|
if (step === "generating") {
|
|
1894
2080
|
return /* @__PURE__ */ jsxRuntime.jsx(ink.Box, { flexDirection: "column", children: /* @__PURE__ */ jsxRuntime.jsxs(ink.Box, { flexDirection: "row", gap: 1, children: [
|
|
1895
2081
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, {}),
|
|
@@ -2974,6 +3160,7 @@ async function runCreate() {
|
|
|
2974
3160
|
};
|
|
2975
3161
|
}
|
|
2976
3162
|
await core.saveLocalConfig(newConfig);
|
|
3163
|
+
const publicKeyResult = await core.savePublicKey(slug, keyPair.publicKey);
|
|
2977
3164
|
log("");
|
|
2978
3165
|
success("Identity created successfully");
|
|
2979
3166
|
log("");
|
|
@@ -2988,6 +3175,12 @@ async function runCreate() {
|
|
|
2988
3175
|
log(` Public Key: ${keyPair.publicKey.slice(0, 32)}...`);
|
|
2989
3176
|
log(` Private Key: ${keyStorageDescription}`);
|
|
2990
3177
|
log("");
|
|
3178
|
+
log(theme3.blue.bold()("Public key saved to:"));
|
|
3179
|
+
log(` ${publicKeyResult.homePath}`);
|
|
3180
|
+
if (publicKeyResult.projectPath) {
|
|
3181
|
+
log(` ${publicKeyResult.projectPath}`);
|
|
3182
|
+
}
|
|
3183
|
+
log("");
|
|
2991
3184
|
if (!existingConfig) {
|
|
2992
3185
|
success(`Set as active identity`);
|
|
2993
3186
|
log("");
|