Sistema de Autenticação e Autorização do SIGEST

Este documento explica didaticamente como funciona o sistema de autenticação e autorização do SIGEST, desenvolvido com Next.js, TypeScript e JWT.

1. Visão Geral

O SIGEST implementa um sistema completo de autenticação e autorização com as seguintes características:

  • Autenticação: Processo de verificar a identidade do usuário
  • Autorização: Processo de verificar se o usuário tem permissão para acessar um recurso
  • Controle de Acesso Baseado em Papéis (RBAC): Permissões baseadas em papéis (ADMIN, MANAGER, OPERATOR, VIEWER)
  • Proteção de Rotas: Tanto no frontend quanto nas APIs

2. Componentes Principais

2.1. Next-Auth para Sessões

O sistema utiliza next-auth para gerenciar sessões de usuário, com:

  • Provedor de credenciais personalizado
  • Estratégia baseada em JWT (JSON Web Tokens)
  • Callbacks para personalização

2.2. JWT para APIs

Além do Next-Auth, o sistema utiliza tokens JWT para autenticar chamadas às APIs:

  • Geração de tokens com a biblioteca jose
  • Verificação de tokens no middleware
  • Expiração de 1 hora por padrão

2.3. Sistema de Permissões

O sistema implementa um controle de acesso baseado em papéis (RBAC):

  • Papéis de usuário: ADMIN, MANAGER, OPERATOR, VIEWER
  • Módulos do sistema (dashboard, clients, contracts, etc.)
  • Ações permitidas (view, create, update, delete, approve, export)

2.4. Middleware para Proteção

Um middleware global intercepta requisições para:

  • Verificar autenticação em páginas
  • Verificar tokens em APIs
  • Validar permissões de acesso

3. Fluxo de Autenticação

  1. O usuário acessa a página de login e envia credenciais
  2. O servidor valida as credenciais e gera um token JWT
  3. O token é armazenado na sessão do Next-Auth
  4. A cada requisição, o middleware verifica a presença do token
  5. Para rotas protegidas sem token válido, o usuário é redirecionado para login

4. Proteção de Páginas

Como Proteger uma Página

Para proteger uma página, siga estes passos:

  1. Localização da Página: Coloque a página dentro da pasta src/app/(authenticated)/:

    src/
    ├── app/
    │   ├── (authenticated)/
    │   │   ├── dashboard/
    │   │   ├── categorias/
    │   │   ├── clientes/
    │   │   └── ... outras páginas protegidas
  2. Verificação de Autenticação no Componente: Use o hook useAuth para verificar o status:

'use client';

import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useAuth } from '../../../hooks/useAuth';

export default function PaginaProtegida() {
  const { status, user } = useAuth();
  const router = useRouter();

  useEffect(() => {
    if (status === 'unauthenticated') {
      router.push('/login');
    }
  }, [status, router]);

  if (status === 'loading') {
    return <div>Carregando...</div>;
  }

  return (
    <div>
      <h1>Página Protegida</h1>
      <p>Bem-vindo, {user?.name}</p>
      {/* Conteúdo da página */}
    </div>
  );
}
  1. Verificação de Permissões: Controle o acesso a funcionalidades específicas:
import { useAuth } from '../../../hooks/useAuth';
import { usePermissions } from '../../../lib/permissions';

export default function PaginaComPermissoes() {
  const { user } = useAuth();
  const { canCreate, canDelete } = usePermissions(user?.role);

  return (
    <div>
      <h1>Página com Permissões</h1>

      {/* Botão visível apenas para quem pode criar */}
      {canCreate('clients') && (
        <button>Novo Cliente</button>
      )}

      {/* Ação disponível apenas para quem pode excluir */}
      {canDelete('clients') && (
        <button>Excluir Cliente</button>
      )}
    </div>
  );
}

5. Proteção de APIs

Como Proteger uma API

