Redirigir a la página anterior después de la autenticación en node.js usando passport.js


101

Estoy tratando de establecer un mecanismo de inicio de sesión usando node.js, express y passport.js. El inicio de sesión en sí funciona bastante bien, también las sesiones se almacenan bien con redis, pero tengo algunos problemas para redirigir al usuario a donde comenzó antes de que se le solicite que se autentique.

por ejemplo, el usuario sigue el enlace y http://localhost:3000/hiddenluego es redirigido a, http://localhost:3000/loginpero luego quiero que sea redirigido nuevamente a http://localhost:3000/hidden.

El propósito de esto es que, si el usuario accede aleatoriamente a una página en la que necesita iniciar sesión primero, será redirigido al sitio / login proporcionando sus credenciales y luego será redirigido de nuevo al sitio al que intentó acceder anteriormente.

Aquí está mi publicación de inicio de sesión

app.post('/login', function (req, res, next) {
    passport.authenticate('local', function (err, user, info) {
        if (err) {
            return next(err)
        } else if (!user) { 
            console.log('message: ' + info.message);
            return res.redirect('/login') 
        } else {
            req.logIn(user, function (err) {
                if (err) {
                    return next(err);
                }
                return next(); // <-? Is this line right?
            });
        }
    })(req, res, next);
});

y aquí mi método asegurado

function ensureAuthenticated (req, res, next) {
  if (req.isAuthenticated()) { 
      return next();
  }
  res.redirect('/login');
}

que se engancha en la /hiddenpágina

app.get('/hidden', ensureAuthenticated, function(req, res){
    res.render('hidden', { title: 'hidden page' });
});

La salida html para el sitio de inicio de sesión es bastante simple

<form method="post" action="/login">

  <div id="username">
    <label>Username:</label>
    <input type="text" value="bob" name="username">
  </div>

  <div id="password">
    <label>Password:</label>
    <input type="password" value="secret" name="password">
  </div>

  <div id="info"></div>
    <div id="submit">
    <input type="submit" value="submit">
  </div>

</form>

Hola, también estoy haciendo lo mismo, la única diferencia es que cuando el usuario inicia sesión correctamente, lo redirigiré a la página welcome.html con un mensaje como 'Nombre de usuario de bienvenida'. Escuche que UserName será su LoginName. ¿Puede mostrarme cómo pasar el valor del cuadro de texto a la página siguiente?
— Dhrumil Shah

sin saber mucho, supongo que simplemente páselo. Según mi ejemplo, podría ser: res.render ('oculto', {título: 'página oculta', nombre de usuario: 'Tyrion Lannister'});
— Alx

Respuestas:


84

No sé sobre pasaporte, pero así es como lo hago:

Tengo un middleware que uso con app.get('/account', auth.restrict, routes.account)ese conjunto redirectToen la sesión ... luego redirijo a / login

auth.restrict = function(req, res, next){
    if (!req.session.userid) {
        req.session.redirectTo = '/account';
        res.redirect('/login');
    } else {
        next();
    }
};

Luego routes.login.post, hago lo siguiente:

var redirectTo = req.session.redirectTo || '/';
delete req.session.redirectTo;
// is authenticated ?
res.redirect(redirectTo);

gracias por su respuesta. ¿No está configurando el redireccionamiento / cuenta directamente en su código? ¿Qué sucede si tiene otro enlace, digamos / pro_accounts usando el mismo auth.restrict que serían redirigidos a / account ....
— Alx

4
Es verdad. Siempre redirijo a / account después de que inician sesión, pero podría reemplazarlo conreq.session.redirect_to = req.path
— chovy

1
hm .. no es exactamente lo que estoy buscando. Llamo a secureAuthenticated para averiguar si un usuario ya está autorizado o no, de lo contrario, se redirige a / login. Desde esta vista, necesito una forma de volver a / account. Llamar a req.path dentro de / login me devuelve simple / login. El uso de referer tampoco funciona y no es muy seguro si el navegador configura correctamente el referer ...
— Alx

6
Debe configurarlo req.session.redirect_to = req.pathdentro de su middleware de autenticación. Luego léelo y bórralo en login.postruta.
— Chovy

esta no es una gran solución para el pasaporte que acepta successRedirect como una opción en un punto en el que la solicitud no está disponible
— linuxdan

110

En su ensureAuthenticatedmétodo, guarde la URL de retorno en la sesión de esta manera:

...
req.session.returnTo = req.originalUrl; 
res.redirect('/login');
...

Luego puede actualizar su ruta passport.authenticate a algo como:

app.get('/auth/google/return', passport.authenticate('google'), function(req, res) {
    res.redirect(req.session.returnTo || '/');
    delete req.session.returnTo;
}); 

