Данные сеанса, по-видимому, не отправляются с сервера 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' });
  }
});

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

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

Привет! Пожалуйста, проверьте, хранится ли в вашем браузере файл cookie с исходным кодом Messenger-tu85.onrender.com после регистрации. А для браузера это будет сторонний файл cookie.

WeDoTheBest4You 17.07.2024 10:07

@WeDoTheBest4You, файл cookie не отправляется. его нет в моих заголовках запросов на вкладке «Сеть».

hypedstan 17.07.2024 11:12

@WeDoTheBest4You, поэтому я обошел это, выполнив res.cookie после аутентификации пользователя, и теперь файл cookie можно найти на вкладке сети браузера. Теперь проблема заключается в том, как получить идентификатор пользователя из файла cookie при отправке запроса на сервер. Как его десериализовать.

hypedstan 17.07.2024 18:35

@WeDoTheBest4You Я решил эту проблему, завтра отправлю ответ на свой пост.

hypedstan 17.07.2024 22:58

Привет, хорошие новости. Было много вопросов по поводу сторонних файлов cookie. Если ваше решение или обходной путь будут опубликованы, это определенно будет очень полезно для всех.

WeDoTheBest4You 18.07.2024 07:20

@WeDoTheBest4You Я опубликовал свое решение.

hypedstan 18.07.2024 14:46
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
63
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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);

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

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

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

Другие вопросы по теме

Невозможно опубликовать файл изображения на сервере Express с помощью Angular 18.0.6
Ошибка типа: невозможно прочитать свойства неопределенного значения (чтение «коллекции») (соединение mongodb)
Не удалось создать пользовательский тип ответа в Express.js с помощью TypeScript
Как создать диаграммы на моем веб-сайте JavaScript, используя данные из базы данных MySQL. Использование внешнего интерфейса EJS
Как обрабатывать несколько запросов (Express.js) с помощью операции вставки mongodb
VITE: Не удалось загрузить сценарий модуля: ожидался сценарий модуля JavaScript, но сервер ответил MIME-типом «text/html». Строгий тип MIME
Я получаю сообщение об ошибке: AxiosError {сообщение: «Запрос не выполнен с кодом состояния 500», имя: «AxiosError», код: «ERR_BAD_RESPONSE»}
Файлы cookie не передаются через Vercel
Невозможно запустить «node index.js» из-за синтаксической ошибки
Невозможно получить доступ к куки авторизации