Portuguese (Português) translation by Erick Patrick (you can also view the original English article)
Implementar estratégias robustas de autenticação para qualquer aplicação pode ser uma tarefa bem assustadora e as apliocações Node.js não são exceções.
Nesse tutorial, desenvolveremos uma aplicação em Node.js, a partir do zero, e usaremos um middleware relativamente novo, porém, bastante popular, o Passport, para cuidar da autenticação de nossa aplicação
A documentação do Passport a descreve como "um middleware simples e discreto para node". E realmente é.
Ao posicionar-se como um middleware, o Passaport faz um excelente trabalho ao separar os outros pontos de uma aplicação web das necessidades de autenticação. Isso permite que o Passaport seja facilmente configurada em qualquer aplicação baseada em Express, da mesma forma que configuramos qualquer outro middleware como o logging, body-parsing, cookie-parsing, manipuladores de sessão, etc.
Esse tutorial assume que tenha um entendimento básico de Node.js e da framework Express e tentará manter o foco na parte de autenticação, embora criaremos uma aplicação baseada em Express, a partir do zero, indo da criação das rotas à adição da camada de autenticação a alguma dessas rotas.
Estratégias de Autenticação
O Passport provê mais de 140 mecanismos de autenticação para escolhermos. Você pode autenticar em relação a uma instância remota/local de base de dados ou usar algum provedor OAuth para autenticar com redes sociais, como Facebook, Twitter, Google, etc, ou ainda pode escolher algum método de uma lista extensiva de provedores que dão suporte a autenticação com Passport e provê um módulo para usarmos.
Mas, não se preocupe: Você não precisa adicionar qualquer estratégia/mecanismo de autenticação que sua aplicação não precise. Todas essas estratégias são independentes umas das outras e empacotadas como módulos separados do node, e não estão inclusas por padrão, ao instalar o Passaport: npm install passport
Nesse tutorial, usaremos uma Estratégia de Autenticação Local do Passport para autenticar os usuários em relação a uma instância configurada e local do Mongo DB, guardando os detalhes do usuário nessa base de dados. Para usar essa estratégia, precisamos instalar o módulo passport-local: npm install passport-local
Mas, espere: Antes de abrir sua linha de comando e começar a executar esses comandos, comecemos criando a aplicação Express e adicionemos algumas rotas (como as login
, registration
e home
) e tentemos adicionar nosso middleware de autenticação. Note que usaremos o Express 4 nesse tutorial, mas, com alguns ajustes, o Passport funcionará do mesmo jeito com o Express 3.
Preparando a Aplicação
Se você não o tiver feito ainda, instale o Express & express-generator para gerar o esqueleto de uma aplicação, bastando executar express passport-mongo
na sua linha de comando. A estrutura da aplicação gerada deve parecer com isso:

Removamos parte da funcionalidade padrão a qual não usaremos - vá em frente e apague o arquivo users.js
bem como as referências a ele do arquivo app.js
.
Adicionando Dependências ao Projeto
Abra o arquivo package.json
e adicione os módulos passport
e passport-local
como dependência.
"passport": "~0.2.0", "passport-local": "~1.0.0"
Como guardaremos os detalhes dos usuários no MongoDB, usaremos o Mongoose como nossa ferramenta modeladora de objetos de dados. Outra maneira de instalar e salvar essas dependências no package.json
é digitando:
npm install mongoose --save
Seu package.json
deve parecer com isso:

