Entendendo a autenticação JWT
Índice
Neste artigo, veremos o que é o JSON Web Token.
Vamos passar por uma explicação básica do JWT, sua estrutura e, finalmente, criaremos um servidor simples que pegará alguns dados e os inserirá em um JWT.
O que é um Token da Web JSON e por que precisamos dele?
O JSON Web Token (JWT) é uma maneira segura, compacta e independente de transmitir informações entre várias partes na forma de um objeto JSON.
Imagine um login em um aplicativo, como o Tinder. O Tinder permite que os usuários efetuem login usando seu perfil do Facebook. Portanto, quando o usuário seleciona a opção de fazer login usando o Facebook, o aplicativo entra em contato com o servidor de autenticação do Facebook com as credenciais do usuário (nome de usuário e senha).
Depois que o servidor de autenticação verificar as credenciais do usuário, ele criará um JWT e o enviará ao usuário. O aplicativo agora obtém esse JWT e permite ao usuário acessar seus dados.
A estrutura do JWT
Um JSON Web Token consiste em três partes separadas por um ".". Eles são:
- Header (Cabeçalho)
- Payload (Corpo do token)
- Assinatura
Header
O header
geralmente consiste em duas partes: o tipo do token e o algoritmo de hash que está sendo usado.
{
"alg": "HS256",
"typ": "JWT"
}
Payload
O payload
é onde estão as informações reais que queremos enviar são armazenadas. Aqui está um exemplo de payload simples. Saiba que os payloads podem ser muito mais complicados do que isso para garantir melhor segurança.
{
"id": 42,
"name": "Guilherme Esteves",
"admin": true
}
Signature
A assinatura (signature
) é usada para verificar se a mensagem não foi alterada antes de chegar ao seu destino. Isso geralmente é feito usando chaves privadas.
Essas três partes geralmente são codificadas em três seqüências de Base64-URI que são separadas por um .
entre elas.
Para decodificar, verificar, gerar ou simplesmente brincar com JWTs, confira o JWT.IO Debugger .
Agora que temos um entendimento básico do que é um JSON, vamos dar uma olhada em como criar um servidor de autenticação simples que emita o JWT, que usaremos para acessar uma API.
Dando início ao projeto
Eu escrevi um projeto simples de API que usarei aqui para entendermos como o JWT funciona.
Basta clonar o projeto no Github, e rodar npm install
ou yarn install
dependendo do package manager que estiver usando.
Você vai perceber que dentro do index.js
há todo o código para o servidor funcionar, com nossas rotas. Você pode usar o
VSCode
para abrir o projeto.
Bibliotecas necessárias
import cors from 'cors'
import express from 'express'
import jwt from 'jsonwebtoken'
import expressjwt from 'express-jwt'
import bodyParser from 'body-parser'
O express
é a biblioteca que vai criar nosso servidor web, express-jwt
, body-parser
e cors
são como plugins para o express, que ajudam com a geração do JWT, parsing de JSON e CORS. O jsonwebtoken
é uma lib que facilita a verificação da assinatura de um JWT.
Também criei um arquivo chamado users.js
que contém os usuários & passwords fictícios do nosso sistema. Ao invés de usar um banco de dados e encriptar o password, esse é o jeito mais simples de mostrar as funcionalidades do JWT.
import users from './users.js'
Montando o servidor e o validador de JWT
Nas próximas linhas, instancio o servidor que vai ter as rotas que vamos usar e uso a biblioteca mostrada acima para poder injetar no express e validar a autenticação.
const app = express()
const SECRET = 'Sup3rS3cr3t'
const isAuthenticated = expressjwt({ secret: SECRET })
A contante SECRET
é usada para ser nosso segredo na hora de encriptar e desencriptar nosso token de autenticação. Normalmente é usado uma chave privada RSA mas para deixar nosso exemplo simples e focado no JWT, iremos usar só uma string.
app.use(cors())
app.use(bodyParser.json())
app.set('port', process.env.PORT || 3000)
Adiciono o cors
ao nosso servidor, que
será útil para acessar a api através de um outro domínio
que o frontend possa estar. O bodyParser
é usado para parsear o body das requests e o set port na app, é usado para colocar a porta (seja via uma varável de ambiente PORT
ou com o padrão 3000).
Rotas da nossa aplicação
GETs
// Routes
app.get('/', (req, res) => res.status(200).send(`Olá mundo`))
app.get('/public', (req, res) =>
res.status(200).send('Todo mundo pode ver isso!')
)
app.get('/secret', isAuthenticated, (req, res) =>
res.status(200).send('Apenas pessoas logadas podem me ver')
)
Temos 2 rotas públicas /
e /public
que ao serem chamdas, retornam os respectivos textos e a /secret
que vamos usar para entender a função do JWT, já que não conseguimos chamá-la se não tivermos logados.
A rota /secret
ao ser chamada via browser, produzirá um erro por não estarmos logados.
Usando o postman para se autenticar
O Exemplo_de_JWT.postman_collection.json
, dentro do projeto será usado para importar no postman e fazer as chamadas para o servidor usando o JWT.
Agora que criamos um servidor simples que pode lidar com solicitações GET
e POST
, vamos criar um emissor de JWT simples. O arquivo users.js
previamente declarado, contém os usuários junto com suas senhas, como mostrado abaixo:
export default [
{ id: 1, username: 'clarkKent', password: 'superman' },
{ id: 2, username: 'bruceWayne', password: 'batman' },
]
Agora vamos escrever a rota /login
. Primeiro verificaremos se a solicitação enviada contém um nome de usuário e uma senha. Se não for esse o caso, o servidor deverá responder com um status 400. Depois iremos buscar nos nossos usuários para tentar encontrar um que corresponda com username
e password
cadastrado. Se o usuário e senha não estiverem corretos, devolveremos um 401, que significa não autorizado.
Caso esteja tudo certo e o usuário tenha se autenticado, criaremos um token válido para ele e devolveremos. Note que o SECRET
é usado para assinar o token.
app.post('/login', (req, res) => {
const { username, password } = req.body || {}
if (!username || !password) {
return res
.status(400)
.send('Erro. Digite o nome de usuário e senha corretos')
}
const user = users.find((u) => {
return u.username === username && u.password === password
})
if (!user) {
return res.sendStatus(401)
}
const token = jwt.sign(
{
id: user.id,
username: user.username,
},
SECRET,
{ expiresIn: '1 hour' }
)
return res.status(200).send({ access_token: token })
})
Agora vá para o Postman e use o seguinte body dentro da chamada de login:
{
"username": "clarkKent",
"password": "superman"
}
A resposta será um token com validade definida e as informações que colocamos previamente:
Ao copiar o token presente no access_token
e colar no
jwt.io
temos o seguinte resultado:
Lembra-se que a rota /secret
estava dando erro? Bom, agora vamos no postman, na parte de Headers
e vamos colocar um novo header chamado Authorization
com o prefixo Bearer
e o token:
Conclusão
Comparado a outros tokens da web como SWTs (Simple Web Tokens) ou SAML (Security Assertion Markup Language), o JWT é muito mais simples, pois é baseado em JSON, que é mais fácil de entender do que XML. Se codificarmos o JSON, ele ficará ainda menor em tamanho que o SAML, facilitando a transmissão em ambientes HTML e HTTP.
Em termos de segurança, os SWTs usam uma única chave, enquanto o JWT e o SAML usam um par de chaves pública e privada para melhor autenticação. Falando do ponto de vista de uso, os JWTs são usados em escala da Internet. Isso significa que é mais fácil processar nos dispositivos do usuário, seja notebooks ou celulares.
Além da autenticação, o JSON Web Token é uma maneira excelente e segura de transmitir dados entre várias partes. O fato de as JWTs terem assinaturas, facilita a identificação fácil de todos os remetentes de informações. Tudo que você precisa é a chave correta.
Obrigado por ler até aqui, espero que esse artigo tenha sido útil para você.