create-fluxstack 1.4.0 → 1.5.0

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.
Files changed (55) hide show
  1. package/.env.example +8 -1
  2. package/CRYPTO-AUTH-MIDDLEWARE-GUIDE.md +475 -0
  3. package/CRYPTO-AUTH-MIDDLEWARES.md +473 -0
  4. package/CRYPTO-AUTH-USAGE.md +491 -0
  5. package/EXEMPLO-ROTA-PROTEGIDA.md +347 -0
  6. package/QUICK-START-CRYPTO-AUTH.md +221 -0
  7. package/app/client/src/App.tsx +4 -1
  8. package/app/client/src/pages/CryptoAuthPage.tsx +394 -0
  9. package/app/server/index.ts +4 -0
  10. package/app/server/routes/crypto-auth-demo.routes.ts +167 -0
  11. package/app/server/routes/example-with-crypto-auth.routes.ts +235 -0
  12. package/app/server/routes/exemplo-posts.routes.ts +161 -0
  13. package/app/server/routes/index.ts +5 -1
  14. package/config/index.ts +9 -1
  15. package/core/cli/generators/index.ts +5 -2
  16. package/core/cli/generators/plugin.ts +580 -0
  17. package/core/cli/generators/template-engine.ts +5 -0
  18. package/core/cli/index.ts +88 -3
  19. package/core/cli/plugin-discovery.ts +33 -12
  20. package/core/framework/server.ts +10 -0
  21. package/core/plugins/dependency-manager.ts +89 -22
  22. package/core/plugins/index.ts +4 -0
  23. package/core/plugins/manager.ts +3 -2
  24. package/core/plugins/module-resolver.ts +216 -0
  25. package/core/plugins/registry.ts +28 -1
  26. package/core/utils/logger/index.ts +4 -0
  27. package/core/utils/version.ts +1 -1
  28. package/create-fluxstack.ts +117 -8
  29. package/fluxstack.config.ts +253 -114
  30. package/package.json +117 -117
  31. package/plugins/crypto-auth/README.md +722 -172
  32. package/plugins/crypto-auth/ai-context.md +1282 -0
  33. package/plugins/crypto-auth/cli/make-protected-route.command.ts +383 -0
  34. package/plugins/crypto-auth/client/CryptoAuthClient.ts +136 -159
  35. package/plugins/crypto-auth/client/components/AuthProvider.tsx +35 -94
  36. package/plugins/crypto-auth/client/components/LoginButton.tsx +36 -53
  37. package/plugins/crypto-auth/client/components/ProtectedRoute.tsx +17 -37
  38. package/plugins/crypto-auth/client/components/index.ts +1 -4
  39. package/plugins/crypto-auth/client/index.ts +1 -1
  40. package/plugins/crypto-auth/config/index.ts +34 -0
  41. package/plugins/crypto-auth/index.ts +84 -152
  42. package/plugins/crypto-auth/package.json +65 -64
  43. package/plugins/crypto-auth/server/AuthMiddleware.ts +19 -75
  44. package/plugins/crypto-auth/server/CryptoAuthService.ts +60 -167
  45. package/plugins/crypto-auth/server/index.ts +15 -2
  46. package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +65 -0
  47. package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +26 -0
  48. package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +76 -0
  49. package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +45 -0
  50. package/plugins/crypto-auth/server/middlewares/helpers.ts +140 -0
  51. package/plugins/crypto-auth/server/middlewares/index.ts +22 -0
  52. package/plugins/crypto-auth/server/middlewares.ts +19 -0
  53. package/test-crypto-auth.ts +101 -0
  54. package/plugins/crypto-auth/client/components/SessionInfo.tsx +0 -242
  55. package/plugins/crypto-auth/plugin.json +0 -29