Agora, instale as dependências e rode a aplicação padrão, executando os comandos npm install && npm start
. Ele começará o download das dependências e iniciará nosso servidor node. Você pode verificar nosso app Express no endereço http://localhost:3000/, embora não tenha muito o que se ver por lá.
Logo mudaremos isso, criando um app Express completa, com página de registro de novos usuários; página de login dos usuários; e autenticação dos usuários registrados usando Passport.
Criando o Modelo com Mongoose
Já que salvaremos os dados do usuário no Mongo, criemos um modelo User usando Mongoose e o salvemos como models/user.js
em nosso app.
var mongoose = require('mongoose'); module.exports = mongoose.model('User',{ username: String, password: String, email: String, gender: String, address: String });
Basicamente, criamos o model usando Mongoose, o qual poderemos realizar operações CRUD na base de dados usada.
Configurando o Mongo
Se não tiver o Mongo instalado localmente, recomendo que use serviços de bases de dado na nuvem, como o Modulus ou o MongoLab. Criar uma instância do MongoDB usando algum desses serviços, além de gratuito, só leva alguns cliques.
Depois de criar uma base de dados nesses serviços, ele fornecerá uma URI parecida com isso mongodb://<dbuser>:<dbpassword>@novus.modulusmongo.net:27017/<dbName>
, que poderá ser usada para a realização das operações CRUD. É uma boa ideia manter a configuração da base de dados em um arquivo separado que pode ser utilizado quando necessário. Dessa forma, criaremos um módulo node, db.js
, dessa forma:
module.exports = { 'url' : 'mongodb://<dbuser>:<dbpassword>@novus.modulusmongo.net:27017/<dbName>' }
Se você, assim como eu, usa uma instância local do Mongo, é hora de iniciar a daemon mongod
e o db.js
deverá ficar assim, para esse tipo de situação:
module.exports = { 'url' : 'mongodb://localhost/passport' }
Agora, usaremos essa configuração no app.js
e conectaremos a ela usando as APIs do Mongoose
var dbConfig = require('./db.js'); var mongoose = require('mongoose'); mongoose.connect(dbConfig.url);
Configurando o Passport
O Passport apenas provê o mecanismo de manipulação da autenticação, deixando a parte de manipulação de sessão para nós mesmos. Dessa forma, usaremos o express-session para lidar com essa parte. Abra o arquivo app.js
e cole o código abaixo antes de configurarmos as rotas:
// Configuring Passport var passport = require('passport'); var expressSession = require('express-session'); app.use(expressSession({secret: 'minhaChaveSecreta'})); app.use(passport.initialize()); app.use(passport.session());
Isso é necessário se quisermos que nossas sessões sejam persistentes por natureza. Antes de executar o app, precisamos instalar o módulo express-session e adicioná-lo na lista de dependências do package.json
. Faça isso, executando o comando a seguir na linha de comando: npm install --save express-session
Serializando e Deserializando Instâncias de Usuário
O Passport também precisa serializar e deserializar instâncias de objetos do armazenador de sessões, para dar suporte às sessões do login, assim, toda requisição subsequente não precisará lidar com credenciais do usuário. Ele provê dois métodos, serializeUser
e deserializeUser
para esse propósito:
passport.serializeUser(function(user, done) { done(null, user._id); }); passport.deserializeUser(function(id, done) { User.findById(id, function(err, user) { done(err, user); }); });
Usando as Estratégias do Passport
Definiremos estratégias do Passport para manipular o login e o cadastro de usuários. Cada uma delas será uma instância da Estratégia Local de Autenticação do Passport e será criada usando a função passport.use()
. Nós usaremos o módulo connect-flash para nos ajudar a manipular os erros, usando mensagens flash que podem ser usadas para mostrar os erros para o usuário.
Estratégia de Login
A estratégia de login parecerá com isso:
// passport/login.js
passport.use('login', new LocalStrategy
({
passReqToCallback : true
},
function(req, username, password, done) {
// verifica no mongo se o nome de usuário existe ou não
User.findOne({ 'username' : username },
function(err, user) {
// Em caso de erro, retorne usando o método done
if (err)
return done(err);
// Nome de usuário não existe, logar o erro & redirecione de volta
if (!user){
console.log('Usuário não encontrado para usuário '+username);
return done(null, false,
req.flash('message', 'Usuário não encontrado.'));
}
// Usuário existe mas a senha está errada, logar o erro
if (!isValidPassword(user, password)){
console.log('Senha Inválida');
return done(null, false,
req.flash('message', 'Senha Inválida'));
}
// Tanto usuário e senha estão corretos, retorna usuário através
// do método done, e, agora, será considerado um sucesso
return done(null, user);
}
);
}));
O primeiro parâmetro de passport.use()
é o nome da estratégia que utilizaremos para identificar a estratégia em questão, quando precisarmos utiliza-la futuramente. O segundo parâmetro é o tipo da estratégia que queremos criar. Aqui, usamos username-password (usuário-senha) ou a LocalStrategy
. É preciso notar que, por padrão, a LocalStrategy
espera encontrar as credenciais do usuário nos parâmetros username
& password
, mas permite que usemos qualquer outro parâmetro nomeado também. A variável de configuração passReqToCallback
permite-nos acessar o objeto request
na callback, possibilitando, assim, usarmos qualquer parâmetro associado à requisição
Depois, usamos a API do Mongoose para encontrar o usuário em nossa coleção de Usuários, para verificar se é um usuário válido ou não. O último parâmetro em nossa callback, done
, denota um método útil que nos permite sinalizar o sucesso ou falha do módulo Passport. Para especificar uma falha, ou o primeiro parâmetro contém um erro ou o segundo parâmetro deve ser algum valor que, se convertido para booleano, equivalha a false
. Para denotar sucesso, o primeiro parâmetro deve ser null
e o segundo parâmetro, se convertido para booleano, deve equivaler a true
, que, nesse caso, estará disponível no objeto request
.
Como senhas são, inerentemente, fracas, nós sempre devemos criptografá-las antes de salvá-las na base de dados. Por isso, usamos o módulo bcrypt-nodejs para auxiliar na criptografia e descriptografia das senhas.
var isValidPassword = function(user, password){ return bCrypt.compareSync(password, user.password); }
Se você acha que só ver trechos de código dificultas as coisas, preferindo ver o código completo e a aplicação em ação, sinta-se a vontade para navegar pelo código.
Estratégia de Registro
Agora, definiremos a próxima estratégia, a qual manipulará a inscrição de novos usuários e criará um novo registro em nossa base de dados Mongo DB:
passport.use('signup', new LocalStrategy
({
passReqToCallback : true
},
function(req, username, password, done) {
findOrCreateUser = function(){
// Busca usuário pelo nome apresentado
User.findOne({'username':username},function(err, user) {
// Em caso de erro, retornar
if (err){
console.log('Erro no Registro: '+err);
return done(err);
}
// Usuário existe
if (user) {
console.log('Usuário já existe');
return done(null, false,
req.flash('message','Usuário já existe'));
} else {
// Se não houver usuário com aquele e-mail
// criaremos o novo usuário
var newUser = new User();
// Atribuindo as credenciais locais
newUser.username = username;
newUser.password = createHash(password);
newUser.email = req.param('email');
newUser.firstName = req.param('firstName');
newUser.lastName = req.param('lastName');
// salva o usuário
newUser.save(function(err) {
if (err){
console.log('Erro ao salvar usuário: '+err);
throw err;
}
console.log('Registro de usuário com sucesso');
return done(null, newUser);
});
}
});
};
// Atrasa a execução do método findOrCreateUser e o executa
// na próxima oportunidade dentro do loop de eventos
process.nextTick(findOrCreateUser);
});
);
Nós usamos, novamente, a API do Mongoose para verificar se já existe usuário com o nome de usuário fornecido. Se não existir, criaremos o usuário e salvamos as informações no MongoDB. Caso contrário retornaremos o erro usando a callback done
e mensagens flash. Note que usamos o módulo bcrypt-nodejs
para a criação do hash da senha, antes de savá-la:
// Gera hash usando bCrypt var createHash = function(password){ return bCrypt.hashSync(password, bCrypt.genSaltSync(10), null); }
Criando as Rotas
Se verificássemos a estrutura geral de nossa aplicação, ela estaria assim:

