jerkjs 2.0.1 → 2.0.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/README.md +4 -6
- package/docs/EXTENSION_MANUAL.md +955 -0
- package/docs/FIREWALL_MANUAL.md +416 -0
- package/docs/HOOKS_REFERENCE_IMPROVED.md +596 -0
- package/docs/MANUAL_API_SDK.md +536 -0
- package/docs/MARIADB_TOKENS_IMPLEMENTATION.md +110 -0
- package/docs/MIDDLEWARE_MANUAL.md +518 -0
- package/docs/OAUTH2_GOOGLE_MANUAL.md +405 -0
- package/docs/frontend-and-sessions.md +6 -6
- 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/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/mvc_welcome/README.md +34 -0
- package/examples/mvc_welcome/app.js +50 -0
- package/examples/mvc_welcome/controllers/welcomeController.js +41 -0
- package/examples/mvc_welcome/package.json +26 -0
- package/examples/mvc_welcome/views/home/welcome.html +82 -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/index.js +11 -3
- package/lib/core/handler.js +1 -1
- package/lib/core/hooks.js +1 -1
- package/lib/core/router.js +1 -1
- package/lib/core/server.js +1 -1
- package/lib/loader/controllerLoader.js +1 -1
- package/lib/loader/routeLoader.js +1 -1
- package/lib/middleware/auditLogger.js +1 -1
- package/lib/middleware/authenticator.js +1 -1
- package/lib/middleware/compressor.js +1 -1
- package/lib/middleware/cors.js +1 -1
- package/lib/middleware/firewall.js +1 -1
- package/lib/middleware/rateLimiter.js +1 -1
- package/lib/middleware/session.js +1 -1
- package/lib/middleware/validator.js +1 -1
- package/lib/mvc/controllerBase.js +207 -0
- package/lib/mvc/viewEngine.js +752 -0
- package/lib/utils/configParser.js +1 -1
- package/lib/utils/logger.js +1 -1
- package/lib/utils/openapiGenerator.js +1 -1
- package/lib/utils/tokenManager.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"path": "/",
|
|
4
|
+
"method": "GET",
|
|
5
|
+
"controller": "./controllers/mainController.js",
|
|
6
|
+
"handler": "home",
|
|
7
|
+
"auth": "none"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"path": "/public",
|
|
11
|
+
"method": "GET",
|
|
12
|
+
"controller": "./controllers/publicController.js",
|
|
13
|
+
"handler": "getPublicData",
|
|
14
|
+
"auth": "none"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"path": "/products",
|
|
18
|
+
"method": "GET",
|
|
19
|
+
"controller": "./controllers/productController.js",
|
|
20
|
+
"handler": "getAllProducts",
|
|
21
|
+
"auth": "none"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"path": "/products/:id",
|
|
25
|
+
"method": "GET",
|
|
26
|
+
"controller": "./controllers/productController.js",
|
|
27
|
+
"handler": "getProductById",
|
|
28
|
+
"auth": "none"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"path": "/health",
|
|
32
|
+
"method": "GET",
|
|
33
|
+
"controller": "./controllers/healthController.js",
|
|
34
|
+
"handler": "getStatus",
|
|
35
|
+
"auth": "none"
|
|
36
|
+
}
|
|
37
|
+
]
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Ejemplo API con Funcionalidad OAuth 2.0
|
|
2
|
+
|
|
3
|
+
Este ejemplo demuestra cómo implementar autenticación OAuth 2.0 usando el Framework JERK.
|
|
4
|
+
|
|
5
|
+
## Características
|
|
6
|
+
|
|
7
|
+
- Implementación de flujo OAuth 2.0 (simulado para demostración)
|
|
8
|
+
- Generación de tokens JWT tras autenticación OAuth
|
|
9
|
+
- Rutas protegidas que requieren tokens JWT
|
|
10
|
+
- Simulación del proceso de autorización y callback
|
|
11
|
+
- Controladores organizados por funcionalidad
|
|
12
|
+
|
|
13
|
+
## Configuración
|
|
14
|
+
|
|
15
|
+
Este ejemplo simula el flujo OAuth para fines de demostración. En un entorno real, necesitarías:
|
|
16
|
+
|
|
17
|
+
1. Registrar tu aplicación con un proveedor OAuth (Google, GitHub, etc.)
|
|
18
|
+
2. Obtener Client ID y Client Secret
|
|
19
|
+
3. Configurar la URL de callback en tu aplicación
|
|
20
|
+
4. Ajustar las rutas y controladores según tus necesidades
|
|
21
|
+
|
|
22
|
+
## Uso
|
|
23
|
+
|
|
24
|
+
1. Inicia el servidor:
|
|
25
|
+
```bash
|
|
26
|
+
node app.js
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
2. El servidor escuchará en `http://localhost:8093`
|
|
30
|
+
|
|
31
|
+
## Endpoints
|
|
32
|
+
|
|
33
|
+
- `GET /` - Página de inicio con instrucciones
|
|
34
|
+
- `GET /auth/google` - Iniciar flujo OAuth con Google (simulado)
|
|
35
|
+
- `GET /auth/callback` - Callback de OAuth (simulado)
|
|
36
|
+
- `GET /profile` - Perfil de usuario (requiere token JWT)
|
|
37
|
+
- `GET /protected` - Contenido protegido (requiere token JWT)
|
|
38
|
+
|
|
39
|
+
## Flujo de OAuth simulado
|
|
40
|
+
|
|
41
|
+
1. Visita `/auth/google` para iniciar el proceso OAuth
|
|
42
|
+
2. El sistema simulará la redirección a Google y el callback
|
|
43
|
+
3. Se generará un token JWT para el usuario autenticado
|
|
44
|
+
4. Usa el token para acceder a endpoints protegidos
|
|
45
|
+
|
|
46
|
+
## Estructura del proyecto
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
examples/v2_oauth/
|
|
50
|
+
├── app.js # Punto de entrada de la aplicación
|
|
51
|
+
├── routes.json # Definición de rutas
|
|
52
|
+
├── controllers/ # Controladores de las rutas
|
|
53
|
+
│ ├── mainController.js
|
|
54
|
+
│ ├── oauthController.js
|
|
55
|
+
│ ├── userController.js
|
|
56
|
+
│ └── protectedController.js
|
|
57
|
+
└── README.md
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Implementación real
|
|
61
|
+
|
|
62
|
+
Para implementar OAuth en un entorno real:
|
|
63
|
+
|
|
64
|
+
1. Registra tu aplicación con un proveedor OAuth
|
|
65
|
+
2. Configura las credenciales en el código
|
|
66
|
+
3. Implementa la lógica para intercambiar el código de autorización por tokens
|
|
67
|
+
4. Almacena de forma segura los tokens de acceso y refresh
|
|
68
|
+
5. Usa los tokens para acceder a las APIs del proveedor
|
|
69
|
+
|
|
70
|
+
El framework proporciona la estrategia `oauth2Strategy` que puedes usar con tus credenciales reales.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const {
|
|
2
|
+
APIServer,
|
|
3
|
+
Authenticator,
|
|
4
|
+
RouteLoader,
|
|
5
|
+
Logger,
|
|
6
|
+
TokenManager
|
|
7
|
+
} = require('../../index.js');
|
|
8
|
+
|
|
9
|
+
async function startServer() {
|
|
10
|
+
// Crear instancia del servidor
|
|
11
|
+
const server = new APIServer({
|
|
12
|
+
port: 8093,
|
|
13
|
+
host: 'localhost'
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// Crear instancia del logger
|
|
17
|
+
const logger = new Logger({ level: 'info' });
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
// Crear instancia del TokenManager
|
|
21
|
+
const tokenManager = new TokenManager({
|
|
22
|
+
storage: 'memory'
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Crear instancia del autenticador
|
|
26
|
+
const authenticator = new Authenticator({ logger });
|
|
27
|
+
|
|
28
|
+
// Registrar estrategia de autenticación OAuth2 REAL del framework
|
|
29
|
+
// Esta es la verdadera implementación del framework
|
|
30
|
+
authenticator.use('oauth2', authenticator.oauth2Strategy({
|
|
31
|
+
clientId: process.env.OAUTH_CLIENT_ID || 'tu-client-id-aqui',
|
|
32
|
+
clientSecret: process.env.OAUTH_CLIENT_SECRET || 'tu-client-secret-aqui',
|
|
33
|
+
callbackURL: 'http://localhost:8093/auth/callback',
|
|
34
|
+
authorizationURL: 'https://accounts.google.com/o/oauth2/v2/auth',
|
|
35
|
+
tokenURL: 'https://oauth2.googleapis.com/token'
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
// Registrar estrategia JWT para tokens generados internamente
|
|
39
|
+
authenticator.use('jwt-oauth', async (req, options = {}) => {
|
|
40
|
+
const authHeader = req.headers.authorization;
|
|
41
|
+
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
|
|
42
|
+
|
|
43
|
+
if (!token) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Validar el token usando el secreto de las variables de entorno
|
|
48
|
+
const secret = process.env.JWT_SECRET;
|
|
49
|
+
|
|
50
|
+
// Verificar que el secreto esté definido
|
|
51
|
+
if (!secret) {
|
|
52
|
+
logger.error('JWT_SECRET no está definido en las variables de entorno');
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const decoded = tokenManager.validateToken(token, secret);
|
|
57
|
+
|
|
58
|
+
if (decoded) {
|
|
59
|
+
req.user = decoded;
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return false;
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Agregar el autenticador al servidor para que pueda ser usado por el RouteLoader
|
|
67
|
+
server.authenticator = authenticator;
|
|
68
|
+
|
|
69
|
+
// Cargar rutas desde archivo JSON
|
|
70
|
+
const routeLoader = new RouteLoader();
|
|
71
|
+
await routeLoader.loadRoutes(server, './routes.json');
|
|
72
|
+
|
|
73
|
+
// Iniciar el servidor
|
|
74
|
+
server.start();
|
|
75
|
+
|
|
76
|
+
logger.info('Servidor iniciado en http://localhost:8093');
|
|
77
|
+
logger.info('Configura tus credenciales OAuth para usar la autenticación real');
|
|
78
|
+
logger.info('Variables de entorno necesarias:');
|
|
79
|
+
logger.info('- OAUTH_CLIENT_ID: Tu Client ID de Google OAuth');
|
|
80
|
+
logger.info('- OAUTH_CLIENT_SECRET: Tu Client Secret de Google OAuth');
|
|
81
|
+
} catch (error) {
|
|
82
|
+
logger.error('Error iniciando el servidor:', error.message);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Iniciar el servidor
|
|
88
|
+
startServer();
|
|
89
|
+
|
|
90
|
+
module.exports = { startServer };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const mainController = {
|
|
2
|
+
home: (req, res) => {
|
|
3
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
4
|
+
res.end(`
|
|
5
|
+
<!DOCTYPE html>
|
|
6
|
+
<html>
|
|
7
|
+
<head>
|
|
8
|
+
<title>Ejemplo OAuth con JERK Framework</title>
|
|
9
|
+
<style>
|
|
10
|
+
body { font-family: Arial, sans-serif; margin: 40px; }
|
|
11
|
+
.endpoint { margin: 10px 0; padding: 10px; background-color: #f0f0f0; border-radius: 5px; }
|
|
12
|
+
a { color: #007bff; text-decoration: none; }
|
|
13
|
+
a:hover { text-decoration: underline; }
|
|
14
|
+
</style>
|
|
15
|
+
</head>
|
|
16
|
+
<body>
|
|
17
|
+
<h1>Ejemplo OAuth con JERK Framework</h1>
|
|
18
|
+
<p>Este ejemplo demuestra la funcionalidad OAuth 2.0 del framework.</p>
|
|
19
|
+
|
|
20
|
+
<h2>Endpoints disponibles:</h2>
|
|
21
|
+
<div class="endpoint">
|
|
22
|
+
<strong><a href="/auth/google">GET /auth/google</a></strong> - Iniciar flujo OAuth con Google
|
|
23
|
+
</div>
|
|
24
|
+
<div class="endpoint">
|
|
25
|
+
<strong><a href="/profile">GET /profile</a></strong> - Perfil de usuario (requiere token JWT)
|
|
26
|
+
</div>
|
|
27
|
+
<div class="endpoint">
|
|
28
|
+
<strong><a href="/protected">GET /protected</a></strong> - Contenido protegido (requiere token JWT)
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<h2>Instrucciones:</h2>
|
|
32
|
+
<ol>
|
|
33
|
+
<li>Visita <a href="/auth/google">/auth/google</a> para iniciar el flujo OAuth</li>
|
|
34
|
+
<li>El callback OAuth manejará la autenticación y generará un token JWT</li>
|
|
35
|
+
<li>Usa el token generado para acceder a los endpoints protegidos</li>
|
|
36
|
+
</ol>
|
|
37
|
+
|
|
38
|
+
<p><em>Nota: Este es un ejemplo funcional. En un entorno real, necesitarías configurar credenciales reales de OAuth con un proveedor como Google, GitHub, etc.</em></p>
|
|
39
|
+
</body>
|
|
40
|
+
</html>
|
|
41
|
+
`);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
module.exports = mainController;
|
|
@@ -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": "jerk-oauth-example",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Ejemplo de API con funcionalidad OAuth 2.0 usando el Framework JERK",
|
|
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": "JERK 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 JERK.
|
|
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 JERK.
|
|
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
|