added interface to save user tokens

This commit is contained in:
tigerbot 2017-10-17 18:36:36 -06:00
parent 9c7aaa4f98
commit cfaa8d4959
2 changed files with 106 additions and 0 deletions

View File

@ -50,6 +50,9 @@ module.exports.create = function (deps, conf) {
prom.then(function (result) { prom.then(function (result) {
res.send(deps.recase.snakeCopy(result)); res.send(deps.recase.snakeCopy(result));
}).catch(function (err) { }).catch(function (err) {
if (conf.debug) {
console.log(err);
}
res.statusCode = err.statusCode || 500; res.statusCode = err.statusCode || 500;
err.message = err.message || err.toString(); err.message = err.message || err.toString();
res.end(JSON.stringify({error: {message: err.message, code: err.code}})); res.end(JSON.stringify({error: {message: err.message, code: err.code}}));
@ -524,6 +527,24 @@ module.exports.create = function (deps, conf) {
handlePromise(req, res, promise); handlePromise(req, res, promise);
}; };
var tokens = { restful: {} };
tokens.restful.getAll = function (req, res) {
handlePromise(req, res, deps.storage.tokens.all());
};
tokens.restful.getOne = function (req, res) {
handlePromise(req, res, deps.storage.tokens.get(req.params.id));
};
tokens.restful.save = function (req, res) {
handlePromise(req, res, deps.storage.tokens.save(req.body));
};
tokens.restful.revoke = function (req, res) {
var promise = deps.storage.tokens.remove(req.params.id).then(function (success) {
return {success: success};
});
handlePromise(req, res, promise);
};
var app = require('express')(); var app = require('express')();
// Handle all of the API endpoints using the old definition style, and then we can // Handle all of the API endpoints using the old definition style, and then we can
@ -555,5 +576,11 @@ module.exports.create = function (deps, conf) {
app.put( '/config/domains/:domId', config.restful.updateDomain); app.put( '/config/domains/:domId', config.restful.updateDomain);
app.delete('/config/domains/:domId', config.restful.removeDomain); app.delete('/config/domains/:domId', config.restful.removeDomain);
app.use( '/tokens', makeCorsHandler(['GET', 'POST', 'DELETE']));
app.get( '/tokens', tokens.restful.getAll);
app.get( '/tokens/:id', tokens.restful.getOne);
app.post( '/tokens', tokens.restful.save);
app.delete('/tokens/:id', tokens.restful.revoke);
return app; return app;
}; };

View File

@ -3,6 +3,8 @@
var PromiseA = require('bluebird'); var PromiseA = require('bluebird');
var path = require('path'); var path = require('path');
var fs = PromiseA.promisifyAll(require('fs')); var fs = PromiseA.promisifyAll(require('fs'));
var jwt = require('jsonwebtoken');
var crypto = require('crypto');
module.exports.create = function (deps, conf) { module.exports.create = function (deps, conf) {
var hrIds = require('human-readable-ids').humanReadableIds; var hrIds = require('human-readable-ids').humanReadableIds;
@ -93,6 +95,82 @@ module.exports.create = function (deps, conf) {
} }
} }
var userTokens = {
_filename: 'user-tokens.json'
, _convertToken(id, token) {
// convert the token into something that looks more like what OAuth3 uses internally
// as sessions so we can use it with OAuth3. We don't use OAuth3's internal session
// storage because it effectively only supports storing tokens based on provider URI.
// We also use the token as the `access_token` instead of `refresh_token` because the
// refresh functionality is closely tied to the storage.
var decoded = jwt.decode(token);
return {
id: id
, access_token: token
, token: decoded
, provider_uri: decoded.iss || decoded.issuer || decoded.provider_uri
, client_uri: decoded.azp
, scope: decoded.scp || decoded.scope || decoded.grants
};
}
, all: function allUserTokens() {
var self = this;
return read(self._filename).then(function (tokens) {
return Object.keys(tokens).map(function (id) {
return self._convertToken(id, tokens[id]);
});
});
}
, get: function getUserToken(id) {
var self = this;
return read(self._filename).then(function (tokens) {
return self._convertToken(id, tokens[id]);
});
}
, save: function saveUserToken(newToken) {
var self = this;
return read(self._filename).then(function (tokens) {
var rawToken;
if (typeof newToken === 'string') {
rawToken = newToken;
} else {
rawToken = newToken.refresh_token || newToken.access_token;
}
if (typeof rawToken !== 'string') {
throw new Error('cannot save invalid session: missing refresh_token and access_token');
}
var decoded = jwt.decode(rawToken);
var idHash = crypto.createHash('sha256');
idHash.update(decoded.sub || decoded.ppid || decoded.appScopedId || '');
idHash.update(decoded.iss || decoded.issuer || '');
idHash.update(decoded.aud || decoded.audience || '');
var scope = decoded.scope || decoded.scp || decoded.grants || '';
idHash.update(scope.split(/[,\s]+/mg).sort().join(','));
var id = idHash.digest('hex');
tokens[id] = rawToken;
return write(self._filename, tokens).then(function () {
return self.get(id);
});
});
}
, remove: function removeUserToken(id) {
var self = this;
return read(self._filename).then(function (tokens) {
var present = delete tokens[id];
if (!present) {
return present;
}
return write(self._filename, tokens).then(function () {
return true;
});
});
}
};
var mdnsId = { var mdnsId = {
_filename: 'mdns-id' _filename: 'mdns-id'
, get: function () { , get: function () {
@ -119,6 +197,7 @@ module.exports.create = function (deps, conf) {
owners: owners owners: owners
, config: config , config: config
, updateConf: updateConf , updateConf: updateConf
, tokens: userTokens
, mdnsId: mdnsId , mdnsId: mdnsId
}; };
}; };