estatehelm 1.0.4 → 1.0.6
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/index.js +25 -116
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1454,25 +1454,21 @@ function prompt(question) {
|
|
|
1454
1454
|
});
|
|
1455
1455
|
});
|
|
1456
1456
|
}
|
|
1457
|
-
var ALLOWED_CALLBACK_PORTS = [11033, 11034, 11035];
|
|
1458
1457
|
async function findAvailablePort() {
|
|
1459
|
-
|
|
1460
|
-
const available = await isPortAvailable(port);
|
|
1461
|
-
if (available) {
|
|
1462
|
-
return port;
|
|
1463
|
-
}
|
|
1464
|
-
}
|
|
1465
|
-
throw new Error(
|
|
1466
|
-
`No available ports for CLI callback. Ports ${ALLOWED_CALLBACK_PORTS.join(", ")} are all in use.`
|
|
1467
|
-
);
|
|
1468
|
-
}
|
|
1469
|
-
function isPortAvailable(port) {
|
|
1470
|
-
return new Promise((resolve) => {
|
|
1458
|
+
return new Promise((resolve, reject) => {
|
|
1471
1459
|
const server = http.createServer();
|
|
1472
|
-
server.listen(
|
|
1473
|
-
server.
|
|
1460
|
+
server.listen(0, "127.0.0.1", () => {
|
|
1461
|
+
const address = server.address();
|
|
1462
|
+
const port = typeof address === "object" && address ? address.port : 0;
|
|
1463
|
+
server.close(() => {
|
|
1464
|
+
if (port > 0) {
|
|
1465
|
+
resolve(port);
|
|
1466
|
+
} else {
|
|
1467
|
+
reject(new Error("Failed to find available port"));
|
|
1468
|
+
}
|
|
1469
|
+
});
|
|
1474
1470
|
});
|
|
1475
|
-
server.on("error",
|
|
1471
|
+
server.on("error", reject);
|
|
1476
1472
|
});
|
|
1477
1473
|
}
|
|
1478
1474
|
function createApiClient(token) {
|
|
@@ -1544,85 +1540,11 @@ async function decryptDeviceCredentials(encryptedPayload) {
|
|
|
1544
1540
|
);
|
|
1545
1541
|
return new Uint8Array(plaintext);
|
|
1546
1542
|
}
|
|
1547
|
-
function
|
|
1548
|
-
const verifierBytes = crypto.getRandomValues(new Uint8Array(32));
|
|
1549
|
-
const verifier = base64Encode(verifierBytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
1550
|
-
const encoder = new TextEncoder();
|
|
1551
|
-
const data = encoder.encode(verifier);
|
|
1552
|
-
const hashBuffer = crypto.subtle.digestSync ? crypto.subtle.digestSync("SHA-256", data) : null;
|
|
1553
|
-
const cryptoNode = require("crypto");
|
|
1554
|
-
const hash = cryptoNode.createHash("sha256").update(verifier).digest();
|
|
1555
|
-
const challenge = hash.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
1556
|
-
return { verifier, challenge };
|
|
1557
|
-
}
|
|
1558
|
-
var GOOGLE_CLIENT_ID = "51644152299-ikanidsebtsn6sukgk0sg1hq5h95k2h6.apps.googleusercontent.com";
|
|
1559
|
-
async function createNativeLoginFlow() {
|
|
1560
|
-
const response = await fetch(`${KRATOS_URL}/self-service/login/api`, {
|
|
1561
|
-
method: "GET",
|
|
1562
|
-
headers: { Accept: "application/json" }
|
|
1563
|
-
});
|
|
1564
|
-
if (!response.ok) {
|
|
1565
|
-
const error = await response.text();
|
|
1566
|
-
throw new Error(`Failed to create login flow: ${response.status} - ${error}`);
|
|
1567
|
-
}
|
|
1568
|
-
const flow = await response.json();
|
|
1569
|
-
return flow.id;
|
|
1570
|
-
}
|
|
1571
|
-
async function submitIdTokenToKratos(flowId, idToken, provider = "google") {
|
|
1572
|
-
const response = await fetch(`${KRATOS_URL}/self-service/login?flow=${flowId}`, {
|
|
1573
|
-
method: "POST",
|
|
1574
|
-
headers: {
|
|
1575
|
-
"Content-Type": "application/json",
|
|
1576
|
-
Accept: "application/json"
|
|
1577
|
-
},
|
|
1578
|
-
body: JSON.stringify({
|
|
1579
|
-
method: "oidc",
|
|
1580
|
-
provider,
|
|
1581
|
-
id_token: idToken
|
|
1582
|
-
})
|
|
1583
|
-
});
|
|
1584
|
-
if (!response.ok) {
|
|
1585
|
-
const error = await response.json().catch(() => ({}));
|
|
1586
|
-
throw new Error(
|
|
1587
|
-
error.error?.message || error.ui?.messages?.[0]?.text || `Kratos login failed: ${response.status}`
|
|
1588
|
-
);
|
|
1589
|
-
}
|
|
1590
|
-
const result = await response.json();
|
|
1591
|
-
if (!result.session_token) {
|
|
1592
|
-
throw new Error("No session_token in Kratos response");
|
|
1593
|
-
}
|
|
1594
|
-
return result.session_token;
|
|
1595
|
-
}
|
|
1596
|
-
async function exchangeCodeForTokens(code, codeVerifier, redirectUri) {
|
|
1597
|
-
const response = await fetch("https://oauth2.googleapis.com/token", {
|
|
1598
|
-
method: "POST",
|
|
1599
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1600
|
-
body: new URLSearchParams({
|
|
1601
|
-
client_id: GOOGLE_CLIENT_ID,
|
|
1602
|
-
code,
|
|
1603
|
-
code_verifier: codeVerifier,
|
|
1604
|
-
grant_type: "authorization_code",
|
|
1605
|
-
redirect_uri: redirectUri
|
|
1606
|
-
})
|
|
1607
|
-
});
|
|
1608
|
-
if (!response.ok) {
|
|
1609
|
-
const error = await response.json().catch(() => ({}));
|
|
1610
|
-
throw new Error(error.error_description || error.error || "Token exchange failed");
|
|
1611
|
-
}
|
|
1612
|
-
const tokens = await response.json();
|
|
1613
|
-
if (!tokens.id_token) {
|
|
1614
|
-
throw new Error("No id_token in Google response");
|
|
1615
|
-
}
|
|
1616
|
-
return {
|
|
1617
|
-
idToken: tokens.id_token,
|
|
1618
|
-
accessToken: tokens.access_token
|
|
1619
|
-
};
|
|
1620
|
-
}
|
|
1621
|
-
function waitForOAuthCallback(port) {
|
|
1543
|
+
function waitForCallback(port) {
|
|
1622
1544
|
return new Promise((resolve, reject) => {
|
|
1623
1545
|
const server = http.createServer((req, res) => {
|
|
1624
1546
|
const url = new URL(req.url || "/", `http://127.0.0.1:${port}`);
|
|
1625
|
-
const
|
|
1547
|
+
const sessionToken = url.searchParams.get("session_token");
|
|
1626
1548
|
const error = url.searchParams.get("error");
|
|
1627
1549
|
if (error) {
|
|
1628
1550
|
res.writeHead(400, { "Content-Type": "text/html" });
|
|
@@ -1638,7 +1560,7 @@ function waitForOAuthCallback(port) {
|
|
|
1638
1560
|
reject(new Error(url.searchParams.get("error_description") || error));
|
|
1639
1561
|
return;
|
|
1640
1562
|
}
|
|
1641
|
-
if (
|
|
1563
|
+
if (sessionToken) {
|
|
1642
1564
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
1643
1565
|
res.end(`
|
|
1644
1566
|
<!DOCTYPE html>
|
|
@@ -1649,7 +1571,7 @@ function waitForOAuthCallback(port) {
|
|
|
1649
1571
|
</html>
|
|
1650
1572
|
`);
|
|
1651
1573
|
server.close();
|
|
1652
|
-
resolve(
|
|
1574
|
+
resolve(sessionToken);
|
|
1653
1575
|
return;
|
|
1654
1576
|
}
|
|
1655
1577
|
res.writeHead(404);
|
|
@@ -1666,35 +1588,22 @@ function waitForOAuthCallback(port) {
|
|
|
1666
1588
|
async function login() {
|
|
1667
1589
|
console.log("\nEstateHelm Login");
|
|
1668
1590
|
console.log("================\n");
|
|
1669
|
-
console.log("
|
|
1670
|
-
const flowId = await createNativeLoginFlow();
|
|
1591
|
+
console.log("Starting authentication server...");
|
|
1671
1592
|
const port = await findAvailablePort();
|
|
1672
|
-
const
|
|
1673
|
-
const
|
|
1674
|
-
|
|
1675
|
-
authUrl.searchParams.set("client_id", GOOGLE_CLIENT_ID);
|
|
1676
|
-
authUrl.searchParams.set("redirect_uri", redirectUri);
|
|
1677
|
-
authUrl.searchParams.set("response_type", "code");
|
|
1678
|
-
authUrl.searchParams.set("scope", "openid email profile");
|
|
1679
|
-
authUrl.searchParams.set("code_challenge", challenge);
|
|
1680
|
-
authUrl.searchParams.set("code_challenge_method", "S256");
|
|
1681
|
-
authUrl.searchParams.set("access_type", "offline");
|
|
1682
|
-
console.log("\nOpening browser for Google authentication...");
|
|
1593
|
+
const callbackUrl = `http://127.0.0.1:${port}/callback`;
|
|
1594
|
+
const loginUrl = `${APP_URL}/cli-auth?callback=${encodeURIComponent(callbackUrl)}`;
|
|
1595
|
+
console.log("\nOpening browser for authentication...");
|
|
1683
1596
|
console.log(`If the browser doesn't open, visit:
|
|
1684
|
-
${
|
|
1597
|
+
${loginUrl}
|
|
1685
1598
|
`);
|
|
1686
|
-
const
|
|
1687
|
-
await (0, import_open.default)(
|
|
1599
|
+
const tokenPromise = waitForCallback(port);
|
|
1600
|
+
await (0, import_open.default)(loginUrl);
|
|
1688
1601
|
console.log("Waiting for authentication...");
|
|
1689
|
-
const
|
|
1690
|
-
console.log("Exchanging authorization code...");
|
|
1691
|
-
const { idToken } = await exchangeCodeForTokens(code, verifier, redirectUri);
|
|
1692
|
-
console.log("Completing authentication...");
|
|
1693
|
-
const sessionToken = await submitIdTokenToKratos(flowId, idToken, "google");
|
|
1602
|
+
const sessionToken = await tokenPromise;
|
|
1694
1603
|
console.log("Authentication successful!");
|
|
1695
1604
|
console.log(`Token: ${sanitizeToken(sessionToken)}`);
|
|
1696
1605
|
await saveBearerToken(sessionToken);
|
|
1697
|
-
await saveRefreshToken("");
|
|
1606
|
+
await saveRefreshToken("kratos-session-no-refresh");
|
|
1698
1607
|
const client = createApiClient(sessionToken);
|
|
1699
1608
|
console.log("\nFetching encryption keys...");
|
|
1700
1609
|
const serverWrapSecret = await getServerWrapSecret(client);
|