lib/auth/passport_init.js
'use strict';
const passport = require('koa-passport');
const $err = require('../util/application_error');
const Metadata = require('../util/meta');
const symbols = require('./symbols');
const coreSymbols = require('../core/symbols');
/**
* Encapsulates the initialization of passport.js.
*
* @param {Ravel} ravelInstance - A reference to an instance of a Ravel app.
* @param {object} router - A koa router.
* @private
*/
module.exports = function (ravelInstance, router) {
/**
* Retrieve the first registered Module which is decorated with `@authconfig`.
*
* @returns {object} The first `@authconfig` `Module`.
* @private
*/
function getAuthMod () {
let authMod = null;
for (const m of Object.keys(ravelInstance[coreSymbols.modules])) {
const mod = ravelInstance[coreSymbols.modules][m];
if (Metadata.getClassMetaValue(Object.getPrototypeOf(mod), '@authconfig', 'enabled', false)) {
authMod = mod;
break;
}
}
if (authMod === null) {
throw new $err.NotFound('Module annotated with @authconfig is required and was not found.');
}
return authMod;
}
/**
* Using the 'post config koa' hook, initalize passport using a reference to the internal koa app.
*
* @private
*/
ravelInstance.once('post config koa', function (app) {
const providers = ravelInstance.authenticationProviders();
if (providers.length > 0) {
app.use(passport.initialize());
app.use(passport.session());
app.use(async function (ctx, next) {
// overwrite ctx.passport with deprecation message
Object.defineProperty(ctx, 'passport', {
configurable: true,
get: () => {
ravelInstance.$log.warn('ctx.passport is deprecated. Please use ctx.state instead.');
return ctx.state;
}
});
await next();
});
}
});
/**
* @private
*/
ravelInstance.once('post module init', function () {
const providers = ravelInstance.authenticationProviders();
if (providers.length > 0) {
ravelInstance[symbols.authConfigModule] = getAuthMod();
passport.serializeUser(function (user, done) {
ravelInstance[symbols.authConfigModule].serializeUser(user)
.then((id) => {
done(null, id);
}).catch((err) => {
done(err, null);
});
});
passport.deserializeUser(function (userId, done) {
ravelInstance[symbols.authConfigModule].deserializeUser(userId)
.then((user) => {
done(null, user);
}).catch((err) => {
done(err, null);
});
});
const verify = function (providerName) {
return function (...verifyArgs) {
const done = verifyArgs[verifyArgs.length - 1];
const args = verifyArgs.slice(0, verifyArgs.length - 1);
// add provider name to the argument list for verify(),
// so that @authconfig module can make different decisions
// for different providers.
args.unshift(providerName);
ravelInstance[symbols.authConfigModule].verify(...args)
.then((user) => {
done(null, user);
}).catch((err) => {
done(err, null);
});
};
};
for (const p of providers) {
p.init(router, passport, verify(p.name));
}
}
});
};