create-fluxstack 1.4.1 → 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 (51) 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/plugin.ts +324 -34
  16. package/core/cli/generators/template-engine.ts +5 -0
  17. package/core/cli/plugin-discovery.ts +33 -12
  18. package/core/framework/server.ts +10 -0
  19. package/core/plugins/dependency-manager.ts +89 -22
  20. package/core/plugins/index.ts +4 -0
  21. package/core/plugins/manager.ts +3 -2
  22. package/core/plugins/module-resolver.ts +216 -0
  23. package/core/plugins/registry.ts +28 -1
  24. package/core/utils/logger/index.ts +4 -0
  25. package/fluxstack.config.ts +253 -114
  26. package/package.json +117 -117
  27. package/plugins/crypto-auth/README.md +722 -172
  28. package/plugins/crypto-auth/ai-context.md +1282 -0
  29. package/plugins/crypto-auth/cli/make-protected-route.command.ts +383 -0
  30. package/plugins/crypto-auth/client/CryptoAuthClient.ts +136 -159
  31. package/plugins/crypto-auth/client/components/AuthProvider.tsx +35 -94
  32. package/plugins/crypto-auth/client/components/LoginButton.tsx +36 -53
  33. package/plugins/crypto-auth/client/components/ProtectedRoute.tsx +17 -37
  34. package/plugins/crypto-auth/client/components/index.ts +1 -4
  35. package/plugins/crypto-auth/client/index.ts +1 -1
  36. package/plugins/crypto-auth/config/index.ts +34 -0
  37. package/plugins/crypto-auth/index.ts +84 -152
  38. package/plugins/crypto-auth/package.json +65 -64
  39. package/plugins/crypto-auth/server/AuthMiddleware.ts +19 -75
  40. package/plugins/crypto-auth/server/CryptoAuthService.ts +60 -167
  41. package/plugins/crypto-auth/server/index.ts +15 -2
  42. package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +65 -0
  43. package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +26 -0
  44. package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +76 -0
  45. package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +45 -0
  46. package/plugins/crypto-auth/server/middlewares/helpers.ts +140 -0
  47. package/plugins/crypto-auth/server/middlewares/index.ts +22 -0
  48. package/plugins/crypto-auth/server/middlewares.ts +19 -0
  49. package/test-crypto-auth.ts +101 -0
  50. package/plugins/crypto-auth/client/components/SessionInfo.tsx +0 -242
  51. package/plugins/crypto-auth/plugin.json +0 -29
