create-fluxstack 1.5.0 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +1 -8
- package/app/client/src/App.tsx +1 -4
- package/app/server/index.ts +0 -4
- package/app/server/routes/index.ts +1 -5
- package/config/index.ts +1 -9
- package/core/cli/generators/plugin.ts +34 -324
- package/core/cli/generators/template-engine.ts +0 -5
- package/core/cli/plugin-discovery.ts +12 -33
- package/core/framework/server.ts +0 -10
- package/core/plugins/dependency-manager.ts +22 -89
- package/core/plugins/index.ts +0 -4
- package/core/plugins/manager.ts +2 -3
- package/core/plugins/registry.ts +1 -28
- package/core/utils/logger/index.ts +0 -4
- package/core/utils/version.ts +1 -1
- package/fluxstack.config.ts +114 -253
- package/package.json +117 -117
- package/CRYPTO-AUTH-MIDDLEWARE-GUIDE.md +0 -475
- package/CRYPTO-AUTH-MIDDLEWARES.md +0 -473
- package/CRYPTO-AUTH-USAGE.md +0 -491
- package/EXEMPLO-ROTA-PROTEGIDA.md +0 -347
- package/QUICK-START-CRYPTO-AUTH.md +0 -221
- package/app/client/src/pages/CryptoAuthPage.tsx +0 -394
- package/app/server/routes/crypto-auth-demo.routes.ts +0 -167
- package/app/server/routes/example-with-crypto-auth.routes.ts +0 -235
- package/app/server/routes/exemplo-posts.routes.ts +0 -161
- package/core/plugins/module-resolver.ts +0 -216
- package/plugins/crypto-auth/README.md +0 -788
- package/plugins/crypto-auth/ai-context.md +0 -1282
- package/plugins/crypto-auth/cli/make-protected-route.command.ts +0 -383
- package/plugins/crypto-auth/client/CryptoAuthClient.ts +0 -302
- package/plugins/crypto-auth/client/components/AuthProvider.tsx +0 -131
- package/plugins/crypto-auth/client/components/LoginButton.tsx +0 -138
- package/plugins/crypto-auth/client/components/ProtectedRoute.tsx +0 -89
- package/plugins/crypto-auth/client/components/index.ts +0 -12
- package/plugins/crypto-auth/client/index.ts +0 -12
- package/plugins/crypto-auth/config/index.ts +0 -34
- package/plugins/crypto-auth/index.ts +0 -162
- package/plugins/crypto-auth/package.json +0 -66
- package/plugins/crypto-auth/server/AuthMiddleware.ts +0 -181
- package/plugins/crypto-auth/server/CryptoAuthService.ts +0 -186
- package/plugins/crypto-auth/server/index.ts +0 -22
- package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +0 -65
- package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +0 -26
- package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +0 -76
- package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +0 -45
- package/plugins/crypto-auth/server/middlewares/helpers.ts +0 -140
- package/plugins/crypto-auth/server/middlewares/index.ts +0 -22
- package/plugins/crypto-auth/server/middlewares.ts +0 -19
- package/test-crypto-auth.ts +0 -101
|
@@ -1,788 +0,0 @@
|
|
|
1
|
-
# 🔐 FluxStack Crypto Auth Plugin
|
|
2
|
-
|
|
3
|
-
Sistema de autenticação baseado em criptografia **Ed25519** para FluxStack. Autenticação stateless sem sessões, usando assinaturas criptográficas.
|
|
4
|
-
|
|
5
|
-
## 📋 Índice
|
|
6
|
-
|
|
7
|
-
- [O Que É](#-o-que-é)
|
|
8
|
-
- [Como Funciona](#-como-funciona)
|
|
9
|
-
- [Instalação](#-instalação)
|
|
10
|
-
- [Configuração](#️-configuração)
|
|
11
|
-
- [Uso Básico](#-uso-básico)
|
|
12
|
-
- [CLI Commands](#-cli-commands)
|
|
13
|
-
- [Middlewares Disponíveis](#-middlewares-disponíveis)
|
|
14
|
-
- [Helpers e Utilitários](#-helpers-e-utilitários)
|
|
15
|
-
- [Fluxo de Autenticação](#-fluxo-de-autenticação)
|
|
16
|
-
- [Segurança](#-segurança)
|
|
17
|
-
- [Troubleshooting](#-troubleshooting)
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## 🎯 O Que É
|
|
22
|
-
|
|
23
|
-
**Crypto Auth** é um plugin de autenticação que usa **assinaturas digitais Ed25519** ao invés de sessões tradicionais.
|
|
24
|
-
|
|
25
|
-
### ✨ Principais Características
|
|
26
|
-
|
|
27
|
-
- ✅ **Stateless**: Sem sessões, sem armazenamento de tokens
|
|
28
|
-
- ✅ **Zero Trust**: Cada requisição é validada independentemente
|
|
29
|
-
- ✅ **Ed25519**: Criptografia de curva elíptica (rápida e segura)
|
|
30
|
-
- ✅ **Anti-Replay**: Proteção contra replay attacks com timestamps e nonces
|
|
31
|
-
- ✅ **Admin Support**: Sistema de permissões com chaves administrativas
|
|
32
|
-
- ✅ **TypeScript**: Totalmente tipado
|
|
33
|
-
- ✅ **CLI Integration**: Geração automática de rotas protegidas
|
|
34
|
-
|
|
35
|
-
### 🔄 Diferenças vs. Auth Tradicional
|
|
36
|
-
|
|
37
|
-
| Característica | Auth Tradicional | Crypto Auth |
|
|
38
|
-
|----------------|------------------|-------------|
|
|
39
|
-
| **Armazenamento** | Sessões no servidor | Nenhum |
|
|
40
|
-
| **Escalabilidade** | Limitada (sessões) | Infinita (stateless) |
|
|
41
|
-
| **Segurança** | Token JWT ou session | Assinatura Ed25519 |
|
|
42
|
-
| **Chave privada** | Armazenada no servidor | **NUNCA** sai do cliente |
|
|
43
|
-
| **Performance** | Depende do DB/cache | Ultra-rápida (validação local) |
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## 🔬 Como Funciona
|
|
48
|
-
|
|
49
|
-
### 1. **Cliente Gera Par de Chaves (Uma Vez)**
|
|
50
|
-
|
|
51
|
-
```typescript
|
|
52
|
-
// No navegador (usando TweetNaCl ou similar)
|
|
53
|
-
const keypair = nacl.sign.keyPair()
|
|
54
|
-
|
|
55
|
-
// Armazenar no localStorage (chave privada NUNCA sai do navegador)
|
|
56
|
-
localStorage.setItem('privateKey', toHex(keypair.secretKey))
|
|
57
|
-
localStorage.setItem('publicKey', toHex(keypair.publicKey))
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### 2. **Cliente Assina Cada Requisição**
|
|
61
|
-
|
|
62
|
-
```typescript
|
|
63
|
-
// Para cada request
|
|
64
|
-
const timestamp = Date.now()
|
|
65
|
-
const nonce = generateRandomNonce()
|
|
66
|
-
const message = `${timestamp}:${nonce}:${requestBody}`
|
|
67
|
-
|
|
68
|
-
// Assinar com chave privada
|
|
69
|
-
const signature = nacl.sign.detached(message, privateKey)
|
|
70
|
-
|
|
71
|
-
// Enviar headers
|
|
72
|
-
headers = {
|
|
73
|
-
'x-public-key': publicKeyHex,
|
|
74
|
-
'x-timestamp': timestamp,
|
|
75
|
-
'x-nonce': nonce,
|
|
76
|
-
'x-signature': toHex(signature)
|
|
77
|
-
}
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### 3. **Servidor Valida Assinatura**
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
// Plugin valida automaticamente
|
|
84
|
-
const isValid = nacl.sign.detached.verify(
|
|
85
|
-
message,
|
|
86
|
-
signature,
|
|
87
|
-
publicKey
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
if (!isValid) {
|
|
91
|
-
throw new Error('Invalid signature')
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Verificar timestamp (previne replay attacks)
|
|
95
|
-
if (Math.abs(Date.now() - timestamp) > maxTimeDrift) {
|
|
96
|
-
throw new Error('Timestamp expired')
|
|
97
|
-
}
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
### 4. **Sem Armazenamento de Estado**
|
|
101
|
-
|
|
102
|
-
- ✅ Servidor valida usando **apenas** a chave pública enviada
|
|
103
|
-
- ✅ Nenhuma sessão ou token armazenado
|
|
104
|
-
- ✅ Cada requisição é independente
|
|
105
|
-
|
|
106
|
-
---
|
|
107
|
-
|
|
108
|
-
## 📦 Instalação
|
|
109
|
-
|
|
110
|
-
O plugin já vem incluído no FluxStack. Para habilitá-lo:
|
|
111
|
-
|
|
112
|
-
### 1. **Adicionar ao `fluxstack.config.ts`**
|
|
113
|
-
|
|
114
|
-
```typescript
|
|
115
|
-
import { cryptoAuthPlugin } from './plugins/crypto-auth'
|
|
116
|
-
|
|
117
|
-
export default defineConfig({
|
|
118
|
-
plugins: {
|
|
119
|
-
enabled: [
|
|
120
|
-
cryptoAuthPlugin
|
|
121
|
-
],
|
|
122
|
-
config: {
|
|
123
|
-
'crypto-auth': {
|
|
124
|
-
enabled: true,
|
|
125
|
-
maxTimeDrift: 300000, // 5 minutos
|
|
126
|
-
adminKeys: [
|
|
127
|
-
'a1b2c3d4e5f6...', // Chaves públicas de admins (hex 64 chars)
|
|
128
|
-
'f6e5d4c3b2a1...'
|
|
129
|
-
],
|
|
130
|
-
enableMetrics: true
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
})
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### 2. **Variáveis de Ambiente (Opcional)**
|
|
138
|
-
|
|
139
|
-
```bash
|
|
140
|
-
# .env
|
|
141
|
-
CRYPTO_AUTH_ENABLED=true
|
|
142
|
-
CRYPTO_AUTH_MAX_TIME_DRIFT=300000
|
|
143
|
-
CRYPTO_AUTH_ADMIN_KEYS=a1b2c3d4e5f6...,f6e5d4c3b2a1...
|
|
144
|
-
CRYPTO_AUTH_ENABLE_METRICS=true
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
---
|
|
148
|
-
|
|
149
|
-
## ⚙️ Configuração
|
|
150
|
-
|
|
151
|
-
### Schema de Configuração
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
{
|
|
155
|
-
enabled: boolean // Habilitar/desabilitar plugin
|
|
156
|
-
maxTimeDrift: number // Máximo drift de tempo (ms) - previne replay
|
|
157
|
-
adminKeys: string[] // Chaves públicas de administradores
|
|
158
|
-
enableMetrics: boolean // Habilitar logs de métricas
|
|
159
|
-
}
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
### Valores Padrão
|
|
163
|
-
|
|
164
|
-
```typescript
|
|
165
|
-
{
|
|
166
|
-
enabled: true,
|
|
167
|
-
maxTimeDrift: 300000, // 5 minutos
|
|
168
|
-
adminKeys: [],
|
|
169
|
-
enableMetrics: true
|
|
170
|
-
}
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
---
|
|
174
|
-
|
|
175
|
-
## 🚀 Uso Básico
|
|
176
|
-
|
|
177
|
-
### Opção 1: CLI (Recomendado)
|
|
178
|
-
|
|
179
|
-
```bash
|
|
180
|
-
# Criar rota com auth obrigatória
|
|
181
|
-
bun flux crypto-auth:make:route users
|
|
182
|
-
|
|
183
|
-
# Criar rota admin-only
|
|
184
|
-
bun flux crypto-auth:make:route admin-panel --auth admin
|
|
185
|
-
|
|
186
|
-
# Criar rota com auth opcional
|
|
187
|
-
bun flux crypto-auth:make:route blog --auth optional
|
|
188
|
-
|
|
189
|
-
# Criar rota pública
|
|
190
|
-
bun flux crypto-auth:make:route public-api --auth public
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
### Opção 2: Manual
|
|
194
|
-
|
|
195
|
-
```typescript
|
|
196
|
-
// app/server/routes/users.routes.ts
|
|
197
|
-
import { Elysia, t } from 'elysia'
|
|
198
|
-
import { cryptoAuthRequired, getCryptoAuthUser } from '@/plugins/crypto-auth/server'
|
|
199
|
-
|
|
200
|
-
export const usersRoutes = new Elysia({ prefix: '/users' })
|
|
201
|
-
|
|
202
|
-
// ========================================
|
|
203
|
-
// 🔒 ROTAS PROTEGIDAS
|
|
204
|
-
// ========================================
|
|
205
|
-
.guard({}, (app) =>
|
|
206
|
-
app.use(cryptoAuthRequired())
|
|
207
|
-
|
|
208
|
-
.get('/', ({ request }) => {
|
|
209
|
-
const user = getCryptoAuthUser(request)!
|
|
210
|
-
|
|
211
|
-
return {
|
|
212
|
-
message: 'Lista de usuários',
|
|
213
|
-
authenticatedAs: user.publicKey.substring(0, 8) + '...',
|
|
214
|
-
isAdmin: user.isAdmin
|
|
215
|
-
}
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
.post('/', ({ request, body }) => {
|
|
219
|
-
const user = getCryptoAuthUser(request)!
|
|
220
|
-
|
|
221
|
-
return {
|
|
222
|
-
message: 'Usuário criado',
|
|
223
|
-
createdBy: user.publicKey.substring(0, 8) + '...'
|
|
224
|
-
}
|
|
225
|
-
}, {
|
|
226
|
-
body: t.Object({
|
|
227
|
-
name: t.String(),
|
|
228
|
-
email: t.String()
|
|
229
|
-
})
|
|
230
|
-
})
|
|
231
|
-
)
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
### Registrar Rotas
|
|
235
|
-
|
|
236
|
-
```typescript
|
|
237
|
-
// app/server/routes/index.ts
|
|
238
|
-
import { usersRoutes } from './users.routes'
|
|
239
|
-
|
|
240
|
-
export const apiRoutes = new Elysia({ prefix: '/api' })
|
|
241
|
-
.use(usersRoutes)
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
---
|
|
245
|
-
|
|
246
|
-
## 🎛️ CLI Commands
|
|
247
|
-
|
|
248
|
-
### `crypto-auth:make:route`
|
|
249
|
-
|
|
250
|
-
Gera arquivos de rotas com proteção crypto-auth automaticamente.
|
|
251
|
-
|
|
252
|
-
#### **Sintaxe**
|
|
253
|
-
|
|
254
|
-
```bash
|
|
255
|
-
bun flux crypto-auth:make:route <name> [options]
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
#### **Argumentos**
|
|
259
|
-
|
|
260
|
-
- `name` - Nome da rota (ex: posts, users, admin)
|
|
261
|
-
|
|
262
|
-
#### **Opções**
|
|
263
|
-
|
|
264
|
-
- `--auth, -a` - Tipo de autenticação (required, admin, optional, public)
|
|
265
|
-
- `--output, -o` - Diretório de saída (padrão: app/server/routes)
|
|
266
|
-
- `--force, -f` - Sobrescrever arquivo existente
|
|
267
|
-
|
|
268
|
-
#### **Exemplos**
|
|
269
|
-
|
|
270
|
-
```bash
|
|
271
|
-
# Rota com auth obrigatória
|
|
272
|
-
bun flux crypto-auth:make:route posts
|
|
273
|
-
|
|
274
|
-
# Rota admin-only com output customizado
|
|
275
|
-
bun flux crypto-auth:make:route admin --auth admin --output src/routes
|
|
276
|
-
|
|
277
|
-
# Forçar sobrescrita
|
|
278
|
-
bun flux crypto-auth:make:route users --force
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
#### **Templates Gerados**
|
|
282
|
-
|
|
283
|
-
| Tipo | Descrição | Rotas Geradas |
|
|
284
|
-
|------|-----------|---------------|
|
|
285
|
-
| `required` | Auth obrigatória | GET, POST, PUT, DELETE (CRUD completo) |
|
|
286
|
-
| `admin` | Apenas admins | GET, POST, DELETE |
|
|
287
|
-
| `optional` | Auth opcional | GET (lista), GET (detalhes com conteúdo extra) |
|
|
288
|
-
| `public` | Sem auth | GET (lista), GET (detalhes) |
|
|
289
|
-
|
|
290
|
-
---
|
|
291
|
-
|
|
292
|
-
## 🛡️ Middlewares Disponíveis
|
|
293
|
-
|
|
294
|
-
### 1. `cryptoAuthRequired()`
|
|
295
|
-
|
|
296
|
-
Autenticação **obrigatória**. Bloqueia requisições não autenticadas.
|
|
297
|
-
|
|
298
|
-
```typescript
|
|
299
|
-
import { cryptoAuthRequired, getCryptoAuthUser } from '@/plugins/crypto-auth/server'
|
|
300
|
-
|
|
301
|
-
.guard({}, (app) =>
|
|
302
|
-
app.use(cryptoAuthRequired())
|
|
303
|
-
.get('/protected', ({ request }) => {
|
|
304
|
-
const user = getCryptoAuthUser(request)! // ✅ Sempre existe
|
|
305
|
-
return { user }
|
|
306
|
-
})
|
|
307
|
-
)
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
**Comportamento:**
|
|
311
|
-
- ✅ Requisição autenticada → Prossegue
|
|
312
|
-
- ❌ Requisição não autenticada → `401 Unauthorized`
|
|
313
|
-
|
|
314
|
-
---
|
|
315
|
-
|
|
316
|
-
### 2. `cryptoAuthAdmin()`
|
|
317
|
-
|
|
318
|
-
Apenas **administradores**. Valida se a chave pública está em `adminKeys`.
|
|
319
|
-
|
|
320
|
-
```typescript
|
|
321
|
-
import { cryptoAuthAdmin, getCryptoAuthUser } from '@/plugins/crypto-auth/server'
|
|
322
|
-
|
|
323
|
-
.guard({}, (app) =>
|
|
324
|
-
app.use(cryptoAuthAdmin())
|
|
325
|
-
.delete('/delete/:id', ({ request }) => {
|
|
326
|
-
const user = getCryptoAuthUser(request)! // ✅ Sempre admin
|
|
327
|
-
return { message: 'Deletado' }
|
|
328
|
-
})
|
|
329
|
-
)
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
**Comportamento:**
|
|
333
|
-
- ✅ Chave pública está em `adminKeys` → Prossegue
|
|
334
|
-
- ❌ Não é admin → `403 Forbidden`
|
|
335
|
-
- ❌ Não autenticado → `401 Unauthorized`
|
|
336
|
-
|
|
337
|
-
---
|
|
338
|
-
|
|
339
|
-
### 3. `cryptoAuthOptional()`
|
|
340
|
-
|
|
341
|
-
Autenticação **opcional**. Não bloqueia, mas identifica usuários autenticados.
|
|
342
|
-
|
|
343
|
-
```typescript
|
|
344
|
-
import { cryptoAuthOptional, getCryptoAuthUser } from '@/plugins/crypto-auth/server'
|
|
345
|
-
|
|
346
|
-
.guard({}, (app) =>
|
|
347
|
-
app.use(cryptoAuthOptional())
|
|
348
|
-
.get('/feed', ({ request }) => {
|
|
349
|
-
const user = getCryptoAuthUser(request) // ⚠️ Pode ser null
|
|
350
|
-
|
|
351
|
-
if (user) {
|
|
352
|
-
return {
|
|
353
|
-
message: 'Feed personalizado',
|
|
354
|
-
recommendations: [...],
|
|
355
|
-
user: user.publicKey.substring(0, 8) + '...'
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
return {
|
|
360
|
-
message: 'Feed público',
|
|
361
|
-
trending: [...]
|
|
362
|
-
}
|
|
363
|
-
})
|
|
364
|
-
)
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
**Comportamento:**
|
|
368
|
-
- ✅ Requisição autenticada → `user` disponível
|
|
369
|
-
- ✅ Requisição não autenticada → `user = null`, requisição prossegue
|
|
370
|
-
|
|
371
|
-
---
|
|
372
|
-
|
|
373
|
-
### 4. `cryptoAuthPermissions(permissions: string[])`
|
|
374
|
-
|
|
375
|
-
Valida **permissões customizadas**.
|
|
376
|
-
|
|
377
|
-
```typescript
|
|
378
|
-
import { cryptoAuthPermissions, getCryptoAuthUser } from '@/plugins/crypto-auth/server'
|
|
379
|
-
|
|
380
|
-
.guard({}, (app) =>
|
|
381
|
-
app.use(cryptoAuthPermissions(['write', 'delete']))
|
|
382
|
-
.put('/edit/:id', ({ request }) => {
|
|
383
|
-
const user = getCryptoAuthUser(request)! // ✅ Tem as permissões
|
|
384
|
-
return { message: 'Editado' }
|
|
385
|
-
})
|
|
386
|
-
)
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
**Comportamento:**
|
|
390
|
-
- ✅ Usuário tem todas as permissões → Prossegue
|
|
391
|
-
- ❌ Falta alguma permissão → `403 Forbidden`
|
|
392
|
-
|
|
393
|
-
> **Nota**: Sistema de permissões requer extensão customizada. Por padrão, apenas `isAdmin` é verificado.
|
|
394
|
-
|
|
395
|
-
---
|
|
396
|
-
|
|
397
|
-
## 🔧 Helpers e Utilitários
|
|
398
|
-
|
|
399
|
-
### `getCryptoAuthUser(request)`
|
|
400
|
-
|
|
401
|
-
Retorna o usuário autenticado ou `null`.
|
|
402
|
-
|
|
403
|
-
```typescript
|
|
404
|
-
import { getCryptoAuthUser } from '@/plugins/crypto-auth/server'
|
|
405
|
-
|
|
406
|
-
.get('/profile', ({ request }) => {
|
|
407
|
-
const user = getCryptoAuthUser(request)
|
|
408
|
-
|
|
409
|
-
if (!user) {
|
|
410
|
-
return { error: 'Not authenticated' }
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
return {
|
|
414
|
-
publicKey: user.publicKey,
|
|
415
|
-
isAdmin: user.isAdmin
|
|
416
|
-
}
|
|
417
|
-
})
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
**Retorno:**
|
|
421
|
-
```typescript
|
|
422
|
-
{
|
|
423
|
-
publicKey: string // Chave pública do usuário (hex)
|
|
424
|
-
isAdmin: boolean // Se é administrador
|
|
425
|
-
} | null
|
|
426
|
-
```
|
|
427
|
-
|
|
428
|
-
---
|
|
429
|
-
|
|
430
|
-
### `isCryptoAuthAuthenticated(request)`
|
|
431
|
-
|
|
432
|
-
Verifica se a requisição está autenticada.
|
|
433
|
-
|
|
434
|
-
```typescript
|
|
435
|
-
import { isCryptoAuthAuthenticated } from '@/plugins/crypto-auth/server'
|
|
436
|
-
|
|
437
|
-
.get('/status', ({ request }) => {
|
|
438
|
-
const isAuth = isCryptoAuthAuthenticated(request)
|
|
439
|
-
|
|
440
|
-
return {
|
|
441
|
-
authenticated: isAuth,
|
|
442
|
-
message: isAuth ? 'Você está logado' : 'Você não está logado'
|
|
443
|
-
}
|
|
444
|
-
})
|
|
445
|
-
```
|
|
446
|
-
|
|
447
|
-
**Retorno:** `boolean`
|
|
448
|
-
|
|
449
|
-
---
|
|
450
|
-
|
|
451
|
-
### `isCryptoAuthAdmin(request)`
|
|
452
|
-
|
|
453
|
-
Verifica se o usuário é administrador.
|
|
454
|
-
|
|
455
|
-
```typescript
|
|
456
|
-
import { isCryptoAuthAdmin } from '@/plugins/crypto-auth/server'
|
|
457
|
-
|
|
458
|
-
.get('/admin-check', ({ request }) => {
|
|
459
|
-
const isAdmin = isCryptoAuthAdmin(request)
|
|
460
|
-
|
|
461
|
-
return {
|
|
462
|
-
isAdmin,
|
|
463
|
-
access: isAdmin ? 'granted' : 'denied'
|
|
464
|
-
}
|
|
465
|
-
})
|
|
466
|
-
```
|
|
467
|
-
|
|
468
|
-
**Retorno:** `boolean`
|
|
469
|
-
|
|
470
|
-
---
|
|
471
|
-
|
|
472
|
-
### `hasCryptoAuthPermission(request, permission)`
|
|
473
|
-
|
|
474
|
-
Verifica se o usuário tem uma permissão específica.
|
|
475
|
-
|
|
476
|
-
```typescript
|
|
477
|
-
import { hasCryptoAuthPermission } from '@/plugins/crypto-auth/server'
|
|
478
|
-
|
|
479
|
-
.get('/can-delete', ({ request }) => {
|
|
480
|
-
const canDelete = hasCryptoAuthPermission(request, 'delete')
|
|
481
|
-
|
|
482
|
-
return { canDelete }
|
|
483
|
-
})
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
**Retorno:** `boolean`
|
|
487
|
-
|
|
488
|
-
---
|
|
489
|
-
|
|
490
|
-
## 🔄 Fluxo de Autenticação
|
|
491
|
-
|
|
492
|
-
### Diagrama Completo
|
|
493
|
-
|
|
494
|
-
```
|
|
495
|
-
┌─────────────┐ ┌─────────────┐
|
|
496
|
-
│ Cliente │ │ Servidor │
|
|
497
|
-
│ (Browser) │ │ (Elysia) │
|
|
498
|
-
└──────┬──────┘ └──────┬──────┘
|
|
499
|
-
│ │
|
|
500
|
-
│ 1. Gera par de chaves Ed25519 (uma vez) │
|
|
501
|
-
│ privateKey, publicKey │
|
|
502
|
-
│ localStorage.setItem(...) │
|
|
503
|
-
│ │
|
|
504
|
-
│ 2. Para cada request: │
|
|
505
|
-
│ - timestamp = Date.now() │
|
|
506
|
-
│ - nonce = random() │
|
|
507
|
-
│ - message = `${timestamp}:${nonce}:${body}` │
|
|
508
|
-
│ - signature = sign(message, privateKey) │
|
|
509
|
-
│ │
|
|
510
|
-
│ 3. Envia request com headers │
|
|
511
|
-
│────────────────────────────────────────────────>│
|
|
512
|
-
│ x-public-key: <publicKey> │
|
|
513
|
-
│ x-timestamp: <timestamp> │
|
|
514
|
-
│ x-nonce: <nonce> │
|
|
515
|
-
│ x-signature: <signature> │
|
|
516
|
-
│ │
|
|
517
|
-
│ │ 4. Middleware valida:
|
|
518
|
-
│ │ - Reconstrói message
|
|
519
|
-
│ │ - verify(message, signature, publicKey)
|
|
520
|
-
│ │ - Verifica timestamp
|
|
521
|
-
│ │ - Verifica se é admin (se necessário)
|
|
522
|
-
│ │
|
|
523
|
-
│ 5a. ✅ Válido │
|
|
524
|
-
│<────────────────────────────────────────────────│
|
|
525
|
-
│ 200 OK { data: ... } │
|
|
526
|
-
│ │
|
|
527
|
-
│ 5b. ❌ Inválido │
|
|
528
|
-
│<────────────────────────────────────────────────│
|
|
529
|
-
│ 401 Unauthorized { error: ... } │
|
|
530
|
-
│ │
|
|
531
|
-
```
|
|
532
|
-
|
|
533
|
-
---
|
|
534
|
-
|
|
535
|
-
## 🔒 Segurança
|
|
536
|
-
|
|
537
|
-
### ✅ Proteções Implementadas
|
|
538
|
-
|
|
539
|
-
1. **Anti-Replay Attacks**
|
|
540
|
-
- Timestamp validation (maxTimeDrift)
|
|
541
|
-
- Nonce único por requisição
|
|
542
|
-
- Assinatura inclui timestamp + nonce
|
|
543
|
-
|
|
544
|
-
2. **Stateless Security**
|
|
545
|
-
- Sem sessões (não há o que roubar)
|
|
546
|
-
- Chave privada **NUNCA** sai do cliente
|
|
547
|
-
- Validação criptográfica a cada request
|
|
548
|
-
|
|
549
|
-
3. **Admin Protection**
|
|
550
|
-
- Lista whitelist de chaves públicas administrativas
|
|
551
|
-
- Validação dupla (auth + isAdmin)
|
|
552
|
-
|
|
553
|
-
4. **Type Safety**
|
|
554
|
-
- TypeScript completo
|
|
555
|
-
- Validação de schemas com TypeBox
|
|
556
|
-
|
|
557
|
-
### ⚠️ Considerações de Segurança
|
|
558
|
-
|
|
559
|
-
1. **HTTPS Obrigatório**
|
|
560
|
-
```typescript
|
|
561
|
-
// Sempre use HTTPS em produção
|
|
562
|
-
if (process.env.NODE_ENV === 'production' && !request.url.startsWith('https')) {
|
|
563
|
-
throw new Error('HTTPS required')
|
|
564
|
-
}
|
|
565
|
-
```
|
|
566
|
-
|
|
567
|
-
2. **Rotação de Chaves**
|
|
568
|
-
```typescript
|
|
569
|
-
// Cliente deve permitir rotação de chaves
|
|
570
|
-
function rotateKeys() {
|
|
571
|
-
const newKeypair = nacl.sign.keyPair()
|
|
572
|
-
// Migrar dados assinados com chave antiga
|
|
573
|
-
// Atualizar localStorage
|
|
574
|
-
}
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
3. **Rate Limiting**
|
|
578
|
-
```typescript
|
|
579
|
-
// Adicionar rate limiting para prevenir brute force
|
|
580
|
-
import { rateLimit } from '@/plugins/rate-limit'
|
|
581
|
-
|
|
582
|
-
.use(rateLimit({ max: 100, window: '15m' }))
|
|
583
|
-
.use(cryptoAuthRequired())
|
|
584
|
-
```
|
|
585
|
-
|
|
586
|
-
4. **Admin Keys em Ambiente**
|
|
587
|
-
```bash
|
|
588
|
-
# .env
|
|
589
|
-
CRYPTO_AUTH_ADMIN_KEYS=key1,key2,key3
|
|
590
|
-
```
|
|
591
|
-
|
|
592
|
-
---
|
|
593
|
-
|
|
594
|
-
## 🧪 Troubleshooting
|
|
595
|
-
|
|
596
|
-
### ❌ Erro: "Authentication required"
|
|
597
|
-
|
|
598
|
-
**Problema**: Requisição sem headers de autenticação.
|
|
599
|
-
|
|
600
|
-
**Solução**:
|
|
601
|
-
```typescript
|
|
602
|
-
// Cliente deve enviar headers
|
|
603
|
-
headers: {
|
|
604
|
-
'x-public-key': publicKeyHex,
|
|
605
|
-
'x-timestamp': Date.now().toString(),
|
|
606
|
-
'x-nonce': generateNonce(),
|
|
607
|
-
'x-signature': signatureHex
|
|
608
|
-
}
|
|
609
|
-
```
|
|
610
|
-
|
|
611
|
-
---
|
|
612
|
-
|
|
613
|
-
### ❌ Erro: "Invalid signature"
|
|
614
|
-
|
|
615
|
-
**Problema**: Assinatura não corresponde à mensagem.
|
|
616
|
-
|
|
617
|
-
**Causas comuns**:
|
|
618
|
-
1. Chave privada incorreta
|
|
619
|
-
2. Mensagem reconstruída diferente
|
|
620
|
-
3. Ordem dos campos alterada
|
|
621
|
-
|
|
622
|
-
**Solução**:
|
|
623
|
-
```typescript
|
|
624
|
-
// Garantir ordem exata dos campos
|
|
625
|
-
const message = `${timestamp}:${nonce}:${JSON.stringify(body)}`
|
|
626
|
-
```
|
|
627
|
-
|
|
628
|
-
---
|
|
629
|
-
|
|
630
|
-
### ❌ Erro: "Timestamp drift too large"
|
|
631
|
-
|
|
632
|
-
**Problema**: Diferença entre timestamp do cliente e servidor excede `maxTimeDrift`.
|
|
633
|
-
|
|
634
|
-
**Solução**:
|
|
635
|
-
```typescript
|
|
636
|
-
// Sincronizar relógio do cliente com servidor
|
|
637
|
-
const serverTime = await fetch('/api/time').then(r => r.json())
|
|
638
|
-
const timeDrift = Date.now() - serverTime.timestamp
|
|
639
|
-
// Ajustar timestamps futuros
|
|
640
|
-
```
|
|
641
|
-
|
|
642
|
-
---
|
|
643
|
-
|
|
644
|
-
### ❌ Erro: "Admin access required"
|
|
645
|
-
|
|
646
|
-
**Problema**: Usuário não está na lista de `adminKeys`.
|
|
647
|
-
|
|
648
|
-
**Solução**:
|
|
649
|
-
```typescript
|
|
650
|
-
// Adicionar chave pública ao config
|
|
651
|
-
{
|
|
652
|
-
'crypto-auth': {
|
|
653
|
-
adminKeys: [
|
|
654
|
-
'a1b2c3d4e5f6...', // Sua chave pública
|
|
655
|
-
]
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
```
|
|
659
|
-
|
|
660
|
-
---
|
|
661
|
-
|
|
662
|
-
### 🔍 Debug Mode
|
|
663
|
-
|
|
664
|
-
Habilitar logs detalhados:
|
|
665
|
-
|
|
666
|
-
```typescript
|
|
667
|
-
// fluxstack.config.ts
|
|
668
|
-
{
|
|
669
|
-
'crypto-auth': {
|
|
670
|
-
enableMetrics: true
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
```
|
|
674
|
-
|
|
675
|
-
Verificar logs:
|
|
676
|
-
```bash
|
|
677
|
-
# Requisições autenticadas
|
|
678
|
-
Requisição autenticada {
|
|
679
|
-
publicKey: "a1b2c3d4...",
|
|
680
|
-
isAdmin: false,
|
|
681
|
-
path: "/api/users",
|
|
682
|
-
method: "GET"
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
# Falhas de autenticação
|
|
686
|
-
Falha na autenticação {
|
|
687
|
-
error: "Invalid signature",
|
|
688
|
-
path: "/api/users",
|
|
689
|
-
method: "POST"
|
|
690
|
-
}
|
|
691
|
-
```
|
|
692
|
-
|
|
693
|
-
---
|
|
694
|
-
|
|
695
|
-
## 📚 Recursos Adicionais
|
|
696
|
-
|
|
697
|
-
### Documentação Relacionada
|
|
698
|
-
|
|
699
|
-
- [`QUICK-START-CRYPTO-AUTH.md`](../../QUICK-START-CRYPTO-AUTH.md) - Início rápido em 5 minutos
|
|
700
|
-
- [`EXEMPLO-ROTA-PROTEGIDA.md`](../../EXEMPLO-ROTA-PROTEGIDA.md) - Tutorial passo-a-passo
|
|
701
|
-
- [`CRYPTO-AUTH-MIDDLEWARE-GUIDE.md`](../../CRYPTO-AUTH-MIDDLEWARE-GUIDE.md) - Referência de middlewares
|
|
702
|
-
|
|
703
|
-
### Bibliotecas Cliente Recomendadas
|
|
704
|
-
|
|
705
|
-
- **JavaScript/TypeScript**: [TweetNaCl.js](https://github.com/dchest/tweetnacl-js)
|
|
706
|
-
- **React**: [@stablelib/ed25519](https://github.com/StableLib/stablelib)
|
|
707
|
-
|
|
708
|
-
### Exemplo de Cliente
|
|
709
|
-
|
|
710
|
-
```typescript
|
|
711
|
-
// client-auth.ts
|
|
712
|
-
import nacl from 'tweetnacl'
|
|
713
|
-
import { encodeHex, decodeHex } from 'tweetnacl-util'
|
|
714
|
-
|
|
715
|
-
export class CryptoAuthClient {
|
|
716
|
-
private privateKey: Uint8Array
|
|
717
|
-
private publicKey: Uint8Array
|
|
718
|
-
|
|
719
|
-
constructor() {
|
|
720
|
-
// Carregar ou gerar chaves
|
|
721
|
-
const stored = localStorage.getItem('cryptoAuthKeys')
|
|
722
|
-
|
|
723
|
-
if (stored) {
|
|
724
|
-
const keys = JSON.parse(stored)
|
|
725
|
-
this.privateKey = decodeHex(keys.privateKey)
|
|
726
|
-
this.publicKey = decodeHex(keys.publicKey)
|
|
727
|
-
} else {
|
|
728
|
-
const keypair = nacl.sign.keyPair()
|
|
729
|
-
this.privateKey = keypair.secretKey
|
|
730
|
-
this.publicKey = keypair.publicKey
|
|
731
|
-
|
|
732
|
-
localStorage.setItem('cryptoAuthKeys', JSON.stringify({
|
|
733
|
-
privateKey: encodeHex(keypair.secretKey),
|
|
734
|
-
publicKey: encodeHex(keypair.publicKey)
|
|
735
|
-
}))
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
async fetch(url: string, options: RequestInit = {}) {
|
|
740
|
-
const timestamp = Date.now().toString()
|
|
741
|
-
const nonce = encodeHex(nacl.randomBytes(16))
|
|
742
|
-
const body = options.body || ''
|
|
743
|
-
|
|
744
|
-
const message = `${timestamp}:${nonce}:${body}`
|
|
745
|
-
const signature = nacl.sign.detached(
|
|
746
|
-
new TextEncoder().encode(message),
|
|
747
|
-
this.privateKey
|
|
748
|
-
)
|
|
749
|
-
|
|
750
|
-
const headers = {
|
|
751
|
-
...options.headers,
|
|
752
|
-
'x-public-key': encodeHex(this.publicKey),
|
|
753
|
-
'x-timestamp': timestamp,
|
|
754
|
-
'x-nonce': nonce,
|
|
755
|
-
'x-signature': encodeHex(signature)
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
return fetch(url, { ...options, headers })
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
getPublicKey() {
|
|
762
|
-
return encodeHex(this.publicKey)
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
// Uso
|
|
767
|
-
const authClient = new CryptoAuthClient()
|
|
768
|
-
const response = await authClient.fetch('/api/users', {
|
|
769
|
-
method: 'POST',
|
|
770
|
-
body: JSON.stringify({ name: 'João' })
|
|
771
|
-
})
|
|
772
|
-
```
|
|
773
|
-
|
|
774
|
-
---
|
|
775
|
-
|
|
776
|
-
## 🤝 Contribuindo
|
|
777
|
-
|
|
778
|
-
Para reportar bugs ou sugerir melhorias, abra uma issue no repositório do FluxStack.
|
|
779
|
-
|
|
780
|
-
---
|
|
781
|
-
|
|
782
|
-
## 📄 Licença
|
|
783
|
-
|
|
784
|
-
Este plugin é parte do FluxStack e segue a mesma licença do framework.
|
|
785
|
-
|
|
786
|
-
---
|
|
787
|
-
|
|
788
|
-
**Desenvolvido com ❤️ pela FluxStack Team**
|