Problem with multple servers
clluiz opened this issue · comments
In my production environment we have 3 servers. When I set a strategy in one of them, when the redirect comes it may go to a server where the strategy was not set causing the Unknown authenticate strategy error.
How can I solve this problem?
Can you elaborate a bit more on what you mean by having the "strategy set"? And why they are not all similar?
I have two routes:
router.get('/auth/adfs', function () { // there is other code in this function passport.use('strategy name', strategy); });
In this route I create and set the strategy dynamically because some parameters are send from another api.
My second url:
router.post('/auth/adfs/callback/:parameter', function () { passport.authenticate('strategy name', function()) });
This is the callback url. The exception Unknown authentication strategy is thrown in this line: passport.authenticate('strategy name', function()).
I think this happens because the callback url is being called in a server other than the one the strategy was initially set.
Are you using some sort of in-memory state storage?
We are using redis.
This sounds like a configuration issue in how you're selecting the strategy based off the information I have here.
let config = {
realm: tenant.authMethods.adfs.realm,
identityProviderUrl: tenant.authMethods.adfs.identityProviderUrl + (userName ? '?userName=${userName}' : ''),
protocol: tenant.authMethods.adfs.protocol,
checkDestination: false,
checkExpiration: false,
checkRecipient: false,
checkAudience: false,
passReqToCallback: true,
path: '${nconf.get('KENOBY_BACKEND_URL')}/social/auth/adfs/callback/${tenant._id}'
};
if(!_.isEmpty(tenant.authMethods.adfs.certificate)) {
config.cert = processCertificate(tenant.authMethods.adfs.certificate);
} else {
config.thumbprint = tenant.authMethods.adfs.thumbprint;
}
return new Wsfedsaml2(config, function (req, profile, done) {
co(function *() {
let propertyPrefix = 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/';
let email = _.get(profile, '${propertyPrefix}emailaddress', '').toLowerCase();
let domain = email.substring(email.indexOf('@'), email.length);
let user = yield User.findOne({'email' : email}).lean();
if (!user) {
let name = _.get(profile, '${propertyPrefix}nameidentifier', '');
let tenant = yield Tenant.findOne({'domains' : domain}).select('_id name').lean();
if (!tenant) return done(null, false);
user = new User({
'name' : name,
'email' : email,
'tenant' : tenant._id,
'profiles' : [{
'tenant' : tenant._id,
'roles' : ['INDICATION']
}]
});
yield user.save();
user = yield User.findOne(user._id).lean();
}
return done(null, user);
});
});
};
let authorizeLoginPassport = function* (next) {
var ctx = this;
let query = {};
let destination = `${nconf.get('KENOBY_FRONTEND_URL')}/#/login`;
if (this.query.tenant) {
query = {_id : this.query.tenant};
} else {
let splited = this.query.email ? this.query.email.split('@') : [];
let domain = splited.length > 1 ? splited[1] : '';
query = {domains: {$in : [`@${domain}`]}};
if(this.query.jobsite) {
destination = `${nconf.get('KENOBY_JOBSITE_URL')}/${this.query.jobsite}`;
yield setUserDestination(this.query.email, destination);
}
}
let tenant = yield Tenant.findOne(query).select('name authMethods.adfs').lean();
let strategy = loginStrategy(tenant, this.query.email);
passport.use(`adfs-${tenant._id.toString()}`, strategy);
yield passport.authenticate(`adfs-${tenant._id.toString()}`, function* (err, user) {
if (err) {
return err; // eslint-disable-line
}
if (!user) {
redisClient.expire(`adfs-${this.query.email}`);
return ctx.redirect(destination);
}
}).call(this, next);
};
let authorizeLoginCallback = function* (next) {
var ctx = this;
yield passport.authenticate(`adfs-${this.params.tenant}`, function* (err, user) {
if (err) { return next(err); }
let destination = yield shouldRedirectUserToJobsite(user.email);
if(!destination) {
destination = `${nconf.get('KENOBY_FRONTEND_URL')}/#/login`;
}
if (!user) {
return ctx.redirect(destination);
}
if (!ctx.session) this.throw(401);
if (!ctx.params.tenant) this.throw(401);
let userId = user._id;
let token = ctx.state.generateToken(userId);
yield new Promise((resolve, reject) => {
redisClient.set(token, userId, function (err) {
if (err) return reject(err);
redisClient.expire(token, 60 * 60); // cache for 60m
resolve();
});
});
let url = `${destination}?token=${token}`;
redisClient.del(`adfs-${user.email}`);
ctx.redirect(url);
yield next;
}).call(this, next);
};
router.post('/auth/adfs/callback/:tenant', AdfsController.authorizeLoginCallback);
router.get('/auth/adfs', AdfsController.authorizeLoginPassport);
@machuga The flow starts by calling the /auth/adfs passing the parameters. Then after the user enter user and password the callback are called by the adfs.
Note that the startegy is set at execution time.
let tenant = yield Tenant.findOne(query).select('name authMethods.adfs').lean();
let strategy = loginStrategy(tenant, this.query.email);
passport.use(`adfs-${tenant._id.toString()}`, strategy);
Have you verified the other machines still have these query params available?
They don't have it. Because the strategy is set inside a route.