Данные сеанса, по-видимому, не отправляются с сервера Express js на клиент Sveltekit

все, поэтому я закончил создание своего веб-приложения и развернул свой сервер и интерфейс отдельно.

Итак, проблема, похоже, в том, что мой сервер не отправляет данные сеанса клиенту (я так думаю, я не уверен).

Я собираюсь поделиться частями своего кода, имеющими отношение к проблеме.

так что это мои сообщения/+page.svelte -

<script>
import { onMount } from 'svelte';
onMount(async () => {
        try {
            console.info('Fetching logged in user ID');
            const authResponse = await fetch('https://messenger-tu85.onrender.com/api/loggedInUserId', {
                credentials: 'include'
            });

            console.info('Auth Response:', authResponse);
            if (!authResponse.ok) {
                window.location.href = '/signin';
                return;
            }

    // other javascript code
</script>

Как видно из приведенного выше фрагмента кода, на моей странице сообщений я пытаюсь запретить доступ к странице неаутентифицированным пользователям. Я делаю это, отправляя запрос на выборку на конечную точку на сервере, чтобы определить, вошел ли пользователь в систему или нет. Это конечная точка -

app.get('/api/loggedInUserId', async (req, res) => {
  console.info('Session:', req.session);
  console.info('User:', req.user);
  try {
    const user = await User.findById(req.user._id);
    if (!user) {
      throw new Error('User not found');
    }
    res.json({ loggedInUserId: user._id });
  } catch (error) {
    console.error('Error fetching user:', error);
    res.status(500).json({ error: 'Server Error' });
  }
});

Проблема в том, что мое приложение регистрирует пользователя после аутентификации в Google с помощью Passport, но даже после успешного входа в систему с помощью Google я не могу получить доступ к странице сообщений, а конечная точка с сервера выводит на консоль следующее:

Session: Session {
  cookie: {
    path: '/',
    _expires: 2024-07-17T15:28:12.066Z,
    originalMaxAge: 86400000,
    httpOnly: false,
    secure: true,
    sameSite: 'none'
  }
}
User: undefined
Error fetching user: TypeError: Cannot read properties of undefined (reading '_id')

Это мой Passport.js, простите, что все публикую, не могу понять, какую часть пропустить —

const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const User = require('../models/User');

module.exports = function(passport) {
  passport.use(new GoogleStrategy({
      clientID: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      callbackURL: 'https://messenger-tu85.onrender.com/auth/google/callback',
    },
    (accessToken, refreshToken, profile, done) => {
      console.info('Google profile:', profile);

      User.findOne({ googleId: profile.id })
        .then(existingUser => {
          if (existingUser) {
            console.info('Existing user found:', existingUser);
            return done(null, existingUser);
          } else {
            const tempUser = {
              googleId: profile.id,
              email: profile.emails[0].value
            };
            console.info('Creating new temp user:', tempUser);
            return done(null, tempUser);
          }
        })
        .catch(err => {
          console.error('Error finding user:', err);
          return done(err);
        });
    }
  ));

  passport.serializeUser((user, done) => {
    console.info('Serialize user:', user);
    if (user._id) {
      done(null, user._id);
    } else {
      done(null, user);
    }
  });

  passport.deserializeUser((id, done) => {
    console.info('Deserialize user id:', id);
    if (typeof id === 'object' && id.googleId) {
      done(null, id);
    } else {
      User.findById(id)
        .then(user => {
          console.info('Deserialized user:', user);
          done(null, user);
        })
        .catch(err => {
          console.error('Error fetching user:', err);
          done(err);
        });
    }
  });
};

Теперь возьмите вот это: "console.info('Найдено существующий пользователь:', существующийпользователь);" консоль моего сервера распечатывает -

Existing user found: {
  _id: new ObjectId('6693d7a09886ac04b8666b2e'),
  googleId: 'prints out my google id here',
  email: 'prints out my email here',
  username: 'prints out my username i chose when i registered on my own application',
  selectedUsers: [ new ObjectId('6693da089886ac04b8666b58') ],
  __v: 0
}

Также эта часть "console.info('Сериализация пользователя:', user);" распечатывает -

Serialize user: {
  _id: new ObjectId('6693d7a09886ac04b8666b2e'),
  googleId: 'my google id',
  email: 'my email',
  username: 'my username',
  selectedUsers: [ new ObjectId('6693da089886ac04b8666b58') ],
  __v: 0
}

Итак, насколько я понимаю, эта сериализация предназначена для установки данных в сеансе, и тот факт, что данные вошедшего в систему пользователя были сериализованы, означает, что они существуют в сеансе, верно?

Однако паспорт.deserializeUser никогда не достигается, поскольку операторы консоли никогда не распечатываются.

Это мой session.js –

const session = require('express-session');
const MongoStore = require('connect-mongo');

module.exports = (passport) => {
  return session({
    secret: process.env.SECRET,
    resave: false,
    saveUninitialized: false,
    store: MongoStore.create({
      mongoUrl: process.env.MONGODB_URI,
      collectionName: 'sessions'
    }),
    cookie: {
      maxAge: 24 * 60 * 60 * 1000, // 1 day
      secure: process.env.NODE_ENV === 'production', // Set to true in production
      httpOnly: false,
      sameSite: 'none' // Adjust based on your cross-origin requirements
    }
  });
};

Еще раз прошу прощения, если заставляю вас читать много кода, я просто не знаю, какие части опустить, что может быть источником моей проблемы.

Это мой server.js, где, как я подозреваю, может быть проблема:

// imports at top of the file
dotenv.config();
const connectDB = require('./config/mongoose');
connectDB();

// Initialize Passport
const initializePassport = require('./config/passport');
initializePassport(passport);
const server = createServer(app);
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use(require('./middleware/session')(passport));

app.use(cors({
  origin: 'https://svelte-of1p.onrender.com',
  credentials: true
}));

app.use(session(passport)); // does not work even if i remove this line
app.use(passport.initialize());
app.use(passport.session());

app.get('/api/loggedInUserId', async (req, res) => {
  console.info('Session:', req.session);
  console.info('User:', req.user);
  try {
    const user = await User.findById(req.user._id);
    if (!user) {
      throw new Error('User not found');
    }
    res.json({ loggedInUserId: user._id });
  } catch (error) {
    console.error('Error fetching user:', error);
    res.status(500).json({ error: 'Server Error' });
  }
});

Вот и все. Надеюсь, моя проблема была хорошо объяснена, и кто-то может предложить решение.

Заранее спасибо.

🤔 А знаете ли вы, что...
Node.js обеспечивает высокую скорость обработки ввода-вывода (I/O), благодаря асинхронной модели.


63
1

Ответ:

Решено

Итак, проблема в том, что мой клиент и сервер развернуты отдельно в разных доменах. Мой сервер обрабатывает аутентификацию, но должен быть способ сообщить клиенту, что конкретный пользователь аутентифицирован. Откуда клиент мог об этом узнать?

Когда я вхожу на сервер, для пользователя создается сеанс с уникальным идентификатором сеанса.

Я помещаю этот идентификатор сеанса в файл cookie и отправляю этот файл cookie клиенту —

router.get('/auth/google/callback',
  passport.authenticate('google', { failureRedirect: 'https://svelte-of1p.onrender.com/signin' }),
  (req, res) => {
    if (req.user) {
      // Manually set the cookie
      res.cookie('connect.sid', req.sessionID, {
        maxAge: 24 * 60 * 60 * 1000, // 1 day
        secure: process.env.NODE_ENV === 'production', // True in production
        httpOnly: false, // Allow client-side access
        sameSite: 'None' // Allow cross-site cookies
      });
      if (req.user.username) {
        res.redirect('https://svelte-of1p.onrender.com');
      } else {
        res.redirect('https://svelte-of1p.onrender.com/select');
      }
    } else {
      res.redirect('https://svelte-of1p.onrender.com/signin');
    }
  }
);

Когда клиент отправляет запрос на сервер, файл cookie десериализуется. Мы извлекаем идентификатор сеанса из файла cookie (помните, что мы поместили уникальный идентификатор сеанса при создании файла cookie), после извлечения идентификатора сеанса мы затем ищем пользователя, который связан с этим уникальным идентификатором сеанса.

Если пользователь существует, мы знаем, что он вошел в систему, и можем авторизовать его.

function deserializeUser(req, res, next) {
    try {
      const cookie = req.headers.cookie;
      if (!cookie || !cookie.includes('connect.sid')) {
        return res.status(401).json({ error: 'Unauthorized' });
      }
  
      const sessionID = cookie.split('connect.sid=')[1].split(';')[0].trim();
  
      req.sessionStore.get(sessionID, (err, session) => {
        if (err || !session) {
          return res.status(500).json({ error: 'Session retrieval failed' });
        }
  
        if (!session.passport || !session.passport.user) {
          return res.status(401).json({ error: 'Unauthorized' });
        }
  
        req.user = { _id: session.passport.user }; 
  
        next();
      });
    } catch (error) {
      res.status(500).json({ error: 'Server Error' });
    }
  }
  
  module.exports = deserializeUser;
  

Вышеуказанное является промежуточным программным обеспечением. Чтобы затем применить промежуточное программное обеспечение ко всем моим конечным точкам API, в моем server.js у меня есть:

const deserializeUser = require('./middleware/deserializeUser');
app.use('/api', deserializeUser);

Это всегда кажется таким простым после двух дней, потраченных на отладку 😂

Посмотрите, как просто это звучит, но меня это расстроило.

В любом случае, разместите это здесь на случай, если кто-нибудь увидит это в будущем и столкнется с подобной проблемой.