Autenticação com Clerk e Auth.js: soluções prontas vs implementação própria

1. O cenário da autenticação moderna em aplicações web

1.1. Complexidade crescente: OAuth 2.0, OpenID Connect, SSO, MFA

A autenticação em aplicações web modernas deixou de ser simples validação de usuário e senha. Hoje, uma aplicação típica precisa lidar com OAuth 2.0 para login social (Google, GitHub, Apple), OpenID Connect para identidade federada, Single Sign-On (SSO) para ambientes corporativos, e Multi-Factor Authentication (MFA) para conformidade de segurança. Cada um desses protocolos exige implementação cuidadosa de fluxos de redirecionamento, validação de tokens JWT, renovação de refresh tokens e armazenamento seguro de credenciais.

1.2. O dilema build vs. buy: tempo de desenvolvimento vs. controle total

O desenvolvedor enfrenta uma escolha estratégica: construir uma solução própria, que oferece controle total sobre cada aspecto da autenticação, ou adotar uma solução pronta, que acelera o desenvolvimento mas introduz dependências externas. Esta decisão impacta diretamente o cronograma do projeto, o orçamento e a postura de segurança da aplicação.

1.3. Visão geral das soluções: Clerk (SaaS), Auth.js (framework agnóstico) e implementação própria

Três abordagens dominam o cenário atual: Clerk, uma plataforma SaaS que oferece autenticação como serviço gerenciado; Auth.js (antigo NextAuth.js), um framework open source modular e agnóstico; e a implementação própria, onde toda a lógica de autenticação é construída do zero. Cada uma atende a diferentes perfis de projeto, desde MVPs até sistemas corporativos com requisitos regulatórios rigorosos.

2. Clerk: autenticação como serviço gerenciado

2.1. Recursos prontos para uso: login social, MFA, gerenciamento de usuários, UI components

Clerk oferece um conjunto completo de funcionalidades prontas: login com mais de 20 provedores sociais, MFA via TOTP ou SMS, gerenciamento de perfil de usuário, recuperação de senha e componentes de UI pré-construídos (modal de login, botões sociais, formulário de registro). Tudo isso é entregue via SDK, eliminando a necessidade de escrever código de autenticação.

Exemplo de integração com Clerk em Next.js:

// pages/_app.js
import { ClerkProvider } from '@clerk/nextjs'

function MyApp({ Component, pageProps }) {
  return (
    <ClerkProvider>
      <Component {...pageProps} />
    </ClerkProvider>
  )
}

export default MyApp
// pages/index.js
import { UserButton, SignInButton, useUser } from '@clerk/nextjs'

export default function Home() {
  const { isSignedIn, user } = useUser()

  return (
    <div>
      {isSignedIn ? (
        <div>
          <p>Bem-vindo, {user.firstName}!</p>
          <UserButton />
        </div>
      ) : (
        <SignInButton mode="modal" />
      )}
    </div>
  )
}

2.2. Integração simplificada: SDKs para React, Next.js, Vue, e APIs REST

Clerk fornece SDKs oficiais para os principais frameworks frontend e uma API REST completa. A integração típica leva minutos: instalar o pacote, configurar as variáveis de ambiente e envolver a aplicação com o provider. O gerenciamento de sessões, renovação de tokens e armazenamento seguro são tratados automaticamente.

2.3. Trade-offs: custo por usuário ativo, dependência de terceiros e lock-in

O modelo de precificação do Clerk é baseado em número de usuários ativos mensais (MAU). Para projetos em escala, os custos podem se tornar significativos. Além disso, a dependência de um serviço externo introduz riscos de disponibilidade e lock-in tecnológico — migrar para outra solução exige refatorar toda a camada de autenticação.

3. Auth.js (antigo NextAuth.js): a abordagem modular e open source

3.1. Filosofia “bring your own database”: adaptadores para Prisma, MongoDB, Drizzle, etc.

Auth.js adota uma filosofia modular onde você escolhe o banco de dados e os provedores de autenticação. Adaptadores oficiais conectam a biblioteca a Prisma, MongoDB, Drizzle, Supabase, entre outros. Isso dá controle total sobre onde e como os dados de usuário são armazenados.

Exemplo de configuração com Prisma:

// pages/api/auth/[...nextauth].js
import NextAuth from "next-auth"
import GoogleProvider from "next-auth/providers/google"
import { PrismaAdapter } from "@auth/prisma-adapter"
import { PrismaClient } from "@prisma/client"

const prisma = new PrismaClient()

export default NextAuth({
  adapter: PrismaAdapter(prisma),
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    }),
  ],
  session: {
    strategy: "jwt",
  },
  callbacks: {
    async session({ session, token }) {
      session.user.id = token.sub
      return session
    },
  },
})

Auth.js suporta mais de 80 provedores OAuth (Google, GitHub, Facebook, Twitter, Discord, etc.), além de autenticação por credenciais (email/senha) e magic links. A arquitetura permite adicionar provedores personalizados facilmente.

3.3. Limitações: sem UI nativa, necessidade de configurar banco de dados, manutenção contínua

Diferente do Clerk, Auth.js não fornece componentes de UI prontos — você precisa construir telas de login, registro e gerenciamento de perfil. Também exige configuração de banco de dados, migrações e manutenção contínua de segurança (atualizações de dependências, patches de vulnerabilidades).

4. Implementação própria: quando vale a pena reinventar a roda?

