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';
|
'use strict';
|
||||||
|
|
||||||
module.exports.create = function (cli, engine/*, dnsd*/) {
|
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() {
|
function runHttp() {
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
@ -8,7 +88,79 @@ module.exports.create = function (cli, engine/*, dnsd*/) {
|
||||||
var app = express();
|
var app = express();
|
||||||
var httpServer = require('http').createServer(app);
|
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) {
|
app.get('/api/peers', function (req, res) {
|
||||||
engine.peers.all(function (err, peers) {
|
engine.peers.all(function (err, peers) {
|
||||||
res.send({ peers: 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 () {
|
httpServer.listen(cli.http, function () {
|
||||||
console.log(httpServer.address().address + '#' + httpServer.address().port + ' (http)');
|
console.log(httpServer.address().address + '#' + httpServer.address().port + ' (http)');
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,6 +4,12 @@
|
||||||
<title>ADNS</title>
|
<title>ADNS</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<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/peers">/api/peers</a></p>
|
||||||
<p><a href="/api/zones">/api/zones</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
|
<p><a data-href="/api/zones/:zone/records" class="js-zone">/api/zones/<code
|
||||||
|
@ -15,6 +21,10 @@
|
||||||
<input class="js-name"
|
<input class="js-name"
|
||||||
type="text" placeholder="example.com"/></p>
|
type="text" placeholder="example.com"/></p>
|
||||||
|
|
||||||
|
<ul class="js-zones">
|
||||||
|
<li class="js-zone">blah</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<script src="/js/app.js"></script>
|
<script src="/js/app.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -1,28 +1,87 @@
|
||||||
(function () {
|
(function () {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
if (!Element.prototype.matches) {
|
||||||
|
Element.prototype.matches = Element.prototype.msMatchesSelector;
|
||||||
|
}
|
||||||
function $qs(qs) {
|
function $qs(qs) {
|
||||||
return document.querySelector(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) {
|
var auth = localStorage.getItem('auth');
|
||||||
console.log('ev.target.tagName:');
|
|
||||||
console.log(ev.target.tagName);
|
$qs('input.js-jwt').value = auth || '';
|
||||||
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)) {
|
$on('button.js-jwt', 'click', function (/*ev*/) {
|
||||||
$qs('code.js-zone').innerHTML = ev.target.value || ':zone';
|
auth = $qs('input.js-jwt').value;
|
||||||
// $qs('a.js-zone').setAttribute('data-href', ...)
|
return window.fetch(
|
||||||
$qs('a.js-zone').href =
|
'/api/verify-auth'
|
||||||
$qs('a.js-zone').dataset.href.replace(/:zone/, ev.target.value || ':zone');
|
, { method: 'GET'
|
||||||
return;
|
, headers: new window.Headers({ 'Authorization': 'Bearer ' + auth })
|
||||||
}
|
}
|
||||||
if ('INPUT' === ev.target.tagName && /\bjs-name\b/.test(ev.target.className)) {
|
).then(function (resp) {
|
||||||
$qs('code.js-name').innerHTML = ev.target.value || ':name';
|
return resp.json().then(function (data) {
|
||||||
$qs('a.js-name').href =
|
if (data.error) {
|
||||||
$qs('a.js-name').dataset.href.replace(/:name/, ev.target.value || ':name');
|
console.error(data.error);
|
||||||
return;
|
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": {
|
"dependencies": {
|
||||||
"dig.js": "git+https://git.coolaj86.com/coolaj86/dig.js#v1.3",
|
"dig.js": "git+https://git.coolaj86.com/coolaj86/dig.js#v1.3",
|
||||||
"express": "^4.16.2",
|
"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