guias
Segurança

Autenticação Segura

Como implementar login seguro no seu app sem cometer erros comuns.

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.com

Importante: NEXTAUTH_SECRET deve ser uma string aleatória longa. Use openssl rand -base64 32 para 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 64

Nã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