@@ -1,65 +1,66 @@
1
- {
2
- "name": "@fluxstack/crypto-auth-plugin",
3
- "version": "1.0.0",
4
- "description": "Plugin de autenticação criptográfica Ed25519 para FluxStack",
5
- "main": "index.ts",
6
- "types": "index.ts",
7
- "exports": {
8
- ".": {
9
- "import": "./index.ts",
10
- "types": "./index.ts"
11
- },
12
- "./client": {
13
- "import": "./client/index.ts",
14
- "types": "./client/index.ts"
15
- },
16
- "./server": {
17
- "import": "./server/index.ts",
18
- "types": "./server/index.ts"
19
- }
20
- },
21
- "keywords": [
22
- "fluxstack",
23
- "plugin",
24
- "authentication",
25
- "ed25519",
26
- "cryptography",
27
- "security",
28
- "react",
29
- "typescript"
30
- ],
31
- "author": "FluxStack Team",
32
- "license": "MIT",
33
- "peerDependencies": {
34
- "react": ">=16.8.0"
35
- },
36
- "peerDependenciesMeta": {
37
- "react": {
38
- "optional": true
39
- }
40
- },
41
- "dependencies": {
42
- "@noble/curves": "^1.2.0",
43
- "@noble/hashes": "^1.3.2"
44
- },
45
- "devDependencies": {
46
- "@types/react": "^18.0.0",
47
- "typescript": "^5.0.0"
48
- },
49
- "fluxstack": {
50
- "version": "^1.0.0",
51
- "hooks": [
52
- "setup",
53
- "onServerStart",
54
- "onRequest",
55
- "onResponse"
56
- ],
57
- "category": "auth",
58
- "tags": [
59
- "authentication",
60
- "ed25519",
61
- "cryptography",
62
- "security"
63
- ]
64
- }
1
+ {
2
+ "name": "@fluxstack/crypto-auth-plugin",
3
+ "version": "1.0.0",
4
+ "description": "Plugin de autenticação criptográfica Ed25519 para FluxStack",
5
+ "main": "index.ts",
6
+ "types": "index.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./index.ts",
10
+ "types": "./index.ts"
11
+ },
12
+ "./client": {
13
+ "import": "./client/index.ts",
14
+ "types": "./client/index.ts"
15
+ },
16
+ "./server": {
17
+ "import": "./server/index.ts",
18
+ "types": "./server/index.ts"
19
+ }
20
+ },
21
+ "keywords": [
22
+ "fluxstack",
23
+ "plugin",
24
+ "authentication",
25
+ "ed25519",
26
+ "cryptography",
27
+ "security",
28
+ "react",
29
+ "typescript"
30
+ ],
31
+ "author": "FluxStack Team",
32
+ "license": "MIT",
33
+ "peerDependencies": {
34
+ "react": ">=16.8.0"
35
+ },
36
+ "peerDependenciesMeta": {
37
+ "react": {
38
+ "optional": true
39
+ }
40
+ },
41
+ "dependencies": {
42
+ "@noble/curves": "1.2.0",
43
+ "@noble/hashes": "1.3.2"
44
+ },
45
+ "devDependencies": {
46
+ "@types/react": "^18.0.0",
47
+ "typescript": "^5.0.0"
48
+ },
49
+ "fluxstack": {
50
+ "plugin": true,
51
+ "version": "^1.0.0",
52
+ "hooks": [
53
+ "setup",
54
+ "onServerStart",
55
+ "onRequest",
56
+ "onResponse"
57
+ ],
58
+ "category": "auth",
59
+ "tags": [
60
+ "authentication",
61
+ "ed25519",
62
+ "cryptography",
63
+ "security"
64
+ ]
65
+ }
65
66
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Middleware de Autenticação
3
- * Intercepta requisições e valida autenticação
2
+ * Middleware de Autenticação Simplificado
3
+ * Apenas valida autenticação - routing é feito pelos middlewares Elysia
4
4
  */
5
5
 
6
6
  import type { RequestContext } from '../../../core/plugins/types'
@@ -14,57 +14,35 @@ export interface Logger {
14
14
  }
15
15
 
16
16
  export interface AuthMiddlewareConfig {
17
- protectedRoutes: string[]
18
- publicRoutes: string[]
19
17
  logger?: Logger
20
18
  }
21
19
 
22
20
  export interface AuthMiddlewareResult {
23
21
  success: boolean
24
- required: boolean
25
22
  error?: string
26
23
  user?: {
27
- sessionId: string
24
+ publicKey: string
28
25
  isAdmin: boolean
29
- isSuperAdmin: boolean
30
26
  permissions: string[]
31
27
  }
32
28
  }
33
29
 
