A autenticação baseada em JWT (JSON Web Token) é uma solução amplamente utilizada para proteger aplicações modernas. Combinando Vite, React com TypeScript (React-TS) e JWT, é possível construir uma aplicação front-end performática, bem estruturada e segura.
Neste guia, você aprenderá como configurar uma aplicação React-TS com Vite para se comunicar com um backend que utiliza JWT para autenticação, incluindo a atualização automática de tokens.
O Que É JWT?
JWT (JSON Web Token) é um padrão aberto usado para transmitir informações entre partes de forma segura. Ele é composto por três partes principais: header, payload e signature. Essa estrutura permite verificar a autenticidade do token e garantir que os dados não foram alterados durante o transporte.
O uso de JWT é comum em APIs REST, pois permite autenticar usuários sem armazenar sessões no servidor, tornando o sistema mais escalável.
Por Que Usar Vite + React-TS?
Vite é uma ferramenta de build altamente eficiente, projetada para melhorar a produtividade no desenvolvimento front-end. Quando combinada com React e TypeScript, oferece:
- Desenvolvimento rápido com Hot Module Replacement
- Suporte nativo ao TypeScript
- Estrutura modular e escalável
Essas vantagens fazem do conjunto Vite + React-TS uma excelente escolha para integrar com backends que usam JWT para autenticação.
Configurando a Comunicação com o Backend
Para implementar a autenticação com JWT na sua aplicação front-end, você precisa seguir alguns passos básicos:
- Fazer login no backend e receber um access_token
- Armazenar esse token localmente
- Incluir o token nos cabeçalhos das requisições HTTP para acessar rotas protegidas
- Gerenciar a atualização do token com refresh_token
Estrutura Inicial da Aplicação
Primeiro, crie seu projeto com Vite e React-TS:
npm create vite@latest my-app --template react-ts
cd my-app
npm install
Instale também dependências úteis para chamadas HTTP:
npm install axios jwt-decode
Criando o Serviço de Autenticação
Crie um arquivo src/services/auth.service.ts
para lidar com as requisições de login e token:
import axios from 'axios'
const API_URL = 'http://localhost:3000'
const apiClient = axios.create({
baseURL: API_URL
})
export const login = async (username: string, password: string) => {
const response = await apiClient.post('/login', { username, password })
if (response.data.accessToken) {
localStorage.setItem('user', JSON.stringify(response.data))
}
return response.data
}
export const logout = () => {
localStorage.removeItem('user')
}
export const refreshToken = async () => {
const user = JSON.parse(localStorage.getItem('user') || '{}')
const response = await apiClient.post('/refresh-token', {
refreshToken: user.refreshToken
})
const updatedUser = {
...user,
accessToken: response.data.accessToken
}
localStorage.setItem('user', JSON.stringify(updatedUser))
return updatedUser
}
export const getCurrentUser = () => {
return JSON.parse(localStorage.getItem('user') || 'null')
}
Middleware para Requisições Protegidas
Você pode criar um interceptor do Axios para adicionar automaticamente o access_token nos cabeçalhos das requisições:
// src/interceptors/auth.interceptor.ts
import axios from 'axios'
import { refreshToken } from '../services/auth.service'
const setupInterceptors = () => {
axios.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true
try {
await refreshToken()
return axios(originalRequest)
} catch (err) {
console.error('Falha ao renovar token:', err)
return Promise.reject(err)
}
}
return Promise.reject(error)
}
)
}
export default setupInterceptors
No seu main.tsx
, chame o interceptor:
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import setupInterceptors from './interceptors/auth.interceptor'
setupInterceptors()
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>
)
Gerenciando Rotas Protegidas
Você pode criar um componente de rota protegida para garantir que apenas usuários autenticados acessem certas páginas:
// src/components/ProtectedRoute.tsx
import { Navigate, Outlet } from 'react-router-dom'
import { getCurrentUser } from '../services/auth.service'
const ProtectedRoute = () => {
const user = getCurrentUser()
return user ? <Outlet /> : <Navigate to="/login" />
}
export default ProtectedRoute
Use-o nas rotas da seguinte forma:
// src/router.tsx
import { createBrowserRouter } from 'react-router-dom'
import ProtectedRoute from './components/ProtectedRoute'
import Home from './pages/Home'
import Login from './pages/Login'
const router = createBrowserRouter([
{
path: '/',
element: <ProtectedRoute />,
children: [
{
index: true,
element: <Home />
}
]
},
{
path: '/login',
element: <Login />
}
])
export default router
Implementando a Página de Login
// src/pages/Login.tsx
import React, { useState } from 'react'
import { login } from '../services/auth.service'
import { useNavigate } from 'react-router-dom'
const Login = () => {
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const navigate = useNavigate()
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault()
try {
await login(username, password)
navigate('/')
} catch (err) {
alert('Erro no login')
}
}
return (
<div>
<h2>Login</h2>
<form onSubmit={handleLogin}>
<input
type="text"
placeholder="Usuário"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<input
type="password"
placeholder="Senha"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button type="submit">Entrar</button>
</form>
</div>
)
}
export default Login
Estrutura Final do Projeto
Seu projeto terá uma estrutura semelhante a esta:
my-app/
├── public/
├── src/
│ ├── components/
│ │ └── ProtectedRoute.tsx
│ ├── interceptors/
│ │ └── auth.interceptor.ts
│ ├── pages/
│ │ ├── Login.tsx
│ │ └── Home.tsx
│ ├── services/
│ │ └── auth.service.ts
│ ├── router.tsx
│ ├── main.tsx
│ └── App.tsx
├── package.json
└── tsconfig.json
Considerações sobre Segurança
Armazenar tokens JWT no localStorage não é seguro, pois permite que scripts maliciosos acessem esses dados via XSS (Cross-Site Scripting). Uma alternativa mais segura é utilizar cookies com os atributos HttpOnly e Secure para armazenar refresh tokens, enquanto o access token é mantido temporariamente em memória.
Além disso, recomenda-se:
- Utilizar HTTPS para todas as comunicações entre cliente e servidor
- Definir tempos curtos de expiração para tokens
- Implementar mecanismos de invalidação de tokens no backend
- Evitar armazenar informações sensíveis no lado do cliente
Conclusão
Combinar Vite, React-TS e JWT é uma escolha poderosa para construir aplicações front-end modernas, rápidas e seguras. Ao usar refresh tokens e interceptores HTTP, você melhora a experiência do usuário mantendo a segurança e evitando reautenticações frequentes.
Este guia cobriu desde a criação do projeto até a configuração completa de autenticação com JWT, incluindo armazenamento seguro, renovação automática de token e rotas protegidas. Agora você está pronto para implementar essas práticas em seus projetos reais.