Autenticação é onde vibecoders mais erram. Este guia mostra como fazer login corretamente — sem reinventar a roda.
Regra #1: Não invente auth
// ❌ NUNCA faça isso
app.post("/login", (req, res) => {
const user = db.find({ email: req.body.email });
if (user.password === req.body.password) {
res.json({ token: user.id });
}
});Problemas: senha em plain text, sem hashing, token previsível, sem rate limiting, sem proteção contra brute force.
Use uma biblioteca de auth. Sempre.
Opções recomendadas
| Biblioteca | Melhor para | Dificuldade |
|---|---|---|
| Supabase Auth | Apps com Supabase | Fácil |
| Better Auth | Controle total | Médio |
| NextAuth (Auth.js) | Next.js | Médio |
| Clerk | SaaS / multi-tenant | Fácil |
| Lucia | Leve e flexível | Médio |
Supabase Auth (mais fácil)
Se você já usa Supabase, o auth vem incluso:
import { createClient } from "@supabase/supabase-js";
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
);
// Login com Google
await supabase.auth.signInWithOAuth({ provider: "google" });
// Login com email + senha
await supabase.auth.signUp({
email: "user@email.com",
password: "senha-forte-123",
});
// Login
await supabase.auth.signInWithPassword({
email: "user@email.com",
password: "senha-forte-123",
});
// Logout
await supabase.auth.signOut();
// Verificar sessão
const {
data: { user },
} = await supabase.auth.getUser();O Supabase cuida de: hashing de senhas, tokens JWT, refresh tokens, rate limiting e email verification.
Better Auth
Biblioteca moderna com controle total:
npm install better-auth// lib/auth.ts
import { betterAuth } from "better-auth";
export const auth = betterAuth({
database: process.env.DATABASE_URL,
emailAndPassword: { enabled: true },
socialProviders: {
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
},
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
},
},
});veloz env set GITHUB_CLIENT_ID=...
veloz env set GITHUB_CLIENT_SECRET=...
veloz env set GOOGLE_CLIENT_ID=...
veloz env set GOOGLE_CLIENT_SECRET=...NextAuth (Auth.js)
O mais popular para Next.js:
npm install next-auth// app/api/auth/[...nextauth]/route.ts
import NextAuth from "next-auth";
import GitHub from "next-auth/providers/github";
import Google from "next-auth/providers/google";
const handler = NextAuth({
providers: [
GitHub({
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
}),
Google({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
],
});
export { handler as GET, handler as POST };veloz env set NEXTAUTH_SECRET=$(openssl rand -base64 32)
veloz env set NEXTAUTH_URL=https://meuapp.onveloz.comImportante:
NEXTAUTH_SECRETdeve ser uma string aleatória longa. Useopenssl rand -base64 32para gerar.
Erros comuns
Senhas em plain text
// ❌ Senha salva sem hashing
await db.user.create({ data: { password: req.body.password } });
// ✅ Usar bcrypt
import { hash, compare } from "bcryptjs";
const hashed = await hash(req.body.password, 12);
await db.user.create({ data: { password: hashed } });
// Para verificar
const valid = await compare(req.body.password, user.password);Melhor: Use uma biblioteca de auth que faz isso automaticamente.
JWT secret fraco
// ❌ Secret previsível
const token = jwt.sign(payload, "meu-secret");
// ✅ Secret forte via variável de ambiente
const token = jwt.sign(payload, process.env.JWT_SECRET!);# Gerar secret forte
openssl rand -base64 64Não verificar sessão no servidor
// ❌ Confiar apenas no client
if (localStorage.getItem("token")) {
// Mostrar conteúdo protegido
}
// ✅ Verificar no servidor
export default async function Dashboard() {
const session = await getServerSession()
if (!session) redirect("/login")
return <h1>Bem-vindo, {session.user.name}</h1>
}Não proteger API routes
// ❌ API sem autenticação
export async function GET() {
const users = await db.user.findMany();
return Response.json(users);
}
// ✅ Verificar auth antes
export async function GET() {
const session = await getServerSession();
if (!session) {
return Response.json({ error: "Não autorizado" }, { status: 401 });
}
const users = await db.user.findMany();
return Response.json(users);
}Checklist de segurança
- Usando uma biblioteca de auth (não implementação própria)
- Senhas com hashing (bcrypt, argon2)
- Secrets fortes e aleatórios
- Sessão verificada no servidor (não só no client)
- API routes protegidas
- HTTPS em produção (automático na Veloz)
- Cookies com
httpOnly,secure,sameSite - Rate limiting no login (para evitar brute force)
- Email verification habilitado
Próximos passos
- Proteção de API — Rate limiting e validação
- Variáveis de Ambiente Seguras — Gerenciar secrets
- HTTPS e SSL — Comunicação segura