const fs = require("fs") const path = require("path") const crypto = require("crypto") const jwt = require("jsonwebtoken") const invariant = require("invariant") const forge = require("node-forge") const db = require("./RedisClient") let keys if (process.env.NODE_ENV === "production") { keys = { public: fs.readFileSync(path.resolve(__dirname, "../public.key"), "utf8"), private: process.env.PRIVATE_KEY } invariant(keys.private, "Missing $PRIVATE_KEY environment variable") } else { // Generate a random keypair for dev/testing. // See https://gist.github.com/sebadoom/2b70969e70db5da9a203bebd9cff099f const keypair = forge.rsa.generateKeyPair({ bits: 2048 }) keys = { public: forge.pki.publicKeyToPem(keypair.publicKey, 72), private: forge.pki.privateKeyToPem(keypair.privateKey, 72) } } function getCurrentSeconds() { return Math.floor(Date.now() / 1000) } function createTokenId() { return crypto.randomBytes(16).toString("hex") } function createToken(scopes = {}) { return new Promise((resolve, reject) => { const payload = { jti: createTokenId(), iss: "https://unpkg.com", iat: getCurrentSeconds(), scopes } jwt.sign(payload, keys.private, { algorithm: "RS256" }, (error, token) => { if (error) { reject(error) } else { resolve(token) } }) }) } const RevokedTokensSet = "revoked-tokens" function verifyToken(token) { return new Promise((resolve, reject) => { const options = { algorithms: ["RS256"] } jwt.verify(token, keys.public, options, (error, payload) => { if (error) { reject(error) } else { if (payload.jti) { db.sismember(RevokedTokensSet, payload.jti, (error, value) => { if (error) { reject(error) } else { resolve(value === 0 ? payload : null) } }) } else { resolve(null) } } }) }) } function revokeToken(token) { return verifyToken(token).then(payload => { if (payload) { return new Promise((resolve, reject) => { db.sadd(RevokedTokensSet, payload.jti, error => { if (error) { reject(error) } else { resolve() } }) }) } }) } function removeAllRevokedTokens() { return new Promise((resolve, reject) => { db.del(RevokedTokensSet, error => { if (error) { reject(error) } else { resolve() } }) }) } function getPublicKey() { return keys.public } module.exports = { createToken, verifyToken, revokeToken, removeAllRevokedTokens, getPublicKey }