A autenticação é um ponto importante em qualquer aplicação que tenha sua distribuição baseada em usuários. Pensando nisso, trazemos este tutorial de como criar uma autenticação baseada em tokens para autenticar usuários que acessarem sua API. O módulo que vamos utilizar para trabalhar com os tokens é o jsonwebtoken .

 

 

O que vamos construir?

Vamos construir uma mini API que terá a autenticação baseada em WebTokens, então teremos um registro de usuários (Utilizando o MongoDB para armazenarmos os usuários), as rotas que serão liberadas apenas se o usuário estiver autenticado, e o middleware que fará a verificação do token para nós.
Para isso vamos precisar de uma maquina com o NPM,
NodeJS instalados e também do aplicativo POSTman , com ele vamos conseguir fazer os testes nas rotas de uma forma mais simples.

 

Para isso vamos precisar da seguinte estrutura de arquivos:

 
  - app/
---- models/
------------ user.js
- config.js
- package.json
- server.js
   

 

 

Mão na massa!

No seu arquivo 'package.json' informe um nome para sua aplicação e deixe o 'main' com o nome de seu arquivo server.js

 
  {
    "name": "api-token",
    "main": "server.js"
}
   

Instalando os pacotes do npm.
No seu console digite "
npm install express body-parser morgan mongoose jsonwebtoken --save "
Dessa forma o npm vai baixar os pacotes e já inserir eles nas dependências do nosso projeto, dentro do package.json

express            É o pacote mais simples para criarmos as rotas do nosso app.
body-parser      Com ele conseguimos trabalhar com os dados enviados no body das requisições http que nossas rotas estão recebendo
morgan             Com ele para cada requisição que nosso app receber, um log é gerado no nosso console. (Usamos para facilitar a visualização do que está sendo feito)

mongoose         É uma biblioteca do Node para MongoDB usada para para modelar os dados da sua aplicação utilizando esquemas.

nodemon         Pacote usado para subir sua aplicação com a vantagem de que a cada vez que alterar ou criar um arquivo js ele reinicia automaticamente.
jsonwebtoken    Este é o pacote responsável pela geração dos tokens de autenticação.

 

 

Criando o modelo dos dados do usuário que será salvo no banco.

No arquivo user.js que fica na pasta /app/models/user.js, faça a seguinte configuração:

 
  // Vamos adicionar nosso pacote do mongoose e criar um objeto com schema do nosso usuário que será salvo no banco.
var mongoose = require('mongoose')

var usuarioSchema = new mongoose.Schema({
  name: String,
  password: String
})

// Agora aqui vamos devolver o Schema do usuário para quer acessar este arquivo
module.exports = mongoose.model('Usuario', usuarioSchema)
   

 

Criando o nosso banco utilizando o mLab.

- Acesse o site do  mLab  e siga os próximos passos.

- Clique em Create New ao lado de Create from backup .

- Depois selecione a opção Single-node , marque o check de Sandbox , de um nome para seu banco e depois clique em Create new MongoDB deployment .

 

- Após este passo, selecione o seu banco e adicione um usuário para ele na aba Users clicando no botão Add database user. Note que marquei em vermelho URL do nosso banco, mongodb://< dbuser >:< dbpassword >@ds163377.mlab.com:63377/token-api onde está < dbuser > e < dbpassword > mude para os dados do usuário que você cadastrou. No meu caso ficou da seguinte forma: mongodb://admin:admin@ds163377.mlab.com:63377/token-api

 

Arquivo config.js

Agora adicionamos nossa variável 'secret' que será usada para a geração do token e também adicionamos a url do nosso banco. 

 
  module.exports = {
  'secret': 'itsasecret',
  'url': 'mongodb://admin:admin@ds163377.mlab.com:63377/token-api'
  // Caso for utilizar o mongoDB localmente
  // 'url' : 'mongodb://localhost/projeto-api'
}
   

 

 

Arquivo server.js

Nele vamos informar todos os pacotes que vamos utilizar, configurar nossa aplicação com as variáveis importantes, conexão com o banco, criar as rotas básicas como a de criação de um usuário de testes e criar as rotas da API que serão dividas da seguinte forma:

 
  - Rota basica -
POST - /cadastrar
Vamos cadastrar o nome e senha que o usuário nos forneceu.

- API -

