Autenticação com Refine, ZenStack e Prisma: Login, Registro e Recuperação de Senha

Posted by

Este artigo apresenta como implementar um sistema completo de autenticação com login, registro, esqueci minha senha e reset de senha, utilizando Refine com Ant Design, ZenStack e Prisma. A solução combina validação automática, ACL declarativa e experiência de usuário moderna.

1. Backend com ZenStack e Prisma

A base da aplicação é construída com ZenStack sobre o Prisma ORM. O modelo User é configurado com validações de campo e regras de acesso, utilizando o esquema ZModel.

model User {
  id        String   @id @default(cuid())
  email     String   @unique @email
  password  String   @length(min: 8)
  createdAt DateTime @default(now())

  @@allow('create', auth() == null)
  @@allow('read,update', auth().userId == id)
}

Esse modelo permite criação pública de usuários, leitura e atualização apenas por quem estiver autenticado como o próprio usuário.

Configuração básica

  1. Inicie o projeto com ZenStack: npx zenstack init
  2. Gere o Prisma Client com políticas de acesso: npx zenstack generate
  3. Aplique as migrações no banco: npx prisma db push
  4. Use o enhance(prisma) para aplicar as regras ACL automaticamente.

Endpoints REST personalizados

Implemente endpoints específicos para:

  • /api/register
  • /api/login
  • /api/forgotPassword
  • /api/resetPassword

Estes endpoints devem validar entradas, consultar o banco com Prisma, aplicar as políticas do ZenStack e retornar tokens JWT para controle de sessão.

2. Frontend com Refine e Ant Design

Refine é um framework React focado em produtividade e administração. Utilizando o template com Ant Design, é possível criar uma interface com autenticação completa.

Criação do projeto

Execute o comando para gerar um projeto Refine com Ant Design:

npm create refine-app@latest -- --preset refine-antd

O projeto gerado já vem com suporte a rotas de autenticação e gerenciamento de sessão.

Configuração das rotas de autenticação

No arquivo App.tsx, registre o authProvider e defina as rotas públicas:

<Refine authProvider={authProvider} dataProvider={dataProvider}>
  <Routes>
    <Route path="/login" element={<AuthPage type="login" />} />
    <Route path="/register" element={<AuthPage type="register" />} />
    <Route path="/forgot-password" element={<AuthPage type="forgotPassword" />} />
    <Route path="/update-password" element={<AuthPage type="updatePassword" />} />
    {/* demais rotas protegidas */}
  </Routes>
</Refine>

Os componentes <AuthPage /> são formulários prontos com validação de campos, integração com o authProvider e compatibilidade com Ant Design.

3. authProvider do Refine conectado ao backend

O authProvider é responsável por integrar os formulários do Refine aos endpoints do backend com ZenStack. A seguir está uma implementação básica:

const authProvider = {
  login: async ({ email, password }) => {
    const response = await fetch("/api/login", {
      method: "POST",
      body: JSON.stringify({ email, password }),
    });
    if (response.ok) {
      const { token } = await response.json();
      localStorage.setItem("token", token);
      return { success: true };
    }
    return { success: false };
  },
  register: async ({ email, password }) => {
    const response = await fetch("/api/register", {
      method: "POST",
      body: JSON.stringify({ email, password }),
    });
    if (response.ok) {
      const { token } = await response.json();
      localStorage.setItem("token", token);
      return { success: true };
    }
    return { success: false };
  },
  forgotPassword: async ({ email }) => {
    await fetch("/api/forgotPassword", {
      method: "POST",
      body: JSON.stringify({ email }),
    });
    return { success: true };
  },
  updatePassword: async ({ token, newPassword }) => {
    const response = await fetch("/api/resetPassword", {
      method: "POST",
      body: JSON.stringify({ token, newPassword }),
    });
    return response.ok ? { success: true } : { success: false };
  },
  logout: async () => {
    localStorage.removeItem("token");
    return { success: true };
  },
  checkAuth: async () => {
    return localStorage.getItem("token") ? Promise.resolve() : Promise.reject();
  },
  getPermissions: async () => null,
  getUserIdentity: async () => {
    const token = localStorage.getItem("token");
    if (token) {
      // opcional: decodificar token
      return { id: "userId", email: "[email protected]" };
    }
    return null;
  },
};

4. Integração segura entre frontend e backend

A comunicação entre frontend e backend é feita via requisições HTTP autenticadas com JWT. O token é salvo no localStorage e enviado nas requisições subsequentes. No backend, o ZenStack aplica as políticas de autorização automaticamente, garantindo segurança baseada em identidade.

5. Benefícios desta arquitetura

  • Validação de dados feita no nível do modelo Prisma com ZenStack.
  • Controle de acesso declarativo com ACL em ZModel.
  • Formulários prontos com Ant Design e Refine.
  • Redução de código duplicado para autenticação.
  • Integração natural com React, APIs REST e Prisma.

Conclusão

Utilizar Refine com Ant Design no frontend e ZenStack com Prisma no backend é uma abordagem moderna e eficiente para construir sistemas com autenticação completa. A arquitetura proposta oferece segurança, validação robusta, experiência de usuário aprimorada e escalabilidade para aplicações reais.

Leave a Reply

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *