UNPKG

authentic-client-es6-promises

Version:
273 lines (219 loc) 5.67 kB
var AsyncCache = require('async-cache') var isUrl = require('is-url-superb') var jsonist = require('jsonist') var jwt = require('jsonwebtoken') var Wildemitter = require('wildemitter') function handler (method, url, opts, cb) { var _url = getUrl(url) return new Promise((resolve, reject) => { jsonist[method === 'delete' ? method : 'get'](_url, opts, ( err, body, res ) => { if (err) { reject(err) return } if (res.statusCode >= 400) { var e = new Error(res.statusMessage) e.statusCode = res.statusCode e.body = body reject(e) return } resolve({ body, res }) }) }) } function verifyToken (cache, token) { return new Promise((resolve, reject) => { cache.get('pubKey', (err, pubKey) => { if (err) { reject(err) return } Error.captureStackTrace = Error.captureStackTrace || function () { } jwt.verify(token, pubKey, { algorithms: ['RS256'] }, (err, data) => { if (err) { reject(err) } else { resolve(data) } }) }) }) } function createCache (pubKeyUrl, cacheDuration) { return new AsyncCache({ maxAge: cacheDuration || 1000 * 60 * 60, load: function (key, cb) { return jsonist.get(pubKeyUrl, function (err, body) { if (err) return cb(err) var pubKey = ((body || {}).data || {}).publicKey if (!pubKey) return cb(new Error('Could not retrieve public key')) return cb(null, pubKey) }) } }) } function getUrl (url) { return process.browser && url.match(/^\//) ? window.location.origin + url : url } function addAuthHeader (token, opts) { if (!token) return opts return Object.assign({}, opts, { headers: { authorization: 'Bearer ' + token } }) } function post (url, data, opts) { return dataHandler('post', url, data, opts) } function put (url, data, opts) { return dataHandler('put', url, data, opts) } function get (url, opts) { return handler('get', url, opts) } function del (url, opts) { return handler('delete', url, opts) } function dataHandler (method, url, data, opts) { var _url = getUrl(url) return new Promise((resolve, reject) => { jsonist[method === 'put' ? method : 'post'](_url, data, opts, ( err, body, res ) => { if (err) { reject(err) return } if (res.statusCode >= 400) { var e = new Error(res.statusMessage) e.statusCode = res.statusCode e.body = body reject(e) return } resolve({body, res}) }) }) } class Client { constructor (opts) { this.server = opts.server this.prefix = opts.prefix || '/auth' this.email = opts.email this.authToken = opts.authToken this.pubKeyUrl = (opts.pubKeyUrl || this.server + this.prefix + '/public-key') this.cache = createCache(this.pubKeyUrl, opts.cacheDuration) } signup (opts) { var url = this.getEndpoint('signup') return post(url, opts, {}).then((res) => res.body) } confirm (opts) { var self = this var url = this.getEndpoint('confirm') this.setEmail(opts.email) return post(url, opts, {}).then((res) => { var data = res.body self.setAuthToken(data.data.authToken) return data }) } login (opts) { var self = this var url = this.getEndpoint('login') this.setEmail(opts.email) return post(url, opts, {}).then((res) => { var data = res.body self.setAuthToken(data.data.authToken) return data }) } changePassword (opts) { var self = this var url = this.getEndpoint('change-password') this.setEmail(opts.email) return post(url, opts, {}).then((res) => { var data = res.body self.setAuthToken(data.data.authToken) return data }) } changePasswordRequest (opts) { var url = this.getEndpoint('change-password-request') return post(url, opts, {}) } delete (url, opts) { var token = this.authToken if (!token) { return del(url, opts) } return this.verifyToken().then(() => { return del(url, addAuthHeader(token, opts)) }) } put (url, data, opts) { var token = this.authToken if (!token) { return post(url, data, opts) } return this.verifyToken().then(() => { return put(url, data, addAuthHeader(token, opts)) }) } post (url, data, opts) { var token = this.authToken if (!token) { return post(url, data, opts) } return this.verifyToken().then(() => { return post(url, data, addAuthHeader(token, opts)) }) } get (url, opts) { var token = this.authToken if (!token) { return get(url, opts) } return this.verifyToken().then(() => { return get(url, addAuthHeader(token, opts)) }) } logout () { this.setAuthToken(null) this.setEmail(null) } getEndpoint (name) { return this.server + this.prefix + '/' + name } setAuthToken (token) { this.authToken = token this.emit('authToken', token) } setEmail (email) { this.email = email this.emit('email', email) } verifyToken () { var self = this return verifyToken(this.cache, this.authToken).catch((err) => { if (err) { self.logout() throw err } }) } } Wildemitter.mixin(Client) module.exports = function (opts) { if (!opts) throw new Error('opts are required argument') if (!isUrl(opts.server)) throw new Error('opts.server must be url') if (!(this instanceof Client)) return new Client(opts) }