Para proteger um endpoint de API, siga estes passos:

  1. Localização da API: Crie o arquivo dentro de src/app/api/:

    src/
    ├── app/
    │   ├── api/
    │   │   ├── clients/
    │   │   │   └── route.ts
    │   │   ├── categories/
    │   │   │   └── route.ts
    │   │   └── ... outras APIs
  2. Verificação do Token: O middleware já verifica o token automaticamente, então você só precisa acessar os dados do usuário na API:

// src/app/api/clients/route.ts
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
  // O middleware já verificou o token e adicionou x-user-payload no header
  const userPayload = request.headers.get('x-user-payload');
  const user = userPayload ? JSON.parse(userPayload) : null;

  // Agora você pode usar os dados do usuário (id, email, role)
  console.log(`API acessada por: ${user.email} (${user.role})`);

  // Implementar a lógica da API...
  return NextResponse.json({ data: [] });
}
  1. Chamando APIs Autenticadas no Frontend: Use o hook useAuth para obter o token:
import { useAuth } from '../../../hooks/useAuth';

export default function ClientesPage() {
  const { getToken } = useAuth();

  const fetchClientes = async () => {
    const token = await getToken();
    if (!token) {
      // Tratar erro de autenticação
      return;
    }

    const response = await fetch('/api/clients', {
      headers: {
        'Authorization': `Bearer ${token}`
      }
    });

    // Processar resposta...
  };

  return (
    // Renderizar componente...
  );
}

6. Proteção de Componentes

Como Criar Componentes com Controle de Acesso

Os componentes podem implementar controle de acesso através do sistema de permissões:

  1. Componente com Verificação de Permissão:
// src/components/ActionButton.tsx
'use client';

import { useAuth } from '../hooks/useAuth';
import { usePermissions } from '../lib/permissions';
import { Button } from './ui/button';
import { SystemModuleType, ActionType } from '../lib/permissions';

interface ActionButtonProps {
  module: SystemModuleType;
  action: ActionType;
  children: React.ReactNode;
  onClick: () => void;
}

export function ActionButton({ module, action, children, onClick }: ActionButtonProps) {
  const { user } = useAuth();
  const { hasPermission } = usePermissions(user?.role);

  if (!hasPermission(module, action)) {
    return null; // Componente não é renderizado se não tiver permissão
  }

  return (
    <Button onClick={onClick}>
      {children}
    </Button>
  );
}
  1. Uso do Componente:
import { ActionButton } from '../../../components/ActionButton';

export default function ClientesPage() {
  const handleNovoCliente = () => {
    // Lógica para criar cliente
  };

  return (
    <div>
      <h1>Clientes</h1>

      <ActionButton 
        module="clients" 
        action="create" 
        onClick={handleNovoCliente}
      >
        Novo Cliente
      </ActionButton>

      {/* Outros elementos da página */}
    </div>
  );
}

7. Como o Middleware Funciona

O middleware do SIGEST (src/middleware.ts) realiza as seguintes tarefas:

  1. Para Páginas Normais:

    • Verifica se existe um token na sessão usando getToken do Next-Auth
    • Redireciona para a página de login se não houver token
    • Permite o acesso se o token for válido
  2. Para APIs:

    • Verifica o header Authorization com o prefixo Bearer
    • Valida o token JWT usando verifyJWT
    • Verifica as permissões do usuário para o módulo e ação requisitados
    • Adiciona os dados do usuário no header x-user-payload para uso na API
    • Retorna erro 401 (não autenticado) ou 403 (não autorizado) se necessário

8. Tokens, Sessões e Cookies: Entendendo as Diferenças

8.1. O que é um Token?

Um token é uma string criptografada que contém informações específicas sobre o usuário e permissões:

  • JWT (JSON Web Token): É um padrão aberto (RFC 7519) que define uma forma compacta e autocontida de transmitir informações entre partes como um objeto JSON.
  • Estrutura: Consiste em três partes separadas por pontos: header.payload.signature
  • Características:
    • Autocontido (contém todas as informações necessárias)
    • Pode ser verificado e confiado por estar assinado digitalmente
    • Não precisa de consultas ao banco de dados para validação
    • Tipicamente armazenado no lado do cliente (localStorage, memoria, etc.)

