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>
Alx
fuente
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);
chovy
fuente
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;
}); 
linuxdan
fuente
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
14

Eche un vistazo a connect-secure-login , que funciona junto con Passport para hacer exactamente lo que desea.

Jared Hanson
fuente
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;
Deksden
fuente
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

igneosaurio
fuente
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.

Alexander Danilov
fuente