Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node with Express session issue

I use the following code which works, However after a few success calls (5-10), we sometimes get an internal server error:

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

I've tried all the latest open source versions.

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

My code from the stack is:

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

Which is the end of the code here:

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

This is the full code (I pass the app which is simply an express server):

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

logon.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
  });
};

Sometimes when I debug, I see that the function is running out from GetUsers which is an async function and stops in })(req, res, next);, maybe it's an async issue.

We want to use this code in prod instead of the previous Java implementation.

If I can use another technique for oidc, please let me know.


UPDATE

Each should be a single call and log in this order:

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

But, when I get the error:

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

All the successful calls have the right log order (1-3).

When it fails, the first call User claims received doesn't happen, just the second and the error.

If there is another way to achieve this (other lib's etc), please let me know.


I've found this library which may help as it doesn't use passport (I want to reduce deps to see where the problem is coming from).

When I try something like this:

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

I get this error: issuer response_mode supporting only "query" or "fragment", but when I run the code above (in the beginning of the post) with the same issuer and response_mode, everything is working, any ideas?

like image 285
Beno Odr Avatar asked Aug 05 '20 05:08

Beno Odr


1 Answers

We faced a similar issue but we had a more intermittent behavior, we had the error when logging in on Safari but not on Chrome.

From what I understood, this is because the session cookie is getting set when we are first authenticating, which stores the state, code-verifier (only if using PKCE flow) and other values the OIDC client needs to validate the authentication.

However, when the /callback URL is hit, the browser sends this session cookie to the server to complete authentication.

Whenever this cookie is not sent, thats when this error occurs, because the callback assumes this to be a new request and it crashes...

for us this was behaving in 2 ways.

  1. Cookie { Same-site: 'Lax' secure: true }

    Works for chrome but this did not work on safari

  2. Cookie { Same-site: 'None' secure: true }

    Works for chrome and on safari

this needs to be set on the express-session middleware (sorry Im not sure of the syntax thats needed)

like image 148
user2296208 Avatar answered Oct 21 '22 15:10

user2296208