我是 saml 新手,使用 Nodejs + Express + passport-saml + okta 身份提供者。我知道这是一个重复的问题,但不知何故我无法通过在互联网上查看很多线程来解决这个问题。
我在项目中使用了 yeoman express 生成器。这是我的设置:
服务器在使用 https 的 ngnix 后面。因此,如果我点击https://mywebsite.com,它会在内部重定向到该服务器上的 localhost:3000 。
express.js
var samlUtil = require('./saml-util.js');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
limit: '2mb',
extended: true
}));
app.use(compress());
app.use(cookieParser(config.SERVER_KEYS.SERVER_SECRET));
app.use(express.static(config.root + '/public'));
app.use(methodOverride());
app.use(session({
secret: config.SERVER_KEYS.SERVER_SECRET,
resave: false,
saveUninitialized: true,
cookie: {
expires: false,
secure: true
}
}));
app.use(samlUtil.initialize());
app.use(samlUtil.session());
app.get('/saml/response', samlUtil.protected, function(req, res) {
res.end("Hello " + req.session.passport.user);
});
app.get('/saml/invalid', function(req, res) {
res.end("Authentication failed");
});
app.post('/saml/callback', samlUtil.authenticate('saml', {
failureRedirect: '/saml/response/',
failureFlash: true
}), function(req, res) {
req.session.save(function() {
res.redirect('/saml/response/');
})
});
app.get('/saml/login', samlUtil.authenticate('saml', {
failureRedirect: '/saml/response/',
failureFlash: true
}), function(req, res) {
res.redirect('/saml/response/');
});
saml-util.js
var path = require('path');
var passport = require('passport');
var root = path.normalize(__dirname + '/../..');
var constant = require(root + '/app/util/constants.js');
var config = require(constant.APP_CONFIG_FILE);
var SamlStrategy = require('passport-saml').Strategy;
var users = [];
function findByEmail(email, fn) {
for (var i = 0, len = users.length; i < len; i++) {
var user = users[i];
if (user.email === email) {
return fn(null, user);
}
}
return fn(null, null);
}
// Passport session setup.
// To support persistent login sessions, Passport needs to be able to
// serialize users into and deserialize users out of the session. Typically,
// this will be as simple as storing the user ID when serializing, and finding
// the user by ID when deserializing.
passport.serializeUser(function(user, done) {
console.log('serializing');
done(null, user.email);
});
passport.deserializeUser(function(id, done) {
console.log('de-serializing');
findByEmail(id, function(err, user) {
done(err, user);
});
});
passport.use(new SamlStrategy({
issuer: config.SAML.ISSUER_URL,
path: config.SAML.PATH,
entryPoint: config.SAML.ENTRY_POINT,
cert: config.SAML.CERTIFICATE,
}, function(profile, done) {
console.log('got profile');
console.log(profile);
if (!profile.email) {
return done(new Error("No email found"), null);
}
process.nextTick(function() {
console.log('Finding by email');
findByEmail(profile.email, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
console.log('new user');
users.push(profile);
return done(null, profile);
}
console.log('existing user');
return done(null, user);
})
});
}));
passport.protected = function protected(req, res, next) {
console.log('is isAuthenticated =' + req.isAuthenticated());
if (req.isAuthenticated()) {
return next();
}
res.redirect('/saml/invalid');
};
exports = module.exports = passport;
怎么了:
- 我可以点击 URL:/saml/login
- 被重定向到 okta 登录页面(我有身份设置)
- 我登录成功
我被重定向到 URL:/saml/callback 并响应:
{issuer: { _: 'http://www.okta.com/exkctyzcknbMikNjl0h7', '$': { Format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:entity', 'xmlns:saml2': 'urn:oasis:names:tc:SAML:2.0:assertion' } }, sessionIndex: '_3acb290873febaf825cd', nameID: 'ashutosh@myemail.com', nameIDFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', nameQualifier: undefined, spNameQualifier: undefined, firstName: 'Ashutosh', lastName: 'Pandey', email: 'ashutosh@myemail.com', getAssertionXml: [Function] }
在 /saml/callback URL 中,我可以看到 req.user 中返回的值,但 saml-util 中的 req.isAuthenticated() 始终返回 false。