UNPKG

@curvenote/cli

Version:
121 lines (120 loc) 4.95 kB
import fs from 'node:fs'; import path from 'node:path'; import jwt from 'jsonwebtoken'; import { chalkLogger, LogLevel } from 'myst-cli-utils'; import { getConfigPath } from './utils/index.js'; export function decodeTokenAndCheckExpiry(token, log, throwErrors = true, kindForExpiryCheck = 'session') { const rawDecoded = jwt.decode(token); if (!rawDecoded || typeof rawDecoded === 'string') throw new Error('Could not decode session token. Please ensure that the API token is valid.'); const decoded = rawDecoded; const timeLeft = decoded.exp * 1000 - Date.now(); if (!decoded.ignoreExpiration && timeLeft < 0) { if (throwErrors) { throw new Error('The API token has expired. You can remove your token using: `curvenote token remove`'); } return { decoded, expired: true }; } if (!decoded.ignoreExpiration) { if (kindForExpiryCheck === 'session' && timeLeft < 30 * 1000) { if (throwErrors) log.warn(`The token has less than 30 seconds remaining`); return { decoded, expired: 'soon' }; } if (kindForExpiryCheck === 'user' && timeLeft < 24 * 60 * 60 * 1000) { if (throwErrors) log.warn(`The token has less than 1 day remaining`); return { decoded, expired: 'soon' }; } } return { decoded, expired: false }; } export function validateSessionToken(token, log) { const { decoded } = decodeTokenAndCheckExpiry(token, log); const { aud, cfg, iss } = decoded; if (typeof aud !== 'string') throw new Error('Expected an audience on the token (string).'); if (!(iss === null || iss === void 0 ? void 0 : iss.endsWith('tokens/session'))) throw new Error('Expected a session token.'); if (typeof cfg === 'string') log.debug(`SessionToken contains a "cfg" claim, reading configuration from api at ${cfg}.`); return { token, decoded }; } /** * Return `current` token and `saved` available tokens * * Curvenote tokens can come from 2 places: * - CURVENOTE_TOKEN environment variable * - Curvenote config file saved to your system * * The curvenote config may have a list of available tokens and a current token; * this function will return the available tokens as `saved` and the `current` token. * * If CURVENOTE_TOKEN environment variable is found, it will be returned as `current`, * taking priority over any current token in your config file. The field `environment` * will be set to `true`, indicating current came from the environment variable. */ export function getTokens(log = chalkLogger(LogLevel.info, process.cwd())) { const env = process.env.CURVENOTE_TOKEN; if (env) { log.warn('Using the CURVENOTE_TOKEN env variable.'); } const configPath = getConfigPath(); let config; if (fs.existsSync(configPath)) { try { config = JSON.parse(fs.readFileSync(configPath).toString()); } catch (error) { log.debug(`\n\n${error === null || error === void 0 ? void 0 : error.stack}\n\n`); if (env) { log.error('Could not read settings file; continuing with CURVENOTE_TOKEN env variable'); } else { throw new Error('Could not read settings file to get Curvenote token'); } } } return { saved: config === null || config === void 0 ? void 0 : config.tokens, current: env !== null && env !== void 0 ? env : config === null || config === void 0 ? void 0 : config.token, environment: !!env, }; } /** * Write token config data to file */ export function writeConfigFile(data) { const configPath = getConfigPath(); if (!fs.existsSync(configPath)) { fs.mkdirSync(path.dirname(configPath), { recursive: true }); } fs.writeFileSync(configPath, JSON.stringify(data)); } /** * Replace current token in saved config file */ export function updateCurrentTokenConfig(log, token) { const { saved } = getTokens(); writeConfigFile({ tokens: saved, token }); } export function summarizeAsString({ note, username, email, api }) { return `"${username}" <${email}> at ${api}${note ? ` "${note}"` : ''}`; } export function getCurrentTokenRecord(tokens) { var _a, _b; const data = tokens !== null && tokens !== void 0 ? tokens : getTokens(); if (!data.current) return; if (data.environment) { const { decoded } = decodeTokenAndCheckExpiry(data.current, chalkLogger(LogLevel.info)); return { token: data.current, api: decoded.aud, email: decoded.email, username: (_a = decoded.name) !== null && _a !== void 0 ? _a : decoded.user_id, note: 'From environment variable', }; } return (_b = data.saved) === null || _b === void 0 ? void 0 : _b.find(({ token }) => token === data.current); }