4.1. Pilha técnica necessária: bcrypt/argon2, JWT, sessões, refresh tokens

Uma implementação própria exige conhecimento profundo de criptografia (bcrypt ou argon2 para hash de senhas), geração e validação de JWTs, gerenciamento de sessões (com refresh tokens e rotação), proteção contra CSRF e XSS, e armazenamento seguro de tokens no cliente (httpOnly cookies).

Exemplo mínimo de hash e verificação de senha:

// utils/auth.js
import bcrypt from 'bcryptjs'

const saltRounds = 12

export async function hashPassword(password) {
  return await bcrypt.hash(password, saltRounds)
}

export async function verifyPassword(password, hash) {
  return await bcrypt.compare(password, hash)
}

// Exemplo de token JWT
import jwt from 'jsonwebtoken'

export function generateToken(userId) {
  return jwt.sign({ userId }, process.env.JWT_SECRET, { expiresIn: '1h' })
}

export function verifyToken(token) {
  return jwt.verify(token, process.env.JWT_SECRET)
}

4.2. Casos de uso justificáveis: requisitos regulatórios (LGPD, HIPAA), customização extrema

Implementação própria se justifica quando:
- Requisitos regulatórios exigem controle total sobre dados de autenticação (LGPD, HIPAA, PCI-DSS)
- A aplicação precisa de fluxos de autenticação altamente customizados (autenticação biométrica, integração com sistemas legados)
- O volume de usuários é tão grande que o custo de SaaS se torna proibitivo

4.3. Armadilhas comuns: vulnerabilidades em senhas, CSRF, XSS, gerenciamento de sessões

As armadilhas mais comuns incluem: usar algoritmos de hash inadequados (MD5, SHA1), não implementar rate limiting em tentativas de login, expor tokens em URLs ou localStorage, não validar corretamente refresh tokens, e ignorar proteção contra CSRF em endpoints de autenticação.

5. Comparação prática: tempo, custo e segurança

5.1. Tempo de implementação: horas (Clerk) vs. dias (Auth.js) vs. semanas (própria)

  • Clerk: 2-4 horas para integração completa com login social, MFA e UI pronta
  • Auth.js: 2-5 dias para configurar provedores, banco de dados e construir UI
  • Implementação própria: 2-4 semanas para implementação segura com testes e auditoria

5.2. Custo total de propriedade: assinatura SaaS vs. infraestrutura vs. horas de engenharia

  • Clerk: Gratuito até 10.000 MAU, depois US$ 0,02/MAU (plano Business: US$ 25/mês para 1.000 MAU)
  • Auth.js: Gratuito (open source), custos de infraestrutura (banco de dados, servidor)
  • Implementação própria: Custo de horas de engenharia (estimativa: US$ 5.000-US$ 20.000 para implementação inicial)

5.3. Segurança: certificações SOC 2 (Clerk) vs. auditoria comunitária (Auth.js) vs. responsabilidade total (própria)

  • Clerk: Certificação SOC 2 Tipo II, equipe de segurança dedicada, patches automáticos
  • Auth.js: Auditoria comunitária, vulnerabilidades reportadas publicamente, depende de atualizações do mantenedor
  • Implementação própria: Responsabilidade total da equipe — exige auditoria de segurança externa e manutenção contínua

6. Como escolher: matriz de decisão para diferentes cenários

6.1. Projetos pequenos e MVPs: Clerk como aceleração máxima

Para MVPs, hackathons ou projetos pessoais, Clerk oferece a aceleração máxima. Em horas, você tem autenticação completa, UI pronta e gerenciamento de usuários. O custo inicial é zero (plano gratuito) e você pode focar no produto.

6.2. Aplicações de médio porte com requisitos específicos: Auth.js como equilíbrio

Para aplicações que precisam de controle sobre dados (armazenamento próprio) mas não querem construir tudo do zero, Auth.js oferece o melhor equilíbrio. Você mantém controle sobre o banco de dados, pode customizar fluxos e não paga por usuário ativo.

6.3. Grandes sistemas com compliance rigoroso: implementação própria ou híbrida

Sistemas que lidam com dados sensíveis (saúde, finanças, governo) ou precisam de certificações específicas devem considerar implementação própria ou uma abordagem híbrida — usar Clerk/Auth.js para frontend e construir backend próprio para lógica crítica.

7. Estratégias híbridas e boas práticas

7.1. Usar Clerk/Auth.js como camada inicial e migrar para própria quando necessário

Uma estratégia comum é começar com Clerk ou Auth.js para acelerar o lançamento e, conforme a aplicação cresce e surgem requisitos específicos, migrar gradualmente para implementação própria. Isso reduz o risco inicial e permite aprendizado contínuo.

7.2. Combinando soluções: Clerk para frontend, Auth.js para backend, ou vice-versa

É possível usar Clerk para gerenciar autenticação no frontend (componentes de UI, login social) e Auth.js no backend para validação de tokens e autorização. Ou usar Auth.js para autenticação e Clerk apenas para gerenciamento de usuários.

7.3. Checklist de segurança independente da escolha: hashing, rate limiting, logs de auditoria

Independente da solução escolhida, toda aplicação deve implementar:
- Hashing de senhas com bcrypt/argon2 (custo >= 12)
- Rate limiting em endpoints de login e registro
- Logs de auditoria para eventos de autenticação
- Proteção contra CSRF em formulários
- Uso de httpOnly cookies para tokens
- Validação rigorosa de entrada em todos os campos

Referências