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
125 lines • 4.75 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.IdentityFederationBuilder = void 0;
const koa_passport_1 = require("koa-passport");
const idp_1 = require("../../idp");
const error_1 = require("./error");
class IdentityFederationBuilder {
constructor(builder) {
this.builder = builder;
this.scopes = {};
this.callbacks = {};
this.config = {};
this._prefix = "/federate";
this.getCallbackURL = (providerName) => this.builder.app.getURL(`${this._prefix}/${providerName}`, true);
this.passport = new koa_passport_1.KoaPassport();
}
get prefix() {
return this._prefix;
}
setCallbackPrefix(prefix) {
this.builder.assertBuilding();
this._prefix = prefix;
this.builder.logger.info(`OIDC federation route path configured:`, `${this.builder.app.prefix}${prefix}/:path`);
return this;
}
get providerNames() {
this.builder.assertBuilding(true);
return Object.keys(this.callbacks);
}
setProviderConfigurationMap(configMap) {
this.builder.assertBuilding();
this.config = { ...this.config, ...configMap };
return this;
}
_dangerouslyBuild() {
this.builder.assertBuilding();
for (const [provider, options] of Object.entries(this.config)) {
if (!options || !options.clientID) {
continue;
}
// create strategy and apply
const { scope, callback, strategy, ...restOptions } = options;
this.scopes[provider] = typeof scope === "string" ? scope.split(" ").map(s => s.trim()).filter(s => !!s) : scope;
this.callbacks[provider] = callback;
const commonCallbackArgs = {
scope: this.scopes[provider],
idp: this.builder.idp,
logger: this.builder.logger,
};
const callbackURL = this.getCallbackURL(provider);
this.passport.use(strategy({
...restOptions,
scope,
callbackURL,
}, async (accessToken, refreshToken, profile, next) => {
this.builder.logger.info(`try identity federation from ${provider} with ${this.scopes[provider].join(", ")} scopes:`, profile);
try {
next(null, {
accessToken,
profile,
...commonCallbackArgs,
});
}
catch (error) {
next(error);
}
}));
// remember scopes and callback handlers
this.builder.logger.info(`enable identity federation from ${provider} with ${this.scopes[provider].join(", ")} scopes: ${callbackURL}`);
}
}
assertProvider(provider) {
if (!this.providerNames.includes(provider)) {
throw new error_1.OIDCProviderProxyErrors.InvalidFederationProvider();
}
}
async handleRequest(ctx, next, provider) {
this.assertProvider(provider);
return new Promise((resolve, reject) => {
this.passport.authenticate(provider, {
scope: this.scopes[provider],
session: false,
failWithError: true,
prompt: "consent",
})(ctx, next)
.catch(reject)
.then(resolve);
});
}
async handleCallback(ctx, next, provider) {
this.assertProvider(provider);
return new Promise((resolve, reject) => {
this.passport.authenticate(provider, {
scope: this.scopes[provider],
session: false,
failWithError: true,
prompt: "consent",
}, async (err, args) => {
try {
if (err) {
throw err;
}
const identity = await this.callbacks[provider]({
idp: this.builder.idp,
logger: this.builder.logger,
scope: this.scopes[provider],
...args,
});
if (!identity) {
throw new idp_1.IAMErrors.IdentityNotExists();
}
resolve(identity);
}
catch (error) {
reject(error);
}
})(ctx, next)
.catch((err) => {
reject(err);
});
});
}
}
exports.IdentityFederationBuilder = IdentityFederationBuilder;
//# sourceMappingURL=federation.js.map