Почему PassportJS в Node не удаляет сеанс при выходе из системы

У меня возникли проблемы с выходом моей системы с помощью PassportJS. Кажется, что маршрут выхода из системы вызывается, но он не удаляет сеанс. Я хочу, чтобы он вернул 401, если пользователь не вошел в систему по определенному маршруту. Я вызываю authenticateUser, чтобы проверить, зарегистрирован ли пользователь.

Спасибо большое!

/******* This in index.js *********/
// setup passport for username & passport authentication
adminToolsSetup.setup(passport);

// admin tool login/logout logic
app.post("/adminTool/login",
    passport.authenticate('local', {
        successRedirect: '/adminTool/index.html',
        failureRedirect: '/',
        failureFlash: false })
);
app.get('/adminTool/logout', adminToolsSetup.authenticateUser, function(req, res){
    console.log("logging out");
    console.log(res.user);
    req.logout();
    res.redirect('/');
});


// ******* This is in adminToolSetup ********
// Setting up user authentication to be using user name and passport as authentication method,
// this function will fetch the user information from the user name, and compare the password     for authentication
exports.setup = function(passport) {
    setupLocalStrategy(passport);
    setupSerialization(passport);
}

function setupLocalStrategy(passport) {
    passport.use(new LocalStrategy(
        function(username, password, done) {
            console.log('validating user login');
            dao.retrieveAdminbyName(username, function(err, user) {
                if (err) { return done(err); }
                if (!user) {
                    return done(null, false, { message: 'Incorrect username.' });
                }
                // has password then compare password
                var hashedPassword = crypto.createHash('md5').update(password).digest("hex");
                if (user.adminPassword != hashedPassword) {
                    console.log('incorrect password');
                    return done(null, false, { message: 'Incorrect password.' });
                }
                console.log('user validated');
                return done(null, user);
            });
        }
    ));
}

function setupSerialization(passport) {
    // serialization
    passport.serializeUser(function(user, done) {
        console.log("serialize user");
        done(null, user.adminId);
    });

    // de-serialization
    passport.deserializeUser(function(id, done) {
        dao.retrieveUserById(id, function(err, user) {
            console.log("de-serialize user");
            done(err, user);
        });
    });
}

// authenticating the user as needed
exports.authenticateUser = function(req, res, next) {
    console.log(req.user);
    if (!req.user) {
        return res.send("401 unauthorized", 401);
    }
    next();
}

Ответ 1

Ответ Brices велик, но я все же заметил важное различие; руководство по паспорту предлагает использовать .logout() (также псевдоним как .logout()) как таковой:

app.get('/logout', function(req, res){
  req.logout();
  res.redirect('/'); //Can fire before session is destroyed?
});

Но, как упоминалось выше, это ненадежно. Я обнаружил, что он вел себя так, как ожидалось, при реализации предложения Brices следующим образом:

app.get('/logout', function (req, res){
  req.session.destroy(function (err) {
    res.redirect('/'); //Inside a callback… bulletproof!
  });
});

Надеюсь, это поможет!

Ответ 2

Идти в эту же проблему. Использование req.session.destroy(); вместо req.logout(); работает, но я не знаю, является ли это лучшей практикой.

Ответ 3

session.destroy может быть недостаточно, чтобы убедиться, что пользователь полностью вышел из системы, вам также нужно очистить файл cookie сеанса.

Проблема заключается в том, что если ваше приложение также используется как API для одностраничного приложения (не рекомендуется, но довольно распространено), тогда могут быть обработаны некоторые запросы, выраженные, которые были запущены до выхода из системы и завершены после выхода из системы, Если это так, то этот более длительный запрос будет восстанавливать сеанс в redis после его удаления. И поскольку браузер по-прежнему имеет тот же файл cookie, в следующий раз, когда вы откроете страницу, вы будете успешно авторизованы.

req.session.destroy(function() {
    res.clearCookie('connect.sid');
    res.redirect('/');
});

Что может произойти иначе:

  • Получен ответ 1 (любой запрос)
  • Req 1 загружает сеанс из redis в память
  • Выход из запроса
  • Выход из режима req загружает сеанс
  • Выход из системы req уничтожает сеанс
  • Logout req отправляет перенаправление в браузер (cookie не удаляется)
  • Req 1 завершает обработку
  • Req 1 сохраняет сеанс из памяти в redis
  • Пользователь открывает страницу без диалогового окна входа, так как cookie и сеанс находятся на месте.

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

Ответ 4

