我不知道下一步要走什么,所以我将在这里发布我的问题,因为我已经看到了与此问题相关的一些问题。不幸的是,所提供的解决方案在我的情况下不起作用,并且我不知道该尝试什么。
所以有一些背景知识:我有一个针对ADFS系统进行身份验证的NodeJS / ExpressJS / passport-saml应用程序。该问题的SSO部分工作正常,但我似乎无法使SLO部分正常工作。
发生的情况是,当我启动SP启动或IdP启动的注销时,它挂在第一个SP上。此第一个SP已正确注销,但随后重定向到第一个SP的登录页面,并一直等待输入凭据,从而有效地终止了必须执行的重定向链。
到目前为止,我已经尝试了很多,包括在我的SLO ADFS端点/ NodeJS服务器上同时使用POST和HTTP重定向绑定,修改路由等。>
当前实现如下:SLO端点配置(对于每个SP相同,涂黑的部分包含):SP-服务器上的passport-saml配置如下:
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IMPORTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// // NodeJS native const path = require('path'); const fs = require('fs'); // NodeJS packages const SamlStrategy = require('passport-saml').Strategy; const { Database } = require('../../Database'); // Custom imports const { ApplicationConfiguration } = require('../../ApplicationConfiguration'); // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CONSTANTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// let strategy = {}; // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ INIT ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// /** * Initialise the passport saml strategy with the necessary configuration parameters. */ const initStrategy = () => { // Get additional required configuration const config = ApplicationConfiguration.getProperties([ ['CGS_HOST'], ['AUTH_PORT'], ['SSO', 'host'], ['SSO', 'identifier'], ['SSO', 'cert'], ['SSO', 'algorithm'], ['HTTPS_CERT_PRIVATE_PATH'], ]); // Define the SAML strategy based on configuration strategy = new SamlStrategy( { // URL that should be configured inside the AD FS as return URL for authentication requests callbackUrl: `https://${<sp_host_name>}:${<sp_port_value>}/sso/callback`, // URL on which the AD FS should be reached entryPoint: <idp_host_name>, // Identifier for the CIR-COO application in the AD FS issuer: <sp_identifier_in_idp>, identifierFormat: null, // CIR-COO private certificate privateCert: fs.readFileSync(<sp_server_private_cert_path>, 'utf8'), // Identity Provider's public key cert: fs.readFileSync(<idp_server_public_cert_path>, 'utf8'), authnContext: ['urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'], // AD FS signature hash algorithm with which the response is encrypted signatureAlgorithm: <idp_signature_algorithm>, // Single Log Out URL AD FS logoutUrl: <idp_host_name>, // Single Log Out callback URL logoutCallbackUrl: `https://${<sp_host_name>}:${<sp_port_value>}/slo/callback`, // skew that is acceptable between client and server when checking validity timestamps acceptedClockSkewMs: -1, }, async (profile, done) => { // Map ADFS groups to Group without ADFS\\ characters const roles = profile.Roles.map(role => role.replace('ADFS\\', '')); // Get id's from the roles const queryResult = await Database.executeQuery('auth-groups', 'select_group_ids_by_name', [roles]); // Map Query result to Array for example: [1,2] const groupIds = queryResult.map(group => group.id); done(null, { sessionIndex: profile.sessionIndex, nameID: profile.nameID, nameIDFormat: profile.nameIDFormat, id: profile.DistinguishedName, username: profile.DistinguishedName, displayName: profile.DisplayName, groups: profile.Roles, mail: profile.Emailaddress, groupIds, }); }, ); // Return the passport strategy return strategy; }; // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PASSPORT CONFIG ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// /** * Initialise the passport instance and add the saml passport strategy to it for authentication * @param {Object} passport - Passport object */ const initPassport = (passport) => { // (De)serialising passport.serializeUser((user, done) => { done(null, user); }); passport.deserializeUser((user, done) => { done(null, user); }); // Initialise the strategy const passportStrategy = initStrategy(); // Addition strategy to passport passport.use('saml', passportStrategy); }; // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HELPERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// /** * Get the metadata from the Service Provider (this server). * @param {String} publicPath - Path to public certificate * @return {Promise<any>} - Metadata object for this application */ const getMetaData = publicPath => new Promise((resolve) => { const metaData = strategy.generateServiceProviderMetadata({}, fs.readFileSync(path.join(publicPath), 'utf8')); resolve(metaData); }); /** * Construct a Single Logout Request and send it to the IdP. * @param {Object} req - Default request object * @param {Object} res - Default response object */ const logout = (req, res) => { // Construct SLO request for IdP strategy.logout(req, (err, url) => { req.logOut(); // Redirect to SLO callback URL and send logout request. return res.redirect(url); }); }; const getStrategy = () => strategy; module.exports = { initPassport, getStrategy, getMetaData, logout, };
以及相关的路线和功能如下:
const logOutLocalSession = sid => new Promise(((resolve, reject) => { log.info(`Received request to destroy session with sid ${sid}.`); // Destroy local session store.destroy(sid, (err) => { if (err) { log.error(`Error occurred while logging out local session with SID ${sid}: ${err}`); reject('Onbekende fout opgetreden bij uitloggen lokaal.'); } log.info(`Successfully logged out user locally with SID ${sid}.`); resolve(); }); })); const logOutAllSessions = async (req, res) => { // Extract username to get all sessions const { username } = req.session.passport.user; log.info(`Received request to log user ${username} out of all sessions.`); const sessionIdsRes = await Database.executeQuery('sessions', 'select_sids_by_user_id', [username]); // Loop over all sessions and destroy them const destroyPromises = []; sessionIdsRes.forEach((sessionIdRes) => { destroyPromises.push(logOutLocalSession(sessionIdRes.sid)); }); await Promise.all(destroyPromises); // Remove local session from request req.session = null; log.info(`User ${username} logged out successfully from all known sessions.`); }; const logOutIdp = (req, res) => { const { username } = req.session.passport.user; log.info(`Received request to log out user ${username} on Identity Provider.`); const strategy = passportImpl.getStrategy(); // Create logout request for IdP strategy.logout(req, async (err, url) => { // Destroy local sessions logOutAllSessions(req, res); // Redirect to SLO callback URL and send logout request. return res.redirect(url); }); }; // SP initiated logout sequence app.get('/auth/logout', (req, res) => { const { username } = req.session.passport.user; // If user not logged in, redirect to login if (!req.user) { return res.redirect('/saml/login'); } if (username === 'Administrator' || username === 'Support user') { logOutLocalSession(req.session.id); } else { logOutIdp(req, res); } }); // IdP initiated logout sequence or from other SP app.post('/slo/callback', logOutAllSessions);
如果缺少某些信息,我将能够提供。我希望我能为下一步的尝试找到一些线索!预先感谢!
我不知道下一步要走什么,所以我将在这里发布我的问题,因为我已经看到了与此问题相关的一些问题。不幸的是,提供的解决方案在我的情况下不起作用,并且我不...
关于ADFS配置:
“受信任的URL”应该是ADFS注销端点-您可以在元数据中看到它-这样ADFS可以清除cookie。