moleculer-iam
Version:
Centralized IAM module for moleculer. Including a certified OIDC provider and an Identity provider for user profile, credentials, and custom claims management. Custom claims could be defined/updated by declarative schema which contains claims validation a
138 lines • 6.32 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.IAMServer = void 0;
const tslib_1 = require("tslib");
const http = tslib_1.__importStar(require("http"));
const http2 = tslib_1.__importStar(require("http2"));
const https = tslib_1.__importStar(require("https"));
const kleur = tslib_1.__importStar(require("kleur"));
const url = tslib_1.__importStar(require("url"));
const koa_1 = tslib_1.__importDefault(require("koa"));
const koa_helmet_1 = tslib_1.__importDefault(require("koa-helmet"));
const koa_json_1 = tslib_1.__importDefault(require("koa-json"));
const koa_mount_1 = tslib_1.__importDefault(require("koa-mount"));
// @ts-ignore
const koa_no_trailing_slash_1 = tslib_1.__importDefault(require("koa-no-trailing-slash"));
// @ts-ignore
const koa_locale_1 = tslib_1.__importDefault(require("koa-locale"));
const logging_1 = require("./logging");
class IAMServer {
constructor(props, opts) {
this.props = props;
this.working = false;
const options = this.options = opts || {};
const { logger, op } = props;
this.logger = logger || console;
// create web server application
const app = this.app = new koa_1.default();
app.env = op.app.env = "production";
app.proxy = op.app.proxy = true;
// apply middleware
app.use(logging_1.logging(this.logger, options.logging));
app.use(koa_no_trailing_slash_1.default());
app.use(koa_helmet_1.default(options.security));
app.use(koa_json_1.default({
pretty: true,
spaces: 2,
}));
// set locale into context
koa_locale_1.default(app, "locale");
// prepare to persist some query strings on redirection
const { persistentQueryParamsKeys = [] } = this.options;
const persistentQueryParamsKeysWithDefaults = [...new Set(persistentQueryParamsKeys.concat(["locale", "theme"]))];
app.use((ctx, next) => {
// parsed by precedence of ?locale=ko-KR, Cookie: locale=ko-KR, Accept-Language: ko-KR
// ref: https://github.com/koa-modules/locale
// @ts-ignore
const locale = op.parseLocale(ctx.getLocaleFromQuery() || ctx.getLocaleFromCookie() || ctx.getLocaleFromHeader());
ctx.locale = locale;
// persist some query strings on redirection
return next()
.then(() => {
// reassign locale/theme query for redirection response
if (!ctx.headerSent) {
const redirect = ctx.response.get("Location");
if (redirect.startsWith("/") || redirect.startsWith(op.issuer)) {
const { protocol, auth, slashes, host, hash, query, pathname } = url.parse(redirect, true);
for (const key of persistentQueryParamsKeysWithDefaults) {
if (typeof ctx.query[key] !== "undefined") {
query[key] = ctx.query[key];
}
}
ctx.response.set("Location", url.format({ protocol, auth, slashes, host, hash, query, pathname }));
}
}
});
});
}
async start() {
if (this.working) {
return;
}
// start op
const { op } = this.props;
await op.start();
// mount optional app routes and oidc provider routes
if (this.options.app) {
this.app.use(await this.options.app(op));
}
// mount op app
this.app.use(koa_mount_1.default(op.app));
// start servers
const config = this.options;
const handler = this.app.callback();
if (config.http2s) {
const { hostname, port = 443, ...opts } = config.http2s;
this.http2s = http2.createSecureServer(opts, handler);
this.http2s.listen(port, hostname, undefined, this.listenCallback("http2", "https", hostname, port));
}
if (config.http2) {
const { hostname, port = 8080, ...opts } = config.http2;
this.http2 = http2.createServer(opts, handler);
this.http2.listen(port, hostname, undefined, this.listenCallback("http2", "http", hostname, port));
}
if (config.https) {
const { hostname, port = 443, ...opts } = config.https;
this.https = https.createServer(opts, handler);
this.https.listen(port, hostname, undefined, this.listenCallback("https", "https", hostname, port));
}
if (config.http || !this.https && !this.http2 && !this.http2s) {
const { hostname, port = 8080, ...opts } = config.http || { hostname: "localhost" };
this.http = http.createServer(opts, handler);
this.http.listen(port, hostname, undefined, this.listenCallback("http", "http", hostname, port));
}
this.working = true;
this.logger.info(`IAM server has been started`);
}
listenCallback(protocol, scheme, hostname, port) {
const { op } = this.props;
const serverURL = kleur.blue(`${scheme}://${hostname}:${port}`);
const discoveryURL = kleur.yellow(`${op.issuer}/.well-known/openid-configuration`);
return () => {
this.logger.info(`${kleur.blue(protocol.toUpperCase() + " server")} is listening\n* Web server bound to: ${serverURL}\n* OIDC discovery endpoint: ${discoveryURL}`);
};
}
async stop() {
if (!this.working) {
return;
}
if (this.http) {
this.http.close(() => this.logger.info(`http server has been stopped`));
}
if (this.https) {
this.https.close(() => this.logger.info(`https server has been stopped`));
}
if (this.http2) {
this.http2.close(() => this.logger.info(`http2 server has been stopped`));
}
if (this.http2s) {
this.http2s.close(() => this.logger.info(`http2s server has been stopped`));
}
// stop op
await this.props.op.stop();
this.working = false;
this.logger.info(`IAM server has been stopped`);
}
}
exports.IAMServer = IAMServer;
//# sourceMappingURL=server.js.map