Como autenticar e autorizar o usuário usando o JWT no NodeJS

Autenticação e autorização é o conceito básico de segurança do computador. Você usa suas credenciais (como nome de usuário e senha) para provar sua identidade e se identificar como um usuário registrado e obter privilégios adicionais.

Isso também se aplica quando você faz login em serviços online usando suas contas do Facebook ou do Google.

Neste artigo, vamos construir uma API Nodejs com autenticação JWT (JSON Web Tokens). As ferramentas que vamos usar neste tutorial são:

  • Expressjs
  • Banco de dados MongoDB
  • Mangusto
  • Dotenv
  • Bcryptjs
  • Jsonwebtoken

Autenticação vs. Autorização

O que é Autenticação?

Autenticação é o processo de identificação de usuários por meio da aquisição de credenciais como e-mail, senha e tokens. As credenciais fornecidas são comparadas com as credenciais do usuário cadastrado, que estão disponíveis no arquivo do sistema informático local ou em qualquer banco de dados. Se as credenciais fornecidas corresponderem aos dados disponíveis no banco de dados, o processo de autenticação será concluído e o usuário poderá acessar os recursos.

O que é Autorização?

A autorização acontece após a autenticação. Toda autorização deve ter um processo de autenticação. É o processo de permitir que os usuários acessem recursos dos sistemas ou de um site. Neste tutorial, autorizaremos usuários logados a acessarem os dados do usuário. Se o usuário não estiver logado, ele não poderá usar o acesso aos dados.

Os melhores exemplos de autorização são plataformas de mídia social como Facebook e Twitter. Você não pode acessar o conteúdo de mídia social sem ter uma conta.

Outro exemplo de autorização é o conteúdo baseado em assinatura, sua autenticação pode ser feita por login no site, mas você não terá autorização para acessar o conteúdo até não se inscrever.

Pré-requisito

Antes de seguir em frente, suponho que você tenha um conhecimento básico de Javascript e MongoDB e bom conhecimento de Nodejs.

Certifique-se de ter instalado o node e o npm em sua máquina local. Para verificar se node e npm estão instalados em seu computador, abra o prompt de comando e digite node -v e npm -v. Isso deve mostrar o seguinte resultado.

Suas versões podem ser diferentes das minhas. O NPM é baixado automaticamente com o nó. Se você ainda não baixou, baixe-o no Site NodeJS.

Você precisará de um IDE (ambiente de desenvolvimento integrado) para escrever código. Neste tutorial, estou usando o editor de código VS. Se você tiver outro, pode usá-lo também. Se você não tiver nenhum IDE instalado em seu computador, você pode baixá-lo do Site do Visual Studio. Faça o download com base em seu sistema local.

  13 melhores bicicletas sob a mesa para seu escritório em casa, para ser saudável

Configuração do projeto

Crie um nome de pasta nodeapi em qualquer lugar em seu computador local e abra-o com vs-code. Abra o terminal vs-code e inicialize o gerenciador de pacotes do nó digitando.

npm init -y

Certifique-se de estar no diretório nodeapi.

O comando acima criará um arquivo package.json que contém todas as dependências que usaremos neste projeto.

Agora vamos baixar todos os pacotes mencionados acima, agora digite e insira-os no terminal.

npm install express dotenv jsonwebtoken mongoose bcryptjs

Agora, você terá arquivos e pastas, conforme mostrado abaixo.

Criando o servidor e conectando o banco de dados

Agora crie um arquivo chamado index.js e uma pasta chamada config. Dentro do config, crie dois arquivos chamados conn.js para se conectar ao banco de dados e config.env para declarar as variáveis ​​de ambiente. Escreva o código abaixo nos respectivos arquivos.

index.js

const express = require('express');
const dotenv = require('dotenv');

//Configure dotenv files above using any other library and files
dotenv.config({path:'./config/config.env'}); 

//Creating an app from express
const app = express();

