保存到 Express 会话的值在以后的请求中不存在

问题描述 投票:0回答:1

我的应用程序充当第三方服务的 OAuth 服务器,该服务器将通过我们的应用程序中的访问。身份验证过程全部发生在幕后,没有中间屏幕供用户单击登录按钮(因为该过程已经从我们的应用程序内启动)。挑战在于,由于所有 OAuth 请求都是从

iframe
内的第 3 方发起的(因此我们无法与它们交互或拦截它们),因此我们识别应进行身份验证的用户的唯一方法是通过会话饼干。

因此,在注册或登录我们的应用程序时,我们将

req.session.userId
设置为等于当前登录用户的 id。我们期望当
iframe
向我们的 OAuth 端点发起请求时,从
GET /oauth/code
开始,我们可以读取传入请求中
req.session.userId
的值。然而,无论我在 Express 会话上进行哪组配置,这似乎都不起作用。通过更详细地检查请求,我确实注意到具有
userId
值的会话存在于
req.sessionStore
中,因此我设置了 authCode 处理程序来循环这些会话并找到设置了
userId
的会话。

这似乎有效,直到我们在实际环境中注意到在某些特定情况下,用户会登录我们的应用程序并在其 iframe 中看到完全不同的用户帐户。 (一个示例是用户 A 在我们的应用程序和第 3 方中进行身份验证,然后用户 B 打开一个隐身窗口并直接导航到第 3 方网站,而无需登录我们的应用程序)。在这种情况下,用户 A 的会话似乎根据用户 B 的请求出现在 sessionStore 中。这显然是不行的,因此它似乎回到了绘图板并试图确定为什么我们没有得到

userId
根据传入请求从
req.session
返回值。

我将在这里发布相关代码,但如果还有其他内容可以帮助更好地理解问题,请告诉我。

/api/app.js

const session = require('express-session');

// Express Session Middleware
app.set('trust proxy', 1); // this is for ngrok in our local instnace
app.use(session({
  secret: 'starboard penguin disco',
  resave: true,
  name: 'test',
  proxy: true,
  saveUninitialized: true,
  cookie: {
    httpOnly: true,
    sameSite: 'none', 
    secure: false, // this is false now for local testing but true in production
  },
}));

/api/auth.js

exports.login = async (req, res, next) => {
  try {
    const user = await db.User.findOne({
      email: req.body.email,
    });
    const { id, fullName, email } = user;

    const isMatch = await user.comparePassword(req.body.password, next);

    if (isMatch) {
      ({ accessToken, refreshToken } = generateTokens(user));

      // Save user id to sesson cookie
      req.session.userId = id;

      return res.status(201).json({
        id,
        email,
        fullName,
        jwt: accessToken,
        refresh_token: refreshToken,
      });
    }

    return next({
      status: 400,
      message: 'Invalid email or password',
    });
  } catch (err) {
    return next({
      status: 400,
      message: 'Invalid email or password',
    });
  }
};

/api/handlers/oauth.js

// This handles GET calls to /oauth/code and is the first of 3 requests made by 3rd party
exports.getAuthCode = async (req, res) => {
  try {
    // Extract user ID from request
    let userId = '';
    console.log(req.session.userId) // returns undefined
    const sessionStore = promisify(req.sessionStore.all.bind(req.sessionStore));
    const sessions = await sessionStore();
    Object.values(sessions).forEach((session) => {
      if (session.userId) {
        userId = session.userId;
      }
    });
    console.log('getting user id from session');
    console.log(userId);

    // Generate a secure random authorization code
    const codeValue = crypto.randomBytes(16).toString('hex');
    // Save the code along with the client ID, redirect URI, and user ID
    const authCode = await AuthorizationCode.create({
      code: codeValue,
      clientId: req.query.client_id, // Assuming this is passed by the client
      redirectUri: req.query.redirect_uri, // Assuming this is passed by the client
      expiresAt: new Date(Date.now() + 10 * 60 * 1000), // Code expires in 10 minutes
      user: { id: userId },
    });

    await authCode.save();
    return res.redirect(`${req.query.redirect_uri}?code=${authCode.code}`);
  } catch (err) {
    console.error(err);
    return res.status(err.code || 500).json(err);
  }
};
node.js express session session-cookies express-session
1个回答
0
投票

会话配置:您的会话配置似乎适合处理身份验证。但是,我们可以进行一些调整来增强安全性和可靠性:

app.use(session({
  secret: 'starboard penguin disco',
  resave: false, // Set to false to prevent unnecessary session updates
  name: 'test',
  proxy: true,
  saveUninitialized: false, // Set to false to only save initialized sessions
  cookie: {
    httpOnly: true,
    sameSite: 'none', 
    secure: true, // Set to true to enforce HTTPS-only cookies in production
  },
}));

所做的更改:

resave:设置为 false 以防止不必要的会话更新。 saveUninitialized:设置为 false 仅保存已初始化的会话(即有数据)。这有助于减少存储开销。 secure:设置为 true 以在生产环境中强制执行仅 HTTPS 的 cookie。确保您的生产环境设置为 HTTPS。 会话存储:您在 req.sessionStore 中循环会话以查找用户 ID 的方法似乎效率低下,并且容易出现您所描述的问题。相反,您应该直接从 req.session 访问用户 ID:

exports.getAuthCode = async (req, res) => {
  try {
    const userId = req.session.userId; // Access user ID directly from session

    if (!userId) {
      throw new Error('User ID not found in session');
    }

    // Generate a secure random authorization code
    const codeValue = crypto.randomBytes(16).toString('hex');
    // Save the code along with the client ID, redirect URI, and user ID
    const authCode = await AuthorizationCode.create({
      code: codeValue,
      clientId: req.query.client_id,
      redirectUri: req.query.redirect_uri,
      expiresAt: new Date(Date.now() + 10 * 60 * 1000),
      user: { id: userId },
    });

    await authCode.save();
    return res.redirect(`${req.query.redirect_uri}?code=${authCode.code}`);
  } catch (err) {
    console.error(err);
    return res.status(err.code || 500).json(err);
  }
};

这可确保您始终使用会话中存储的用户 ID 来生成授权代码。 检查一下我已经修复了一些东西并简要提及它

© www.soinside.com 2019 - 2024. All rights reserved.