Agora, definiremos as rotas da aplicação no módulo a seguir, o qual usa uma instância do Passport criada no arquivo app.js
. Salve esse módulo em routes/index.js
module.exports = function(passport){ /* Requisição GET para página de LOGIN. */ router.get('/', function(req, res) { // Mostra a página de Login com qualquer mensagem flash, caso exista res.render('index', { message: req.flash('message') }); }); /* Requisição POST para LOGIN */ router.post('/login', passport.authenticate('login', { successRedirect: '/home', failureRedirect: '/', failureFlash : true })); /*Requisição GET para página de Registro */ router.get('/signup', function(req, res){ res.render('register',{message: req.flash('message')}); }); /* Requisição POST para Registros */ router.post('/signup', passport.authenticate('signup', { successRedirect: '/home', failureRedirect: '/signup', failureFlash : true })); return router; }
A parte mais importante do trecho acima é o uso de passport.authenticate()
para delegar a autenticação das estratégias de login
e registro
quando uma requisição HTTP POST
for feita para as rotas /login
e /signup
, respectivamente. Note que não é obrigatório nomear as estratégias no caminho das rotas e elas podem ser nomeadas de qualquer outra forma.
Criando as Visões em Jade
Agora, criaremos as duas visões a seguir, para nossa aplicação:
-
layout.jade
contendo o layout básico & estilos CSS -
index.jade
contendo a página de login com seu formulário e a opção de criar uma nova conta
extends layout block content div.container div.row div.col-sm-6.col-md-4.col-md-offset-4 h1.text-center.login-title Acesse nosso Passport app div.account-wall img(class='profile-img', src='https://lh5.googleusercontent.com/-b0-k99FZlyE/AAAAAAAAAAI/AAAAAAAAAAA/eu7opA4byxI/photo.jpg?sz=120') form(class='form-signin', action='/login', method='POST') input(type='text', name='username' class='form-control', placeholder='Email',required, autofocus) input(type='password', name='password' class='form-control', placeholder='Senha', required) button(class='btn btn-lg btn-primary btn-block', type='submit') Acessar span.clearfix a(href='/signup', class='text-center new-account') Criar nova conta #message if message h1.text-center.error-message #{message}
Graças ao Bootstrap, nossa página de Login está assim:

Precisamos de duas outras visões para os detalhes do registro e para a página inicial da aplicação:
-
register.jade
contendo o formulário de registro -
home.jade
com uma saudação e mostrando os detalhes do usuário
Se não estiver familiarizado com Jade, veja a documentação aqui.
Implementando a Funcionalidade de Saída
O Passport, sendo um middleware, pode adicionar certas propriedades e métodos nos objetos de requisição e resposta. Lançando mão dessa possibilidade, temos o método request.logout()
que invalida a sessão do usuário em nossas propriedades.
/* Manipula a saída */ router.get('/signout', function(req, res) { req.logout(); res.redirect('/'); });
Protegendo as Rotas
O Passport também dá a possibilidade de restringir acesso a certas rotas que não sejam permitidas para usuários anônimos. Isso significa que, se algum usuário tentar acessar http://localhost:3000/home sem estar autenticado na aplicação, ele será redirecionado para página inicial.
/* GET Home Page */ router.get('/home', isAuthenticated, function(req, res){ res.render('home', { user: req.user }); }); // Assim como qualquer middleware, é quintessencial chamarmos next() // Se o usuário estiver autenticado var isAuthenticated = function (req, res, next) { if (req.isAuthenticated()) return next(); res.redirect('/'); }
Conclusão
O Passport não é a única maneira que temos disponivel para criar estratégias de autenticação em aplicações Node.js. Exitem alternativas como a EveryAuth, mas a modularidade, flexibilidade, suporte da comunidade e o fato de ser apenas um middleware, faz do Passport a melhor escolha.
Para uma comparação detalhada entre os dois, eis um postinteressante & informativo, na perspectiva do próprio criador do Passport.
Seja o primeiro a saber sobre novas traduções–siga @tutsplus_pt no Twitter!
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Update me weeklyEnvato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post