POST - /api/authenticate
Vamos verificar o nome  e senha no nosso banco e liberar um Token para o usuário utilizar, caso o nome e senha estejam corretos. Esta rota não vai precisar de um Token, pois é nela que o usuário vai conseguir ele.

GET - /api
Nela vamos mostrar uma mensagem padrão, nessa rota vamos verificar se o usuário requisitou ela usando o Token.

GET - /api/users
Vamos listar todos os usuários cadastrados, porém apenas se quem requisitar estiver utilizando um Token válido.   

Como vai ficar o nosso arquivo server.js

 
  // Vamos referenciar os pacotes que precisamos
var express = require('express')
var app = express()
var bodyParser = require('body-parser')
var morgan = require('morgan')
var mongoose = require('mongoose')

var jwt = require('jsonwebtoken')
var config = require('./config')
var User = require('./app/models/user')

var port = process.env.PORT || 3000
mongoose.Promise = global.Promise
mongoose.connect(config.url) // Conectamos no banco
app.set('superSecret', config.secret) // Variável secret

app.use(bodyParser.urlencoded({
  extended: false
}))
app.use(bodyParser.json())

// Logs das requisições
app.use(morgan('dev'))

// Rotas ===========
// rota basica

app.get('/', (req, res) => {
  res.send('Olá, a API está em http://localhost:' + port + '/api')
})

app.post('/cadastrar', (req, res) => {
  // Criamos um novo usuário utilizando o schema que montamos na pasta models
  var novoUsuario = new User({
    name: req.body.name, // A instrução req.body nos retorna um objeto com os dados que foram enviados através de uma requisição
    password: req.body.password
  })

  novoUsuario.save((err) => {
    if (err) throw err // Se tiver algum erro na hora de salvar, usamos o throw para retornar uma "exception"

    console.log('Usuário cadastrado com sucesso')
    res.json({
      success: true
    }) // Se tudo ocorrer bem, retornamos um json dizendo que deu certo
  })
})

// Rotas da API
// Utiliza uma instancia do Router para as rotas da API
var apiRoutes = express.Router()

// Rota de autenticacao de usuário (POST /api/authenticate)

apiRoutes.post('/authenticate', (req, res) => {
  console.log(req.body)
  User.findOne({
    name: req.body.name
  }, (err, user) => { // O findOne é como um Select, passando um filtro que é o 'name'
    if (err) throw err

    // Verificamos se o usuário existe
    if (!user) {
      res.json({
        success: false,
        message: 'A autenticação falhou, o usuário não foi encontrado :C'
      })
    }
      // Verificamos se a senha é correta
    if (user.password !== req.body.password) {
      res.json({
        success: false,
        message: 'A autenticação falhou, a senha está incorreta :C'
      })
    } else {
      // Se não tiver nenhum erro, então criamos o Token para ele
      var token = jwt.sign(user, app.get('superSecret'), {
        expiresIn: '1440m'
      }) // Aqui dizemos que o Token expira em 1440 minutos (24 hrs)

      // Retornamos um json dizendo que deu certo junto com o seu Token
      res.json({
        success: true,
        message: 'Aproveite seu token!',
        token: token
      })
    }
  })
})

// middleware para validar o Token
apiRoutes.use((req, res, next) => {
  // Aqui vamos verificar o header da requisição, os parametros e o corpo da requisição, procurando o token
  var token = req.body.token || req.query.token || req.headers['x-access-token']

  // Se o token existir
  if (token) {
    // Verificamos se o token está batendo com a nossa Secret
    jwt.verify(token, app.get('superSecret'), (err, decoded) => {
      if (err) {
        return res.json({
          success: false,
          message: 'A autenticação com o token falhou.'
        })
      } else {
        // Se o token estiver válido, então salvamos ele e liberamos o acesso, fazemos o trabalho do porteiro de um prédio aqui.
        req.decoded = decoded
        next()
      }
    })
  } else {
    // Se quem requisitou não informou o token, devolvemos um erro para ele.
    return res.status(403).send({
      success: false,
      message: 'Nenhum token foi informado.'
    })
  }
})

// Rota para nos devolver uma mensagem aleatória (GET /api)
apiRoutes.get('/', (req, res) => {
  res.json({
    message: 'Bem vindo a API mais dahora no mundo!'
  })
})

// Rota que retorna todos os usuários cadastrados no banco (GET /api/users)
apiRoutes.get('/users', (req, res) => {
  User.find({}, (err, users) => { // O que fizemos aqui foi basicamente um Select na "tabela" de usuários
    if (err) { throw err }
    res.json(users)
  })
})