У меня была такая же проблема, и она вообще не была проблемой с функциями Passport, а скорее так, как я называл мой маршрут /logout. Я использовал fetch для вызова маршрута:

(Плохо)

fetch('/auth/logout')
  .then([other stuff]);

Выключает, что не отправляет файлы cookie, поэтому сеанс не продолжается, и я полагаю, что res.logout() применяется к другому сеансу? Во всяком случае, выполните следующие исправления:

(Хорошо)

fetch('/auth/logout', { credentials: 'same-origin' })
  .then([other stuff]);

Ответ 5

У меня были те же проблемы, капитал O исправил его;

app.get('/logout', function (req, res){
  req.logOut()  // <-- not req.logout();
  res.redirect('/')
});

Ответ 6

Недавно у меня была такая же проблема, и ни один из ответов не устранил проблему. Возможно, это неправильно, но похоже, что это связано с состоянием гонки.

Изменение сведений о сеансе к приведенным ниже параметрам, похоже, устранило проблему для меня. Я тестировал его примерно 10 раз или сейчас, и все, кажется, работает правильно.

app.use(session({
    secret: 'secret',
    saveUninitialized: false,
    resave: false
}));

В основном я просто изменил saveUninitialized и resave с true на false. Это, похоже, устранило проблему.

Просто для справки Я использую стандартный метод req.logout(); в моем пути выхода из системы. Я не использую сеанс уничтожения, как говорили другие люди.

app.get('/logout', function(req, res) {
    req.logout();
    res.redirect('/');
});

Ответ 7

Я использовал как req.logout(), так и req.session.destroy() и отлично работал.

server.get('/logout', (req, res) => {
  req.logout();
  req.session.destroy();
  res.redirect('/');
});

Как раз упомянуть, я использую Redis в качестве хранилища сеансов.

Ответ 8

Уничтожение сеанса самостоятельно выглядит странно. Я столкнулся с этой проблемой, имеющей следующую конфигурацию:

"express": "^4.12.3",
"passport": "^0.2.1",
"passport-local": "^1.0.0",

Я должен сказать, что эта конфигурация работает хорошо. Причина моей проблемы была в пользовательском sessionStore, который я определил здесь:

app.use(expressSession({
    ...
    store: dbSessionStore,
    ...
}));

Чтобы быть уверенным, что ваша проблема здесь тоже, просто закомментируйте строку хранилища и запустите без сохранения сеанса. Если это сработает, вы должны заглянуть в свой собственный магазин сеансов. В моем случае метод set был определен неправильно. Когда вы используете метод req.logout() session store destroy(), который не был вызван, как я думал раньше. Вместо этого вызывается метод set с обновленным сеансом.

Удачи, надеюсь, этот ответ поможет вам.

Ответ 9

У меня есть опыт, который когда-то не работает, потому что вам не удается правильно настроить паспорт. Например, я делаю vhost, но в основном приложении я устанавливаю паспорт, как это неправильно.

app.js (почему не так? см. blockqoute ниже)

require('./modules/middleware.bodyparser')(app);
require('./modules/middleware.passport')(app);
require('./modules/middleware.session')(app);
require('./modules/app.config.default.js')(app, express);

// default router across domain
app.use('/login', require('./controllers/loginController'));
app.get('/logout', function (req, res) {
    req.logout();
    res.redirect('/');
});

// vhost setup
app.use(vhost('sub1.somehost.dev', require('./app.host.sub1.js')));
app.use(vhost('somehost.dev', require('./app.host.main.js')));

на самом деле, он не может войти в систему, но мне это удается, потому что я продолжаю делать больше ошибок. путем установки другой паспортной установки здесь, поэтому форма сеанса app.js доступна для app.host.sub1.js

app.host.sub1.js

// default app configuration
require('./modules/middleware.passport')(app);
require('./modules/app.config.default.js')(app, express);

Итак, когда я хочу выйти из системы... он не работает, потому что app.js сделал что-то неправильно, начав инициализировать passport.js до express-session.js, что неверно!!.

Однако, этот код может решить проблемы в любом случае, как упоминают другие.

app.js

app.get('/logout', function (req, res) {
    req.logout();
    req.session.destroy(function (err) {
        if (err) {
            return next(err);
        }

        // destroy session data
        req.session = null;

        // redirect to homepage
        res.redirect('/');
    });
});

Но в моем случае правильный способ - заменить express-session.js до passport.js

документ также упоминает

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

Итак, проблема с выходом из системы в моем случае.

app.js

