📚 Módulo 07: Frontend - Segurança com BFF e Route Handlers
No ecossistema React (SPA), a prática comum era enviar a senha para a API, receber o token JWT e salvá-lo no localStorage. Essa abordagem é vulnerável a ataques XSS (Cross-Site Scripting), pois qualquer script malicioso injetado na sua página consegue ler o seu token e roubar a sua conta.
No Next.js, temos um servidor à nossa disposição. Adotaremos o padrão BFF (Backend-For-Frontend) utilizando Route Handlers do Next.js e protegeremos nosso token blindando-o em um Cookie HttpOnly.
🛡️ 1. A Arquitetura do Login Seguro
Veja como o servidor intermediário atua para proteger o navegador de ter acesso direto ao token de segurança:
sequenceDiagram
participant Browser as Cliente (Navegador)
participant BFF as Next.js (Route Handler)
participant Nest as API NestJS
Browser->>BFF: POST /api/auth (Login + Senha)
Note over BFF: O Next.js atua como BFF
BFF->>Nest: POST /auth/login (Login + Senha)
Nest-->>BFF: JSON com { Token JWT, User }
Note over BFF: Next.js guarda o Token em Cookie HttpOnly
BFF-->>Browser: Responde Sucesso + Set-Cookie HTTP
Note over Browser: O Javascript do frontend não consegue ler o Cookie!
🔒 2. Criando a Rota do BFF (Route Handler)
No App Router do Next.js, a pasta src/app/api/ atua como um servidor backend real. Criaremos a rota interna que fará o meio-de-campo com o NestJS.
Instale a biblioteca de controle de cookies:
npm install cookies-next
Crie o arquivo do BFF em src/app/api/auth/login/route.ts:
import { NextResponse } from 'next/server';
import { cookies } from 'next/headers';
export async function POST(request: Request) {
const body = await request.json();
// 1. O Next.js (BFF) conversa com o NestJS (Backend Real)
const res = await fetch('http://localhost:3000/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
if (!res.ok) {
return NextResponse.json({ erro: 'Credenciais inválidas' }, { status: 401 });
}
const dados = await res.json(); // { usuario: {...}, token: "ey..." }
// 2. O Next.js empacota o Token em um Cookie Blindado!
cookies().set({
name: 'tecloja_token',
value: dados.token,
httpOnly: true, // PROÍBE o JavaScript do navegador de ler este cookie (Mitiga XSS)
secure: process.env.NODE_ENV === 'production', // Apenas HTTPS em produção
sameSite: 'lax', // Proteção contra ataques CSRF
maxAge: 60 * 60 * 24, // 1 Dia
path: '/',
});
// 3. Devolvemos para o React apenas os dados inofensivos do usuário
return NextResponse.json({ usuario: dados.usuario });
}
💻 3. A Tela de Login (Client Component)
Agora, criaremos o formulário visual em React. Como ele tem inputs e gerencia estados, será um "use client".
Crie em src/app/login/page.tsx:
"use client";
import React, { useState } from 'react';
import { useRouter } from 'next/navigation';
export default function LoginPage() {
const [email, setEmail] = useState('');
const [senha, setSenha] = useState('');
const [erro, setErro] = useState('');
const router = useRouter();
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault();
// O navegador fala APENAS com a rota local do Next.js (/api/auth/login)
const res = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, senha }),
});
if (res.ok) {
// O Next.js já injetou o Cookie HttpOnly silenciosamente no navegador.
// Agora redirecionamos o usuário para o painel de administração.
router.push('/admin');
router.refresh();
} else {
setErro('Email ou senha incorretos.');
}
};
return (
<div className="min-h-screen flex items-center justify-center bg-slate-900">
<div className="bg-slate-800 p-8 rounded-xl shadow-2xl border border-slate-700 w-full max-w-md">
<h2 className="text-2xl font-bold text-white mb-6 text-center">Login Administrativo</h2>
{erro && <div className="bg-red-500/20 border border-red-500 text-red-400 p-3 rounded mb-4">{erro}</div>}
<form onSubmit={handleLogin} className="flex flex-col gap-4">
<div>
<label className="text-slate-400 text-sm">E-mail</label>
<input
type="email"
className="w-full mt-1 bg-slate-900 border border-slate-700 text-white rounded p-3 outline-none focus:border-blue-500"
value={email}
onChange={e => setEmail(e.target.value)}
required
/>
</div>
<div>
<label className="text-slate-400 text-sm">Senha</label>
<input
type="password"
className="w-full mt-1 bg-slate-900 border border-slate-700 text-white rounded p-3 outline-none focus:border-blue-500"
value={senha}
onChange={e => setSenha(e.target.value)}
required
/>
</div>
<button type="submit" className="mt-4 bg-blue-600 hover:bg-blue-500 text-white font-bold py-3 rounded transition-colors">
Entrar
</button>
</form>
</div>
</div>
);
}
🛑 4. Protegendo as Rotas de Administração (Middleware)
No React (Vite), protegíamos rotas com “Guards” de componentes no frontend. No Next.js, temos um mecanismo global e de altíssima performance: O Middleware que roda na Edge Network antes da página existir.
Crie o arquivo na raiz do projeto src/middleware.ts:
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// Pega a URL que o usuário está tentando acessar
const { pathname } = request.nextUrl;
// Se a rota for administrativa, vamos verificar o escudo
if (pathname.startsWith('/admin')) {
// Tenta ler o cookie HttpOnly que nós gravamos no momento do login
const token = request.cookies.get('tecloja_token')?.value;
if (!token) {
// Se não tem token, bloqueia a passagem e joga pra tela de login
return NextResponse.redirect(new URL('/login', request.url));
}
}
// Se tudo estiver certo, libera a passagem
return NextResponse.next();
}
// Configura o middleware para observar apenas rotas específicas
export const config = {
matcher: ['/admin/:path*'],
};
✅ Pré-Requisitos deste Módulo
Antes de passar para o gerenciamento de estado global com Zustand e Server Actions no Módulo 08, certifique-se de que:
- A base do catálogo de produtos e estilos Tailwind foi construída no Módulo 06.
- A API NestJS está ativa e responde a requisições de login no endpoint
/auth/login.
🤔 Por que fizemos assim?
- Por que adotar o padrão BFF (Backend-For-Frontend) e cookies
HttpOnlypara o JWT em vez de salvar nolocalStorage? Em SPAs tradicionais, salvar o token JWT nolocalStorageexpõe a aplicação a roubos de sessão por injeção de scripts maliciosos (XSS - Cross-Site Scripting), que conseguem ler dados do armazenamento local. Com o Next.js como BFF, a senha de login é enviada para uma rota interna do servidor Next, que faz a chamada para o NestJS e envelopa o token em cookiesHttpOnly. O JavaScript do cliente é proibido de ler esse cookie, garantindo que o token fique inacessível a atacantes. - Por que ativar os parâmetros
secure: trueesameSite: 'lax'na configuração do cookie no BFF? O atributosecuregarante que o cookie de autenticação trafegue exclusivamente sob o protocolo de segurança HTTPS em produção, evitando interceptações em redes públicas. OsameSite: 'lax'impede que o navegador anexe automaticamente o cookie em chamadas de redirecionamentos iniciados em sites de terceiros, blindando o e-commerce contra ataques de falsificação de requisições CSRF (Cross-Site Request Forgery). - Por que validar o acesso às rotas administrativas no arquivo
src/middleware.tsdo Next.js? O middleware executa em nível de servidor antes de qualquer página ou componente ser renderizado ou enviado ao cliente. Validar a presença do cookietecloja_tokennessa camada permite barrar e redirecionar acessos de usuários não autenticados na rota/adminimediatamente, poupando banda e processamento do servidor de renderização (SSR).
🔍 Checkpoint
- Redirecionamento Ativo: Sem estar autenticado, tente forçar a URL do navegador direto para
http://localhost:3000/admin. O middleware deve interceptar o acesso e redirecionar você instantaneamente para/login. - Cookie HttpOnly Presente: Faça login com credenciais válidas. Abra a aba Inspecionador do Navegador -> Application -> Cookies. Verifique se a chave
tecloja_tokenexiste e se a colunaHTTPOnlypossui um sinalizador ativo (indicando bloqueio de leitura JS). - BFF em Rede: Valide no console de rede do navegador que a requisição de login foi enviada para o caminho local
/api/auth/logine não diretamente para a API de NestJS.
⚠️ Erros Comuns
| Erro | Causa | Solução |
|---|---|---|
Loop infinito de redirecionamento do middleware para a rota /login |
O matcher de segurança do middleware está monitorando todas as rotas da aplicação, incluindo a própria tela de login. | Certifique-se de configurar a propriedade matcher com a expressão exata ['/admin/:path*'] para limitar as checagens apenas a rotas administrativas. |
O TypeScript acusa erro ao tentar ler cookies usando a API cookies() |
O desenvolvedor tentou utilizar o módulo de cabeçalhos e cookies do Next em um arquivo configurado como Client Component ("use client"). |
Cookies HttpOnly só podem ser manipulados do lado do servidor. Utilize chamadas HTTP do BFF ou Server Actions para interagir com dados protegidos no lado do cliente. |
| O login do BFF retorna erro de conexão rejeitada (Connection Refused) | O endereço físico de chamada da API do NestJS no Route Handler está offline ou possui o número de porta incorreto. | Verifique se a API do NestJS está rodando ativamente e aponte a chamada do fetch no route do login para a porta e domínio corretos (normalmente http://localhost:3000). |
🏁 Conclusão
Sensacional! Subimos o nível da nossa segurança para um padrão corporativo irretocável. Usamos a API nativa do Next.js para criar um servidor intermediário (BFF) que blindou o token JWT e protegemos nossas páginas administrativas com Middleware.
No Módulo 08, completaremos a aplicação integrando o carrinho de compras com a biblioteca de estado moderno Zustand, e utilizaremos as inovadoras Server Actions para executar o faturamento do pedido.