34
30
  export class AuthMiddleware {
35
31
  private authService: CryptoAuthService
36
- private config: AuthMiddlewareConfig
37
32
  private logger?: Logger
38
33
 
39
- constructor(authService: CryptoAuthService, config: AuthMiddlewareConfig) {
34
+ constructor(authService: CryptoAuthService, config: AuthMiddlewareConfig = {}) {
40
35
  this.authService = authService
41
- this.config = config
42
36
  this.logger = config.logger
43
37
  }
44
38
 
45
39
  /**
46
- * Autenticar requisição
40
+ * Autenticar requisição (sem path matching - é responsabilidade dos middlewares Elysia)
47
41
  */
48
42
  async authenticate(context: RequestContext): Promise<AuthMiddlewareResult> {
49
43
  const path = context.path
50
44
  const method = context.method
51
45
 
52
- // Verificar se a rota é pública
53
- if (this.isPublicRoute(path)) {
54
- return {
55
- success: true,
56
- required: false
57
- }
58
- }
59
-
60
- // Verificar se a rota requer autenticação
61
- if (!this.isProtectedRoute(path)) {
62
- return {
63
- success: true,
64
- required: false
65
- }
66
- }
67
-
68
46
  // Extrair headers de autenticação
69
47
  const authHeaders = this.extractAuthHeaders(context.headers)
70
48
  if (!authHeaders) {
@@ -76,15 +54,14 @@ export class AuthMiddleware {
76
54
 
77
55
  return {
78
56
  success: false,
79
- required: true,
80
57
  error: "Headers de autenticação obrigatórios"
81
58
  }
82
59
  }
83
60
 
84
- // Validar sessão
61
+ // Validar assinatura da requisição
85
62
  try {
86
- const validationResult = await this.authService.validateSession({
87
- sessionId: authHeaders.sessionId,
63
+ const validationResult = await this.authService.validateRequest({
64
+ publicKey: authHeaders.publicKey,
88
65
  timestamp: authHeaders.timestamp,
89
66
  nonce: authHeaders.nonce,
90
67
  signature: authHeaders.signature,
@@ -92,16 +69,15 @@ export class AuthMiddleware {
92
69
  })
93
70
 
94
71
  if (!validationResult.success) {
95
- this.logger?.warn("Falha na validação da sessão", {
72
+ this.logger?.warn("Falha na validação da assinatura", {
96
73
  path,
97
74
  method,
98
- sessionId: authHeaders.sessionId.substring(0, 8) + "...",
75
+ publicKey: authHeaders.publicKey.substring(0, 8) + "...",
99
76
  error: validationResult.error
100
77
  })
101
78
 
102
79
  return {
103
80
  success: false,
104
- required: true,
105
81
  error: validationResult.error
106
82
  }
107
83
  }
@@ -109,13 +85,12 @@ export class AuthMiddleware {
109
85
  this.logger?.debug("Requisição autenticada com sucesso", {
110
86
  path,
111
87
  method,
112
- sessionId: authHeaders.sessionId.substring(0, 8) + "...",
88
+ publicKey: authHeaders.publicKey.substring(0, 8) + "...",
113
89
  isAdmin: validationResult.user?.isAdmin
114
90
  })
115
91
 
116
92
  return {
117
93
  success: true,
118
- required: true,
119
94
  user: validationResult.user
120
95
  }
121
96
  } catch (error) {
@@ -127,53 +102,26 @@ export class AuthMiddleware {
127
102
 
128
103
  return {
129
104
  success: false,
130
- required: true,
131
105
  error: "Erro interno de autenticação"
132
106
  }
133
107
  }
134
108
  }
135
109
 
136
- /**
137
- * Verificar se a rota é pública
138
- */
139
- private isPublicRoute(path: string): boolean {
140
- return this.config.publicRoutes.some(route => {
141
- if (route.endsWith('/*')) {
142
- const prefix = route.slice(0, -2)
143
- return path.startsWith(prefix)
144
- }
145
- return path === route
146
- })
147
- }
148
-
149
- /**
150
- * Verificar se a rota é protegida
151
- */
152
- private isProtectedRoute(path: string): boolean {
153
- return this.config.protectedRoutes.some(route => {
154
- if (route.endsWith('/*')) {
155
- const prefix = route.slice(0, -2)
156
- return path.startsWith(prefix)
157
- }
158
- return path === route
159
- })
160
- }
161
-
162
110
  /**
163
111
  * Extrair headers de autenticação
164
112
  */
165
113
  private extractAuthHeaders(headers: Record<string, string>): {
166
- sessionId: string
114
+ publicKey: string
167
115
  timestamp: number
168
116
  nonce: string
169
117
  signature: string
170
118
  } | null {
171
- const sessionId = headers['x-session-id']
119
+ const publicKey = headers['x-public-key']
172
120
  const timestampStr = headers['x-timestamp']
173
121
  const nonce = headers['x-nonce']
174
122
  const signature = headers['x-signature']
175
123
 
176
- if (!sessionId || !timestampStr || !nonce || !signature) {
124
+ if (!publicKey || !timestampStr || !nonce || !signature) {
177
125
  return null
178
126
  }
179
127
 
@@ -183,7 +131,7 @@ export class AuthMiddleware {
183
131
  }
184
132
 
185
133
  return {
186
- sessionId,
134
+ publicKey,
187
135
  timestamp,
188
136
  nonce,
189
137
  signature
@@ -207,7 +155,7 @@ export class AuthMiddleware {
207
155
  }
208
156
 
209
157
  /**
210
- * Verificar se usuário tem permissão para acessar rota
158
+ * Verificar se usuário tem permissão
211
159
  */
212
160
  hasPermission(user: any, requiredPermission: string): boolean {
213
161
  if (!user || !user.permissions) {
@@ -225,13 +173,9 @@ export class AuthMiddleware {
225
173
  }
226
174
 
227
175
  /**
228
- * Obter estatísticas do middleware
176
+ * Obter estatísticas do serviço de autenticação
229
177
  */
230
178
  getStats() {
231
- return {
232
- protectedRoutes: this.config.protectedRoutes.length,
233
- publicRoutes: this.config.publicRoutes.length,
234
- authService: this.authService.getStats()
235
- }
179
+ return this.authService.getStats()
236
180
  }
237
- }
181
+ }
@@ -1,11 +1,13 @@
1
1
  /**
2
2
  * Serviço de Autenticação Criptográfica
3
- * Implementa autenticação baseada em Ed25519
3
+ * Implementa autenticação baseada em Ed25519 - SEM SESSÕES
4
+ * Cada requisição é validada pela assinatura da chave pública
4
5
  */
5
6
 
6
7
  import { ed25519 } from '@noble/curves/ed25519'
7
8
  import { sha256 } from '@noble/hashes/sha256'
8
- import { bytesToHex, hexToBytes } from '@noble/hashes/utils'
9
+ import { hexToBytes } from '@noble/hashes/utils'
10
+
9
11
  export interface Logger {
10
12
  debug(message: string, meta?: any): void
11
13
  info(message: string, meta?: any): void
@@ -13,221 +15,128 @@ export interface Logger {
13
15
  error(message: string, meta?: any): void
14
16
  }
15
17
 
16
- export interface SessionData {
17
- sessionId: string
18
- publicKey: string
19
- createdAt: Date
20
- lastUsed: Date
21
- isAdmin: boolean
22
- permissions: string[]
23
- }
24
-
25
18
  export interface AuthResult {
26
19
  success: boolean
27
- sessionId?: string
28
20
  error?: string
29
21
  user?: {
30
- sessionId: string
22
+ publicKey: string
31
23
  isAdmin: boolean
32
- isSuperAdmin: boolean
33
24
  permissions: string[]
34
25
  }
35
26
  }
36
27
 
37
28
  export interface CryptoAuthConfig {
38
- sessionTimeout: number
39
29
  maxTimeDrift: number
40
30
  adminKeys: string[]
41
31
  logger?: Logger
42
32
  }
43
33
 
44
34
  export class CryptoAuthService {
45
- private sessions: Map<string, SessionData> = new Map()
46
35
  private config: CryptoAuthConfig
47
36
  private logger?: Logger
37
+ private usedNonces: Map<string, number> = new Map() // Para prevenir replay attacks
48
38
 
49
39
  constructor(config: CryptoAuthConfig) {
50
40
  this.config = config
51
41
  this.logger = config.logger
52
42
 
53
- // Limpar sessões expiradas a cada 5 minutos
43
+ // Limpar nonces antigos a cada 5 minutos
54
44
  setInterval(() => {
55
- this.cleanupExpiredSessions()
45
+ this.cleanupOldNonces()
56
46
  }, 5 * 60 * 1000)
57
47
  }
58
48
 
59
49
  /**
60
- * Inicializar uma nova sessão
50
+ * Validar assinatura de requisição
51
+ * PRINCIPAL: Valida se assinatura é válida para a chave pública fornecida
61
52
  */
62
- async initializeSession(data: { publicKey?: string }): Promise<AuthResult> {
63
- try {
64
- let publicKey: string
65
-
66
- if (data.publicKey) {
67
- // Validar chave pública fornecida
68
- if (!this.isValidPublicKey(data.publicKey)) {
69
- return {
70
- success: false,
71
- error: "Chave pública inválida"
72
- }
73
- }
74
- publicKey = data.publicKey
75
- } else {
76
- // Gerar novo par de chaves
77
- const privateKey = ed25519.utils.randomPrivateKey()
78
- publicKey = bytesToHex(ed25519.getPublicKey(privateKey))
79
- }
80
-
81
- const sessionId = publicKey
82
- const isAdmin = this.config.adminKeys.includes(publicKey)
83
-
84
- const sessionData: SessionData = {
85
- sessionId,
86
- publicKey,
87
- createdAt: new Date(),
88
- lastUsed: new Date(),
89
- isAdmin,
90
- permissions: isAdmin ? ['admin', 'read', 'write'] : ['read']
91
- }
92
-
93
- this.sessions.set(sessionId, sessionData)
94
-
95
- this.logger?.info("Nova sessão inicializada", {
96
- sessionId: sessionId.substring(0, 8) + "...",
97
- isAdmin,
98
- permissions: sessionData.permissions
99
- })
100
-
101
- return {
102
- success: true,
103
- sessionId,
104
- user: {
105
- sessionId,
106
- isAdmin,
107
- isSuperAdmin: isAdmin,
108
- permissions: sessionData.permissions
109
- }
110
- }
111
- } catch (error) {
112
- this.logger?.error("Erro ao inicializar sessão", { error })
113
- return {
114
- success: false,
115
- error: "Erro interno ao inicializar sessão"
116
- }
117
- }
118
- }
119
-
120
- /**
121
- * Validar uma sessão com assinatura
122
- */
123
- async validateSession(data: {
124
- sessionId: string
53
+ async validateRequest(data: {
54
+ publicKey: string
125
55
  timestamp: number
126
56
  nonce: string
127
57
  signature: string
128
58
  message?: string
129
59
  }): Promise<AuthResult> {
130
60
  try {
131
- const { sessionId, timestamp, nonce, signature, message = "" } = data
61
+ const { publicKey, timestamp, nonce, signature, message = "" } = data
132
62
 
133
- // Verificar se a sessão existe
134
- const session = this.sessions.get(sessionId)
135
- if (!session) {
63
+ // Validar chave pública
64
+ if (!this.isValidPublicKey(publicKey)) {
136
65
  return {
137
66
  success: false,
138
- error: "Sessão não encontrada"
67
+ error: "Chave pública inválida"
139
68
  }
140
69
  }
141
70
 
142
- // Verificar se a sessão não expirou
71
+ // Verificar drift de tempo (previne replay de requisições antigas)
143
72
  const now = Date.now()
144
- const sessionAge = now - session.lastUsed.getTime()
145
- if (sessionAge > this.config.sessionTimeout) {
146
- this.sessions.delete(sessionId)
73
+ const timeDrift = Math.abs(now - timestamp)
74
+ if (timeDrift > this.config.maxTimeDrift) {
147
75
  return {
148
76
  success: false,
149
- error: "Sessão expirada"
77
+ error: "Timestamp inválido ou expirado"
150
78
  }
151
79
  }
152
80
 
153
- // Verificar drift de tempo
154
- const timeDrift = Math.abs(now - timestamp)
155
- if (timeDrift > this.config.maxTimeDrift) {
81
+ // Verificar nonce (previne replay attacks)
82
+ const nonceKey = `${publicKey}:${nonce}`
83
+ if (this.usedNonces.has(nonceKey)) {
156
84
  return {
157
85
  success: false,
158
- error: "Timestamp inválido"
86
+ error: "Nonce já utilizado (possível replay attack)"
159
87
  }
160
88
  }
161
89
 
162
90
  // Construir mensagem para verificação
163
- const messageToVerify = `${sessionId}:${timestamp}:${nonce}:${message}`
91
+ const messageToVerify = `${publicKey}:${timestamp}:${nonce}:${message}`
164
92
  const messageHash = sha256(new TextEncoder().encode(messageToVerify))
165
93
 
166
- // Verificar assinatura
167
- const publicKeyBytes = hexToBytes(session.publicKey)
94
+ // Verificar assinatura usando chave pública
95
+ const publicKeyBytes = hexToBytes(publicKey)
168
96
  const signatureBytes = hexToBytes(signature)
169
-
97
+
170
98
  const isValidSignature = ed25519.verify(signatureBytes, messageHash, publicKeyBytes)
171
-
99
+
172
100
  if (!isValidSignature) {
101
+ this.logger?.warn("Assinatura inválida", {
102
+ publicKey: publicKey.substring(0, 8) + "..."
103
+ })
173
104
  return {
174
105
  success: false,
175
106
  error: "Assinatura inválida"
176
107
  }
177
108
  }
178
109
 
179
- // Atualizar último uso da sessão
180
- session.lastUsed = new Date()
110
+ // Marcar nonce como usado
111
+ this.usedNonces.set(nonceKey, timestamp)
112
+
113
+ // Verificar se é admin
114
+ const isAdmin = this.config.adminKeys.includes(publicKey)
115
+ const permissions = isAdmin ? ['admin', 'read', 'write', 'delete'] : ['read']
116
+
117
+ this.logger?.debug("Requisição autenticada", {
118
+ publicKey: publicKey.substring(0, 8) + "...",
119
+ isAdmin,
120
+ permissions
121
+ })
181
122
 
182
123
  return {
183
124
  success: true,
184
- sessionId,
185
125
  user: {
186
- sessionId,
187
- isAdmin: session.isAdmin,
188
- isSuperAdmin: session.isAdmin,
189
- permissions: session.permissions
126
+ publicKey,
127
+ isAdmin,
128
+ permissions
190
129
  }
191
130
  }
192
131
  } catch (error) {
193
- this.logger?.error("Erro ao validar sessão", { error })
132
+ this.logger?.error("Erro ao validar requisição", { error })
194
133
  return {
195
134
  success: false,
196
- error: "Erro interno ao validar sessão"
135
+ error: "Erro interno ao validar requisição"
197
136
  }
198
137
  }
199
138
  }
200
139
 
201
- /**
202
- * Obter informações da sessão
203
- */
204
- async getSessionInfo(sessionId: string): Promise<SessionData | null> {
205
- const session = this.sessions.get(sessionId)
206
- if (!session) {
207
- return null
208
- }
209
-
210
- // Verificar se não expirou
211
- const now = Date.now()
212
- const sessionAge = now - session.lastUsed.getTime()
213
- if (sessionAge > this.config.sessionTimeout) {
214
- this.sessions.delete(sessionId)
215
- return null
216
- }
217
-
218
- return { ...session }
219
- }
220
-
221
- /**
222
- * Destruir uma sessão
223
- */
224
- async destroySession(sessionId: string): Promise<void> {
225
- this.sessions.delete(sessionId)
226
- this.logger?.info("Sessão destruída", {
227
- sessionId: sessionId.substring(0, 8) + "..."
228
- })
229
- }
230
-
231
140
  /**
232
141
  * Verificar se uma chave pública é válida
233
142
  */
@@ -245,49 +154,33 @@ export class CryptoAuthService {
245
154
  }
246
155
 
247
156
  /**
248
- * Limpar sessões expiradas
157
+ * Limpar nonces antigos (previne crescimento infinito da memória)
249
158
  */
250
- private cleanupExpiredSessions(): void {
159
+ private cleanupOldNonces(): void {
251
160
  const now = Date.now()
161
+ const maxAge = this.config.maxTimeDrift * 2 // Dobro do tempo máximo permitido
252
162
  let cleanedCount = 0
253
163
 
254
- for (const [sessionId, session] of this.sessions.entries()) {
255
- const sessionAge = now - session.lastUsed.getTime()
256
- if (sessionAge > this.config.sessionTimeout) {
257
- this.sessions.delete(sessionId)
164
+ for (const [nonceKey, timestamp] of this.usedNonces.entries()) {
165
+ if (now - timestamp > maxAge) {
166
+ this.usedNonces.delete(nonceKey)
258
167
  cleanedCount++
259
168
  }
260
169
  }
261
170
 
262
171
  if (cleanedCount > 0) {
263
- this.logger?.debug(`Limpeza de sessões: ${cleanedCount} sessões expiradas removidas`)
172
+ this.logger?.debug(`Limpeza de nonces: ${cleanedCount} nonces antigos removidos`)
264
173
  }
265
174
  }
266
175
 
267
176
  /**
268
- * Obter estatísticas das sessões
177
+ * Obter estatísticas do serviço
269
178
  */
270
179
  getStats() {
271
- const now = Date.now()
272
- let activeSessions = 0
273
- let adminSessions = 0
274
-
275
- for (const session of this.sessions.values()) {
276
- const sessionAge = now - session.lastUsed.getTime()
277
- if (sessionAge <= this.config.sessionTimeout) {
278
- activeSessions++
279
- if (session.isAdmin) {
280
- adminSessions++
281
- }
282
- }
283
- }
284
-
285
180
  return {
286
- totalSessions: this.sessions.size,
287
- activeSessions,
288
- adminSessions,
289
- sessionTimeout: this.config.sessionTimeout,
290
- adminKeys: this.config.adminKeys.length
181
+ usedNoncesCount: this.usedNonces.size,
182
+ adminKeys: this.config.adminKeys.length,
183
+ maxTimeDrift: this.config.maxTimeDrift
291
184
  }
292
185
  }
293
186
  }