//Using express.json to get request of json data
app.use(express.json());



//listening to the server
app.listen(process.env.PORT,()=>{
    console.log(`Server is listening at ${process.env.PORT}`);
})

Se você estiver usando dotenv, configure-o em seu arquivo index.js antes de chamar outros arquivos que usam variáveis ​​de ambiente.

conn.js

const mongoose = require('mongoose');

mongoose.connect(process.env.URI, 
    { useNewUrlParser: true,
     useUnifiedTopology: true })
    .then((data) => {
        console.log(`Database connected to ${data.connection.host}`)
})

config.env

URI = 'mongodb+srv://ghulamrabbani883:[email protected]/?retryWrites=true&w=majority'
PORT = 5000

Estou usando o URI do Atlas do mongo-DB, você também pode usar o localhost.

Criando modelos e rotas

Modelo é um layout de seus dados no banco de dados Mongo-DB e será armazenado como um documento JSON. Para criar um modelo, vamos usar o esquema mangusto.

Roteamento refere-se a como um aplicativo responde às solicitações do cliente. Usaremos a função de roteador expresso para criar rotas.

Os métodos de roteamento geralmente recebem dois argumentos. A primeira é a rota e a segunda é a função de retorno de chamada para definir o que essa rota faria a pedido do cliente.

Ele também recebe um terceiro argumento como uma função de middleware quando necessário, como no processo de autenticação. Como estamos construindo uma API autenticada, também usaremos a função de middleware para autorizar e autenticar usuários.

Agora vamos criar duas pastas chamadas rotas e modelos. Dentro das rotas, crie um nome de arquivo userRoute.js e dentro da pasta de modelos, crie um nome de arquivo userModel.js. Após criar os arquivos, escreva o seguinte código nos respectivos arquivos.

userModel.js

const mongoose = require('mongoose');

//Creating Schema using mongoose
const userSchema = new mongoose.Schema({
    name: {
        type:String,
        required:true,
        minLength:[4,'Name should be minimum of 4 characters']
    },
    email:{
        type:String,
        required:true,
        unique:true,
    },
    password:{
        type:String,
        required:true,
        minLength:[8,'Password should be minimum of 8 characters']
    },
    token:{
        type:String
    }
})

//Creating models
const userModel = mongoose.model('user',userSchema);
module.exports = userModel;

userRoute.js

const express = require('express');
//Creating express router
const route = express.Router();
//Importing userModel
const userModel = require('../models/userModel');

//Creating register route
route.post('/register',(req,res)=>{

})
//Creating login routes
route.post('/login',(req,res)=>{

})

//Creating user routes to fetch users data
route.get('/user',(req,res)=>{

})

Implementando a funcionalidade de rota e criando tokens JWT

O que é JWT?

JSON web tokens (JWT) é uma biblioteca javascript que cria e verifica tokens. É um padrão aberto usado para compartilhar informações entre duas partes – um cliente e um servidor. Usaremos duas funções do JWT. A primeira função é assinar para criar um novo token e a segunda função é verificar para verificar o token.

  Como ver se um aplicativo é executado em um Mac M1 com Apple Silicon

O que é bcryptjs?

Bcryptjs é uma função de hash criada por Niels Provos e David Mazières. Ele usa um algoritmo de hash para fazer o hash da senha. Ele tem duas funções mais comuns que usaremos neste projeto. A primeira função bcryptjs é hash para gerar valor de hash e a segunda função é comparar a função para comparar senhas.

Implemente a funcionalidade de rota

A função de retorno de chamada no roteamento usa três argumentos, solicitação, resposta e próxima função. O próximo argumento é opcional; passe isso somente quando você precisar disso. Esses argumentos devem estar na solicitação, na resposta e na próxima ordem. Agora modifique os arquivos userRoute.js, config.env e index.js com os seguintes códigos.

userRoute.js

