WIP add jwt auth, myjqlite
This commit is contained in:
parent
b2ced1a492
commit
72e920c1dd
156
lib/httpd.js
156
lib/httpd.js
|
@ -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)');
|
||||
});
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
|
||||
}());
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue