UNPKG

@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
"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