// Aqui dizemos que as rotas terão o prefixo /api
app.use('/api', apiRoutes)

// Inicia o servidor
app.listen(port)
console.log('Servidor iniciado em http://localhost:' + port)

   

Para executar utilize no console 'nodemon server.js'

 

Vamos por partes! ( Rotas da API)

- Cadastro de usuário

 
  app.post('/cadastrar', (req, res) => {
  // Criamos um novo usuário utilizando o schema que montamos na pasta models
  var novoUsuario = new User({
    name: req.body.name, // A instrução req.body nos retorna um objeto com os dados que foram enviados através de uma requisição
    password: req.body.password
  })

  novoUsuario.save((err) => {
    if (err) throw err // Se tiver algum erro na hora de salvar, usamos o throw para retornar uma "exception"

    console.log('Usuário cadastrado com sucesso')
    res.json({
      success: true
    }) // Se tudo ocorrer bem, retornamos um json dizendo que deu certo
  })
})
   

Teste no POSTman

 

 

- Listagem de usuários

 
  // Rota que retorna todos os usuários cadastrados no banco (GET /api/users)
apiRoutes.get('/users', (req, res) => {
  User.find({}, (err, users) => { // O que fizemos aqui foi basicamente um Select na "tabela" de usuários
    if (err) { throw err }
    res.json(users)
  })
})
   

Teste no POSTman

 

- Autenticação do token.

 
  // Rota de autenticacao de usuário (POST /api/authenticate)
apiRoutes.post('/authenticate', (req, res) => {
  console.log(req.body)
  User.findOne({
    name: req.body.name
  }, (err, user) => { // O findOne é como um Select, passando um filtro que é o 'name'
    if (err) throw err

    // Verificamos se o usuário existe
    if (!user) {
      res.json({
        success: false,
        message: 'A autenticação falhou, o usuário não foi encontrado :C'
      })
    }
      // Verificamos se a senha é correta
    if (user.password !== req.body.password) {
      res.json({
        success: false,
        message: 'A autenticação falhou, a senha está incorreta :C'
      })
    } else {
      // Se não tiver nenhum erro, então criamos o Token para ele
      var token = jwt.sign(user, app.get('superSecret'), {
        expiresIn: '1440m'
      }) // Aqui dizemos que o Token expira em 1440 minutos (24 hrs)

      // Retornamos um json dizendo que deu certo junto com o seu Token
      res.json({
        success: true,
        message: 'Aproveite seu token!',
        token: token
      })
    }
  })
})
   

Testes no POSTman

- Sucesso

- Senha inválida

- Usuário invalido

 

 

Middleware

Neste passo vamos montar a proteção das nossas rotas.

 
  // middleware para validar o Token
apiRoutes.use((req, res, next) => {
  // Aqui vamos verificar o header da requisição, os parametros e o corpo da requisição, procurando o token
  var token = req.body.token || req.query.token || req.headers['x-access-token']

  // Se o token existir
  if (token) {
    // Verificamos se o token está batendo com a nossa Secret
    jwt.verify(token, app.get('superSecret'), (err, decoded) => {
      if (err) {
        return res.json({
          success: false,
          message: 'A autenticação com o token falhou.'
        })
      } else {
        // Se o token estiver válido, então salvamos ele e liberamos o acesso, fazemos o trabalho do porteiro de um prédio aqui.
        req.decoded = decoded
        next()
      }
    })
  } else {
    // Se quem requisitou não informou o token, devolvemos um erro para ele.
    return res.status(403).send({
      success: false,
      message: 'Nenhum token foi informado.'
    })
  }
})
   

Testes no POSTman  

- Sucesso

- Erro (Token inválido)

 

- Erro (Token vazio)

 

 

Conclusão

Recentemente encontrei este tutorial em um site e resolvi traduzir ele e deixar de uma forma mais simples, ele nos da uma boa visão de como proteger nossas rotas utilizando o JSON Web Token, caso queira algum tutorial especifico nos deixe um comentário, valeu pessoal!

Já ia me esquecendo, caso queira verificar o projeto completo baixe o anexo do post,  você também pode clonar ele para sua maquina pelo git clone https://github.com/nulldreams/autenticacao-api-jwt.git

 

Arquivos para download