我有一个 node.js 脚本,使用 passport-saml 来模拟 SP。我的目标是将其连接到此 TestShib IdP,但出现以下错误: SAML 2 SSO 配置文件未配置为依赖方

根据我阅读的内容 here ,我知道我需要提供 SP 元数据,但我不知道如何提供。我知道passport-saml 具有以下功能:generateServiceProviderMetadata(decryptionCert) 和我虽然拥有所需的证书,但我不知道如何使其全部工作。

另外,如果可能的话,我想避免必须注册我的 SP。

这是我的脚本:

const https = require('https');
const fs = require('fs');
const express = require('express');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const session = require('express-session');
const passport = require('passport');
const saml = require('passport-saml');


/*
---------------------------------------------------------------------------------------------------
--  certificates
---------------------------------------------------------------------------------------------------
*/

//  for https server
const https_cert = fs.readFileSync('certificate.pem', 'utf-8');
const https_pvk = fs.readFileSync('privatekey.pem', 'utf-8');

//  from idp's metadata
const idp_cert_1 = 'MIIDAzCCAeugAwIBAgIVAPX0G6LuoXnKS0Muei006mVSBXbvMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAMMEGlkcC50ZXN0c2hpYi5vcmcwHhcNMTYwODIzMjEyMDU0WhcNMzYwODIzMjEyMDU0WjAbMRkwFwYDVQQDDBBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg9C4J2DiRTEhJAWzPt1S3ryhm3M2P3hPpwJwvt2q948vdTUxhhvNMuc3M3S4WNh6JYBs53R+YmjqJAII4ShMGNEmlGnSVfHorex7IxikpuDPKV3SNf28mCAZbQrX+hWA+ann/uifVzqXktOjs6DdzdBnxoVhniXgC8WCJwKcx6JO/hHsH1rG/0DSDeZFpTTcZHj4S9MlLNUtt5JxRzV/MmmB3ObaX0CMqsSWUOQeE4nylSlp5RWHCnx70cs9kwz5WrflnbnzCeHU2sdbNotBEeTHot6a2cj/pXlRJIgPsrL/4VSicPZcGYMJMPoLTJ8mdy6mpR6nbCmP7dVbCIm/DQIDAQABoz4wPDAdBgNVHQ4EFgQUUfaDa2mPi24x09yWp1OFXmZ2GPswGwYDVR0RBBQwEoIQaWRwLnRlc3RzaGliLm9yZzANBgkqhkiG9w0BAQsFAAOCAQEASKKgqTxhqBzROZ1eVy++si+eTTUQZU4+8UywSKLia2RattaAPMAcXUjO+3cYOQXLVASdlJtt+8QPdRkfp8SiJemHPXC8BES83pogJPYEGJsKo19l4XFJHPnPy+Dsn3mlJyOfAa8RyWBS80u5lrvAcr2TJXt9fXgkYs7BOCigxtZoR8flceGRlAZ4p5FPPxQR6NDYb645jtOTMVr3zgfjP6Wh2dt+2p04LG7ENJn8/gEwtXVuXCsPoSCDx9Y0QmyXTJNdV1aB0AhORkWPlFYwp+zOyOIR+3m1+pqWFpn0eT/HrxpdKa74FA3R2kq4R7dXe4G0kUgXTdqXMLRKhDgdmA==';
const idp_cert_2 = 'MIIDAzCCAeugAwIBAgIVAPX0G6LuoXnKS0Muei006mVSBXbvMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAMMEGlkcC50ZXN0c2hpYi5vcmcwHhcNMTYwODIzMjEyMDU0WhcNMzYwODIzMjEyMDU0WjAbMRkwFwYDVQQDDBBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg9C4J2DiRTEhJAWzPt1S3ryhm3M2P3hPpwJwvt2q948vdTUxhhvNMuc3M3S4WNh6JYBs53R+YmjqJAII4ShMGNEmlGnSVfHorex7IxikpuDPKV3SNf28mCAZbQrX+hWA+ann/uifVzqXktOjs6DdzdBnxoVhniXgC8WCJwKcx6JO/hHsH1rG/0DSDeZFpTTcZHj4S9MlLNUtt5JxRzV/MmmB3ObaX0CMqsSWUOQeE4nylSlp5RWHCnx70cs9kwz5WrflnbnzCeHU2sdbNotBEeTHot6a2cj/pXlRJIgPsrL/4VSicPZcGYMJMPoLTJ8mdy6mpR6nbCmP7dVbCIm/DQIDAQABoz4wPDAdBgNVHQ4EFgQUUfaDa2mPi24x09yWp1OFXmZ2GPswGwYDVR0RBBQwEoIQaWRwLnRlc3RzaGliLm9yZzANBgkqhkiG9w0BAQsFAAOCAQEASKKgqTxhqBzROZ1eVy++si+eTTUQZU4+8UywSKLia2RattaAPMAcXUjO+3cYOQXLVASdlJtt+8QPdRkfp8SiJemHPXC8BES83pogJPYEGJsKo19l4XFJHPnPy+Dsn3mlJyOfAa8RyWBS80u5lrvAcr2TJXt9fXgkYs7BOCigxtZoR8flceGRlAZ4p5FPPxQR6NDYb645jtOTMVr3zgfjP6Wh2dt+2p04LG7ENJn8/gEwtXVuXCsPoSCDx9Y0QmyXTJNdV1aB0AhORkWPlFYwp+zOyOIR+3m1+pqWFpn0eT/HrxpdKa74FA3R2kq4R7dXe4G0kUgXTdqXMLRKhDgdmA==';


