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
- Inicie o projeto com ZenStack:
npx zenstack init - Gere o Prisma Client com políticas de acesso:
npx zenstack generate - Aplique as migrações no banco:
npx prisma db push - 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.