ti-enxame.com

Expiração de cookie do Node.js Express Passport

Estou usando o Passport para autenticação no meu aplicativo e também usando o Express. Para resumir meu problema: minha funcionalidade de login funciona bem inicialmente, mas após qualquer sessão de usuário atinge o tempo limite, não os usuários podem fazer login.

Estou usando a estratégia local padrão para autenticação.

Vou incluir um exemplo o mais simples possível com base na minha configuração:

//-------------
//Set up authentication with Passport
//-------------
var userModel = require('./models/user')(db);
passport.use(new LocalStrategy(
    function(username, password, done) {
        var errorMessage = 'Incorrect username/password combination.';
        userModel.GetUserByUsername(username, function(err, user) {
            if (err) { return done(err); }
            if (!user) {
              return done(null, false, { message: errorMessage });
            }

            user.validatePassword(password, function(isPasswordCorrect) {
                if (!isPasswordCorrect)
                {
                    return done(null, false, { message: errorMessage });
                }

                //Update with login date
                userModel.UpdateUserWithLogin(username, user.currentLoginTime, function(err){
                    //if we have an error here, we should probably just log it
                    if(err)
                    {
                        console.log(err);
                    }
                });

                return done(null, user);
            });
        });
    }
));

passport.serializeUser(function(user, done) {
  done(null, user);
});

passport.deserializeUser(function(user, done) {
    userModel.GetUserByUsername(user._id, function(err, user) {
            done(err, user);
        });
});

//-------------
//Set up express and configure
//-------------
var sessionStore = new SkinStore(db);
var app = express();

app.configure(function(){
    app.set('port', process.env.PORT || 3000);
    app.set('views', __dirname + '/views');
    app.engine('html', consolidate.swig);
    app.set('view engine', 'html');
    swig.init({
        root: '.',
        allowErrors: true, // allows errors to be thrown and caught by express instead of suppressed
        autoescape: false});

    app.use(express.logger('dev'));

    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(express.cookieParser("[mysecrethere]"));
    app.use(express.session({   store: sessionStore,
                            cookie: { expires : new Date(Date.now() + 3600000) } //1 Hour
                            }));
    app.use(passport.initialize());
    app.use(passport.session());
    app.use(flash());
    app.use(expressValidator);

    app.use(express.static(path.join(__dirname, 'public')));

    //Dynamic helpers
    app.use(require('./helpers/DynamicHelpers'));

    app.use(app.router);
});

app.get('/login', routes.login);
app.post('/login', passport.authenticate('local', {failureRedirect: '/login',
                                               badRequestMessage: "Please enter username and password",
                                               failureFlash: true }),
                                               function(req, res) {
                                                    var targetUrl = req.session.pageAfterLogin;
                                                    delete req.session.pageAfterLogin;
                                                    res.redirect(targetUrl || '/account');
                                                });

app.get('/account', IsAuthenticated, routes.account.show);

E a função auxiliar IsAuthenticated:

function IsAuthenticated(req,res,next){
    if(req.isAuthenticated())
    {
        next();
    }
    else
    {
        //save the requested page and then redirected
        req.session.pageAfterLogin = req.url;
        req.flash("error", "You must be logged in first!");
        res.redirect('/login');
    }
}

O que eu acho pela depuração é que, após a autenticação bem-sucedida (e após a expiração de um cookie), eu atingi essa lógica (de cima):

function(req, res) {
    var targetUrl = req.session.pageAfterLogin;
    delete req.session.pageAfterLogin;
    res.redirect(targetUrl || '/account');
}

Onde posso ver que o "req" tem a sessão definida corretamente, com as informações do Passport armazenadas corretamente. Em seguida, o redirecionamento acontece, a solicitação nova não possui informações de sessão armazenadas e possui um ID de sessão totalmente novo. Suspeitei que nenhum cookie estava sendo definido no cliente, e esse parece ser o caso, o que deve explicar a falta de sessões consistentes.

No entanto, não consigo descobrir por que nenhum novo cookie está sendo definido. Há algo errado com a configuração do aplicativo que indique por que isso está acontecendo?

Devo acrescentar que reiniciar a instância do Node.js. corrige o problema, não é algo que seria tolerável na produção.

Obrigado.

UPDATE : Executei o Fiddler para ver o que estava acontecendo com o tráfego HTTP/S e posso ver que, quando funciona inicialmente, estou recebendo um conjunto de cookies no navegador (tentei vários), que é então devolvido ao servidor em solicitações subsequentes.

Quando não funciona, o navegador não está passando cookies para o servidor e, portanto, Node está enviando um cabeçalho Set-Cookie que fornece um novo cookie toda vez Até agora não tive sorte em determinar a causa disso.

17
creativename

Eu descobri, embora eu não ame a resposta.

tl; dr; - use maxAge em vez de expirar.


O problema estava enraizado na data de vencimento definida em cada cookie (que é automaticamente definida pelo Express). Percebi que todos os cookies configurados tinham a mesma data de validade, o que acabou por acabar no passado e, portanto, expirar instantaneamente.

A causa disso estava aqui:

cookie: { expires : new Date(Date.now() + 3600000) }

A nova data estava sendo criada apenas uma vez, na inicialização do servidor. Isso fazia com que a data de validade fosse a mesma sempre. Com base no código da postagem original, não consigo entender por que não funciona e, ainda assim, todos os exemplos que encontrei on-line usam exatamente o mesmo código. Eu verifiquei isso definindo uma função que criou essa data e verificando se ela só foi chamada no início do servidor.

Para corrigir esse problema, estou definindo maxAge em vez de "expira". maxAge leva vários milissegundos, em vez de uma data, e parece estar definindo a data de validade em todos os cookies corretamente.

Gostaria muito de saber se alguém pode explicar por que isso está acontecendo em primeiro lugar, já que outros parecem usá-lo com sucesso. Alguma idéia?

Veja meu código de trabalho abaixo

app.configure(function(){
    app.set('port', process.env.PORT || 3000);
    app.set('views', __dirname + '/views');
    app.engine('html', consolidate.swig);
    app.set('view engine', 'html');
    swig.init({
        root: '.',
        allowErrors: true, // allows errors to be thrown and caught by express instead of suppressed
        autoescape: false});

    app.use(express.logger('dev'));

    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(express.cookieParser("[mysecrethere]"));
    app.use(express.session({   store: sessionStore,
                            cookie: { maxAge : 3600000 } //1 Hour
                            }));
    app.use(passport.initialize());
    app.use(passport.session());
    app.use(flash());
    app.use(expressValidator);

    app.use(express.static(path.join(__dirname, 'public')));

    //Dynamic helpers
    app.use(require('./helpers/DynamicHelpers'));

    app.use(app.router);
});
28
creativename