@embeddable.com/sdk-core
Version:
Core Embeddable SDK module responsible for web-components bundling and publishing.
128 lines (109 loc) • 3.62 kB
text/typescript
import * as fs from "node:fs/promises";
import axios from "axios";
import provideConfig from "./provideConfig";
import { initLogger, logError } from "./logger";
// @ts-ignore
import reportErrorToRollbar from "./rollbar.mjs";
import { CREDENTIALS_DIR, CREDENTIALS_FILE } from "./credentials";
const oraP = import("ora");
const openP = import("open");
export default async () => {
await initLogger("login");
const breadcrumbs: string[] = [];
const ora = (await oraP).default;
const authenticationSpinner = ora("Waiting for code verification...").start();
try {
const open = (await openP).default;
const config = await provideConfig();
breadcrumbs.push("provideConfig");
await resolveFiles();
breadcrumbs.push("resolveFiles");
const deviceCodePayload = {
client_id: config.authClientId,
audience: config.audienceUrl,
};
const deviceCodeResponse = await axios.post(
`https://${config.authDomain}/oauth/device/code`,
deviceCodePayload,
);
ora(
"Confirm this code on your browser: " +
deviceCodeResponse.data["user_code"],
).info();
const tokenPayload = {
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
device_code: deviceCodeResponse.data["device_code"],
client_id: config.authClientId,
};
await open(deviceCodeResponse.data["verification_uri_complete"]);
let isTerminated = false;
const signalHandler = () => {
isTerminated = true;
authenticationSpinner.fail("Authentication process interrupted");
process.exit(0);
};
process.on("SIGTERM", signalHandler);
process.on("SIGINT", signalHandler);
/**
* This is a recommended way to poll, since it take some time for a user to enter a `user_code` in a browser.
* deviceCodeResponse.data['interval'] is a recommended/calculated polling interval specified in seconds.
*/
while (!isTerminated) {
try {
const tokenResponse = await axios.post(
`https://${config.authDomain}/oauth/token`,
tokenPayload,
);
await fs.writeFile(
CREDENTIALS_FILE,
JSON.stringify(tokenResponse.data),
);
authenticationSpinner.succeed(
"You are successfully authenticated now!",
);
break;
} catch (e: any) {
if (e.response.data?.error !== "authorization_pending") {
authenticationSpinner.fail(
"Authentication failed. Please try again.",
);
process.exit(1);
}
await sleep(deviceCodeResponse.data["interval"] * 1000);
}
}
// Clean up signal handlers
process.off("SIGTERM", signalHandler);
process.off("SIGINT", signalHandler);
} catch (error: unknown) {
authenticationSpinner.fail("Authentication failed. Please try again.");
await logError({ command: "login", breadcrumbs, error });
await reportErrorToRollbar(error);
console.log(error);
process.exit(1);
}
};
export async function getToken() {
try {
const rawCredentials = await fs.readFile(CREDENTIALS_FILE, "utf-8");
const credentials = JSON.parse(rawCredentials.toString());
return credentials?.access_token ?? "";
} catch (_e) {
return "";
}
}
function sleep(ms: number) {
return new Promise((res) => setTimeout(res, ms));
}
export async function resolveFiles() {
try {
await fs.access(CREDENTIALS_DIR);
} catch (_e) {
await fs.mkdir(CREDENTIALS_DIR);
}
try {
await fs.access(CREDENTIALS_FILE);
} catch (e) {
await fs.writeFile(CREDENTIALS_FILE, "");
}
}