@slack/oauth
Version:
Official library for interacting with Slack's Oauth endpoints
98 lines • 4.06 kB
JavaScript
;
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