UNPKG

replit-identity

Version:

A simple package to interact with the Replit CLI's identity command.

126 lines (114 loc) 4.48 kB
const { exec } = require('child_process'); const util = require('node:util'); const execAsync = util.promisify(exec); const { REPLIT_CLI, REPLIT_DEPLOYMENT, REPL_IDENTITY } = process.env; if (!REPLIT_CLI || !REPL_IDENTITY) { const deployment = REPLIT_DEPLOYMENT === '1'; if (deployment) { throw 'Replit Identity is not yet available in deployments.'; } else { throw 'Replit Identity is not available in this environment.'; } } /** * @typedef {Object} Runtime * @property {Object} [interactive] * This is set if the Repl is running interactively and not when the Repl is running in hosting. * @property {string} interactive.cluster - The cluster in which this Repl is running. * @property {string} interactive.subcluster - The subcluster in which this Repl is running. * @property {Object} [hosting] * This is set if the Repl is running in a hosting subcluster. * @property {string} hosting.cluster - The cluster in which this Repl is running. * @property {string} hosting.subcluster - The subcluster in which this Repl is running. * @property {boolean} [deployment] * This is set if the Repl is running in a Deployment. */ /** * @typedef {Object} Info * @property {string} username - The name of the user who created the token. * @property {string} slug - The slug of the repl where the token was created. * @property {number} userId - The id of the user who created the token. * @property {string} replId - The id of the repl where the token was created. * @property {string} [originReplId] - The id of the original repl despite the running environment being a fork. * @property {string} audience - The audience for which the token was created. * @property {Runtime|null} runtime - Runtime information about the Repl. */ /** * Executes Replit CLI's identity command with specified command and arguments. * @function * @param {'create'|'verify'} cmd - The command to execute. * @param {Object} [flags] - An object containing the options for the command. * @param {string} flags.audience - The audience for which the token will be/was created. * @param {string} flags.token - The token to verify. (Used with verify command) * @param {string} flags.json - Returns object. * @returns {Promise<object|string|null>} - Resolves with the result, or rejects on error. */ async function identity(cmd = '', flags = {}) { try { let args = ''; const keys = Object.keys(flags); for (let i = 0; i < keys.length; i++) { const flag = keys[i]; const value = flags[flag]; if (!value) throw `${flag} is required.`; if (typeof value !== 'string') throw `${flag} must be a string.`; args += ` -${flag}="${value.replaceAll('"', '\\"')}"` } const command = `${REPLIT_CLI} identity ${cmd}` + args; const result = await execAsync(command); const output = result.stdout.trim(); return flags.json ? JSON.parse(output) : output; } catch (err) { if (typeof err === 'string') { throw `[Replit Identity] ${cmd}() -> ${err}`; } else { return null; // error is identity mismatch or invalid token } } } /** * Creates a new identity token. * @function * @param {string} audience - The audience for which the token will be created. * @returns {Promise<string|null>} - Resolves with the token, or rejects on error. */ async function create(audience) { return identity('create', { audience }); } /** * Verifies an existing identity token against an audience. * @function * @param {string} token - The identity token to verify. * @param {string} audience - The audience for which the token was created. * @returns {Promise<Info|null>} - Resolves with the Info object, or rejects on error. */ async function verify(token, audience) { const info = await identity('verify', { token, audience, json: 'true' }); return info ? camelize(info) : null; } module.exports = { create, verify }; function camelize(obj) { rename(obj, { replId: 'replid', originReplId: 'originReplid', username: 'user', userId: 'user_id', audience: 'aud', runtime: 'Runtime' }); rename(obj.runtime, { interactive: 'Interactive', hosting: 'Hosting', deployment: 'Deployment', }); return obj; } // supposedly the fastest way to do this function rename(object, keys) { for (const [newKey, oldKey] of Object.entries(keys)) { if (oldKey in object) delete Object.assign(object, { [newKey]: object[oldKey] })[oldKey]; } }