/*
---------------------------------------------------------------------------------------------------
--  passport-saml setup
---------------------------------------------------------------------------------------------------
*/
const saml_strategy = new saml.Strategy(
    {
        'callbackUrl': 'https://localhost:44300/login/callback',
        'entryPoint': 'https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO',
        'issuer': 'https://localhost:44300',
        'decryptionPvk': https_pvk,
        'cert': [idp_cert_1, idp_cert_2]
    },
    function (profile, done)
    {
        console.log('passport.use() profile: %s \n', JSON.stringify(profile));
        return done(
            null,
            {
                'nameIDFormat': profile.nameIDFormat,
                'nameID': profile.nameID
            }
        );
    }
);

passport.serializeUser(function (user, done) {
    console.log('passport.serializeUser() user: %s \n', JSON.stringify(user));
    done(null, user);
});

passport.deserializeUser(function (user, done) {
    console.log('passport.deserializeUser() user: %s \n', JSON.stringify(user));
    done(null, user);
});

passport.use(saml_strategy);

/*
---------------------------------------------------------------------------------------------------
--  express setup
---------------------------------------------------------------------------------------------------
*/
const app = express();

// configure view engine to render EJS templates
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');

// additional settings for logging and parsing
app.use(morgan('dev'));
app.use(cookieParser());
app.use(bodyParser.urlencoded({'extended': true}));
app.use(session({ 'secret': 'this-is-secret', 'resave': false, 'saveUninitialized': false }));

// initialize Passport and restore authentication state, if any, from the session
app.use(passport.initialize());
app.use(passport.session());


/*
---------------------------------------------------------------------------------------------------
--  routes
---------------------------------------------------------------------------------------------------
*/

app.get(
    '/',
    function (req, res) {
        if (req.isAuthenticated()) {
            console.log('GET [/] user authenticated! req.user: %s \n', JSON.stringify(req.user));
            res.render('home', { 'user': req.user });
        } else {
            console.log('GET [/] user not authenticated! \n');
            res.render('home', { 'user': null });
        }
    }
);

app.get(
    '/login',
    passport.authenticate('saml', { 'successRedirect': '/', 'failureRedirect': '/login' })
);


app.post(
    '/login/callback',
    passport.authenticate('saml', { 'failureRedirect': '/', 'failureFlash': true }),
    function(req, res) {
        console.log('POST [/login] \n');
        res.redirect('/');
    }
);


app.get(
    '/profile',
    function(req, res){
        if (req.isAuthenticated()) {
            console.log('GET [/profile] user authenticated! req.user: %s \n', JSON.stringify(req.user));
            res.render('profile', { 'user': req.user });
        } else {
            console.log('GET [/profile] user not authenticated! \n');
            res.redirect('/login');
        }
    }
);


app.get(
    '/logout',
    function(req, res) {
        console.log('GET [/logout] \n');
        passport._strategy('saml').logout(
            req,
            function(err, requestUrl) {
                req.logout();
                res.redirect('/');
            }
        );
    }
);

/*
---------------------------------------------------------------------------------------------------
--  start https server
---------------------------------------------------------------------------------------------------
*/

const server = https.createServer({
    'key': https_pvk,
    'cert': https_cert
}, app);

server.listen(44300, function() {
    console.log('Listening on https://localhost:%d', server.address().port)
});

在此先感谢您的帮助和指导!

最佳答案



为了完整起见,我将列出所需的所有步骤。但是您已经完成了前 2 个,因此您可以跳过它们:

  • Create the cert/key
  • 在您的 SamlStrategy 中,定义 decryptionPvk
    decryptionPvk: fs.readFileSync('./credentials/key.pem', 'utf-8'),
    
  • 最后,您可以创建一个公开元数据的端点(这是您缺少的部分)
    app.get('/metadata',
      function(req, res) {
        const decryptionCert = fs.readFileSync('./credentials/cert.pem', 'utf-8');
        res.type('application/xml');
        res.send((myStrategy.generateServiceProviderMetadata(decryptionCert)));
      }
    );
    

    这是重新格式化以匹配您发布的示例代码的样子:
    app.get(
        '/metadata',
        function(req, res) {
            res.type('application/xml');
            res.send((saml_strategy.generateServiceProviderMetadata(https_cert)));
        }
    );
    

  • 完成后,您应该能够从/metadata 路径访问您的元数据。



    我不是 SAML 专家,但您可能无法避免注册。这仅适用于设置为信任所有 SP 的 IdP,但这没有多大意义,因为 SAML 的一个关键部分是在 IdP 和 SP 之间建立的双向信任。

    关于node.js - 如何使用passport-saml向TestShib IdP提供SP元数据?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49564161/

    10-15 12:12