【问题标题】:How to provide SP metadata to TestShib IdP using passport-saml?如何使用 passport-saml 向 TestShib IdP 提供 SP 元数据?
【发布时间】:2023-04-08 10:10:01
【问题描述】:

我有一个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)
});

提前感谢您的帮助和指导!

【问题讨论】:

    标签: node.js shibboleth passport-saml


    【解决方案1】:

    我知道passport-saml 有这个功能:generateServiceProviderMetadata(decryptionCert),而我虽然有所需的证书,但我不知道如何让它全部工作。

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

    1. Create the cert/key

    2. 在您的 SamlStrategy 中,定义 decryptionPvk

      decryptionPvk: fs.readFileSync('./credentials/key.pem', 'utf-8'),
      
    3. 最后,您可以创建一个公开元数据的端点(这是您缺少的部分)

      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 路径访问您的元数据。

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

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

    【讨论】:

    • 所以我们在 /metadata 中提供了元数据。我们不需要在配置中的某处指定它吗? IDP 如何知道在哪里访问它
    • 你所说的“配置”是什么配置?通常,当您将 SP 设置为使用新的 IdP 时,您会向 IdP 管理员发送 SP 的元数据或指向该元数据的 URL。
    猜你喜欢
    • 2018-07-29
    • 1970-01-01
    • 2017-03-25
    • 2021-07-09
    • 1970-01-01
    • 2015-09-24
    • 2015-04-09
    • 1970-01-01
    • 2021-10-16
    相关资源
    最近更新 更多