UNPKG

@slack/oauth

Version:

Official library for interacting with Slack's Oauth endpoints

98 lines 4.06 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FileStateStore = void 0; const node_fs_1 = __importDefault(require("node:fs")); const node_os_1 = require("node:os"); const node_path_1 = __importDefault(require("node:path")); const logger_1 = require("@slack/logger"); const errors_1 = require("../errors"); class FileStateStore { constructor(args) { this.baseDir = args.baseDir !== undefined ? args.baseDir : `${(0, node_os_1.homedir)()}/.bolt-js-oauth-states`; this.stateExpirationSeconds = args.stateExpirationSeconds !== undefined ? args.stateExpirationSeconds : 600; this.logger = args.logger !== undefined ? args.logger : new logger_1.ConsoleLogger(); } async generateStateParam(installOptions, now) { let state = generateRandomString(); while (this.alreadyExists(state)) { // Still race condition can arise here state = generateRandomString(); } const source = { installOptions, now }; this.writeToFile(state, source); return state; } async verifyStateParam(now, state) { try { // decode the state using the secret let decoded; try { decoded = this.readFile(state); } catch (e) { const message = `Failed to load the data represented by the state parameter (error: ${e})`; throw new errors_1.InvalidStateError(message); } if (decoded !== undefined) { // Check if the state value is not too old const generatedAt = new Date(decoded.now); const passedSeconds = Math.floor((now.getTime() - generatedAt.getTime()) / 1000); if (passedSeconds > this.stateExpirationSeconds) { throw new errors_1.InvalidStateError('The state value is already expired'); } // return installOptions return decoded.installOptions; } } finally { this.deleteFile(state); } throw new errors_1.InvalidStateError('The state value is already expired'); } // ------------------------------------------- // private methods // ------------------------------------------- alreadyExists(filename) { const fullpath = node_path_1.default.resolve(`${this.baseDir}/${filename}`); return node_fs_1.default.existsSync(fullpath); } writeToFile(filename, data) { node_fs_1.default.mkdirSync(this.baseDir, { recursive: true }); const fullpath = node_path_1.default.resolve(`${this.baseDir}/${filename}`); node_fs_1.default.writeFileSync(fullpath, JSON.stringify(data)); } readFile(filename) { const fullpath = node_path_1.default.resolve(`${this.baseDir}/${filename}`); try { const data = node_fs_1.default.readFileSync(fullpath); if (data !== undefined) { return JSON.parse(data.toString()); } return undefined; } catch (e) { this.logger.debug(`Failed to load state data from file (error: ${e})`); return undefined; } } deleteFile(filename) { const fullpath = node_path_1.default.resolve(`${this.baseDir}/${filename}`); if (node_fs_1.default.existsSync(fullpath)) { node_fs_1.default.unlinkSync(fullpath); } } } exports.FileStateStore = FileStateStore; const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; function generateRandomString(length = 10) { let generated = ''; for (let i = 0; i < length; i += 1) { const position = Math.floor(Math.random() * chars.length); generated += chars.charAt(position); } return generated; } //# sourceMappingURL=file-state-store.js.map