javascript - management - ¿Por qué PassportJS en Node no elimina la sesión al desconectarse?
passport js jwt (17)
Aparentemente hay múltiples causas posibles de este problema. En mi caso, el problema era el orden incorrecto de las declaraciones, es decir, el punto final de cierre de sesión se declaró antes de la inicialización del pasaporte. El orden correcto es:
app.use(passport.initialize());
app.use(passport.session());
app.get(''/logout'', function(req, res) {
req.logout();
res.redirect(''/'');
});
Tengo problemas para que mi sistema cierre la sesión con PassportJS. Parece que se está llamando a la ruta de desconexión, pero no está eliminando la sesión. Quiero que devuelva 401, si el usuario no ha iniciado sesión en una ruta específica. Llamo a authenticateUser para verificar si el usuario está conectado.
¡Muchas gracias!
/******* 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();
}
Como está utilizando la autenticación de pasaporte que usa su propia sesión a través de la cookie connect.sid
forma más simple de lidiar con el cierre de sesión es dejar que el pasaporte maneje la sesión.
app.get(''/logout'', function(req, res){
if (req.isAuthenticated()) {
req.logOut()
return res.redirect(''/'') // Handle valid logout
}
return res.status(401) // Handle unauthenticated response
})
Destruir la sesión por ti mismo se ve raro. Me enfrenté a este problema con la siguiente configuración:
"express": "^4.12.3",
"passport": "^0.2.1",
"passport-local": "^1.0.0",
Debo decir que esta configuración funciona bien . El motivo de mi problema fue en la sessionStore
personalizada que sessionStore
aquí:
app.use(expressSession({
...
store: dbSessionStore,
...
}));
Para asegurarse de que su problema aquí también, simplemente comente la línea de la tienda y corra sin que la sesión persista. Si funciona, debe buscar en su tienda de sesiones personalizada. En mi caso set
método de set
se definió incorrecto. Cuando usas req.logout()
método store destroy()
req.logout()
sesión no se invoca como pensé antes. En cambio, invocó el método set
con la sesión actualizada.
Buena suerte, espero que esta respuesta te ayude.
En mi caso, usar callback pasado a req.session.destroy
ayudó solo algunas veces y tuve que recurrir a este truco:
req.session.destroy();
setTimeout(function() {
res.redirect "/";
}, 2000);
No sé por qué esa es la única solución que he podido conseguir para trabajar, pero desafortunadamente la respuesta de @LilbertLloyd no me sirvió de manera consistente.
Puede tener algo que ver con el hecho de que mi página de inicio de sesión en vivo usa SSL (no he podido reproducir el problema en el sitio de ensayo o en mi servidor local). También puede haber algo más en mi aplicación; Estoy usando el módulo derby-passport ya que mi aplicación está utilizando el framework Derby , por lo que es difícil aislar el problema.
Claramente es un problema de tiempo porque primero probé un tiempo de espera de 100 ms, que no fue suficiente.
Lamentablemente, aún no he encontrado una mejor solución.
Enfrenté el problema similar con Passport 0.3.2.
Cuando uso la devolución de llamada personalizada para el inicio de sesión y el registro del pasaporte, el problema persiste.
El problema se resolvió actualizando a Passport 0.4.0 y agregando las líneas
app.get(''/logout'', function(req, res) {
req.logOut();
res.redirect(''/'');
});
Estaba teniendo el mismo problema, y resultó no ser un problema con las funciones de Passport en absoluto, sino más bien en la forma en que estaba llamando mi ruta /logout
. Utilicé fetch para llamar a la ruta:
(Malo)
fetch(''/auth/logout'')
.then([other stuff]);
Resulta que al hacer esto, no se envían cookies, por lo que la sesión no continúa y supongo que res.logout()
se aplica a una sesión diferente. En cualquier caso, hacer lo siguiente lo soluciona:
(Bueno)
fetch(''/auth/logout'', { credentials: ''same-origin'' })
.then([other stuff]);
Estaba teniendo los mismos problemas, el capital O lo solucionó;
app.get(''/logout'', function (req, res){
req.logOut() // <-- not req.logout();
res.redirect(''/'')
});
Esto funcionó para mí:
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'');
});
Se asegura de que tu página no se almacene en el caché
Estoy trabajando con un programador, que sugiere eliminar usuario de req:
app.get(''/logout'', function (req, res){
req.session.destroy(function (err) {
req.user = null;
res.redirect(''/''); //Inside a callback… bulletproof!
});
});
Motivo: necesitamos eliminar de req (pasaportes también haciendo esto pero de manera asincrónica) porque no hay uso de datos de usuario después de cerrar la sesión, incluso esto ahorrará memoria y también puede haber datos de usuario encontrados en el pasaporte y puede crear nueva sesión y redirigir (pero no sin embargo, suceda) Por cierto, esta es nuestra responsabilidad para eliminar lo irrelevante. PassportJS asigna los datos en req.user después de iniciar sesión y también los elimina si usamos req.logout () pero puede que no funcione correctamente algunas veces como NodeJS Asincrónico en la naturaleza
Hace poco tuve este problema y ninguna de las respuestas me solucionó el problema. Podría estar mal, pero parece tener que ver con una condición de carrera.
Cambiar los detalles de la sesión a las siguientes opciones parece haber solucionado el problema. Lo he probado unas 10 veces más o menos y todo parece estar funcionando correctamente.
app.use(session({
secret: ''secret'',
saveUninitialized: false,
resave: false
}));
Básicamente acabo de cambiar saveUninitialized
y saveUninitialized
de true
a false
. Eso parece haber solucionado el problema.
Solo como referencia estoy usando el estándar req.logout();
método en mi ruta de cierre de sesión. No estoy utilizando la sesión de destrucción como lo han mencionado otras personas.
app.get(''/logout'', function(req, res) {
req.logout();
res.redirect(''/'');
});
No sé cómo pero ng-href="/signout"
resolvió mi problema. Anteriormente utilicé el servicio para cerrar la sesión, pero en su lugar lo he usado directamente.
Se encontró con el mismo problema. Usando req.session.destroy();
en lugar de req.logout();
funciona, pero no sé si esta es la mejor práctica.
Tengo una experiencia que, alguna vez, no funciona porque no configuras el pasaporte correctamente. Por ejemplo, hago vhost
, pero en la aplicación principal configuro un pasaporte como este que está mal.
app.js (¿por qué está mal? ver blockqoute a continuación)
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'')));
en realidad, no debe poder iniciar sesión, pero logro hacerlo porque, continúo cometiendo más errores. poniendo aquí otra configuración de pasaporte, así que la sesión de formulario app.js
disponible para app.host.sub1.js
app.host.sub1.js
// default app configuration
require(''./modules/middleware.passport'')(app);
require(''./modules/app.config.default.js'')(app, express);
Entonces, cuando quiero desconectarme ... no funciona porque app.js
hizo algo incorrecto al inicializar passport.js
antes de express-session.js
, lo cual es incorrecto.
Sin embargo, este código puede resolver los problemas de todos modos como otros mencionan.
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(''/'');
});
});
Pero en mi caso la forma correcta es ... cambiar el express-session.js antes de pasaporte.js
document también mencionar
Tenga en cuenta que habilitar la compatibilidad de sesión es completamente opcional, aunque se recomienda para la mayoría de las aplicaciones. Si está habilitado, asegúrese de usar express.session () antes de pasaporte.sesión () para garantizar que la sesión de inicio de sesión se restaure en el orden correcto.
Entonces, resolví el problema de cierre de sesión en mi caso por ..
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);
y ahora req.logout();
es trabajo ahora
Utilicé tanto req.logout()
como req.session.destroy()
y funciona bien.
server.get(''/logout'', (req, res) => {
req.logout();
req.session.destroy();
res.redirect(''/'');
});
Solo para mencionar, uso Redis como tienda de sesiones.
Yo estaba teniendo el mismo problema. Resultó que mi versión del pasaporte no era compatible con Express 4.0. Solo necesita instalar una versión anterior.
npm install --save [email protected]
session.destroy
puede ser insuficiente, para asegurarse de que el usuario está totalmente desconectado, también debe borrar la cookie de sesión.
El problema aquí es que si su aplicación también se utiliza como una API para una aplicación de una sola página (no recomendada pero bastante común), entonces puede haber algunas solicitudes procesadas por correo urgente que se iniciaron antes de cerrar la sesión y finalizar después del cierre de sesión. Si este fuera el caso, esta solicitud de ejecución más larga restaurará la sesión en redis después de que se eliminó. Y como el navegador aún tiene la misma cookie la próxima vez que abra la página, iniciará sesión con éxito.
req.session.destroy(function() {
res.clearCookie(''connect.sid'');
res.redirect(''/'');
});
Eso es lo que tal vez ocurra de otra manera:
- Req 1 (cualquier solicitud) es recibido
- Req 1 carga la sesión de redis a la memoria
- Se requiere iniciar sesión
- Logout req carga sesión
- Logout req destruye la sesión
- Logout req envía redirigir al navegador (no se elimina la cookie)
- Req 1 completa el procesamiento
- Req 1 guarda la sesión de la memoria a redis
- El usuario abre la página sin diálogo de inicio de sesión porque tanto la cookie como la sesión están en su lugar
Idealmente, debe usar la autenticación de tokens para llamadas de API y solo usar sesiones en la aplicación web que solo carga páginas, pero incluso si su aplicación web solo se usa para obtener tokens de API, esta condición de raza aún es posible.
La respuesta de Brice es genial , pero todavía noté una diferencia importante que hacer; la guía de Pasaporte sugiere usar .logout()
(también con alias como .logOut()
) como tal:
app.get(''/logout'', function(req, res){
req.logout();
res.redirect(''/''); //Can fire before session is destroyed?
});
Pero como se mencionó anteriormente, esto no es confiable. Descubrí que se comportó como se esperaba al implementar la sugerencia de Brice de esta manera:
app.get(''/logout'', function (req, res){
req.session.destroy(function (err) {
res.redirect(''/''); //Inside a callback… bulletproof!
});
});
¡Espero que esto ayude!