@mvx/identity
Version:
identity is oidc for mvc, type-mvc is base on koa. Decorator, Ioc, AOP mvc framework on server.
434 lines (432 loc) • 20.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Authenticator = void 0;
var tslib_1 = require("tslib");
var ioc_1 = require("@tsdi/ioc");
var http = require("http");
var ContextExtends_1 = require("./ContextExtends");
var results_1 = require("./results");
var SessionStrategy_1 = require("./SessionStrategy");
var errors_1 = require("../errors");
var IAuthenticator_1 = require("./IAuthenticator");
var mvc_1 = require("@mvx/mvc");
/**
* `Authenticator` constructor.
*
*/
var Authenticator = /** @class */ (function () {
function Authenticator() {
this._userProperty = 'user';
this._rolesProperty = 'roles';
this.strategies = new Map();
this.serializers = [];
this.deserializers = [];
this.infoTransformers = [];
this.use(new SessionStrategy_1.SessionStrategy());
}
Object.defineProperty(Authenticator.prototype, "userProperty", {
get: function () {
return this._userProperty;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Authenticator.prototype, "rolesProperty", {
get: function () {
return this._rolesProperty;
},
enumerable: false,
configurable: true
});
/**
* get strategy.
*
* @param {string} name
* @returns {IStrategy}
* @memberof IAuthenticator
*/
Authenticator.prototype.get = function (name) {
return this.strategies.get(name);
};
Authenticator.prototype.use = function (name, strategy) {
if (strategy === undefined) {
strategy = name;
name = strategy.name;
}
if (!name) {
throw new Error('Authentication strategies must have a name');
}
this.strategies.set(name, strategy);
return this;
};
/**
* Un-utilize the `strategy` with given `name`.
*
* In typical applications, the necessary authentication strategies are static,
* configured once and always available. As such, there is often no need to
* invoke this function.
*
* However, in certain situations, applications may need dynamically configure
* and de-configure authentication strategies. The `use()`/`unuse()`
* combination satisfies these scenarios.
*
* Examples:
*
* passport.unuse('legacy-api');
*
*/
Authenticator.prototype.unuse = function (name) {
this.strategies.delete(name);
return this;
};
/**
* Passport's primary initialization middleware.
*
* Intializes Passport for incoming requests, allowing
* authentication strategies to be applied.
*
* If sessions are being utilized, applications must set up Passport with
* functions to serialize a user into and out of a session. For example, a
* common pattern is to serialize just the user ID into the session (due to the
* fact that it is desirable to store the minimum amount of data in a session).
* When a subsequent request arrives for the session, the full User object can
* be loaded from the database by ID.
*
* Note that additional middleware is required to persist login state, so we
* must use the `connect.session()` middleware _before_ `passport.initialize()`.
*
* If sessions are being used, this middleware must be in use by the
* Koa application for Passport to operate. If the application is
* entirely stateless (not using sessions), this middleware is not necessary,
* but its use will not have any adverse impact.
*
* Options:
* - `userProperty` Property to set on `ctx.state` upon login, defaults to _user_
*
* Examples:
* app.use(connect.cookieParser());
*
* app.use(connect.session({ secret: 'keyboard cat' }));
* app.use(passport.initialize());
* app.use(passport.initialize({ userProperty: 'currentUser' }));
* app.use(passport.session());
*
* passport.serializeUser(function(user, done) {
* done(null, user.id);
* });
*
* passport.deserializeUser(function(id, done) {
* User.findById(id, function (err, user) {
* done(err, user);
* });
* });
*
*/
Authenticator.prototype.initialize = function (options) {
var _this = this;
if (options === void 0) { options = {}; }
this._userProperty = options.userProperty || 'user';
this._rolesProperty = options.rolesProperty || 'roles';
return function (ctx, next) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var session;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
ctx.passport = this;
session = ctx.session;
if (!session) {
throw new Error('Session middleware is needed with passport middleware!');
}
if (!('passport' in session)) {
ctx.session.passport = { user: undefined };
}
if (!('message' in session)) {
session.message = {};
}
ctx.session = session;
ContextExtends_1.contextExtends(ctx);
return [4 /*yield*/, next()];
case 1:
_a.sent();
return [2 /*return*/];
}
});
}); };
};
Authenticator.prototype.authenticate = function (strategyNames, options, callback) {
var _this = this;
if (options === void 0) { options = {}; }
if (ioc_1.isFunction(options)) {
callback = options;
options = {};
}
var multi = true;
// Cast `strategy` to an array, allowing authentication to pass through a chain of
// strategies. The first strategy to succeed, redirect, or error will halt
// the chain. Authentication failures will proceed through each strategy in
// series, ultimately failing if all strategies fail.
//
// This is typically used on API endpoints to allow clients to authenticate
// using their preferred choice of Basic, Digest, token-based schemes, etc.
// It is not feasible to construct a chain of multiple strategies that involve
// redirection (for example both Facebook and Twitter), since the first one to
// redirect will halt the chain.
if (ioc_1.isString(strategyNames)) {
strategyNames = [strategyNames];
multi = false;
}
return function (ctx, next) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var failures, challenges, statuses, challenge, rchallenge, rstatus, status, _i, failures_1, failure;
var _this = this;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
failures = ctx.failures = [];
return [4 /*yield*/, ioc_1.chain(strategyNames.map(function (strategyName) { return function (ctx1, step) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var strategy, res, error_1;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
strategy = this.strategies.get(strategyName);
if (!strategy) {
throw new Error("Unknown authentication strategy \"" + strategyName + "\"");
}
_a.label = 1;
case 1:
_a.trys.push([1, 8, , 9]);
return [4 /*yield*/, strategy.authenticate(ctx1, options)];
case 2:
res = _a.sent();
if (!(res instanceof results_1.FailResult)) return [3 /*break*/, 3];
res.action(ctx1);
return [3 /*break*/, 7];
case 3:
if (!(res instanceof results_1.SuccessResult || res instanceof results_1.RedirectResult)) return [3 /*break*/, 5];
return [4 /*yield*/, res.action(ctx1)];
case 4:
_a.sent();
return [3 /*break*/, 7];
case 5: return [4 /*yield*/, res.action(ctx1, callback)];
case 6:
_a.sent();
return [2 /*return*/, step()];
case 7: return [3 /*break*/, 9];
case 8:
error_1 = _a.sent();
new results_1.FailResult(error_1.toString(), 401).action(ctx1);
return [3 /*break*/, 9];
case 9: return [2 /*return*/];
}
});
}); }; }), ctx)];
case 1:
_a.sent();
if (!!failures.length) return [3 /*break*/, 3];
return [4 /*yield*/, next()];
case 2: return [2 /*return*/, _a.sent()];
case 3:
if (callback) {
if (!multi) {
return [2 /*return*/, callback(null, false, failures[0].challenge, failures[0].status)];
}
else {
challenges = failures.map(function (f) { return f.challenge; });
statuses = failures.map(function (f) { return f.status; });
return [2 /*return*/, callback(null, false, challenges, statuses)];
}
}
// Strategies are ordered by priority. For the purpose of flashing a
// message, the first failure will be displayed.
// const challenge = (failures[0] || {}).challenge || {};
if (options.failureMessage && failures[0].challenge.type) {
challenge = failures[0].challenge;
if (!(challenge.type in ctx.session.message)) {
ctx.session.message[challenge.type] = [];
}
ctx.session.message[challenge.type].push(challenge.messages);
}
if (options.failureRedirect) {
return [2 /*return*/, ctx.redirect(options.failureRedirect)];
}
rchallenge = [];
for (_i = 0, failures_1 = failures; _i < failures_1.length; _i++) {
failure = failures_1[_i];
status = failure.status;
rstatus = rstatus || status;
if (ioc_1.isString(failure.challenge)) {
rchallenge.push(failure.challenge);
}
}
ctx.status = rstatus || 401;
if (ctx.status === 401 && rchallenge.length) {
ctx.set('WWW-Authenticate', rchallenge);
}
if (options.failWithError) {
throw new errors_1.AuthenticationError(rstatus, http.STATUS_CODES[ctx.status]);
}
// ctx.res.statusMessage = http.STATUS_CODES[ctx.status];
ctx.response.message = http.STATUS_CODES[ctx.status];
ctx.res.end(http.STATUS_CODES[ctx.status]);
throw new mvc_1.UnauthorizedError(ctx.response.message);
}
});
}); };
};
/**
* Middleware that will authorize a third-party account using the given
* `strategy` name, with optional `options`.
*
* If authorization is successful, the result provided by the strategy's verify
* callback will be assigned to `ctx.state.account`. The existing login session and
* `ctx.state.user` will be unaffected.
*
* This function is particularly useful when connecting third-party accounts
* to the local account of a user that is currently authenticated.
*
* Examples:
*
* passport.authorize('twitter-authz', { failureRedirect: '/account' });
*/
Authenticator.prototype.authorize = function (strategy, options, callback) {
if (options === void 0) { options = {}; }
options.userProperty = 'account';
return this.authenticate(strategy, options, callback);
};
/**
* Middleware that will restore login state from a session.
*
* Web applications typically use sessions to maintain login state between
* requests. For example, a user will authenticate by entering credentials into
* a form which is submitted to the server. If the credentials are valid, a
* login session is established by setting a cookie containing a session
* identifier in the user's web browser. The web browser will send this cookie
* in subsequent requests to the server, allowing a session to be maintained.
*
* If sessions are being utilized, and a login session has been established,
* this middleware will populate `req.user` with the current user.
*
* Note that sessions are not strictly required for Passport to operate.
* However, as a general rule, most web applications will make use of sessions.
* An exception to this rule would be an API server, which expects each HTTP
* request to provide credentials in an Authorization header.
*
* Examples:
*
* app.use(connect.cookieParser());
* app.use(connect.session({ secret: 'keyboard cat' }));
* app.use(passport.initialize());
* app.use(passport.session());
*
* Options:
* - `pauseStream` Pause the request stream before deserializing the user
* object from the session. Defaults to _false_. Should
* be set to true in cases where middleware consuming the
* request body is configured after passport and the
* deserializeUser method is asynchronous.
*
* @api public
*/
Authenticator.prototype.session = function (options) {
return this.authenticate('session', options);
};
Authenticator.prototype.serializeUser = function (user, ctx) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var _i, _a, layer, obj;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
case 0:
if (typeof user === 'function') {
return [2 /*return*/, this.serializers.push(user)];
}
_i = 0, _a = this.serializers;
_b.label = 1;
case 1:
if (!(_i < _a.length)) return [3 /*break*/, 4];
layer = _a[_i];
return [4 /*yield*/, layer(user, ctx)];
case 2:
obj = _b.sent();
if (obj || obj === 0) {
return [2 /*return*/, obj];
}
_b.label = 3;
case 3:
_i++;
return [3 /*break*/, 1];
case 4: throw new Error('Failed to serialize user into session');
}
});
});
};
Authenticator.prototype.deserializeUser = function (obj, ctx) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var _i, _a, layer, user;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
case 0:
if (ioc_1.isFunction(obj)) {
return [2 /*return*/, this.deserializers.push(obj)];
}
_i = 0, _a = this.deserializers;
_b.label = 1;
case 1:
if (!(_i < _a.length)) return [3 /*break*/, 4];
layer = _a[_i];
return [4 /*yield*/, layer(obj, ctx)];
case 2:
user = _b.sent();
if (user) {
return [2 /*return*/, user];
}
else if (user === null || user === false) {
return [2 /*return*/, false];
}
_b.label = 3;
case 3:
_i++;
return [3 /*break*/, 1];
case 4: throw new Error('Failed to deserialize user out of session');
}
});
});
};
Authenticator.prototype.transformAuthInfo = function (info, ctx) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var _i, _a, layer, tinfo;
return tslib_1.__generator(this, function (_b) {
switch (_b.label) {
case 0:
if (ioc_1.isFunction(info)) {
return [2 /*return*/, this.infoTransformers.push(info)];
}
_i = 0, _a = this.infoTransformers;
_b.label = 1;
case 1:
if (!(_i < _a.length)) return [3 /*break*/, 4];
layer = _a[_i];
return [4 /*yield*/, layer(info, ctx)];
case 2:
tinfo = _b.sent();
if (tinfo) {
return [2 /*return*/, tinfo];
}
_b.label = 3;
case 3:
_i++;
return [3 /*break*/, 1];
case 4: return [2 /*return*/, info];
}
});
});
};
Authenticator.ρAnn = function () {
return { "name": "Authenticator", "params": { "get": ["name"], "use": ["name", "strategy"], "unuse": ["name"], "initialize": ["options"], "authenticate": ["strategyNames", "options", "callback"], "authorize": ["strategy", "options", "callback"], "session": ["options"], "serializeUser": ["user", "ctx"], "deserializeUser": ["obj", "ctx"], "transformAuthInfo": ["info", "ctx"] } };
};
Authenticator = tslib_1.__decorate([
ioc_1.Singleton(IAuthenticator_1.AuthenticatorToken),
tslib_1.__metadata("design:paramtypes", [])
], Authenticator);
return Authenticator;
}());
exports.Authenticator = Authenticator;
//# sourceMappingURL=../sourcemaps/passports/Authenticator.js.map