require('./modules/middleware.bodyparser')(app);
require('./modules/middleware.session')(app);
require('./modules/middleware.passport')(app);
require('./modules/app.config.default.js')(app, express);


// default router across domain
app.use('/login', require('./controllers/loginController'));
app.get('/logout', function (req, res) {
    req.logout();
    res.redirect('/');
});

app.host.sub1.js

// default app configuration
require('./modules/app.config.default.js')(app, express);

и теперь req.logout(); теперь работает.

Ответ 10

У меня была такая же проблема. Оказалось, что моя версия паспорта не совместима с Express 4.0. Просто нужно установить более старую версию.

    npm install --save [email protected]

Ответ 11

Это сработало для меня:

app.get('/user', restrictRoute, function (req, res) {
  res.header('Cache-Control', 'no-cache, private, no-store, must-revalidate,
              max-stale=0, post-check=0, pre-check=0');
});

Он гарантирует, что ваша страница не будет сохранена в кеше

Ответ 12

Я работаю с программистом, который предлагает удалить пользователя req:

app.get('/logout', function (req, res){
  req.session.destroy(function (err) {
    req.user = null;
    res.redirect('/'); //Inside a callback… bulletproof!
  });
});

Причина: нам нужно удалить из req (паспортные данные также делают это, но асинхронно), потому что после выхода из системы нет данных пользователя даже это сэкономит память, а также может быть обнаружено паспортные данные пользователя и может создать новый сеанс и перенаправить (но еще не произойдет) Кстати, это наша обязанность удалить ненужную вещь. PassportJS назначает данные в req.user после входа в систему и также удаляет, если мы используем req.logout(), но может работать некорректно несколько раз, поскольку NodeJS Асинхронный характер

Ответ 13

Я столкнулся с аналогичной проблемой с паспортом 0.3.2.

Когда я использую Пользовательский обратный вызов для входа и регистрации паспорта, проблема сохраняется.

Проблема была решена путем обновления до Passport 0.4.0 и добавления строк

app.get('/logout', function(req, res) {
    req.logOut();
    res.redirect('/');
});

Ответ 14

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

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


app.get('/logout', function(req, res) {
  req.logout();
  res.redirect('/');
});

Ответ 15

Поскольку вы используете аутентификацию паспорта, которая использует его собственный сеанс через файл cookie connect.sid этот самый простой способ справиться с выходом из системы - это позволить паспорту обрабатывать сеанс.

app.get('/logout', function(req, res){
  if (req.isAuthenticated()) {
    req.logOut()
    return res.redirect('/') // Handle valid logout
  }

  return res.status(401) // Handle unauthenticated response
})

Ответ 16

Вы должны использовать req.logout(), чтобы уничтожить сеанс в браузере.

app.get('/logout', function(req, res) {
    req.logout();
    res.redirect('/'); // whatever the route to your default page is
});

Ответ 17

Все примеры здесь делают перенаправление после req.session.destroy. Но имейте в виду, что Express создаст новый сеанс мгновенно для страницы, на которую вы перенаправляете. В сочетании с Postman я обнаружил странное поведение, которое делает вход в Passport сразу после выхода из системы, что эффект Паспорта успешно, но не может хранить идентификатор пользователя в файле сеанса. Причина в том, что почтальон должен обновлять файл cookie во всех запросах этой группы, и это занимает некоторое время. Кроме того, перенаправление в обратном вызове уничтожения не помогает.

Я решил это, не делая перенаправления, а просто возвращаю сообщение json.

Ответ 18

В моем случае использование обратного вызова, переданного в req.session.destroy, помогло только некоторое время, и мне пришлось прибегнуть к этому взлому:

req.session.destroy();
setTimeout(function() {
    res.redirect "/";
}, 2000);

Я не знаю, почему это единственное решение, с которым я смог работать, но, к сожалению, ответ @JulianLloyd не работал у меня последовательно.

Возможно, это связано с тем, что моя страница входа в систему использует SSL (я не смог воспроизвести проблему на промежуточном сайте или моем локальном хосте). В моем приложении может быть что-то еще; Я использую модуль дерби-паспорта, так как мое приложение использует Derby, поэтому сложно изолировать проблему.

Это явно проблема синхронизации, потому что я сначала пробовал тайм-аут в 100 мс, чего было недостаточно.

К сожалению, я еще не нашел лучшего решения.

Ответ 19

Я не знаю, как, но ng-href="/signout" решил мою проблему. Раньше я использовал сервис для выхода из системы, но вместо этого я использовал его напрямую.