authentic-client-es6-promises
Version:
Porting of authentic-client to ES6 & native promises
273 lines (219 loc) • 5.67 kB
JavaScript
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)
}