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,131 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Provedor de Contexto de Autenticação
|
|
3
|
-
* Context Provider React para gerenciar chaves criptográficas
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React, { createContext, useContext, useEffect, useState, type ReactNode } from 'react'
|
|
7
|
-
import { CryptoAuthClient, type KeyPair, type AuthConfig } from '../CryptoAuthClient'
|
|
8
|
-
|
|
9
|
-
export interface AuthContextValue {
|
|
10
|
-
client: CryptoAuthClient
|
|
11
|
-
keys: KeyPair | null
|
|
12
|
-
hasKeys: boolean
|
|
13
|
-
isLoading: boolean
|
|
14
|
-
error: string | null
|
|
15
|
-
createKeys: () => void
|
|
16
|
-
clearKeys: () => void
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const AuthContext = createContext<AuthContextValue | null>(null)
|
|
20
|
-
|
|
21
|
-
export interface AuthProviderProps {
|
|
22
|
-
children: ReactNode
|
|
23
|
-
config?: AuthConfig
|
|
24
|
-
onKeysChange?: (hasKeys: boolean, keys: KeyPair | null) => void
|
|
25
|
-
onError?: (error: string) => void
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export const AuthProvider: React.FC<AuthProviderProps> = ({
|
|
29
|
-
children,
|
|
30
|
-
config = {},
|
|
31
|
-
onKeysChange,
|
|
32
|
-
onError
|
|
33
|
-
}) => {
|
|
34
|
-
const [client] = useState(() => new CryptoAuthClient({ ...config, autoInit: false }))
|
|
35
|
-
const [keys, setKeys] = useState<KeyPair | null>(null)
|
|
36
|
-
const [isLoading, setIsLoading] = useState(true)
|
|
37
|
-
const [error, setError] = useState<string | null>(null)
|
|
38
|
-
|
|
39
|
-
const hasKeys = keys !== null
|
|
40
|
-
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
initializeKeys()
|
|
43
|
-
}, [])
|
|
44
|
-
|
|
45
|
-
useEffect(() => {
|
|
46
|
-
onKeysChange?.(hasKeys, keys)
|
|
47
|
-
}, [hasKeys, keys, onKeysChange])
|
|
48
|
-
|
|
49
|
-
const initializeKeys = () => {
|
|
50
|
-
setIsLoading(true)
|
|
51
|
-
setError(null)
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
const existingKeys = client.getKeys()
|
|
55
|
-
if (existingKeys) {
|
|
56
|
-
setKeys(existingKeys)
|
|
57
|
-
}
|
|
58
|
-
} catch (err) {
|
|
59
|
-
const errorMessage = err instanceof Error ? err.message : 'Erro desconhecido'
|
|
60
|
-
setError(errorMessage)
|
|
61
|
-
onError?.(errorMessage)
|
|
62
|
-
console.error('Erro ao inicializar chaves:', err)
|
|
63
|
-
} finally {
|
|
64
|
-
setIsLoading(false)
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const createKeys = () => {
|
|
69
|
-
setIsLoading(true)
|
|
70
|
-
setError(null)
|
|
71
|
-
|
|
72
|
-
try {
|
|
73
|
-
const newKeys = client.createNewKeys()
|
|
74
|
-
setKeys(newKeys)
|
|
75
|
-
} catch (err) {
|
|
76
|
-
const errorMessage = err instanceof Error ? err.message : 'Erro ao criar chaves'
|
|
77
|
-
setError(errorMessage)
|
|
78
|
-
onError?.(errorMessage)
|
|
79
|
-
throw err
|
|
80
|
-
} finally {
|
|
81
|
-
setIsLoading(false)
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const clearKeys = () => {
|
|
86
|
-
setIsLoading(true)
|
|
87
|
-
setError(null)
|
|
88
|
-
|
|
89
|
-
try {
|
|
90
|
-
client.clearKeys()
|
|
91
|
-
setKeys(null)
|
|
92
|
-
} catch (err) {
|
|
93
|
-
const errorMessage = err instanceof Error ? err.message : 'Erro ao limpar chaves'
|
|
94
|
-
setError(errorMessage)
|
|
95
|
-
onError?.(errorMessage)
|
|
96
|
-
// Mesmo com erro, limpar as chaves locais
|
|
97
|
-
setKeys(null)
|
|
98
|
-
} finally {
|
|
99
|
-
setIsLoading(false)
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const contextValue: AuthContextValue = {
|
|
104
|
-
client,
|
|
105
|
-
keys,
|
|
106
|
-
hasKeys,
|
|
107
|
-
isLoading,
|
|
108
|
-
error,
|
|
109
|
-
createKeys,
|
|
110
|
-
clearKeys
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return (
|
|
114
|
-
<AuthContext.Provider value={contextValue}>
|
|
115
|
-
{children}
|
|
116
|
-
</AuthContext.Provider>
|
|
117
|
-
)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Hook para usar o contexto de autenticação
|
|
122
|
-
*/
|
|
123
|
-
export const useAuth = (): AuthContextValue => {
|
|
124
|
-
const context = useContext(AuthContext)
|
|
125
|
-
if (!context) {
|
|
126
|
-
throw new Error('useAuth deve ser usado dentro de um AuthProvider')
|
|
127
|
-
}
|
|
128
|
-
return context
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export default AuthProvider
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Componente de Botão de Login
|
|
3
|
-
* Componente React para autenticação criptográfica baseada em keypair
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import React, { useState, useEffect } from 'react'
|
|
7
|
-
import { CryptoAuthClient, type KeyPair } from '../CryptoAuthClient'
|
|
8
|
-
|
|
9
|
-
export interface LoginButtonProps {
|
|
10
|
-
onLogin?: (keys: KeyPair) => void
|
|
11
|
-
onLogout?: () => void
|
|
12
|
-
onError?: (error: string) => void
|
|
13
|
-
className?: string
|
|
14
|
-
loginText?: string
|
|
15
|
-
logoutText?: string
|
|
16
|
-
loadingText?: string
|
|
17
|
-
authClient?: CryptoAuthClient
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export const LoginButton: React.FC<LoginButtonProps> = ({
|
|
21
|
-
onLogin,
|
|
22
|
-
onLogout,
|
|
23
|
-
onError,
|
|
24
|
-
className = '',
|
|
25
|
-
loginText = 'Gerar Chaves',
|
|
26
|
-
logoutText = 'Limpar Chaves',
|
|
27
|
-
loadingText = 'Carregando...',
|
|
28
|
-
authClient
|
|
29
|
-
}) => {
|
|
30
|
-
const [client] = useState(() => authClient || new CryptoAuthClient({ autoInit: false }))
|
|
31
|
-
const [hasKeys, setHasKeys] = useState(false)
|
|
32
|
-
const [isLoading, setIsLoading] = useState(false)
|
|
33
|
-
const [keys, setKeys] = useState<KeyPair | null>(null)
|
|
34
|
-
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
checkKeysStatus()
|
|
37
|
-
}, [])
|
|
38
|
-
|
|
39
|
-
const checkKeysStatus = () => {
|
|
40
|
-
try {
|
|
41
|
-
const existingKeys = client.getKeys()
|
|
42
|
-
if (existingKeys) {
|
|
43
|
-
setHasKeys(true)
|
|
44
|
-
setKeys(existingKeys)
|
|
45
|
-
} else {
|
|
46
|
-
setHasKeys(false)
|
|
47
|
-
setKeys(null)
|
|
48
|
-
}
|
|
49
|
-
} catch (error) {
|
|
50
|
-
console.error('Erro ao verificar chaves:', error)
|
|
51
|
-
setHasKeys(false)
|
|
52
|
-
setKeys(null)
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const handleLogin = () => {
|
|
57
|
-
setIsLoading(true)
|
|
58
|
-
try {
|
|
59
|
-
const newKeys = client.createNewKeys()
|
|
60
|
-
setHasKeys(true)
|
|
61
|
-
setKeys(newKeys)
|
|
62
|
-
onLogin?.(newKeys)
|
|
63
|
-
} catch (error) {
|
|
64
|
-
const errorMessage = error instanceof Error ? error.message : 'Erro desconhecido'
|
|
65
|
-
console.error('Erro ao gerar chaves:', error)
|
|
66
|
-
onError?.(errorMessage)
|
|
67
|
-
} finally {
|
|
68
|
-
setIsLoading(false)
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const handleLogout = () => {
|
|
73
|
-
setIsLoading(true)
|
|
74
|
-
try {
|
|
75
|
-
client.clearKeys()
|
|
76
|
-
setHasKeys(false)
|
|
77
|
-
setKeys(null)
|
|
78
|
-
onLogout?.()
|
|
79
|
-
} catch (error) {
|
|
80
|
-
const errorMessage = error instanceof Error ? error.message : 'Erro desconhecido'
|
|
81
|
-
console.error('Erro ao limpar chaves:', error)
|
|
82
|
-
onError?.(errorMessage)
|
|
83
|
-
} finally {
|
|
84
|
-
setIsLoading(false)
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const baseClassName = `
|
|
89
|
-
px-4 py-2 rounded-md font-medium transition-colors duration-200
|
|
90
|
-
focus:outline-none focus:ring-2 focus:ring-offset-2
|
|
91
|
-
disabled:opacity-50 disabled:cursor-not-allowed
|
|
92
|
-
`.trim()
|
|
93
|
-
|
|
94
|
-
if (isLoading) {
|
|
95
|
-
return (
|
|
96
|
-
<button
|
|
97
|
-
disabled
|
|
98
|
-
className={`${baseClassName} bg-gray-400 text-white cursor-not-allowed ${className}`}
|
|
99
|
-
>
|
|
100
|
-
{loadingText}
|
|
101
|
-
</button>
|
|
102
|
-
)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (hasKeys && keys) {
|
|
106
|
-
return (
|
|
107
|
-
<div className="flex items-center gap-3">
|
|
108
|
-
<div className="flex items-center gap-2">
|
|
109
|
-
<div className="w-2 h-2 bg-green-500 rounded-full"></div>
|
|
110
|
-
<span className="text-sm text-gray-600">
|
|
111
|
-
Autenticado
|
|
112
|
-
</span>
|
|
113
|
-
<code className="text-xs bg-gray-100 px-2 py-1 rounded">
|
|
114
|
-
{keys.publicKey.substring(0, 16)}...
|
|
115
|
-
</code>
|
|
116
|
-
</div>
|
|
117
|
-
|
|
118
|
-
<button
|
|
119
|
-
onClick={handleLogout}
|
|
120
|
-
className={`${baseClassName} bg-red-600 hover:bg-red-700 text-white focus:ring-red-500 ${className}`}
|
|
121
|
-
>
|
|
122
|
-
{logoutText}
|
|
123
|
-
</button>
|
|
124
|
-
</div>
|
|
125
|
-
)
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return (
|
|
129
|
-
<button
|
|
130
|
-
onClick={handleLogin}
|
|
131
|
-
className={`${baseClassName} bg-blue-600 hover:bg-blue-700 text-white focus:ring-blue-500 ${className}`}
|
|
132
|
-
>
|
|
133
|
-
{loginText}
|
|
134
|
-
</button>
|
|
135
|
-
)
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export default LoginButton
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Componente de Rota Protegida
|
|
3
|
-
* Protege componentes que requerem chaves criptográficas no client-side
|
|
4
|
-
*
|
|
5
|
-
* NOTA: Este componente apenas verifica se há chaves locais.
|
|
6
|
-
* A autenticação real acontece no backend via validação de assinatura.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import React, { type ReactNode } from 'react'
|
|
10
|
-
import { useAuth } from './AuthProvider'
|
|
11
|
-
|
|
12
|
-
export interface ProtectedRouteProps {
|
|
13
|
-
children: ReactNode
|
|
14
|
-
fallback?: ReactNode
|
|
15
|
-
loadingComponent?: ReactNode
|
|
16
|
-
unauthorizedComponent?: ReactNode
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const ProtectedRoute: React.FC<ProtectedRouteProps> = ({
|
|
20
|
-
children,
|
|
21
|
-
fallback,
|
|
22
|
-
loadingComponent,
|
|
23
|
-
unauthorizedComponent
|
|
24
|
-
}) => {
|
|
25
|
-
const { hasKeys, isLoading, error } = useAuth()
|
|
26
|
-
|
|
27
|
-
// Componente de loading padrão
|
|
28
|
-
const defaultLoadingComponent = (
|
|
29
|
-
<div className="flex items-center justify-center p-8">
|
|
30
|
-
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
|
|
31
|
-
<span className="ml-3 text-gray-600">Verificando chaves...</span>
|
|
32
|
-
</div>
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
// Componente de não autorizado padrão
|
|
36
|
-
const defaultUnauthorizedComponent = (
|
|
37
|
-
<div className="flex flex-col items-center justify-center p-8 bg-red-50 border border-red-200 rounded-lg">
|
|
38
|
-
<div className="text-red-600 mb-4">
|
|
39
|
-
<svg className="w-12 h-12" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
40
|
-
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
|
41
|
-
</svg>
|
|
42
|
-
</div>
|
|
43
|
-
<h3 className="text-lg font-semibold text-red-800 mb-2">Chaves Não Encontradas</h3>
|
|
44
|
-
<p className="text-red-600 text-center">
|
|
45
|
-
Você precisa gerar chaves criptográficas para acessar esta página.
|
|
46
|
-
</p>
|
|
47
|
-
{error && (
|
|
48
|
-
<p className="text-red-500 text-sm mt-2">
|
|
49
|
-
Erro: {error}
|
|
50
|
-
</p>
|
|
51
|
-
)}
|
|
52
|
-
</div>
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
// Mostrar loading enquanto verifica
|
|
56
|
-
if (isLoading) {
|
|
57
|
-
return <>{loadingComponent || defaultLoadingComponent}</>
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Verificar se tem chaves
|
|
61
|
-
if (!hasKeys) {
|
|
62
|
-
return <>{unauthorizedComponent || fallback || defaultUnauthorizedComponent}</>
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Tem chaves, renderizar children
|
|
66
|
-
// NOTA: Isso não garante autenticação no backend!
|
|
67
|
-
// O backend ainda validará a assinatura em cada requisição.
|
|
68
|
-
return <>{children}</>
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* HOC para proteger componentes
|
|
73
|
-
*/
|
|
74
|
-
export function withAuth<P extends object>(
|
|
75
|
-
Component: React.ComponentType<P>,
|
|
76
|
-
options: Omit<ProtectedRouteProps, 'children'> = {}
|
|
77
|
-
) {
|
|
78
|
-
const WrappedComponent = (props: P) => (
|
|
79
|
-
<ProtectedRoute {...options}>
|
|
80
|
-
<Component {...props} />
|
|
81
|
-
</ProtectedRoute>
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
WrappedComponent.displayName = `withAuth(${Component.displayName || Component.name})`
|
|
85
|
-
|
|
86
|
-
return WrappedComponent
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export default ProtectedRoute
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Exportações dos componentes do cliente
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export { LoginButton } from './LoginButton'
|
|
6
|
-
export type { LoginButtonProps } from './LoginButton'
|
|
7
|
-
|
|
8
|
-
export { AuthProvider, useAuth } from './AuthProvider'
|
|
9
|
-
export type { AuthProviderProps, AuthContextValue } from './AuthProvider'
|
|
10
|
-
|
|
11
|
-
export { ProtectedRoute, withAuth } from './ProtectedRoute'
|
|
12
|
-
export type { ProtectedRouteProps } from './ProtectedRoute'
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Exportações principais do cliente de autenticação
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export { CryptoAuthClient } from './CryptoAuthClient'
|
|
6
|
-
export type { KeyPair, AuthConfig, SignedRequestOptions } from './CryptoAuthClient'
|
|
7
|
-
|
|
8
|
-
// Componentes React
|
|
9
|
-
export * from './components'
|
|
10
|
-
|
|
11
|
-
// Re-exportar para compatibilidade
|
|
12
|
-
export { CryptoAuthClient as default } from './CryptoAuthClient'
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Crypto Auth Plugin Configuration
|
|
3
|
-
* Declarative config using FluxStack config system
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { defineConfig, config } from '@/core/utils/config-schema'
|
|
7
|
-
|
|
8
|
-
const cryptoAuthConfigSchema = {
|
|
9
|
-
// Enable/disable plugin
|
|
10
|
-
enabled: config.boolean('CRYPTO_AUTH_ENABLED', true),
|
|
11
|
-
|
|
12
|
-
// Security settings
|
|
13
|
-
maxTimeDrift: config.number('CRYPTO_AUTH_MAX_TIME_DRIFT', 300000, false), // 5 minutes in ms
|
|
14
|
-
|
|
15
|
-
// Admin keys (array of public keys in hex format)
|
|
16
|
-
adminKeys: config.array('CRYPTO_AUTH_ADMIN_KEYS', []),
|
|
17
|
-
|
|
18
|
-
// Metrics and monitoring
|
|
19
|
-
enableMetrics: config.boolean('CRYPTO_AUTH_ENABLE_METRICS', true),
|
|
20
|
-
|
|
21
|
-
// Session configuration (for future features)
|
|
22
|
-
sessionTimeout: config.number('CRYPTO_AUTH_SESSION_TIMEOUT', 1800000, false), // 30 minutes
|
|
23
|
-
|
|
24
|
-
// Nonce configuration
|
|
25
|
-
nonceLength: config.number('CRYPTO_AUTH_NONCE_LENGTH', 16, false), // bytes
|
|
26
|
-
|
|
27
|
-
// Rate limiting (requests per minute per public key)
|
|
28
|
-
rateLimitPerMinute: config.number('CRYPTO_AUTH_RATE_LIMIT', 100, false),
|
|
29
|
-
} as const
|
|
30
|
-
|
|
31
|
-
export const cryptoAuthConfig = defineConfig(cryptoAuthConfigSchema)
|
|
32
|
-
|
|
33
|
-
export type CryptoAuthConfig = typeof cryptoAuthConfig
|
|
34
|
-
export default cryptoAuthConfig
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FluxStack Crypto Auth Plugin
|
|
3
|
-
* Sistema de autenticação baseado em criptografia Ed25519
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { FluxStack, PluginContext, RequestContext, ResponseContext } from "../../core/plugins/types"
|
|
7
|
-
|
|
8
|
-
type Plugin = FluxStack.Plugin
|
|
9
|
-
import { Elysia, t } from "elysia"
|
|
10
|
-
import { CryptoAuthService, AuthMiddleware } from "./server"
|
|
11
|
-
import { makeProtectedRouteCommand } from "./cli/make-protected-route.command"
|
|
12
|
-
|
|
13
|
-
// ✅ Plugin carrega sua própria configuração (da pasta config/ do plugin)
|
|
14
|
-
import { cryptoAuthConfig } from "./config"
|
|
15
|
-
|
|
16
|
-
// Store config globally for hooks to access
|
|
17
|
-
let pluginConfig: any = cryptoAuthConfig
|
|
18
|
-
|
|
19
|
-
export const cryptoAuthPlugin: Plugin = {
|
|
20
|
-
name: "crypto-auth",
|
|
21
|
-
version: "1.0.0",
|
|
22
|
-
description: "Sistema de autenticação baseado em criptografia Ed25519 para FluxStack",
|
|
23
|
-
author: "FluxStack Team",
|
|
24
|
-
priority: 100, // Alta prioridade para autenticação
|
|
25
|
-
category: "auth",
|
|
26
|
-
tags: ["authentication", "ed25519", "cryptography", "security"],
|
|
27
|
-
dependencies: [],
|
|
28
|
-
|
|
29
|
-
configSchema: {
|
|
30
|
-
type: "object",
|
|
31
|
-
properties: {
|
|
32
|
-
enabled: {
|
|
33
|
-
type: "boolean",
|
|
34
|
-
description: "Habilitar autenticação criptográfica"
|
|
35
|
-
},
|
|
36
|
-
maxTimeDrift: {
|
|
37
|
-
type: "number",
|
|
38
|
-
minimum: 30000,
|
|
39
|
-
description: "Máximo drift de tempo permitido em millisegundos (previne replay attacks)"
|
|
40
|
-
},
|
|
41
|
-
adminKeys: {
|
|
42
|
-
type: "array",
|
|
43
|
-
items: { type: "string" },
|
|
44
|
-
description: "Chaves públicas dos administradores (hex 64 caracteres)"
|
|
45
|
-
},
|
|
46
|
-
enableMetrics: {
|
|
47
|
-
type: "boolean",
|
|
48
|
-
description: "Habilitar métricas de autenticação"
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
additionalProperties: false
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
defaultConfig: {
|
|
55
|
-
enabled: true,
|
|
56
|
-
maxTimeDrift: 300000, // 5 minutos
|
|
57
|
-
adminKeys: [],
|
|
58
|
-
enableMetrics: true
|
|
59
|
-
},
|
|
60
|
-
|
|
61
|
-
// CLI Commands
|
|
62
|
-
commands: [
|
|
63
|
-
makeProtectedRouteCommand
|
|
64
|
-
],
|
|
65
|
-
|
|
66
|
-
setup: async (context: PluginContext) => {
|
|
67
|
-
// ✅ Plugin usa sua própria configuração (já importada no topo)
|
|
68
|
-
if (!cryptoAuthConfig.enabled) {
|
|
69
|
-
context.logger.info('Crypto Auth plugin desabilitado por configuração')
|
|
70
|
-
return
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Inicializar serviço de autenticação (SEM SESSÕES)
|
|
74
|
-
const authService = new CryptoAuthService({
|
|
75
|
-
maxTimeDrift: cryptoAuthConfig.maxTimeDrift,
|
|
76
|
-
adminKeys: cryptoAuthConfig.adminKeys,
|
|
77
|
-
logger: context.logger
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
// Inicializar middleware de autenticação (sem path matching)
|
|
81
|
-
const authMiddleware = new AuthMiddleware(authService, {
|
|
82
|
-
logger: context.logger
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
// Armazenar instâncias no contexto global
|
|
86
|
-
;(global as any).cryptoAuthService = authService
|
|
87
|
-
;(global as any).cryptoAuthMiddleware = authMiddleware
|
|
88
|
-
|
|
89
|
-
context.logger.info("✅ Crypto Auth plugin inicializado", {
|
|
90
|
-
mode: 'middleware-based',
|
|
91
|
-
maxTimeDrift: cryptoAuthConfig.maxTimeDrift,
|
|
92
|
-
adminKeys: cryptoAuthConfig.adminKeys.length,
|
|
93
|
-
usage: 'Use cryptoAuthRequired(), cryptoAuthAdmin(), cryptoAuthOptional() nas rotas'
|
|
94
|
-
})
|
|
95
|
-
},
|
|
96
|
-
|
|
97
|
-
// @ts-ignore - plugin property não está no tipo oficial mas é suportada
|
|
98
|
-
plugin: new Elysia({ prefix: "/api/auth" })
|
|
99
|
-
.get("/info", () => ({
|
|
100
|
-
name: "FluxStack Crypto Auth",
|
|
101
|
-
description: "Autenticação baseada em assinatura Ed25519",
|
|
102
|
-
version: "1.0.0",
|
|
103
|
-
mode: "middleware-based",
|
|
104
|
-
how_it_works: {
|
|
105
|
-
step1: "Cliente gera par de chaves Ed25519 (pública + privada) localmente",
|
|
106
|
-
step2: "Cliente armazena chave privada no navegador (NUNCA envia ao servidor)",
|
|
107
|
-
step3: "Para cada requisição, cliente assina com chave privada",
|
|
108
|
-
step4: "Cliente envia: chave pública + assinatura + dados",
|
|
109
|
-
step5: "Servidor valida assinatura usando chave pública recebida"
|
|
110
|
-
},
|
|
111
|
-
required_headers: {
|
|
112
|
-
"x-public-key": "Chave pública Ed25519 (hex 64 chars)",
|
|
113
|
-
"x-timestamp": "Timestamp da requisição (milliseconds)",
|
|
114
|
-
"x-nonce": "Nonce aleatório (previne replay)",
|
|
115
|
-
"x-signature": "Assinatura Ed25519 da mensagem (hex)"
|
|
116
|
-
},
|
|
117
|
-
admin_keys: (global as any).cryptoAuthService?.getStats().adminKeys || 0,
|
|
118
|
-
usage: {
|
|
119
|
-
required: "import { cryptoAuthRequired } from '@/plugins/crypto-auth/server'",
|
|
120
|
-
admin: "import { cryptoAuthAdmin } from '@/plugins/crypto-auth/server'",
|
|
121
|
-
optional: "import { cryptoAuthOptional } from '@/plugins/crypto-auth/server'",
|
|
122
|
-
permissions: "import { cryptoAuthPermissions } from '@/plugins/crypto-auth/server'"
|
|
123
|
-
}
|
|
124
|
-
})),
|
|
125
|
-
|
|
126
|
-
onResponse: async (context: ResponseContext) => {
|
|
127
|
-
if (!cryptoAuthConfig.enableMetrics) return
|
|
128
|
-
|
|
129
|
-
// Log métricas de autenticação
|
|
130
|
-
const user = (context as any).user
|
|
131
|
-
const authError = (context as any).authError
|
|
132
|
-
|
|
133
|
-
if (user) {
|
|
134
|
-
console.debug("Requisição autenticada", {
|
|
135
|
-
publicKey: user.publicKey?.substring(0, 8) + "...",
|
|
136
|
-
isAdmin: user.isAdmin,
|
|
137
|
-
path: context.path,
|
|
138
|
-
method: context.method,
|
|
139
|
-
duration: context.duration
|
|
140
|
-
})
|
|
141
|
-
} else if (authError) {
|
|
142
|
-
console.warn("Falha na autenticação", {
|
|
143
|
-
error: authError,
|
|
144
|
-
path: context.path,
|
|
145
|
-
method: context.method
|
|
146
|
-
})
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
|
|
150
|
-
onServerStart: async (context: PluginContext) => {
|
|
151
|
-
if (cryptoAuthConfig.enabled) {
|
|
152
|
-
context.logger.info("✅ Crypto Auth plugin ativo", {
|
|
153
|
-
mode: 'middleware-based',
|
|
154
|
-
adminKeys: cryptoAuthConfig.adminKeys.length,
|
|
155
|
-
maxTimeDrift: `${cryptoAuthConfig.maxTimeDrift}ms`,
|
|
156
|
-
usage: 'Use cryptoAuthRequired(), cryptoAuthAdmin() nas rotas'
|
|
157
|
-
})
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export default cryptoAuthPlugin
|
|
@@ -1,66 +0,0 @@
|
|
|
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
|
-
}
|
|
66
|
-
}
|