8.2. O que é uma Sessão?

Uma sessão é um mecanismo para manter o estado do usuário entre múltiplas requisições:

  • Estado na Sessão: Armazena informações sobre o usuário logado no servidor
  • Identificador de Sessão: Um código único geralmente armazenado em um cookie no navegador
  • Características:
    • Os dados do usuário são armazenados no servidor (mais seguro)
    • Requer acesso ao armazenamento de sessão para validação
    • Pode ser facilmente invalidada no servidor

8.3. Next-Auth: Integrando Tokens e Sessões

No SIGEST, o Next-Auth é configurado para usar uma estratégia baseada em JWT para sessões:

session: {
  strategy: 'jwt',
}

Isso significa que:

  1. O Next-Auth gera e gerencia tokens JWT
  2. Esses tokens são armazenados em cookies HTTP-only para segurança
  3. A sessão do usuário é reconstruída a partir do token em cada requisição

8.4. Quando Usar Cada Um?

Uso de Sessão (via Next-Auth):

  • Para autenticação de páginas e rotas protegidas no frontend
  • Quando precisamos de uma experiência de usuário integrada com o React/Next.js
  • Para acesso fácil aos dados do usuário em componentes client-side
  • Ideal para interações frequentes entre páginas da aplicação

Uso de Token JWT (direto):

  • Para autenticação de APIs (RESTful)
  • Quando outros sistemas ou aplicativos externos precisam acessar suas APIs
  • Para comunicação entre microserviços
  • Quando não é viável ou desejável usar cookies (ex: aplicativos móveis)

8.5. São Incompatíveis?

Não, tokens e sessões não são incompatíveis. Na verdade, eles se complementam no SIGEST:

  • O Next-Auth utiliza JWTs para implementar sessões (usa tokens como mecanismo interno)
  • As APIs protegidas aceitam o mesmo token via header Authorization
  • O middleware trabalha com ambos, verificando sessão para páginas e token para APIs

8.6. Relação com Cookies

Cookies são apenas um mecanismo de armazenamento, enquanto tokens e sessões são conceitos de autenticação:

  • Cookies: São pequenos arquivos armazenados no navegador do usuário

    • Podem conter IDs de sessão ou tokens JWT
    • São enviados automaticamente em cada requisição para o mesmo domínio
    • Podem ser configurados como HTTP-only (não acessíveis por JavaScript) para maior segurança
  • No SIGEST:

    • O Next-Auth armazena seu JWT em um cookie HTTP-only para autenticação de páginas
    • Para APIs, o token é extraído do cookie pelo Next-Auth e disponibilizado via hook useAuth()
    • Este token então é enviado no header Authorization para APIs

8.7. Fluxo Completo no SIGEST

  1. Usuário faz login → Next-Auth gera um JWT e armazena em cookie HTTP-only
  2. Acesso a páginas → Next-Auth verifica o cookie e reconstrói a sessão
  3. Chamada para APIs → Frontend obtém token via useAuth() e envia no header Authorization
  4. Middleware verifica → Dependendo da rota, usa verificação de sessão ou token JWT

9. Segurança Adicional

O sistema também implementa:

  • Uso de secret para assinar e verificar tokens JWT
  • Tratamento de expiração de tokens (1h por padrão)
  • Configurações especiais para ambiente de desenvolvimento
  • Logs de segurança para auditoria

10. Conclusão

O sistema de autenticação e autorização do SIGEST oferece uma solução completa e segura para:

  • Proteger páginas e APIs
  • Controlar o acesso baseado em papéis
  • Implementar controle fino de permissões por módulo e ação
  • Integrar com Next-Auth para uma experiência de usuário fluida

Este documento serve como guia para entender como funciona o sistema de segurança e como implementar novas funcionalidades seguindo o mesmo padrão.