jerkjs 2.0.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.
- package/LICENSE +200 -0
- package/README.md +171 -0
- package/doc/EXTENSION_MANUAL.md +958 -0
- package/doc/FIREWALL_MANUAL.md +419 -0
- package/doc/HOOKS_REFERENCE_IMPROVED.md +599 -0
- package/doc/MANUAL_API_SDK.md +539 -0
- package/doc/MANUAL_MVC.md +397 -0
- package/doc/MARIADB_TOKENS_IMPLEMENTATION.md +113 -0
- package/doc/MIDDLEWARE_MANUAL.md +521 -0
- package/doc/OAUTH2_GOOGLE_MANUAL.md +408 -0
- package/doc/frontend-and-sessions.md +356 -0
- package/examples/advanced/controllers/productController.js +64 -0
- package/examples/advanced/controllers/userController.js +85 -0
- package/examples/advanced/routes.json +51 -0
- package/examples/advanced_example.js +93 -0
- package/examples/basic/controllers/userController.js +85 -0
- package/examples/basic_example.js +72 -0
- package/examples/frontend/README.md +71 -0
- package/examples/frontend/app.js +71 -0
- package/examples/frontend/controllers/apiController.js +39 -0
- package/examples/frontend/controllers/authController.js +220 -0
- package/examples/frontend/controllers/formController.js +47 -0
- package/examples/frontend/controllers/messageController.js +96 -0
- package/examples/frontend/controllers/pageController.js +178 -0
- package/examples/frontend/controllers/staticController.js +167 -0
- package/examples/frontend/routes.json +90 -0
- package/examples/mvc_example/app.js +138 -0
- package/examples/mvc_example/views/home/index.html +26 -0
- package/examples/mvc_example/views/home/simple.html +3 -0
- package/examples/mvc_example/views/layout.html +23 -0
- package/examples/mvc_example/views/test.html +3 -0
- package/examples/mvc_example/views/user/invalid.html +6 -0
- package/examples/mvc_example/views/user/list.html +36 -0
- package/examples/mvc_example/views/user/notfound.html +6 -0
- package/examples/mvc_example/views/user/profile.html +11 -0
- package/examples/mvc_routes_example/app.js +34 -0
- package/examples/mvc_routes_example/controllers/mainController.js +27 -0
- package/examples/mvc_routes_example/controllers/productController.js +47 -0
- package/examples/mvc_routes_example/controllers/userController.js +76 -0
- package/examples/mvc_routes_example/routes.json +30 -0
- package/examples/mvc_routes_example/views/layout.html +31 -0
- package/examples/mvc_routes_example/views/main/index.html +11 -0
- package/examples/mvc_routes_example/views/product/catalog.html +24 -0
- package/examples/mvc_routes_example/views/user/invalid.html +6 -0
- package/examples/mvc_routes_example/views/user/list.html +40 -0
- package/examples/mvc_routes_example/views/user/notfound.html +6 -0
- package/examples/mvc_routes_example/views/user/profile.html +18 -0
- package/examples/public/README.md +92 -0
- package/examples/public/app.js +72 -0
- package/examples/public/controllers/healthController.js +20 -0
- package/examples/public/controllers/mainController.js +22 -0
- package/examples/public/controllers/userController.js +139 -0
- package/examples/public/routes.json +51 -0
- package/examples/v2/README.md +72 -0
- package/examples/v2/app.js +74 -0
- package/examples/v2/app_fixed.js +74 -0
- package/examples/v2/controllers/authController.js +64 -0
- package/examples/v2/controllers/mainController.js +24 -0
- package/examples/v2/controllers/protectedController.js +12 -0
- package/examples/v2/controllers/userController.js +16 -0
- package/examples/v2/package.json +27 -0
- package/examples/v2/routes.json +30 -0
- package/examples/v2/test_api.sh +47 -0
- package/examples/v2/tokens_example.sqlite +0 -0
- package/examples/v2.1_firewall_demo/README.md +113 -0
- package/examples/v2.1_firewall_demo/app.js +182 -0
- package/examples/v2.1_firewall_demo/package.json +27 -0
- package/examples/v2.1_hooks_demo/README.md +85 -0
- package/examples/v2.1_hooks_demo/app.js +101 -0
- package/examples/v2.1_hooks_demo/controllers/hooksController.js +29 -0
- package/examples/v2.1_hooks_demo/controllers/mainController.js +18 -0
- package/examples/v2.1_hooks_demo/package.json +27 -0
- package/examples/v2.1_hooks_demo/routes.json +16 -0
- package/examples/v2.1_openapi_demo/README.md +82 -0
- package/examples/v2.1_openapi_demo/app.js +296 -0
- package/examples/v2.1_openapi_demo/package.json +26 -0
- package/examples/v2_cors/README.md +82 -0
- package/examples/v2_cors/app.js +108 -0
- package/examples/v2_cors/package.json +23 -0
- package/examples/v2_json_auth/README.md +83 -0
- package/examples/v2_json_auth/app.js +72 -0
- package/examples/v2_json_auth/controllers/authController.js +67 -0
- package/examples/v2_json_auth/controllers/mainController.js +16 -0
- package/examples/v2_json_auth/controllers/protectedController.js +12 -0
- package/examples/v2_json_auth/controllers/tokenController.js +28 -0
- package/examples/v2_json_auth/controllers/userController.js +15 -0
- package/examples/v2_json_auth/package.json +26 -0
- package/examples/v2_json_auth/routes.json +37 -0
- package/examples/v2_json_auth/tokens.json +20 -0
- package/examples/v2_mariadb_auth/README.md +94 -0
- package/examples/v2_mariadb_auth/app.js +81 -0
- package/examples/v2_mariadb_auth/controllers/authController.js +95 -0
- package/examples/v2_mariadb_auth/controllers/mainController.js +31 -0
- package/examples/v2_mariadb_auth/controllers/protectedController.js +12 -0
- package/examples/v2_mariadb_auth/controllers/userController.js +17 -0
- package/examples/v2_mariadb_auth/package.json +27 -0
- package/examples/v2_mariadb_auth/routes.json +37 -0
- package/examples/v2_no_auth/README.md +75 -0
- package/examples/v2_no_auth/app.js +72 -0
- package/examples/v2_no_auth/controllers/healthController.js +14 -0
- package/examples/v2_no_auth/controllers/mainController.js +19 -0
- package/examples/v2_no_auth/controllers/productController.js +31 -0
- package/examples/v2_no_auth/controllers/publicController.js +16 -0
- package/examples/v2_no_auth/package.json +22 -0
- package/examples/v2_no_auth/routes.json +37 -0
- package/examples/v2_oauth/README.md +70 -0
- package/examples/v2_oauth/app.js +90 -0
- package/examples/v2_oauth/controllers/mainController.js +45 -0
- package/examples/v2_oauth/controllers/oauthController.js +247 -0
- package/examples/v2_oauth/controllers/protectedController.js +13 -0
- package/examples/v2_oauth/controllers/userController.js +17 -0
- package/examples/v2_oauth/package.json +26 -0
- package/examples/v2_oauth/routes.json +44 -0
- package/examples/v2_openapi/README.md +77 -0
- package/examples/v2_openapi/app.js +222 -0
- package/examples/v2_openapi/controllers/authController.js +52 -0
- package/examples/v2_openapi/controllers/mainController.js +26 -0
- package/examples/v2_openapi/controllers/productController.js +17 -0
- package/examples/v2_openapi/controllers/userController.js +27 -0
- package/examples/v2_openapi/package.json +26 -0
- package/examples/v2_openapi/routes.json +37 -0
- package/generate_token.js +10 -0
- package/index.js +85 -0
- package/jerk.jpg +0 -0
- package/lib/core/handler.js +86 -0
- package/lib/core/hooks.js +224 -0
- package/lib/core/router.js +204 -0
- package/lib/core/securityEnhancedServer.js +752 -0
- package/lib/core/server.js +369 -0
- package/lib/loader/controllerLoader.js +175 -0
- package/lib/loader/routeLoader.js +341 -0
- package/lib/middleware/auditLogger.js +208 -0
- package/lib/middleware/authenticator.js +565 -0
- package/lib/middleware/compressor.js +218 -0
- package/lib/middleware/cors.js +135 -0
- package/lib/middleware/firewall.js +443 -0
- package/lib/middleware/rateLimiter.js +210 -0
- package/lib/middleware/session.js +301 -0
- package/lib/middleware/validator.js +193 -0
- package/lib/mvc/controllerBase.js +207 -0
- package/lib/mvc/viewEngine.js +752 -0
- package/lib/utils/configParser.js +223 -0
- package/lib/utils/logger.js +145 -0
- package/lib/utils/mariadbTokenAdapter.js +226 -0
- package/lib/utils/openapiGenerator.js +140 -0
- package/lib/utils/sqliteTokenAdapter.js +224 -0
- package/lib/utils/tokenManager.js +254 -0
- package/package.json +47 -0
- package/v2examplle/v2_json_auth/README.md +83 -0
- package/v2examplle/v2_json_auth/app.js +72 -0
- package/v2examplle/v2_json_auth/controllers/authController.js +67 -0
- package/v2examplle/v2_json_auth/controllers/mainController.js +16 -0
- package/v2examplle/v2_json_auth/controllers/protectedController.js +12 -0
- package/v2examplle/v2_json_auth/controllers/tokenController.js +28 -0
- package/v2examplle/v2_json_auth/controllers/userController.js +15 -0
- package/v2examplle/v2_json_auth/package.json +26 -0
- package/v2examplle/v2_json_auth/routes.json +37 -0
- package/v2examplle/v2_json_auth/tokens.json +20 -0
- package/v2examplle/v2_mariadb_auth/README.md +94 -0
- package/v2examplle/v2_mariadb_auth/app.js +81 -0
- package/v2examplle/v2_mariadb_auth/controllers/authController.js +95 -0
- package/v2examplle/v2_mariadb_auth/controllers/mainController.js +31 -0
- package/v2examplle/v2_mariadb_auth/controllers/protectedController.js +12 -0
- package/v2examplle/v2_mariadb_auth/controllers/userController.js +17 -0
- package/v2examplle/v2_mariadb_auth/package.json +27 -0
- package/v2examplle/v2_mariadb_auth/routes.json +37 -0
- package/v2examplle/v2_sqlite_auth/README.md +72 -0
- package/v2examplle/v2_sqlite_auth/app.js +74 -0
- package/v2examplle/v2_sqlite_auth/app_fixed.js +74 -0
- package/v2examplle/v2_sqlite_auth/controllers/authController.js +64 -0
- package/v2examplle/v2_sqlite_auth/controllers/mainController.js +24 -0
- package/v2examplle/v2_sqlite_auth/controllers/protectedController.js +12 -0
- package/v2examplle/v2_sqlite_auth/controllers/userController.js +16 -0
- package/v2examplle/v2_sqlite_auth/package.json +27 -0
- package/v2examplle/v2_sqlite_auth/routes.json +30 -0
- package/v2examplle/v2_sqlite_auth/test_api.sh +47 -0
- package/v2examplle/v2_sqlite_auth/tokens_example.sqlite +0 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
const jwt = require('jsonwebtoken');
|
|
2
|
+
const { Authenticator, TokenManager } = require('../../../index.js');
|
|
3
|
+
|
|
4
|
+
// TokenManager para este controlador
|
|
5
|
+
const tokenManager = new TokenManager({
|
|
6
|
+
storage: 'memory'
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const oauthController = {
|
|
10
|
+
initiateGoogleAuth: (req, res) => {
|
|
11
|
+
// Obtener las credenciales de OAuth
|
|
12
|
+
const clientId = process.env.OAUTH_CLIENT_ID;
|
|
13
|
+
const clientSecret = process.env.OAUTH_CLIENT_SECRET;
|
|
14
|
+
|
|
15
|
+
// Verificar si las credenciales están configuradas
|
|
16
|
+
if (!clientId || clientId === 'tu-client-id-aqui' || !clientSecret || clientSecret === 'tu-client-secret-aqui') {
|
|
17
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
18
|
+
res.end(`
|
|
19
|
+
<!DOCTYPE html>
|
|
20
|
+
<html>
|
|
21
|
+
<head>
|
|
22
|
+
<title>Configurar OAuth con Google</title>
|
|
23
|
+
<style>
|
|
24
|
+
body { font-family: Arial, sans-serif; margin: 40px; text-align: center; }
|
|
25
|
+
.container { max-width: 800px; margin: 0 auto; }
|
|
26
|
+
.setup-instructions {
|
|
27
|
+
margin: 20px 0;
|
|
28
|
+
padding: 20px;
|
|
29
|
+
background-color: #fff3cd;
|
|
30
|
+
border: 1px solid #ffeaa7;
|
|
31
|
+
border-radius: 4px;
|
|
32
|
+
text-align: left;
|
|
33
|
+
}
|
|
34
|
+
.credentials-form {
|
|
35
|
+
margin: 20px 0;
|
|
36
|
+
padding: 20px;
|
|
37
|
+
background-color: #f8f9fa;
|
|
38
|
+
border: 1px solid #dee2e6;
|
|
39
|
+
border-radius: 4px;
|
|
40
|
+
}
|
|
41
|
+
input[type="text"], input[type="password"] {
|
|
42
|
+
width: 100%;
|
|
43
|
+
padding: 8px;
|
|
44
|
+
margin: 5px 0;
|
|
45
|
+
box-sizing: border-box;
|
|
46
|
+
}
|
|
47
|
+
button {
|
|
48
|
+
background-color: #007bff;
|
|
49
|
+
color: white;
|
|
50
|
+
border: none;
|
|
51
|
+
padding: 10px 20px;
|
|
52
|
+
cursor: pointer;
|
|
53
|
+
margin: 10px 5px;
|
|
54
|
+
}
|
|
55
|
+
button:hover {
|
|
56
|
+
background-color: #0056b3;
|
|
57
|
+
}
|
|
58
|
+
</style>
|
|
59
|
+
</head>
|
|
60
|
+
<body>
|
|
61
|
+
<div class="container">
|
|
62
|
+
<h1>Configurar OAuth con Google</h1>
|
|
63
|
+
|
|
64
|
+
<div class="setup-instructions">
|
|
65
|
+
<h3>Para usar la autenticación OAuth real:</h3>
|
|
66
|
+
<ol>
|
|
67
|
+
<li>Registra tu aplicación en <a href="https://console.cloud.google.com/" target="_blank">Google Cloud Console</a></li>
|
|
68
|
+
<li>Ve a "APIs & Services" > "Credentials"</li>
|
|
69
|
+
<li>Crea un "OAuth 2.0 Client IDs"</li>
|
|
70
|
+
<li>Configura "Authorized redirect URIs" con: <code>http://localhost:8093/auth/callback</code></li>
|
|
71
|
+
<li>Copia tu Client ID y Client Secret</li>
|
|
72
|
+
</ol>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<div class="credentials-form">
|
|
76
|
+
<h3>Ingresa tus credenciales OAuth</h3>
|
|
77
|
+
<form id="credentialsForm">
|
|
78
|
+
<label for="clientId">Client ID:</label>
|
|
79
|
+
<input type="text" id="clientId" name="clientId" placeholder="Ingresa tu Client ID">
|
|
80
|
+
|
|
81
|
+
<label for="clientSecret">Client Secret:</label>
|
|
82
|
+
<input type="password" id="clientSecret" name="clientSecret" placeholder="Ingresa tu Client Secret">
|
|
83
|
+
|
|
84
|
+
<div style="margin-top: 15px;">
|
|
85
|
+
<button type="submit">Guardar Credenciales</button>
|
|
86
|
+
<button type="button" onclick="location.href='/'">Volver al inicio</button>
|
|
87
|
+
</div>
|
|
88
|
+
</form>
|
|
89
|
+
|
|
90
|
+
<div id="statusMessage" style="margin-top: 15px; display: none;"></div>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<script>
|
|
94
|
+
document.getElementById('credentialsForm').addEventListener('submit', function(e) {
|
|
95
|
+
e.preventDefault();
|
|
96
|
+
|
|
97
|
+
const clientId = document.getElementById('clientId').value;
|
|
98
|
+
const clientSecret = document.getElementById('clientSecret').value;
|
|
99
|
+
|
|
100
|
+
// Enviar credenciales al servidor
|
|
101
|
+
fetch('/auth/setup', {
|
|
102
|
+
method: 'POST',
|
|
103
|
+
headers: {
|
|
104
|
+
'Content-Type': 'application/json'
|
|
105
|
+
},
|
|
106
|
+
body: JSON.stringify({ clientId, clientSecret })
|
|
107
|
+
})
|
|
108
|
+
.then(response => response.json())
|
|
109
|
+
.then(data => {
|
|
110
|
+
const statusDiv = document.getElementById('statusMessage');
|
|
111
|
+
statusDiv.style.display = 'block';
|
|
112
|
+
|
|
113
|
+
if (data.success) {
|
|
114
|
+
statusDiv.innerHTML = '<p style="color: green;">Credenciales guardadas exitosamente. <a href="/auth/google">Haz clic aquí para iniciar OAuth</a></p>';
|
|
115
|
+
document.getElementById('credentialsForm').reset();
|
|
116
|
+
} else {
|
|
117
|
+
statusDiv.innerHTML = '<p style="color: red;">Error: ' + data.message + '</p>';
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
.catch(error => {
|
|
121
|
+
console.error('Error:', error);
|
|
122
|
+
document.getElementById('statusMessage').innerHTML = '<p style="color: red;">Error al guardar credenciales</p>';
|
|
123
|
+
document.getElementById('statusMessage').style.display = 'block';
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
</script>
|
|
127
|
+
</div>
|
|
128
|
+
</body>
|
|
129
|
+
</html>
|
|
130
|
+
`);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Si las credenciales están configuradas, crear la URL de autorización OAuth
|
|
135
|
+
const redirectUri = encodeURIComponent('http://localhost:8093/auth/callback');
|
|
136
|
+
const scope = encodeURIComponent('email profile');
|
|
137
|
+
const authUrl = `https://accounts.google.com/o/oauth2/v2/auth?` +
|
|
138
|
+
`client_id=${clientId}&` +
|
|
139
|
+
`redirect_uri=${redirectUri}&` +
|
|
140
|
+
`response_type=code&` +
|
|
141
|
+
`scope=${scope}&` +
|
|
142
|
+
`access_type=offline&` +
|
|
143
|
+
`prompt=consent`;
|
|
144
|
+
|
|
145
|
+
// Redirigir al usuario a la URL de autorización de Google
|
|
146
|
+
res.writeHead(302, { 'Location': authUrl });
|
|
147
|
+
res.end();
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
handleCallback: async (req, res) => {
|
|
151
|
+
const { code } = req.query;
|
|
152
|
+
|
|
153
|
+
if (!code) {
|
|
154
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
155
|
+
res.end(JSON.stringify({ error: 'No authorization code received' }));
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
// En una implementación real, intercambiaríamos el código por tokens de acceso
|
|
161
|
+
const clientId = process.env.OAUTH_CLIENT_ID;
|
|
162
|
+
const clientSecret = process.env.OAUTH_CLIENT_SECRET;
|
|
163
|
+
const redirectUri = 'http://localhost:8093/auth/callback';
|
|
164
|
+
|
|
165
|
+
// Simular la obtención de tokens (en una implementación real, haríamos una solicitud HTTP real)
|
|
166
|
+
// Aquí es donde el framework realmente haría el trabajo pesado
|
|
167
|
+
|
|
168
|
+
// Simular la obtención de información del usuario desde Google
|
|
169
|
+
// En una implementación real, usaríamos el access token para llamar a la API de Google
|
|
170
|
+
const userInfo = {
|
|
171
|
+
id: 'demo_user_id',
|
|
172
|
+
email: 'demo@example.com',
|
|
173
|
+
name: 'Demo User',
|
|
174
|
+
picture: 'https://via.placeholder.com/150'
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// Generar un token JWT para el usuario autenticado
|
|
178
|
+
const secret = 'super-secret-key-for-oauth-example';
|
|
179
|
+
const tokenPayload = {
|
|
180
|
+
userId: userInfo.id,
|
|
181
|
+
email: userInfo.email,
|
|
182
|
+
name: userInfo.name,
|
|
183
|
+
provider: 'google',
|
|
184
|
+
iat: Math.floor(Date.now() / 1000),
|
|
185
|
+
exp: Math.floor(Date.now() / 1000) + (60 * 60) // Expira en 1 hora
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const token = jwt.sign(tokenPayload, secret);
|
|
189
|
+
|
|
190
|
+
// Enviar respuesta con el token
|
|
191
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
192
|
+
res.end(`
|
|
193
|
+
<h2>¡Autenticación OAuth exitosa!</h2>
|
|
194
|
+
<p>Usuario autenticado: <strong>${userInfo.name}</strong></p>
|
|
195
|
+
<p>Email: <strong>${userInfo.email}</strong></p>
|
|
196
|
+
<p>Proveedor: <strong>Google</strong></p>
|
|
197
|
+
|
|
198
|
+
<div style="margin: 20px 0; padding: 15px; background-color: #d4edda; border: 1px solid #c3e6cb; border-radius: 4px;">
|
|
199
|
+
<p><strong>Token JWT generado:</strong></p>
|
|
200
|
+
<textarea readonly style="width: 100%; height: 80px; font-family: monospace;">${token}</textarea>
|
|
201
|
+
<p>Este token puede usarse en el header <code>Authorization: Bearer <token></code> para acceder a endpoints protegidos</p>
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
<p><a href="/profile">Ir al perfil del usuario</a> | <a href="/">Volver a la página principal</a></p>
|
|
205
|
+
`);
|
|
206
|
+
} catch (error) {
|
|
207
|
+
console.error('Error en el callback OAuth:', error);
|
|
208
|
+
res.writeHead(500, { 'Content-Type': 'text/html' });
|
|
209
|
+
res.end(`
|
|
210
|
+
<h2>Error en el proceso de autenticación</h2>
|
|
211
|
+
<p>${error.message}</p>
|
|
212
|
+
<p><a href="/auth/google">Intentar de nuevo</a></p>
|
|
213
|
+
`);
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
|
|
217
|
+
setupCredentials: (req, res) => {
|
|
218
|
+
// Este endpoint es para fines de demostración
|
|
219
|
+
// En una implementación real, las credenciales se configurarían en variables de entorno
|
|
220
|
+
let body = '';
|
|
221
|
+
req.on('data', chunk => {
|
|
222
|
+
body += chunk.toString();
|
|
223
|
+
});
|
|
224
|
+
req.on('end', () => {
|
|
225
|
+
try {
|
|
226
|
+
const data = JSON.parse(body);
|
|
227
|
+
|
|
228
|
+
// En una implementación real, esto se haría de forma segura
|
|
229
|
+
// Aquí solo simulamos el proceso para fines de demostración
|
|
230
|
+
|
|
231
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
232
|
+
res.end(JSON.stringify({
|
|
233
|
+
success: true,
|
|
234
|
+
message: 'Credenciales recibidas. En una implementación real, estas se configurarían como variables de entorno.'
|
|
235
|
+
}));
|
|
236
|
+
} catch (error) {
|
|
237
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
238
|
+
res.end(JSON.stringify({
|
|
239
|
+
success: false,
|
|
240
|
+
message: 'Error al procesar las credenciales'
|
|
241
|
+
}));
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
module.exports = oauthController;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const protectedController = {
|
|
2
|
+
getProtectedData: (req, res) => {
|
|
3
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
4
|
+
res.end(JSON.stringify({
|
|
5
|
+
message: 'Datos protegidos accesados exitosamente',
|
|
6
|
+
user: req.user,
|
|
7
|
+
timestamp: new Date().toISOString(),
|
|
8
|
+
provider: 'Este contenido fue protegido usando autenticación JWT generada tras OAuth'
|
|
9
|
+
}));
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
module.exports = protectedController;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const userController = {
|
|
2
|
+
getProfile: (req, res) => {
|
|
3
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
4
|
+
res.end(JSON.stringify({
|
|
5
|
+
profile: {
|
|
6
|
+
id: req.user.userId,
|
|
7
|
+
email: req.user.email,
|
|
8
|
+
name: req.user.name,
|
|
9
|
+
provider: req.user.provider,
|
|
10
|
+
authenticatedAt: new Date().toISOString()
|
|
11
|
+
},
|
|
12
|
+
message: 'Perfil de usuario obtenido exitosamente'
|
|
13
|
+
}));
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
module.exports = userController;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "api-sdk-oauth-example",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Ejemplo de API con funcionalidad OAuth 2.0 usando el Framework API SDK",
|
|
5
|
+
"main": "app.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"start": "node app.js",
|
|
8
|
+
"dev": "nodemon app.js"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"api",
|
|
12
|
+
"sdk",
|
|
13
|
+
"oauth",
|
|
14
|
+
"oauth2",
|
|
15
|
+
"authentication",
|
|
16
|
+
"framework"
|
|
17
|
+
],
|
|
18
|
+
"author": "API SDK Framework",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"jsonwebtoken": "^9.0.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"nodemon": "^3.0.0"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"path": "/",
|
|
4
|
+
"method": "GET",
|
|
5
|
+
"controller": "./controllers/mainController.js",
|
|
6
|
+
"handler": "home",
|
|
7
|
+
"auth": "none"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"path": "/auth/google",
|
|
11
|
+
"method": "GET",
|
|
12
|
+
"controller": "./controllers/oauthController.js",
|
|
13
|
+
"handler": "initiateGoogleAuth",
|
|
14
|
+
"auth": "none"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"path": "/auth/callback",
|
|
18
|
+
"method": "GET",
|
|
19
|
+
"controller": "./controllers/oauthController.js",
|
|
20
|
+
"handler": "handleCallback",
|
|
21
|
+
"auth": "none"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"path": "/auth/setup",
|
|
25
|
+
"method": "POST",
|
|
26
|
+
"controller": "./controllers/oauthController.js",
|
|
27
|
+
"handler": "setupCredentials",
|
|
28
|
+
"auth": "none"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"path": "/profile",
|
|
32
|
+
"method": "GET",
|
|
33
|
+
"controller": "./controllers/userController.js",
|
|
34
|
+
"handler": "getProfile",
|
|
35
|
+
"auth": "jwt-oauth"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"path": "/protected",
|
|
39
|
+
"method": "GET",
|
|
40
|
+
"controller": "./controllers/protectedController.js",
|
|
41
|
+
"handler": "getProtectedData",
|
|
42
|
+
"auth": "jwt-oauth"
|
|
43
|
+
}
|
|
44
|
+
]
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Ejemplo API con Funcionalidad OpenAPI
|
|
2
|
+
|
|
3
|
+
Este ejemplo demuestra cómo crear una API con funcionalidad OpenAPI integrada utilizando el Framework API SDK.
|
|
4
|
+
|
|
5
|
+
## Características
|
|
6
|
+
|
|
7
|
+
- API con autenticación JWT
|
|
8
|
+
- Documentación OpenAPI 3.0 generada automáticamente
|
|
9
|
+
- Interfaz Swagger UI para explorar y probar la API
|
|
10
|
+
- Rutas protegidas y públicas
|
|
11
|
+
- Controladores organizados por funcionalidad
|
|
12
|
+
- Esquemas OpenAPI definidos para modelos de datos
|
|
13
|
+
|
|
14
|
+
## Configuración
|
|
15
|
+
|
|
16
|
+
No se requieren dependencias adicionales más allá del Framework API SDK.
|
|
17
|
+
|
|
18
|
+
## Uso
|
|
19
|
+
|
|
20
|
+
1. Inicia el servidor:
|
|
21
|
+
```bash
|
|
22
|
+
node app.js
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
2. El servidor escuchará en `http://localhost:8092`
|
|
26
|
+
|
|
27
|
+
## Endpoints
|
|
28
|
+
|
|
29
|
+
- `GET /` - Página de inicio
|
|
30
|
+
- `POST /login` - Iniciar sesión y obtener token
|
|
31
|
+
- `GET /users` - Lista de usuarios (requiere token)
|
|
32
|
+
- `GET /products` - Lista de productos (requiere token)
|
|
33
|
+
- `GET /profile` - Perfil de usuario (requiere token)
|
|
34
|
+
- `GET /docs` - Documentación interactiva OpenAPI/Swagger
|
|
35
|
+
- `GET /openapi.json` - Especificación OpenAPI
|
|
36
|
+
|
|
37
|
+
## Ejemplo de uso
|
|
38
|
+
|
|
39
|
+
1. Iniciar sesión:
|
|
40
|
+
```bash
|
|
41
|
+
curl -X POST http://localhost:8092/login \
|
|
42
|
+
-H "Content-Type: application/json" \
|
|
43
|
+
-d '{"username": "admin", "password": "password"}'
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
2. Acceder a usuarios con el token:
|
|
47
|
+
```bash
|
|
48
|
+
curl -X GET http://localhost:8092/users \
|
|
49
|
+
-H "Authorization: Bearer TU_TOKEN_AQUI"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
3. Ver documentación interactiva:
|
|
53
|
+
Visita `http://localhost:8092/docs` en tu navegador
|
|
54
|
+
|
|
55
|
+
## Estructura del proyecto
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
examples/v2_openapi/
|
|
59
|
+
├── app.js # Punto de entrada de la aplicación
|
|
60
|
+
├── routes.json # Definición de rutas
|
|
61
|
+
├── controllers/ # Controladores de las rutas
|
|
62
|
+
│ ├── mainController.js
|
|
63
|
+
│ ├── authController.js
|
|
64
|
+
│ ├── userController.js
|
|
65
|
+
│ └── productController.js
|
|
66
|
+
└── README.md
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Documentación OpenAPI
|
|
70
|
+
|
|
71
|
+
El framework genera automáticamente:
|
|
72
|
+
- Una especificación OpenAPI 3.0 en `/openapi.json`
|
|
73
|
+
- Una interfaz Swagger UI interactiva en `/docs`
|
|
74
|
+
- Esquemas de datos definidos para reutilización
|
|
75
|
+
- Documentación detallada de endpoints, parámetros y respuestas
|
|
76
|
+
- Soporte para diferentes códigos de respuesta HTTP
|
|
77
|
+
- Definición de esquemas de seguridad
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
const {
|
|
2
|
+
APIServer,
|
|
3
|
+
Authenticator,
|
|
4
|
+
RouteLoader,
|
|
5
|
+
Logger,
|
|
6
|
+
OpenApiGenerator,
|
|
7
|
+
TokenManager
|
|
8
|
+
} = require('../../index.js');
|
|
9
|
+
|
|
10
|
+
async function startServer() {
|
|
11
|
+
// Crear instancia del servidor
|
|
12
|
+
const server = new APIServer({
|
|
13
|
+
port: 8092,
|
|
14
|
+
host: 'localhost'
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Crear instancia del logger
|
|
18
|
+
const logger = new Logger({ level: 'info' });
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
// Crear instancia del TokenManager con almacenamiento en memoria
|
|
22
|
+
const tokenManager = new TokenManager({
|
|
23
|
+
storage: 'memory'
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Crear instancia del autenticador
|
|
27
|
+
const authenticator = new Authenticator({ logger });
|
|
28
|
+
|
|
29
|
+
// Registrar estrategia de autenticación JWT
|
|
30
|
+
authenticator.use('jwt-openapi', async (req, options = {}) => {
|
|
31
|
+
const authHeader = req.headers.authorization;
|
|
32
|
+
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
|
|
33
|
+
|
|
34
|
+
if (!token) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Validar el token usando un secreto fijo para este ejemplo
|
|
39
|
+
const secret = 'super-secret-key-for-openapi-example';
|
|
40
|
+
const decoded = tokenManager.validateToken(token, secret);
|
|
41
|
+
|
|
42
|
+
if (decoded) {
|
|
43
|
+
req.user = decoded;
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return false;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Agregar el autenticador al servidor para que pueda ser usado por el RouteLoader
|
|
51
|
+
server.authenticator = authenticator;
|
|
52
|
+
|
|
53
|
+
// Cargar rutas desde archivo JSON
|
|
54
|
+
const routeLoader = new RouteLoader();
|
|
55
|
+
await routeLoader.loadRoutes(server, './routes.json');
|
|
56
|
+
|
|
57
|
+
// Crear instancia del generador de OpenAPI
|
|
58
|
+
const openApiGenerator = new OpenApiGenerator({
|
|
59
|
+
title: 'API de Ejemplo con OpenAPI',
|
|
60
|
+
description: 'Una API de ejemplo que demuestra la funcionalidad OpenAPI del Framework API SDK',
|
|
61
|
+
version: '1.0.0',
|
|
62
|
+
servers: [
|
|
63
|
+
{ url: 'http://localhost:8092', description: 'Servidor de desarrollo' }
|
|
64
|
+
]
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Agregar esquemas a la documentación
|
|
68
|
+
openApiGenerator.addSchema('User', {
|
|
69
|
+
type: 'object',
|
|
70
|
+
properties: {
|
|
71
|
+
id: { type: 'integer', example: 1 },
|
|
72
|
+
name: { type: 'string', example: 'John Doe' },
|
|
73
|
+
email: { type: 'string', example: 'john@example.com' },
|
|
74
|
+
role: { type: 'string', example: 'user' }
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
openApiGenerator.addSchema('Product', {
|
|
79
|
+
type: 'object',
|
|
80
|
+
properties: {
|
|
81
|
+
id: { type: 'integer', example: 1 },
|
|
82
|
+
name: { type: 'string', example: 'Laptop' },
|
|
83
|
+
price: { type: 'number', example: 999.99 },
|
|
84
|
+
category: { type: 'string', example: 'Electronics' }
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
openApiGenerator.addSchema('ApiResponse', {
|
|
89
|
+
type: 'object',
|
|
90
|
+
properties: {
|
|
91
|
+
success: { type: 'boolean', example: true },
|
|
92
|
+
message: { type: 'string', example: 'Operación exitosa' },
|
|
93
|
+
data: { type: 'object', description: 'Datos de la respuesta' }
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Agregar esquema de seguridad
|
|
98
|
+
openApiGenerator.addSecurityScheme('bearerAuth', {
|
|
99
|
+
type: 'http',
|
|
100
|
+
scheme: 'bearer',
|
|
101
|
+
bearerFormat: 'JWT',
|
|
102
|
+
description: 'Autenticación por token JWT'
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Agregar rutas a la documentación OpenAPI
|
|
106
|
+
openApiGenerator.addRoute({
|
|
107
|
+
path: '/users',
|
|
108
|
+
method: 'GET',
|
|
109
|
+
config: {
|
|
110
|
+
summary: 'Obtener todos los usuarios',
|
|
111
|
+
description: 'Devuelve una lista de todos los usuarios registrados',
|
|
112
|
+
security: [{ bearerAuth: [] }],
|
|
113
|
+
responses: {
|
|
114
|
+
'200': {
|
|
115
|
+
description: 'Lista de usuarios',
|
|
116
|
+
content: {
|
|
117
|
+
'application/json': {
|
|
118
|
+
schema: {
|
|
119
|
+
type: 'array',
|
|
120
|
+
items: {
|
|
121
|
+
$ref: '#/components/schemas/User'
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
'401': {
|
|
128
|
+
description: 'No autorizado - Token inválido o ausente'
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
openApiGenerator.addRoute({
|
|
135
|
+
path: '/products',
|
|
136
|
+
method: 'GET',
|
|
137
|
+
config: {
|
|
138
|
+
summary: 'Obtener todos los productos',
|
|
139
|
+
description: 'Devuelve una lista de todos los productos disponibles',
|
|
140
|
+
security: [{ bearerAuth: [] }],
|
|
141
|
+
responses: {
|
|
142
|
+
'200': {
|
|
143
|
+
description: 'Lista de productos',
|
|
144
|
+
content: {
|
|
145
|
+
'application/json': {
|
|
146
|
+
schema: {
|
|
147
|
+
type: 'array',
|
|
148
|
+
items: {
|
|
149
|
+
$ref: '#/components/schemas/Product'
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
'401': {
|
|
156
|
+
description: 'No autorizado - Token inválido o ausente'
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
openApiGenerator.addRoute({
|
|
163
|
+
path: '/login',
|
|
164
|
+
method: 'POST',
|
|
165
|
+
config: {
|
|
166
|
+
summary: 'Iniciar sesión',
|
|
167
|
+
description: 'Autenticación de usuario y obtención de token JWT',
|
|
168
|
+
requestBody: {
|
|
169
|
+
content: {
|
|
170
|
+
'application/json': {
|
|
171
|
+
schema: {
|
|
172
|
+
type: 'object',
|
|
173
|
+
properties: {
|
|
174
|
+
username: { type: 'string', example: 'admin' },
|
|
175
|
+
password: { type: 'string', example: 'password' }
|
|
176
|
+
},
|
|
177
|
+
required: ['username', 'password']
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
responses: {
|
|
183
|
+
'200': {
|
|
184
|
+
description: 'Inicio de sesión exitoso',
|
|
185
|
+
content: {
|
|
186
|
+
'application/json': {
|
|
187
|
+
schema: {
|
|
188
|
+
type: 'object',
|
|
189
|
+
properties: {
|
|
190
|
+
message: { type: 'string', example: 'Inicio de sesión exitoso' },
|
|
191
|
+
token: { type: 'string', example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' },
|
|
192
|
+
user: { $ref: '#/components/schemas/User' }
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
'401': {
|
|
199
|
+
description: 'Credenciales inválidas'
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Agregar ruta de documentación al servidor
|
|
206
|
+
openApiGenerator.addDocumentationRoute(server);
|
|
207
|
+
|
|
208
|
+
// Iniciar el servidor
|
|
209
|
+
server.start();
|
|
210
|
+
|
|
211
|
+
logger.info('Servidor iniciado en http://localhost:8092');
|
|
212
|
+
logger.info('Documentación OpenAPI disponible en http://localhost:8092/docs');
|
|
213
|
+
} catch (error) {
|
|
214
|
+
logger.error('Error iniciando el servidor:', error.message);
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Iniciar el servidor
|
|
220
|
+
startServer();
|
|
221
|
+
|
|
222
|
+
module.exports = { startServer };
|