I'm new to passport
and passport-saml
, and I'm trying to build a Node.js server that uses our University's Shibboleth identity provider for single sign-on. I'm pretty close to getting it all working, but I'm hitting a snag during the /login/callback that I think is related to the encryption configuration.
I am able to redirect the client to the sign-in page, and after a successful sign-in, the IdP does a POST back to my /login/callback route. Then I get this error:
Error: Invalid signature at SAML.validatePostResponse (.../node_modules/passport-saml/lib/passport-saml/saml.js:357:21) at Strategy.authenticate (.../node_modules/passport-saml/lib/passport-saml/strategy.js:68:18) at ...etc...
This sounds like maybe the certificate I'm passing to the cert
config setting isn't correct? I'm assuming that the decryptionPvk
and cert
config settings supposed to be the private key I used to create my server's cert, and the Identity Provider's HTTPS certificate, respectively? Or should they be something else?
I'm using up-to-date versions of node and all the various modules (express, passport, passport-saml, etc.)
And for reference, here's the server script I'm using to test all of this:
"use strict;"
var https = require('https');
var fs = require('fs');
var express = require("express");
var morgan = require('morgan');
var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var session = require('express-session');
var passport = require('passport');
var saml = require('passport-saml');
var cert = fs.readFileSync('./certs/my-server-https-cert.crt', 'utf-8');
var pvk = fs.readFileSync('./certs/my-server-private.key', 'utf-8');
var uwIdpCert = fs.readFileSync('./certs/our-idp-server-https-cert.pem', 'utf-8');
passport.serializeUser(function(user, done){
done(null, user);
});
passport.deserializeUser(function(user, done){
done(null, user);
});
var samlStrategy = new saml.Strategy({
callbackUrl: 'https://my-domain-name.whatever.edu/login/callback',
entryPoint: 'https://my-university/idp/entry/point',
issuer: 'my-entity-id (domain name registered with university IdP)',
decryptionPvk: pvk,
cert: uwIdpCert
}, function(profile, done){
console.log('Profile: %j', profile);
return done(null, profile);
});
passport.use(samlStrategy);
var app = express();
app.use(morgan('dev'));
app.use(bodyParser.urlencoded({extended: true}));
app.use(cookieParser());
app.use(session({secret: fs.readFileSync('./certs/session-secret.txt', 'utf-8')}));
app.use(passport.initialize());
app.use(passport.session());
app.get('/',
passport.authenticate('saml', {failureRedirect: '/login/fail'}),
function(req, res) {
res.send('Hello World!');
}
);
app.post('/login/callback',
passport.authenticate('saml', { failureRedirect: '/login/fail', failureFlash: true }),
function(req, res) {
res.redirect('/');
}
);
app.get('/login/fail',
function(req, res) {
res.send(401, 'Login failed');
}
);
app.get('/Shibboleth.sso/Metadata',
function(req, res) {
res.type('application/xml');
res.send(200, samlStrategy.generateServiceProviderMetadata(cert));
}
);
//general error handler
app.use(function(err, req, res, next){
console.log('Express error!');
console.error(err.stack);
next(err);
});
var server = https.createServer({
key: pvk,
cert: cert
}, app);
server.listen(process.argv[2] || 44300, function(){
console.log('Listening on port %d', server.address().port)
});
Any help or advice would be most appreciated!