//Requiring all the necessary files and libraries
const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');

//Creating express router
const route = express.Router();
//Importing userModel
const userModel = require('../models/userModel');

//Creating register route
route.post("/register", async (req, res) => {

    try {
        const { name, email, password } = req.body;
        //Check emptyness of the incoming data
        if (!name || !email || !password) {
            return res.json({ message: 'Please enter all the details' })
        }

        //Check if the user already exist or not
        const userExist = await userModel.findOne({ email: req.body.email });
        if (userExist) {
            return res.json({ message: 'User already exist with the given emailId' })
        }
        //Hash the password
        const salt = await bcrypt.genSalt(10);
        const hashPassword = await bcrypt.hash(req.body.password, salt);
        req.body.password = hashPassword;
        const user = new userModel(req.body);
        await user.save();
        const token = await jwt.sign({ id: user._id }, process.env.SECRET_KEY, {
            expiresIn: process.env.JWT_EXPIRE,
        });
        return res.cookie({ 'token': token }).json({ success: true, message: 'User registered successfully', data: user })
    } catch (error) {
        return res.json({ error: error });
    }

})
//Creating login routes
route.post('/login', async (req, res) => {
    try {
        const { email, password } = req.body;
        //Check emptyness of the incoming data
        if (!email || !password) {
            return res.json({ message: 'Please enter all the details' })
        }
        //Check if the user already exist or not
        const userExist = await userModel.findOne({email:req.body.email});
        if(!userExist){
            return res.json({message:'Wrong credentials'})
        }
        //Check password match
        const isPasswordMatched = await bcrypt.compare(password,userExist.password);
        if(!isPasswordMatched){
            return res.json({message:'Wrong credentials pass'});
        }
        const token = await jwt.sign({ id: userExist._id }, process.env.SECRET_KEY, {
            expiresIn: process.env.JWT_EXPIRE,
        });
        return res.cookie({"token":token}).json({success:true,message:'LoggedIn Successfully'})
    } catch (error) {
        return res.json({ error: error });
    }

})

//Creating user routes to fetch users data
route.get('/user', async (req, res) => {
    try {
        const user  = await userModel.find();
        if(!user){
            return res.json({message:'No user found'})
        }
        return res.json({user:user})
    } catch (error) {
        return res.json({ error: error });  
    }
})

module.exports = route;

Se você estiver usando a função Async, use o bloco try-catch, caso contrário, ele lançará um erro de rejeição de promessa não tratado.

config.env

URI = 'mongodb+srv://ghulamrabbani883:[email protected]/?retryWrites=true&w=majority'
PORT = 5000
SECRET_KEY = KGGK>HKHVHJVKBKJKJBKBKHKBMKHB
JWT_EXPIRE = 2d

index.js

const express = require('express');
const dotenv = require('dotenv');

//Configure dotenv files above using any other library and files
dotenv.config({path:'./config/config.env'}); 
require('./config/conn');
//Creating an app from express
const app = express();
const route = require('./routes/userRoute');

//Using express.json to get request of json data
app.use(express.json());
//Using routes

app.use('/api', route);

//listening to the server
app.listen(process.env.PORT,()=>{
    console.log(`Server is listening at ${process.env.PORT}`);
})

Criando middleware para autenticar usuário

O que é middleware?

Middleware é uma função que tem acesso à solicitação, objeto de resposta e próxima função no ciclo de solicitação-resposta. A próxima função é invocada quando a execução da função é concluída. Como mencionei acima, use next() quando precisar executar outra função de retorno de chamada ou função de middleware.

  Como obter acesso antecipado ao Ubuntu 18.04

Agora crie uma pasta chamada middleware, e dentro dela, crie o nome do arquivo como auth.js e escreva o código a seguir.

autenticação.js

