enable assets subdomain with cookies
This commit is contained in:
parent
babfb6b38b
commit
a429e48977
1265
lib/apis.js
1265
lib/apis.js
File diff suppressed because it is too large
Load Diff
26
lib/main.js
26
lib/main.js
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
module.exports.create = function (app, xconfx, apiFactories, apiDeps, errorIfApi) {
|
module.exports.create = function (app, xconfx, apiFactories, apiDeps, errorIfApi, errorIfAssets) {
|
||||||
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'));
|
||||||
|
@ -293,10 +293,27 @@ module.exports.create = function (app, xconfx, apiFactories, apiDeps, errorIfApi
|
||||||
// TODO handle assets.example.com/sub/assets/com.example.xyz/
|
// TODO handle assets.example.com/sub/assets/com.example.xyz/
|
||||||
|
|
||||||
app.use('/api', require('connect-send-error').error());
|
app.use('/api', require('connect-send-error').error());
|
||||||
|
app.use('/assets', require('connect-send-error').error());
|
||||||
app.use('/', function (req, res, next) {
|
app.use('/', function (req, res, next) {
|
||||||
// If this doesn't look like an API we can move along
|
// If this doesn't look like an API or assets we can move along
|
||||||
if (!/\/api(\/|$)/.test(req.url)) {
|
|
||||||
// /^api\./.test(req.hostname) &&
|
/*
|
||||||
|
console.log('.');
|
||||||
|
console.log('[main.js] req.url, req.hostname');
|
||||||
|
console.log(req.url);
|
||||||
|
console.log(req.hostname);
|
||||||
|
console.log('.');
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!/\/(api|assets)(\/|$)/.test(req.url)) {
|
||||||
|
//console.log('[main.js] api|assets');
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// keep https://assets.example.com/assets but skip https://example.com/assets
|
||||||
|
if (/\/assets(\/|$)/.test(req.url) && !/(^|\.)(api|assets)(\.)/.test(req.hostname) && !/^[0-9\.]+$/.test(req.hostname)) {
|
||||||
|
//console.log('[main.js] skip');
|
||||||
next();
|
next();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -325,6 +342,7 @@ module.exports.create = function (app, xconfx, apiFactories, apiDeps, errorIfApi
|
||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
app.use('/', errorIfApi);
|
app.use('/', errorIfApi);
|
||||||
|
app.use('/', errorIfAssets);
|
||||||
app.use('/', serveStatic);
|
app.use('/', serveStatic);
|
||||||
app.use('/', serveApps);
|
app.use('/', serveApps);
|
||||||
|
|
||||||
|
|
|
@ -181,6 +181,50 @@ function deepFreeze(obj) {
|
||||||
Object.freeze(obj);
|
Object.freeze(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cookieOauth3(req, res, next) {
|
||||||
|
req.oauth3 = {};
|
||||||
|
|
||||||
|
var token = req.cookies.jwt;
|
||||||
|
|
||||||
|
req.oauth3.encodedToken = token;
|
||||||
|
req.oauth3.verifyAsync = function (jwt) {
|
||||||
|
return verifyToken(jwt || token);
|
||||||
|
};
|
||||||
|
|
||||||
|
return verifyToken(token).then(function (decoded) {
|
||||||
|
req.oauth3.token = decoded;
|
||||||
|
if (!decoded) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ppid = decoded.sub || decoded.ppid || decoded.appScopedId;
|
||||||
|
req.oauth3.ppid = ppid;
|
||||||
|
req.oauth3.accountIdx = ppid+'@'+decoded.iss;
|
||||||
|
|
||||||
|
var hash = require('crypto').createHash('sha256').update(req.oauth3.accountIdx).digest('base64');
|
||||||
|
hash = hash.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+/g, '');
|
||||||
|
req.oauth3.accountHash = hash;
|
||||||
|
|
||||||
|
req.oauth3.rescope = function (sub) {
|
||||||
|
// TODO: this function is supposed to convert PPIDs of different parties to some account
|
||||||
|
// ID that allows application to keep track of permisions and what-not.
|
||||||
|
return PromiseA.resolve(sub || hash);
|
||||||
|
};
|
||||||
|
}).then(function () {
|
||||||
|
deepFreeze(req.oauth3);
|
||||||
|
//Object.defineProperty(req, 'oauth3', {configurable: false, writable: false});
|
||||||
|
next();
|
||||||
|
}, function (err) {
|
||||||
|
if ('E_NO_TOKEN' === err.code) {
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.error('[walnut] cookie lib/oauth3 error:');
|
||||||
|
console.error(err);
|
||||||
|
res.send(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function attachOauth3(req, res, next) {
|
function attachOauth3(req, res, next) {
|
||||||
req.oauth3 = {};
|
req.oauth3 = {};
|
||||||
|
|
||||||
|
@ -215,14 +259,15 @@ function attachOauth3(req, res, next) {
|
||||||
};
|
};
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
deepFreeze(req.oauth3);
|
deepFreeze(req.oauth3);
|
||||||
Object.defineProperty(req, 'oauth3', {configurable: false, writable: false});
|
//Object.defineProperty(req, 'oauth3', {configurable: false, writable: false});
|
||||||
next();
|
next();
|
||||||
}, function (err) {
|
}, function (err) {
|
||||||
console.error('[walnut] lib/oauth3 error:');
|
console.error('[walnut] JWT lib/oauth3 error:');
|
||||||
console.error(err);
|
console.error(err);
|
||||||
res.send(err);
|
res.send(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.attachOauth3 = attachOauth3;
|
module.exports.attachOauth3 = attachOauth3;
|
||||||
|
module.exports.cookieOauth3 = cookieOauth3;
|
||||||
module.exports.verifyToken = verifyToken;
|
module.exports.verifyToken = verifyToken;
|
||||||
|
|
|
@ -150,6 +150,10 @@ module.exports.create = function (webserver, xconfx, state) {
|
||||||
models: models
|
models: models
|
||||||
// TODO don't let packages use this directly
|
// TODO don't let packages use this directly
|
||||||
, Promise: PromiseA
|
, Promise: PromiseA
|
||||||
|
, dns: PromiseA.promisifyAll(require('dns'))
|
||||||
|
, crypto: PromiseA.promisifyAll(require('crypto'))
|
||||||
|
, fs: PromiseA.promisifyAll(require('fs'))
|
||||||
|
, path: require('path')
|
||||||
};
|
};
|
||||||
var apiFactories = {
|
var apiFactories = {
|
||||||
memstoreFactory: { create: scopeMemstore }
|
memstoreFactory: { create: scopeMemstore }
|
||||||
|
@ -180,7 +184,7 @@ module.exports.create = function (webserver, xconfx, state) {
|
||||||
function setupMain() {
|
function setupMain() {
|
||||||
if (xconfx.debug) { console.log('[main] setup'); }
|
if (xconfx.debug) { console.log('[main] setup'); }
|
||||||
mainApp = express();
|
mainApp = express();
|
||||||
require('./main').create(mainApp, xconfx, apiFactories, apiDeps, errorIfApi).then(function () {
|
require('./main').create(mainApp, xconfx, apiFactories, apiDeps, errorIfApi, errorIfAssets).then(function () {
|
||||||
if (xconfx.debug) { console.log('[main] ready'); }
|
if (xconfx.debug) { console.log('[main] ready'); }
|
||||||
// TODO process.send({});
|
// TODO process.send({});
|
||||||
});
|
});
|
||||||
|
@ -225,6 +229,24 @@ module.exports.create = function (webserver, xconfx, state) {
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function errorIfNotAssets(req, res, next) {
|
||||||
|
var hostname = req.hostname || req.headers.host;
|
||||||
|
|
||||||
|
if (!/^assets\.[a-z0-9\-]+/.test(hostname)) {
|
||||||
|
res.send({ error:
|
||||||
|
{ message: "['" + hostname + req.url + "'] protected asset access is restricted to proper 'asset'-prefixed lowercase subdomains."
|
||||||
|
+ " The HTTP 'Host' header must exist and must begin with 'assets.' as in 'assets.example.com'."
|
||||||
|
+ " For development you may test with assets.localhost.daplie.me (or any domain by modifying your /etc/hosts)"
|
||||||
|
, code: 'E_NOT_API'
|
||||||
|
, _hostname: hostname
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
function errorIfApi(req, res, next) {
|
function errorIfApi(req, res, next) {
|
||||||
if (!/^api\./.test(req.headers.host)) {
|
if (!/^api\./.test(req.headers.host)) {
|
||||||
next();
|
next();
|
||||||
|
@ -240,7 +262,25 @@ module.exports.create = function (webserver, xconfx, state) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.send({ error: { code: 'E_NO_IMPL', message: "not implemented" } });
|
res.send({ error: { code: 'E_NO_IMPL', message: "API not implemented" } });
|
||||||
|
}
|
||||||
|
|
||||||
|
function errorIfAssets(req, res, next) {
|
||||||
|
if (!/^assets\./.test(req.headers.host)) {
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// has api. hostname prefix
|
||||||
|
|
||||||
|
// doesn't have /api url prefix
|
||||||
|
if (!/^\/assets\//.test(req.url)) {
|
||||||
|
console.log('[walnut/worker assets] req.url', req.url);
|
||||||
|
res.send({ error: { message: "missing /assets/ url prefix" } });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send({ error: { code: 'E_NO_IMPL', message: "assets handler not implemented" } });
|
||||||
}
|
}
|
||||||
|
|
||||||
app.disable('x-powered-by');
|
app.disable('x-powered-by');
|
||||||
|
@ -258,8 +298,11 @@ module.exports.create = function (webserver, xconfx, state) {
|
||||||
}));
|
}));
|
||||||
app.use('/api', recase);
|
app.use('/api', recase);
|
||||||
|
|
||||||
|
var cookieParser = require('cookie-parser'); // signing is done in JWT
|
||||||
|
|
||||||
app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']);
|
app.set('trust proxy', ['loopback', 'linklocal', 'uniquelocal']);
|
||||||
app.use('/api', errorIfNotApi);
|
app.use('/api', errorIfNotApi);
|
||||||
|
app.use('/assets', /*errorIfNotAssets,*/ cookieParser()); // serializer { path: '/assets', httpOnly: true, sameSite: true/*, domain: assets.example.com*/ }
|
||||||
app.use('/', function (req, res) {
|
app.use('/', function (req, res) {
|
||||||
if (!(req.encrypted || req.secure)) {
|
if (!(req.encrypted || req.secure)) {
|
||||||
// did not come from https
|
// did not come from https
|
||||||
|
|
Loading…
Reference in New Issue