WIP add jwt auth, myjqlite

This commit is contained in:
AJ ONeal 2018-01-12 02:03:41 -07:00
parent b2ced1a492
commit 72e920c1dd
4 changed files with 245 additions and 20 deletions

View File

@ -1,6 +1,86 @@
'use strict';
module.exports.create = function (cli, engine/*, dnsd*/) {
var subparts = (cli.subject || '').split('@');
/*
{
"kty": "EC",
"use": "sig",
"crv": "P-256",
"x": "ogbK2nP6SiEIIp4w8oXBn3dcs6kljFfTbgZYG591tUU",
"y": "sB0AekMYwpvbQfAoW-2LlEWdapNhxynfj1zBtWpE9lo",
"alg": "ES256"
}
*/
var jwt;
var jwk;
var privpem;
var pubpem;
if (!subparts[1]) {
subparts = [ 'root', 'localhost' ];
// TODO generate new random key and store it
jwk = {
//"kid": "thumbnail(pubkey)",
"kty": "EC",
"d": "GRIT-yJVlhAsgIChbNanxv41iCxbZszbHHgK8kbZovs",
"use": "sig",
"crv": "P-256",
"x": "ogbK2nP6SiEIIp4w8oXBn3dcs6kljFfTbgZYG591tUU",
"y": "sB0AekMYwpvbQfAoW-2LlEWdapNhxynfj1zBtWpE9lo",
"alg": "ES256"
};
jwt = require('jsonwebtoken');
privpem = require('jwk-to-pem')(jwk, { private: true });
pubpem = require('jwk-to-pem')(jwk, { private: false });
console.log(privpem);
console.log("================================");
console.log(" JWT Write Authorization Token: ");
console.log("================================");
console.log(jwt.sign(
{ sub: subparts[0]
, iss: subparts[1]
, aud: 'localhost'
, scp: '+rw@adns.org'
}
, privpem
, { notBefore: 0 // from now
, expiresIn: '2h'
, algorithm: 'ES256'
}
));
// expressed as "from now"
/*
{ NotBeforeError: jwt not active
at Object.module.exports [as verify] (digd.js/node_modules/jsonwebtoken/verify.js:117:19)
at digd.js/lib/httpd.js:112:15
at Layer.handle [as handle_request] (digd.js/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (digd.js/node_modules/express/lib/router/index.js:317:13)
at digd.js/node_modules/express/lib/router/index.js:284:7
at Function.process_params (digd.js/node_modules/express/lib/router/index.js:335:12)
at next (digd.js/node_modules/express/lib/router/index.js:275:10)
at expressInit (digd.js/node_modules/express/lib/middleware/init.js:40:5)
at Layer.handle [as handle_request] (digd.js/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (digd.js/node_modules/express/lib/router/index.js:317:13)
name: 'NotBeforeError',
message: 'jwt not active',
date: +050046-12-28T01:12:58.000Z }
*/
console.log("===============================");
console.log(" JWT Read Authorization Token: ");
console.log("===============================");
console.log(jwt.sign(
{ sub: subparts[0]
, iss: subparts[1]
, aud: 'localhost'
, scp: '+r@adns.org'
}
, privpem
, { notBefore: 0 // from now
, algorithm: 'ES256'
}
));
console.log("==========================");
}
function runHttp() {
var path = require('path');
@ -8,7 +88,79 @@ module.exports.create = function (cli, engine/*, dnsd*/) {
var app = express();
var httpServer = require('http').createServer(app);
app.use('/', express.static(path.join(__dirname, 'public')));
app.use('/api', function (req, res, next) {
var auth = (req.headers.authorization || req.query.token || '').split(/\s+/)[1];
var token;
if (!auth) {
res.statusCode = 403;
res.send({ error: { message: "need authorization" } });
return;
}
jwt = jwt || require('jsonwebtoken');
try {
token = jwt.decode(auth);
} catch (e) {
token = null;
}
if (!token || !token.iss) {
res.statusCode = 403;
res.send({ error: { message: "need jwt-format authorization" } });
return;
}
if (subparts[0] === token.sub && subparts[1] === token.iss) {
try {
/*
// { algorithm: 'ES256' }
{ JsonWebTokenError: invalid algorithm
at Object.module.exports [as verify] (digd.js/node_modules/jsonwebtoken/verify.js:90:17)
at digd.js/lib/httpd.js:82:15
at Layer.handle [as handle_request] (digd.js/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (digd.js/node_modules/express/lib/router/index.js:317:13)
at digd.js/node_modules/express/lib/router/index.js:284:7
at Function.process_params (digd.js/node_modules/express/lib/router/index.js:335:12)
at next (digd.js/node_modules/express/lib/router/index.js:275:10)
at expressInit (digd.js/node_modules/express/lib/middleware/init.js:40:5)
at Layer.handle [as handle_request] (digd.js/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (digd.js/node_modules/express/lib/router/index.js:317:13) name: 'JsonWebTokenError', message: 'invalid algorithm' }
*/
// could be that it's private but expecting public, or public but expecting private
/*
Error: error:0906D06C:PEM routines:PEM_read_bio:no start line
at Verify.verify (crypto.js:381:23)
at verify (digd.js/node_modules/jwa/index.js:68:21)
at Object.verify (digd.js/node_modules/jwa/index.js:85:18)
at Object.jwsVerify [as verify] (digd.js/node_modules/jws/lib/verify-stream.js:54:15)
at Object.module.exports [as verify] (digd.js/node_modules/jsonwebtoken/verify.js:96:17)
at digd.js/lib/httpd.js:82:15
at Layer.handle [as handle_request] (digd.js/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (digd.js/node_modules/express/lib/router/index.js:317:13)
at digd.js/node_modules/express/lib/router/index.js:284:7
at Function.process_params (digd.js/node_modules/express/lib/router/index.js:335:12)
*/
jwt.verify(auth, pubpem, { algorithms: [ 'ES256' ] });
} catch(e) {
res.statusCode = 403;
console.error(e);
console.log(auth);
console.log(jwt.decode(auth, { complete: true }));
res.send({ error: { message: "jwt was not verified authorization" } });
return;
}
}
req.auth = auth;
req.token = token;
next();
});
app.get('/api/verify-auth', function (req, res) {
res.send({ success: true });
});
app.get('/api/peers', function (req, res) {
engine.peers.all(function (err, peers) {
res.send({ peers: peers });
@ -69,6 +221,8 @@ module.exports.create = function (cli, engine/*, dnsd*/) {
});
});
app.use('/', express.static(path.join(__dirname, 'public')));
httpServer.listen(cli.http, function () {
console.log(httpServer.address().address + '#' + httpServer.address().port + ' (http)');
});

View File

@ -4,6 +4,12 @@
<title>ADNS</title>
</head>
<body>
<h1>ADNS Zones and Records</h1>
<p>
<label>API JWT:</label> <input class="js-jwt" type="text" placeholder="paste the api token here" />
<button class="js-jwt" type="button">Authorize</button>
</p>
<p><a href="/api/peers">/api/peers</a></p>
<p><a href="/api/zones">/api/zones</a></p>
<p><a data-href="/api/zones/:zone/records" class="js-zone">/api/zones/<code
@ -15,6 +21,10 @@
<input class="js-name"
type="text" placeholder="example.com"/></p>
<ul class="js-zones">
<li class="js-zone">blah</li>
</ul>
<script src="/js/app.js"></script>
</body>
</html>

View File

@ -1,28 +1,87 @@
(function () {
'use strict';
if (!Element.prototype.matches) {
Element.prototype.matches = Element.prototype.msMatchesSelector;
}
function $qs(qs) {
return document.querySelector(qs);
}
function $on(selector, eventname, cb) {
if (!$on._events[eventname]) {
$on._events[eventname] = $on._dispatcher(eventname);
document.addEventListener(eventname, $on._events[eventname]);
}
if (!$on._handlers[eventname]) {
$on._handlers[eventname] = {};
}
if (!$on._handlers[eventname][selector]) {
$on._handlers[eventname][selector] = [];
}
$on._handlers[eventname][selector].push(cb);
}
$on._events = {};
$on._handlers = {};
$on._dispatcher = function (eventname) {
return function (ev) {
//console.log('event: ' + ev.type);
if (!$on._handlers[eventname]) {
console.warn('no handlers for event');
return;
}
var matches = Object.keys($on._handlers[eventname]).some(function (selector) {
if (ev.target.matches(selector)) {
$on._handlers[eventname][selector].forEach(function (cb) { cb(ev); });
return true;
}
});
if (!matches) {
console.warn("no handlers for selector");
}
};
};
$on('body', 'click', function () {
console.log('woo-hoo, that tickles my body!');
});
document.body.addEventListener('keyup', function (ev) {
console.log('ev.target.tagName:');
console.log(ev.target.tagName);
console.log('/\\bjs-zone\\b/.test(ev.target.className):');
console.log(/\bjs-zone\b/.test(ev.target.className));
if ('INPUT' === ev.target.tagName && /\bjs-zone\b/.test(ev.target.className)) {
$qs('code.js-zone').innerHTML = ev.target.value || ':zone';
// $qs('a.js-zone').setAttribute('data-href', ...)
$qs('a.js-zone').href =
$qs('a.js-zone').dataset.href.replace(/:zone/, ev.target.value || ':zone');
return;
}
if ('INPUT' === ev.target.tagName && /\bjs-name\b/.test(ev.target.className)) {
$qs('code.js-name').innerHTML = ev.target.value || ':name';
$qs('a.js-name').href =
$qs('a.js-name').dataset.href.replace(/:name/, ev.target.value || ':name');
return;
}
var auth = localStorage.getItem('auth');
$qs('input.js-jwt').value = auth || '';
$on('button.js-jwt', 'click', function (/*ev*/) {
auth = $qs('input.js-jwt').value;
return window.fetch(
'/api/verify-auth'
, { method: 'GET'
, headers: new window.Headers({ 'Authorization': 'Bearer ' + auth })
}
).then(function (resp) {
return resp.json().then(function (data) {
if (data.error) {
console.error(data.error);
window.alert('Bad HTTP Response: ' + data.error.message);
throw new Error('Bad HTTP Response: ' + data.error.message);
}
console.log(data);
localStorage.setItem('auth', auth);
});
});
});
$on('input.js-zone', 'keyup', function (ev) {
$qs('code.js-zone').innerHTML = ev.target.value || ':zone';
// $qs('a.js-zone').setAttribute('data-href', ...)
$qs('a.js-zone').href =
$qs('a.js-zone').dataset.href.replace(/:zone/, ev.target.value || ':zone');
});
$on('input.js-name', 'keyup', function (ev) {
$qs('code.js-name').innerHTML = ev.target.value || ':name';
$qs('a.js-name').href =
$qs('a.js-name').dataset.href.replace(/:name/, ev.target.value || ':name');
});
}());

View File

@ -49,6 +49,8 @@
"dependencies": {
"dig.js": "git+https://git.coolaj86.com/coolaj86/dig.js#v1.3",
"express": "^4.16.2",
"hexdump.js": "git+https://git.coolaj86.com/coolaj86/hexdump.js#v1.0.4"
"hexdump.js": "git+https://git.coolaj86.com/coolaj86/hexdump.js#v1.0.4",
"jsonwebtoken": "^8.1.0",
"jwk-to-pem": "^1.2.6"
}
}