首页 > 解决方案 > 存在 Express 会话问题的节点

问题描述

我使用以下代码有效,但是在几次成功调用(5-10)之后,我们有时会收到内部服务器错误:

req.session["oidc:accounts.rvm.com"] is undefined

我已经尝试了所有的latest开源版本。

Error: did not find expected authorization request details in session, req.session["oidc:accounts.rvm.com"] is undefined
at /opt/node_app/app/node_modules/openid-client/lib/passport_strategy.js:125:13
at OpenIDConnectStrategy.authenticate (/opt/node_app/app/node_modules/openid-client/lib/passport_strategy.js:173:5)
at attempt (/opt/node_app/app/node_modules/passport/lib/middleware/authenticate.js:366:16)
at authenticate (/opt/node_app/app/node_modules/passport/lib/middleware/authenticate.js:367:7)
at /opt/node_app/app/src/logon.js:92:7 *******
at Layer.handle [as handle_request] (/opt/node_app/app/node_modules/express/lib/router/layer.js:95:5)
at next (/opt/node_app/app/node_modules/express/lib/router/route.js:137:13)
at Route.dispatch (/opt/node_app/app/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/opt/node_app/app/node_modules/express/lib/router/layer.js:95:5)
at /opt/node_app/app/node_modules/express/lib/router/index.js:281:22

我的堆栈代码是:

at /opt/node_app/app/src/logon.js:92:7

这是代码的结尾:

})(req, res, next);   // here is line 92 but not sure if it's related 

这是完整的代码(我通过了app这只是一个快速服务器):

index.js

const express = require('express');
const logon = require('./logon');

const app = express();
const port = process.env.PORT || 4000;

logon(app)
  .then(() => {
    console.log('process started');
  });
app.use(express.json());

app.listen(port,
  () => console.log(`listening on port: ${port}`));

登录.js

const { Issuer, Strategy } = require('openid-client');
const cookieParser = require('cookie-parser');
const cookieSession = require('cookie-session');
const azpi = require('./azpi');
const bodyParser = require('body-parser');
const passport = require('passport');

module.exports = async (app) => {
  let oSrv;
  const durl = `${process.env.srvurl}/.well-known/openid-configuration`;
  try {
    oSrv = await Issuer.discover(durl);
  } catch (err) {
    console.log('error occured', err);
    return;
  }

  app.get('/', prs(), passport.authenticate('oidc'));

  const oSrvCli = new oSrv.Client({
    client_id: process.env.ci,
    client_secret: process.env.cs,
    token_endpoint_auth_method: 'client_secret_basic',
  });

  passport.serializeUser((user, done) => {
    done(null, user);
  });
  passport.deserializeUser((obj, done) => {
    done(null, obj);
  });

  const cfg = {
    scope: 'openid',
    redirect_uri: process.env.ruri,
    response_type: 'code',
    response_mode: 'form_post',
  };

  const prs = () => (req, res, next) => {
    passport.use(
      'oidc',
      new Strategy({ oSrvCli , cfg }, (tokenset, done) => {
        const claims = tokenset.claims();
        // first log
        console.log(`1. ------------User claims received------------);
        const user = {
          name: claims.name,
          id: claims.sub,
          id_token: tokenset.id_token,
        };
        return done(null, user);
      }),
    );
    next();
  };
  app.use(
    bodyParser.urlencoded({
      extended: false,
    }),
  );
  app.use(cookieParser('csec'));
  app.use(
    cookieSession({
      name: 'zta-auth',
      secret: 'csect',
    }),
  );

  app.use(passport.initialize());
  app.use(passport.session());

  app.get('/redirect', async (req, res, next) => {
    await passport.authenticate('oidc', async (err, user) => {
    // print second log
    console.log('2. ------------redirect Called!------------');
      if (err) {
        console.log(`Authentication failed: ${err}`);
        return next(err);
      }
      if (!user) {
        return res.send('no identity');
      }

      req.login(user, async (e) => {
        if (e) {
          console.log('not able to login', e);
          return next(e);
        }
        try {
          const url = await azpi.GetUsers(user.id_token);
          // print last log
          console.log('3. ------------user process finished successfully----');
          return res.redirect(url);
          
        } catch (er) {
          res.send(er.message);
        }
      });
    })(req, res, next);   //here is the error
  });
};

有时当我调试时,我看到该函数正在运行,GetUsers这是一个异步函数并停止在 中})(req, res, next);,可能是一个异步问题。

我们想在 prod 中使用这段代码,而不是之前的 Java 实现。

如果我可以对 oidc 使用另一种技术,请告诉我。


更新

每个都应该是一个单独的呼叫并按以下顺序登录:

1. ------------User claims received------------
2. ------------redirect Called!------------
3. ------------user process finished successfully----

但是,当我收到错误消息时:

1. ------------User claims received------------
2. ------------redirect Called!------------
3. ------------user process finished successfully----

2. ------------redirect Called!------------
Authentication failed: Error: did not find expected authorization request details in session, req.session

所有成功的调用都有正确的日志顺序 (1-3)。

当它失败时,第一次调用User claims received不会发生,只会发生第二次调用和错误。

如果有其他方法可以实现这一点(其他库等),请告诉我。


我发现这个库可能会有所帮助,因为它不使用护照(我想减少部门以查看问题来自何处)。

当我尝试这样的事情时:

app.use(
    auth({
     issuerBaseURL: `${URL}/.well-known/openid-configuration`,
     authorizationParams: {
    ...
     response_mode: 'form_post',
    }

我收到此错误: ,但是当我使用相同的andissuer response_mode supporting only "query" or "fragment"运行上面的代码(在帖子的开头)时,一切正常,有什么想法吗?issuerresponse_mode

标签: javascriptnode.jsasync-awaitpassport.jsopenid-connect

解决方案


这个问题似乎是一种竞争条件,如果您碰巧同时收到两个正在进行的请求,当一个请求完成时,它会在另一个有机会完成之前清除会话 cookie。对于它的价值,你不是唯一遇到这个问题的人。

我认为这不是库本身的问题,我认为问题更多在于会话库。您可能想尝试将/选项设置为express-session的库并检查您是否仍然看到相同的问题,例如saveUninitializedresavefalse

const session = require('express-session');
...
app.use(session({
  saveUninitialized: false,
  resave: false
});

cookie-session此库与您使用的库之间的唯一区别是express-session仅将会话 ID 存储在 cookie 中,数据存储在服务器端。如果您发现它有效,那么您可以考虑使用更多的生产级存储(默认设置是使用内存中的存储)。

FWIW - 你只需要配置一次策略,看看它的作用,如果这是问题的一部分,我会感到惊讶,但我会修复它以防万一


推荐阅读