@@ -1,27 +1,27 @@
1
1
  /**
2
2
  * Cliente de Autenticação Criptográfica
3
- * Gerencia autenticação no lado do cliente
3
+ * Sistema baseado em assinatura Ed25519 SEM sessões no servidor
4
+ *
5
+ * Funcionamento:
6
+ * 1. Cliente gera par de chaves Ed25519 localmente
7
+ * 2. Chave privada NUNCA sai do navegador
8
+ * 3. Cada requisição é assinada automaticamente
9
+ * 4. Servidor valida assinatura usando chave pública recebida
4
10
  */
5
11
 
6
12
  import { ed25519 } from '@noble/curves/ed25519'
7
13
  import { sha256 } from '@noble/hashes/sha256'
8
14
  import { bytesToHex, hexToBytes } from '@noble/hashes/utils'
9
15
 
10
- export interface SessionInfo {
11
- sessionId: string
16
+ export interface KeyPair {
12
17
  publicKey: string
13
18
  privateKey: string
14
- isAdmin: boolean
15
- permissions: string[]
16
19
  createdAt: Date
17
- lastUsed: Date
18
20
  }
19
21
 
20
22
  export interface AuthConfig {
21
- apiBaseUrl?: string
22
23
  storage?: 'localStorage' | 'sessionStorage' | 'memory'
23
24
  autoInit?: boolean
24
- sessionTimeout?: number
25
25
  }
26
26
 
27
27
  export interface SignedRequestOptions extends RequestInit {
@@ -29,16 +29,15 @@ export interface SignedRequestOptions extends RequestInit {
29
29
  }
30
30
 
31
31
  export class CryptoAuthClient {
32
- private session: SessionInfo | null = null
32
+ private keys: KeyPair | null = null
33
33
  private config: AuthConfig
34
34
  private storage: Storage | Map<string, string>
35
+ private readonly STORAGE_KEY = 'fluxstack_crypto_keys'
35
36
 
36
37
  constructor(config: AuthConfig = {}) {
37
38
  this.config = {
38
- apiBaseUrl: '',
39
39
  storage: 'localStorage',
40
40
  autoInit: true,
41
- sessionTimeout: 1800000, // 30 minutos
42
41
  ...config
43
42
  }
44
43
 
@@ -58,75 +57,43 @@ export class CryptoAuthClient {
58
57
  }
59
58
 
60
59
  /**
61
- * Inicializar sessão
60
+ * Inicializar (gerar ou carregar chaves)
62
61
  */
63
- async initialize(): Promise<SessionInfo> {
64
- // Tentar carregar sessão existente
65
- const existingSession = this.loadSession()
66
- if (existingSession && this.isSessionValid(existingSession)) {
67
- this.session = existingSession
68
- return existingSession
62
+ initialize(): KeyPair {
63
+ // Tentar carregar chaves existentes
64
+ const existingKeys = this.loadKeys()
65
+ if (existingKeys) {
66
+ this.keys = existingKeys
67
+ return existingKeys
69
68
  }
70
69
 
71
- // Criar nova sessão
72
- return this.createNewSession()
70
+ // Criar novo par de chaves
71
+ return this.createNewKeys()
73
72
  }
74
73
 
75
74
  /**
76
- * Criar nova sessão
75
+ * Criar novo par de chaves
76
+ * NUNCA envia chave privada ao servidor!
77
77
  */
78
- async createNewSession(): Promise<SessionInfo> {
79
- try {
80
- // Gerar par de chaves
81
- const privateKey = ed25519.utils.randomPrivateKey()
82
- const publicKey = ed25519.getPublicKey(privateKey)
83
-
84
- const sessionId = bytesToHex(publicKey)
85
- const privateKeyHex = bytesToHex(privateKey)
86
-
87
- // Registrar sessão no servidor
88
- const response = await fetch(`${this.config.apiBaseUrl}/api/auth/session/init`, {
89
- method: 'POST',
90
- headers: {
91
- 'Content-Type': 'application/json'
92
- },
93
- body: JSON.stringify({
94
- publicKey: sessionId
95
- })
96
- })
97
-
98
- if (!response.ok) {
99
- throw new Error(`Erro ao inicializar sessão: ${response.statusText}`)
100
- }
101
-
102
- const result = await response.json()
103
- if (!result.success) {
104
- throw new Error(result.error || 'Erro desconhecido ao inicializar sessão')
105
- }
106
-
107
- // Criar objeto de sessão
108
- const session: SessionInfo = {
109
- sessionId,
110
- publicKey: sessionId,
111
- privateKey: privateKeyHex,
112
- isAdmin: result.user?.isAdmin || false,
113
- permissions: result.user?.permissions || ['read'],
114
- createdAt: new Date(),
115
- lastUsed: new Date()
116
- }
78
+ createNewKeys(): KeyPair {
79
+ // Gerar par de chaves Ed25519
80
+ const privateKey = ed25519.utils.randomPrivateKey()
81
+ const publicKey = ed25519.getPublicKey(privateKey)
82
+
83
+ const keys: KeyPair = {
84
+ publicKey: bytesToHex(publicKey),
85
+ privateKey: bytesToHex(privateKey),
86
+ createdAt: new Date()
87
+ }
117
88
 
118
- this.session = session
119
- this.saveSession(session)
89
+ this.keys = keys
90
+ this.saveKeys(keys)
120
91
 
121
- return session
122
- } catch (error) {
123
- console.error('Erro ao criar nova sessão:', error)
124
- throw error
125
- }
92
+ return keys
126
93
  }
127
94
 
128
95
  /**
129
- * Fazer requisição autenticada
96
+ * Fazer requisição autenticada com assinatura
130
97
  */
131
98
  async fetch(url: string, options: SignedRequestOptions = {}): Promise<Response> {
132
99
  const { skipAuth = false, ...fetchOptions } = options
@@ -135,33 +102,30 @@ export class CryptoAuthClient {
135
102
  return fetch(url, fetchOptions)
136
103
  }
137
104
 
138
- if (!this.session) {
139
- await this.initialize()
105
+ if (!this.keys) {
106
+ this.initialize()
140
107
  }
141
108
 
142
- if (!this.session) {
143
- throw new Error('Sessão não inicializada')
109
+ if (!this.keys) {
110
+ throw new Error('Chaves não inicializadas')
144
111
  }
145
112
 
146
- // Preparar headers de autenticação
113
+ // Preparar dados de autenticação
147
114
  const timestamp = Date.now()
148
115
  const nonce = this.generateNonce()
149
116
  const message = this.buildMessage(fetchOptions.method || 'GET', url, fetchOptions.body)
150
117
  const signature = this.signMessage(message, timestamp, nonce)
151
118
 
119
+ // Adicionar headers de autenticação
152
120
  const headers = {
153
121
  'Content-Type': 'application/json',
154
122
  ...fetchOptions.headers,
155
- 'x-session-id': this.session.sessionId,
123
+ 'x-public-key': this.keys.publicKey,
156
124
  'x-timestamp': timestamp.toString(),
157
125
  'x-nonce': nonce,
158
126
  'x-signature': signature
159
127
  }
160
128
 
161
- // Atualizar último uso
162
- this.session.lastUsed = new Date()
163
- this.saveSession(this.session)
164
-
165
129
  return fetch(url, {
166
130
  ...fetchOptions,
167
131
  headers
@@ -169,74 +133,102 @@ export class CryptoAuthClient {
169
133
  }
170
134
 
171
135
  /**
172
- * Obter informações da sessão atual
136
+ * Obter chaves atuais
173
137
  */
174
- getSession(): SessionInfo | null {
175
- return this.session
138
+ getKeys(): KeyPair | null {
139
+ return this.keys
176
140
  }
177
141
 
178
142
  /**
179
- * Verificar se está autenticado
143
+ * Verificar se tem chaves
180
144
  */
181
- isAuthenticated(): boolean {
182
- return this.session !== null && this.isSessionValid(this.session)
145
+ isInitialized(): boolean {
146
+ return this.keys !== null
183
147
  }
184
148
 
185
149
  /**
186
- * Verificar se é admin
150
+ * Limpar chaves (logout)
187
151
  */
188
- isAdmin(): boolean {
189
- return this.session?.isAdmin || false
152
+ clearKeys(): void {
153
+ this.keys = null
154
+ if (this.storage instanceof Map) {
155
+ this.storage.delete(this.STORAGE_KEY)
156
+ } else {
157
+ this.storage.removeItem(this.STORAGE_KEY)
158
+ }
190
159
  }
191
160
 
192
161
  /**
193
- * Obter permissões
162
+ * Importar chave privada existente
163
+ * @param privateKeyHex - Chave privada em formato hexadecimal (64 caracteres)
164
+ * @returns KeyPair com as chaves importadas
165
+ * @throws Error se a chave privada for inválida
194
166
  */
195
- getPermissions(): string[] {
196
- return this.session?.permissions || []
167
+ importPrivateKey(privateKeyHex: string): KeyPair {
168
+ // Validar formato
169
+ if (!/^[a-fA-F0-9]{64}$/.test(privateKeyHex)) {
170
+ throw new Error('Chave privada inválida. Deve ter 64 caracteres hexadecimais.')
171
+ }
172
+
173
+ try {
174
+ // Converter hex para bytes
175
+ const privateKeyBytes = hexToBytes(privateKeyHex)
176
+
177
+ // Derivar chave pública da privada
178
+ const publicKeyBytes = ed25519.getPublicKey(privateKeyBytes)
179
+
180
+ const keys: KeyPair = {
181
+ publicKey: bytesToHex(publicKeyBytes),
182
+ privateKey: privateKeyHex.toLowerCase(),
183
+ createdAt: new Date()
184
+ }
185
+
186
+ this.keys = keys
187
+ this.saveKeys(keys)
188
+
189
+ return keys
190
+ } catch (error) {
191
+ throw new Error('Erro ao importar chave privada: ' + (error as Error).message)
192
+ }
197
193
  }
198
194
 
199
195
  /**
200
- * Fazer logout
196
+ * Exportar chave privada (para backup)
197
+ * @returns Chave privada em formato hexadecimal
198
+ * @throws Error se não houver chaves inicializadas
201
199
  */
202
- async logout(): Promise<void> {
203
- if (this.session) {
204
- try {
205
- await this.fetch(`${this.config.apiBaseUrl}/api/auth/session/logout`, {
206
- method: 'POST'
207
- })
208
- } catch (error) {
209
- console.warn('Erro ao fazer logout no servidor:', error)
210
- }
211
-
212
- this.session = null
213
- this.clearSession()
200
+ exportPrivateKey(): string {
201
+ if (!this.keys) {
202
+ throw new Error('Nenhuma chave inicializada para exportar')
214
203
  }
204
+
205
+ return this.keys.privateKey
215
206
  }
216
207
 
217
208
  /**
218
209
  * Assinar mensagem
219
210
  */
220
211
  private signMessage(message: string, timestamp: number, nonce: string): string {
221
- if (!this.session) {
222
- throw new Error('Sessão não inicializada')
212
+ if (!this.keys) {
213
+ throw new Error('Chaves não inicializadas')
223
214
  }
224
215
 
225
- const fullMessage = `${this.session.sessionId}:${timestamp}:${nonce}:${message}`
216
+ // Construir mensagem completa: publicKey:timestamp:nonce:message
217
+ const fullMessage = `${this.keys.publicKey}:${timestamp}:${nonce}:${message}`
226
218
  const messageHash = sha256(new TextEncoder().encode(fullMessage))
227
- const privateKeyBytes = hexToBytes(this.session.privateKey)
219
+
220
+ const privateKeyBytes = hexToBytes(this.keys.privateKey)
228
221
  const signature = ed25519.sign(messageHash, privateKeyBytes)
229
-
222
+
230
223
  return bytesToHex(signature)
231
224
  }
232
225
 
233
226
  /**
234
227
  * Construir mensagem para assinatura
235
228
  */
236
- private buildMessage(method: string, url: string, body?: any): string {
237
- const urlObj = new URL(url, window.location.origin)
238
- let message = `${method}:${urlObj.pathname}`
239
-
229
+ private buildMessage(method: string, url: string, body?: BodyInit | null): string {
230
+ let message = `${method}:${url}`
231
+
240
232
  if (body) {
241
233
  if (typeof body === 'string') {
242
234
  message += `:${body}`
@@ -252,74 +244,59 @@ export class CryptoAuthClient {
252
244
  * Gerar nonce aleatório
253
245
  */
254
246
  private generateNonce(): string {
255
- const array = new Uint8Array(16)
256
- crypto.getRandomValues(array)
257
- return bytesToHex(array)
258
- }
259
-
260
- /**
261
- * Verificar se sessão é válida
262
- */
263
- private isSessionValid(session: SessionInfo): boolean {
264
- const now = Date.now()
265
- const sessionAge = now - session.lastUsed.getTime()
266
- return sessionAge < (this.config.sessionTimeout || 1800000)
267
- }
268
-
269
- /**
270
- * Salvar sessão no storage
271
- */
272
- private saveSession(session: SessionInfo): void {
273
- const sessionData = JSON.stringify({
274
- ...session,
275
- createdAt: session.createdAt.toISOString(),
276
- lastUsed: session.lastUsed.toISOString()
277
- })
278
-
279
- if (this.storage instanceof Map) {
280
- this.storage.set('crypto-auth-session', sessionData)
281
- } else {
282
- this.storage.setItem('crypto-auth-session', sessionData)
283
- }
247
+ const bytes = new Uint8Array(16)
248
+ crypto.getRandomValues(bytes)
249
+ return bytesToHex(bytes)
284
250
  }
285
251
 
286
252
  /**
287
- * Carregar sessão do storage
253
+ * Carregar chaves do storage
288
254
  */
289
- private loadSession(): SessionInfo | null {
255
+ private loadKeys(): KeyPair | null {
290
256
  try {
291
- let sessionData: string | null
257
+ let data: string | null
292
258
 
293
259
  if (this.storage instanceof Map) {
294
- sessionData = this.storage.get('crypto-auth-session') || null
260
+ data = this.storage.get(this.STORAGE_KEY) || null
295
261
  } else {
296
- sessionData = this.storage.getItem('crypto-auth-session')
262
+ data = this.storage.getItem(this.STORAGE_KEY)
297
263
  }
298
264
 
299
- if (!sessionData) {
265
+ if (!data) {
300
266
  return null
301
267
  }
302
268
 
303
- const parsed = JSON.parse(sessionData)
269
+ const parsed = JSON.parse(data)
270
+
304
271
  return {
305
- ...parsed,
306
- createdAt: new Date(parsed.createdAt),
307
- lastUsed: new Date(parsed.lastUsed)
272
+ publicKey: parsed.publicKey,
273
+ privateKey: parsed.privateKey,
274
+ createdAt: new Date(parsed.createdAt)
308
275
  }
309
276
  } catch (error) {
310
- console.warn('Erro ao carregar sessão:', error)
277
+ console.error('Erro ao carregar chaves:', error)
311
278
  return null
312
279
  }
313
280
  }
314
281
 
315
282
  /**
316
- * Limpar sessão do storage
283
+ * Salvar chaves no storage
317
284
  */
318
- private clearSession(): void {
319
- if (this.storage instanceof Map) {
320
- this.storage.delete('crypto-auth-session')
321
- } else {
322
- this.storage.removeItem('crypto-auth-session')
285
+ private saveKeys(keys: KeyPair): void {
286
+ try {
287
+ const data = JSON.stringify({
288
+ publicKey: keys.publicKey,
289
+ privateKey: keys.privateKey,
290
+ createdAt: keys.createdAt.toISOString()
291
+ })
292
+
293
+ if (this.storage instanceof Map) {
294
+ this.storage.set(this.STORAGE_KEY, data)
295
+ } else {
296
+ this.storage.setItem(this.STORAGE_KEY, data)
297
+ }
298
+ } catch (error) {
299
+ console.error('Erro ao salvar chaves:', error)
323
300
  }
324
301
  }
325
- }
302
+ }
@@ -1,22 +1,19 @@
1
1
  /**
2
2
  * Provedor de Contexto de Autenticação
3
- * Context Provider React para gerenciar estado de autenticação
3
+ * Context Provider React para gerenciar chaves criptográficas
4
4
  */
5
5
 
6
- import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react'
7
- import { CryptoAuthClient, SessionInfo, AuthConfig } from '../CryptoAuthClient'
6
+ import React, { createContext, useContext, useEffect, useState, type ReactNode } from 'react'
7
+ import { CryptoAuthClient, type KeyPair, type AuthConfig } from '../CryptoAuthClient'
8
8
 
9
9
  export interface AuthContextValue {
10
10
  client: CryptoAuthClient
11
- session: SessionInfo | null
12
- isAuthenticated: boolean
13
- isAdmin: boolean
14
- permissions: string[]
11
+ keys: KeyPair | null
12
+ hasKeys: boolean
15
13
  isLoading: boolean
16
14
  error: string | null
17
- login: () => Promise<void>
18
- logout: () => Promise<void>
19
- refresh: () => Promise<void>
15
+ createKeys: () => void
16
+ clearKeys: () => void
20
17
  }
21
18
 
22
19
  const AuthContext = createContext<AuthContextValue | null>(null)
@@ -24,71 +21,59 @@ const AuthContext = createContext<AuthContextValue | null>(null)
24
21
  export interface AuthProviderProps {
25
22
  children: ReactNode
26
23
  config?: AuthConfig
27
- onAuthChange?: (isAuthenticated: boolean, session: SessionInfo | null) => void
24
+ onKeysChange?: (hasKeys: boolean, keys: KeyPair | null) => void
28
25
  onError?: (error: string) => void
29
26
  }
30
27
 
31
28
  export const AuthProvider: React.FC<AuthProviderProps> = ({
32
29
  children,
33
30
  config = {},
34
- onAuthChange,
31
+ onKeysChange,
35
32
  onError
36
33
  }) => {
37
34
  const [client] = useState(() => new CryptoAuthClient({ ...config, autoInit: false }))
38
- const [session, setSession] = useState<SessionInfo | null>(null)
35
+ const [keys, setKeys] = useState<KeyPair | null>(null)
39
36
  const [isLoading, setIsLoading] = useState(true)
40
37
  const [error, setError] = useState<string | null>(null)
41
38
 
42
- const isAuthenticated = session !== null && client.isAuthenticated()
43
- const isAdmin = session?.isAdmin || false
44
- const permissions = session?.permissions || []
39
+ const hasKeys = keys !== null
45
40
 
46
41
  useEffect(() => {
47
- initializeAuth()
42
+ initializeKeys()
48
43
  }, [])
49
44
 
50
45
  useEffect(() => {
51
- onAuthChange?.(isAuthenticated, session)
52
- }, [isAuthenticated, session, onAuthChange])
46
+ onKeysChange?.(hasKeys, keys)
47
+ }, [hasKeys, keys, onKeysChange])
53
48
 
54
- const initializeAuth = async () => {
49
+ const initializeKeys = () => {
55
50
  setIsLoading(true)
56
51
  setError(null)
57
-
52
+
58
53
  try {
59
- const currentSession = client.getSession()
60
- if (currentSession && client.isAuthenticated()) {
61
- setSession(currentSession)
62
- } else {
63
- // Tentar inicializar automaticamente se não houver sessão
64
- try {
65
- const newSession = await client.initialize()
66
- setSession(newSession)
67
- } catch (initError) {
68
- // Falha na inicialização automática é normal se não houver sessão salva
69
- console.debug('Inicialização automática falhou:', initError)
70
- setSession(null)
71
- }
54
+ const existingKeys = client.getKeys()
55
+ if (existingKeys) {
56
+ setKeys(existingKeys)
72
57
  }
73
58
  } catch (err) {
74
59
  const errorMessage = err instanceof Error ? err.message : 'Erro desconhecido'
75
60
  setError(errorMessage)
76
61
  onError?.(errorMessage)
77
- console.error('Erro ao inicializar autenticação:', err)
62
+ console.error('Erro ao inicializar chaves:', err)
78
63
  } finally {
79
64
  setIsLoading(false)
80
65
  }
81
66
  }
82
67
 
83
- const login = async () => {
68
+ const createKeys = () => {
84
69
  setIsLoading(true)
85
70
  setError(null)
86
-
71
+
87
72
  try {
88
- const newSession = await client.createNewSession()
89
- setSession(newSession)
73
+ const newKeys = client.createNewKeys()
74
+ setKeys(newKeys)
90
75
  } catch (err) {
91
- const errorMessage = err instanceof Error ? err.message : 'Erro ao fazer login'
76
+ const errorMessage = err instanceof Error ? err.message : 'Erro ao criar chaves'
92
77
  setError(errorMessage)
93
78
  onError?.(errorMessage)
94
79
  throw err
@@ -97,60 +82,19 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({
97
82
  }
98
83
  }
99
84
 
100
- const logout = async () => {
85
+ const clearKeys = () => {
101
86
  setIsLoading(true)
102
87
  setError(null)
103
-
104
- try {
105
- await client.logout()
106
- setSession(null)
107
- } catch (err) {
108
- const errorMessage = err instanceof Error ? err.message : 'Erro ao fazer logout'
109
- setError(errorMessage)
110
- onError?.(errorMessage)
111
- // Mesmo com erro, limpar a sessão local
112
- setSession(null)
113
- } finally {
114
- setIsLoading(false)
115
- }
116
- }
117
88
 
118
- const refresh = async () => {
119
- setIsLoading(true)
120
- setError(null)
121
-
122
89
  try {
123
- // Verificar se a sessão atual ainda é válida
124
- const currentSession = client.getSession()
125
- if (currentSession && client.isAuthenticated()) {
126
- // Tentar fazer uma requisição de teste para validar no servidor
127
- const response = await client.fetch('/api/auth/session/info')
128
- if (response.ok) {
129
- const result = await response.json()
130
- if (result.success && result.session) {
131
- // Atualizar informações da sessão
132
- const updatedSession = {
133
- ...currentSession,
134
- ...result.session,
135
- lastUsed: new Date()
136
- }
137
- setSession(updatedSession)
138
- } else {
139
- // Sessão inválida no servidor
140
- setSession(null)
141
- }
142
- } else {
143
- // Erro na requisição, sessão pode estar inválida
144
- setSession(null)
145
- }
146
- } else {
147
- setSession(null)
148
- }
90
+ client.clearKeys()
91
+ setKeys(null)
149
92
  } catch (err) {
150
- const errorMessage = err instanceof Error ? err.message : 'Erro ao atualizar sessão'
93
+ const errorMessage = err instanceof Error ? err.message : 'Erro ao limpar chaves'
151
94
  setError(errorMessage)
152
95
  onError?.(errorMessage)
153
- setSession(null)
96
+ // Mesmo com erro, limpar as chaves locais
97
+ setKeys(null)
154
98
  } finally {
155
99
  setIsLoading(false)
156
100
  }
@@ -158,15 +102,12 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({
158
102
 
159
103
  const contextValue: AuthContextValue = {
160
104
  client,
161
- session,
162
- isAuthenticated,
163
- isAdmin,
164
- permissions,
105
+ keys,
106
+ hasKeys,
165
107
  isLoading,
166
108
  error,
167
- login,
168
- logout,
169
- refresh
109
+ createKeys,
110
+ clearKeys
170
111
  }
171
112
 
172
113
  return (