Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/33.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/svg/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Node.js NodeJS/ExpressJS/passport saml ADFS单点注销实现_Node.js_Express_Passport.js_Adfs_Passport Saml - Fatal编程技术网

Node.js NodeJS/ExpressJS/passport saml ADFS单点注销实现

Node.js NodeJS/ExpressJS/passport saml ADFS单点注销实现,node.js,express,passport.js,adfs,passport-saml,Node.js,Express,Passport.js,Adfs,Passport Saml,我不知道下一步要去哪里,所以我将在这里发布我的问题,因为我已经看到了一些关于这个问题的相关问题。不幸的是,在我的情况下,提供的解决方案不起作用,我不知道还有什么可以尝试 所以有一些背景:我有一个NodeJS/ExpressJS/passport saml应用程序,它可以根据ADFS系统进行身份验证。SSO部分工作得很好,但我似乎无法让SLO部分工作 发生的情况是,当我启动SP启动或IdP启动的注销时,它挂起在第一个SP上。第一个SP正在正确注销,但随后它被重定向到第一个SP的登录页面,并一直等待

我不知道下一步要去哪里,所以我将在这里发布我的问题,因为我已经看到了一些关于这个问题的相关问题。不幸的是,在我的情况下,提供的解决方案不起作用,我不知道还有什么可以尝试

所以有一些背景:我有一个NodeJS/ExpressJS/passport saml应用程序,它可以根据ADFS系统进行身份验证。SSO部分工作得很好,但我似乎无法让SLO部分工作

发生的情况是,当我启动SP启动或IdP启动的注销时,它挂起在第一个SP上。第一个SP正在正确注销,但随后它被重定向到第一个SP的登录页面,并一直等待输入凭据,从而有效地停止了必须发生的重定向链

到目前为止,我已经尝试了很多,包括在我的SLO ADFS端点/NodeJS服务器上使用POST和HTTP重定向绑定,修改路由等等

目前的执行情况如下: 对于每个SP,SLO端点配置相等,断电部分包含:

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,
};

如果有一些信息丢失,我将能够提供。我真的希望我能得到一些关于下一步尝试的线索!提前谢谢

在ADFS配置方面:

可信URL应该是ADFS注销端点(您可以在元数据中看到),以便ADFS可以清除Cookie

响应URL应该是应用程序中的端点。这需要SLO响应,以便它可以清除客户端cookie

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);