const userModel = require('../models/userModel');
const jwt = require('jsonwebtoken');
const isAuthenticated = async (req,res,next)=>{
    try {
        const {token} = req.cookies;
        if(!token){
            return next('Please login to access the data');
        }
        const verify = await jwt.verify(token,process.env.SECRET_KEY);
        req.user = await userModel.findById(verify.id);
        next();
    } catch (error) {
       return next(error); 
    }
}

module.exports = isAuthenticated;

Agora instale a biblioteca do analisador de cookies para configurar o cookieParser em seu aplicativo. cookieParser ajuda você a acessar o token armazenado no cookie. Se você não tiver o cookieParser configurado em seu aplicativo nodejs, não poderá acessar os cookies dos cabeçalhos do objeto de solicitação. Agora, escreva no terminal para baixar o analisador de cookies.

npm i cookie-parser

Agora, você tem um cookieParser instalado. Configure seu aplicativo modificando o arquivo index.js e adicione middleware à rota “/user/”.

arquivo index.js

const cookieParser = require('cookie-parser');
const express = require('express');
const dotenv = require('dotenv');

//Configure dotenv files above using any other library and files
dotenv.config({path:'./config/config.env'}); 
require('./config/conn');
//Creating an app from express
const app = express();
const route = require('./routes/userRoute');

//Using express.json to get request of json data
app.use(express.json());
//Configuring cookie-parser
app.use(cookieParser()); 

//Using routes
app.use('/api', route);

//listening to the server
app.listen(process.env.PORT,()=>{
    console.log(`Server is listening at ${process.env.PORT}`);
})

userRoute.js

//Requiring all the necessary files and libraries
const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const isAuthenticated = require('../middleware/auth');

//Creating express router
const route = express.Router();
//Importing userModel
const userModel = require('../models/userModel');

//Creating user routes to fetch users data
route.get('/user', isAuthenticated, async (req, res) => {
    try {
        const user = await userModel.find();
        if (!user) {
            return res.json({ message: 'No user found' })
        }
        return res.json({ user: user })
    } catch (error) {
        return res.json({ error: error });
    }
})

module.exports = route;

A rota “/user” só estará acessível quando o usuário estiver logado.

Verificando as APIs no POSTMAN

Antes de verificar as APIs, você precisa modificar o arquivo package.json. Adicione as seguintes linhas de código.

"scripts": {
    "test": "echo "Error: no test specified" && exit 1",
    "start": "node index.js",
    "dev": "nodemon index.js"
  },

Você pode iniciar o servidor digitando npm start, mas ele será executado apenas uma vez. Para manter seu servidor funcionando enquanto altera os arquivos, você precisará do nodemon. Faça o download digitando no terminal

npm install -g nodemon

-g sinalizador fará o download do nodemon globalmente em seu sistema local. Você não precisa baixá-lo repetidamente para cada novo projeto.

Para executar o servidor, digite npm run dev no terminal. Você obterá o seguinte resultado.

Finalmente, seu código está completo, e o servidor está rodando corretamente, vá até o carteiro e verifique se está funcionando.

O que é POSTMAN?

POSTMAN é uma ferramenta de software para projetar, construir, desenvolver e testar API.

Se você não baixou o carteiro em seu computador, baixe-o do site do carteiro.

Agora abra o carteiro e crie um nome de coleção nodeAPItest, e dentro dele, crie três requisições: register, login e user. Você deve ter os seguintes arquivos.

Ao enviar dados JSON para o “localhost:5000/api/register”, você obterá o seguinte resultado.

Como também estamos criando e salvando tokens em cookies durante o registro, você pode ter os detalhes do usuário ao solicitar a rota “localhost:5000/api/user”. Você pode verificar o restante das solicitações no POSTMAN.

Se você quiser o código completo, você pode obtê-lo no meu conta do github.

Conclusão

Neste tutorial, aprendemos como aplicar autenticação à API NodeJS usando tokens JWT. Também autorizamos os usuários a acessar os dados do usuário.

BOM CODIFICAÇÃO!