unpkg/server/AuthAPI.js

119 lines
2.6 KiB
JavaScript

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
}