7
Trabajé en la primera pasada, pero necesitaba agregar lo siguiente después de la redirección en la ruta passport.authenticate para evitar ser devuelto a la dirección returnTo después del cierre de sesión / inicio de sesión posterior: req.session.returnTo = null;Consulte esta pregunta para obtener información adicional sobre el cierre de sesión predeterminado del pasaporte, que, en el examen de la fuente, parece borrar solo el usuario de la sesión, y no toda la sesión.
— Rob Andren

¿Puedo simplemente pasar una consulta como? callback = / profile en lugar de sesión
— OMGPOP

@OMGPOP probablemente podrías, pero eso no suena muy seguro, ¿hay alguna razón por la que no quieras aprovechar la sesión?
— linuxdan

@linuxdan para nada. pero es problemático extraer el enlace de redireccionamiento y ponerlo en sesión, luego, cuando el usuario vuelva a redireccionar, asigne el enlace de redireccionamiento de la sesión
— OMGPOP

5
En lugar de req.pathlo que usaría, req.originalUrlya que es una combinación de baseUrl y ruta, consulte la documentación
— matthaeus


7

Mi forma de hacer las cosas:

const isAuthenticated = (req, res, next) => {
  if (req.isAuthenticated()) {
    return next()
  }
  res.redirect( `/login?origin=${req.originalUrl}` )
};

GET / controlador de inicio de sesión :

if( req.query.origin )
  req.session.returnTo = req.query.origin
else
  req.session.returnTo = req.header('Referer')

res.render('account/login')

POST / controlador de inicio de sesión :

  let returnTo = '/'
  if (req.session.returnTo) {
    returnTo = req.session.returnTo
    delete req.session.returnTo
  }

  res.redirect(returnTo);

POST / controlador de cierre de sesión (no estoy seguro de si está 100% correcto, los comentarios son bienvenidos):

req.logout();
res.redirect(req.header('Referer') || '/');
if (req.session.returnTo) {
  delete req.session.returnTo
}

Clear returnTo middleware (borra returnTo de la sesión en cualquier ruta, excepto en las rutas de autenticación; para mí son / login y / auth /: provider):

String.prototype.startsWith = function(needle)
{
  return(this.indexOf(needle) == 0)
}

app.use(function(req, res, next) {
  if ( !(req.path == '/login' || req.path.startsWith('/auth/')) && req.session.returnTo) {
    delete req.session.returnTo
  }
  next()
})

Este enfoque tiene dos características :

  • puede proteger algunas rutas con middleware isAuthenticated ;
  • en cualquier página , puede simplemente hacer clic en la URL de inicio de sesión y, después de iniciar sesión, volver a esa página;

Hubo tantas buenas respuestas aquí, pude usar su ejemplo y lo hice funcionar con mi proyecto. ¡Gracias!
— user752746

5

Si está utilizando connect -urance-login, existe una forma integrada y muy fácil de hacer esto con Passport utilizando el successReturnToOrRedirectparámetro. Cuando se usa, el pasaporte lo enviará de regreso a la URL solicitada originalmente o volverá a la URL que proporcionó.

router.post('/login', passport.authenticate('local', {
  successReturnToOrRedirect: '/user/me',
  failureRedirect: '/user/login',
  failureFlash: true
}));

https://github.com/jaredhanson/connect-ensure-login#log-in-and-return-to


Este parece ser un duplicado de la respuesta de @ jaredhanson
— mikemaccana

1

Las respuestas @chovy y @linuxdan tienen un error al no borrar session.returnTosi el usuario va a otra página después de la redirección de inicio de sesión (eso no requiere autenticación) e inicia sesión por allí. Así que agregue este código a sus implementaciones:

// clear session.returnTo if user goes to another page after redirect to login
app.use(function(req, res, next) {
    if (req.path != '/login' && req.session.returnTo) {
        delete req.session.returnTo
    }
    next()
})

Si realiza algunas solicitudes ajax desde la página de inicio de sesión, también puede excluirlas.


Otro enfoque es utilizar flash enensureAuthenticated

req.flash('redirectTo', req.path)
res.redirect('/login')

Y luego en GET iniciar sesión

res.render('login', { redirectTo: req.flash('redirectTo') })

En vista, agregue un campo oculto al formulario de inicio de sesión (ejemplo en jade)

if (redirectTo != '')
    input(type="hidden" name="redirectTo" value="#{redirectTo}")

En POST iniciar sesión

res.redirect(req.body.redirectTo || '/')

Tenga en cuenta que redirectTo se borrará después del primer inicio de sesión GET con él.


Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.