jwt_auth_cognito 0.1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +33 -0
- data/CLAUDE.md +135 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +21 -0
- data/PUBLISH_GUIDE.md +187 -0
- data/README.md +384 -0
- data/Rakefile +11 -0
- data/jwt_auth_cognito.gemspec +52 -0
- data/lib/generators/jwt_auth_cognito/install_generator.rb +88 -0
- data/lib/generators/jwt_auth_cognito/templates/jwt_auth_cognito.rb.erb +129 -0
- data/lib/jwt_auth_cognito/configuration.rb +83 -0
- data/lib/jwt_auth_cognito/jwks_service.rb +141 -0
- data/lib/jwt_auth_cognito/jwt_validator.rb +225 -0
- data/lib/jwt_auth_cognito/railtie.rb +13 -0
- data/lib/jwt_auth_cognito/redis_service.rb +194 -0
- data/lib/jwt_auth_cognito/token_blacklist_service.rb +56 -0
- data/lib/jwt_auth_cognito/version.rb +5 -0
- data/lib/jwt_auth_cognito.rb +41 -0
- data/lib/tasks/jwt_auth_cognito.rake +290 -0
- metadata +207 -0
data/README.md
ADDED
@@ -0,0 +1,384 @@
|
|
1
|
+
# JWT Auth Cognito
|
2
|
+
|
3
|
+
Una gema Ruby para validar tokens JWT de AWS Cognito de forma offline con funcionalidad de blacklist basada en Redis.
|
4
|
+
|
5
|
+
## Características
|
6
|
+
|
7
|
+
- **Validación JWT Offline**: Valida tokens JWT de Cognito sin llamar a las APIs de AWS
|
8
|
+
- **Soporte JWKS**: Recuperación automática y cache de claves públicas desde el endpoint JWKS de Cognito
|
9
|
+
- **Blacklist de Tokens**: Gestión de revocación y blacklist de tokens basada en Redis con soporte TLS completo
|
10
|
+
- **Configuración Flexible**: Soporte para modos de validación seguro (producción) y básico (desarrollo)
|
11
|
+
- **Gestión de Tokens de Usuario**: Rastrear e invalidar todos los tokens de un usuario específico
|
12
|
+
- **Múltiples Tipos de Token**: Soporte para access tokens e ID tokens
|
13
|
+
- **Manejo Integral de Errores**: Degradación elegante cuando Redis no está disponible
|
14
|
+
- **Soporte TLS Avanzado**: Configuración completa de TLS para Redis con certificados CA
|
15
|
+
|
16
|
+
## Instalación
|
17
|
+
|
18
|
+
Agrega esta línea al Gemfile de tu aplicación:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
gem 'jwt_auth_cognito'
|
22
|
+
```
|
23
|
+
|
24
|
+
Y luego ejecuta:
|
25
|
+
|
26
|
+
$ bundle install
|
27
|
+
|
28
|
+
O instálala directamente:
|
29
|
+
|
30
|
+
$ gem install jwt_auth_cognito
|
31
|
+
|
32
|
+
## Configuración
|
33
|
+
|
34
|
+
Configura la gema en un inicializador (ej. `config/initializers/jwt_auth_cognito.rb`):
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
JwtAuthCognito.configure do |config|
|
38
|
+
# Requerido: Configuración de AWS Cognito
|
39
|
+
config.cognito_user_pool_id = 'us-east-1_abcdef123'
|
40
|
+
config.cognito_region = 'us-east-1'
|
41
|
+
config.cognito_client_id = 'tu-client-id' # Opcional, para validación de audiencia
|
42
|
+
config.cognito_client_secret = 'tu-client-secret' # Opcional, para mayor seguridad
|
43
|
+
|
44
|
+
# Requerido: Configuración de Redis para blacklisting
|
45
|
+
config.redis_host = 'localhost'
|
46
|
+
config.redis_port = 6379
|
47
|
+
config.redis_password = 'tu-password-redis' # Opcional
|
48
|
+
config.redis_db = 0
|
49
|
+
|
50
|
+
# Configuración TLS para Redis (Producción)
|
51
|
+
config.redis_ssl = true
|
52
|
+
config.redis_ca_cert_path = '/ruta/a/certificados'
|
53
|
+
config.redis_ca_cert_name = 'redis-ca.crt'
|
54
|
+
config.redis_tls_min_version = 'TLSv1.2'
|
55
|
+
config.redis_tls_max_version = 'TLSv1.3'
|
56
|
+
config.redis_verify_mode = 'peer'
|
57
|
+
|
58
|
+
# Opcional: Configuraciones de cache y validación
|
59
|
+
config.jwks_cache_ttl = 3600 # 1 hora
|
60
|
+
config.validation_mode = :secure # :secure o :basic
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
### Variables de Entorno
|
65
|
+
|
66
|
+
La gema soporta configuración mediante variables de entorno:
|
67
|
+
|
68
|
+
```bash
|
69
|
+
# Configuración de Cognito
|
70
|
+
COGNITO_USER_POOL_ID=us-east-1_abcdef123
|
71
|
+
COGNITO_REGION=us-east-1
|
72
|
+
COGNITO_CLIENT_ID=tu-client-id
|
73
|
+
COGNITO_CLIENT_SECRET=tu-client-secret
|
74
|
+
|
75
|
+
# Configuración de Redis
|
76
|
+
REDIS_HOST=localhost
|
77
|
+
REDIS_PORT=6379
|
78
|
+
REDIS_PASSWORD=tu-password
|
79
|
+
REDIS_DB=0
|
80
|
+
REDIS_TLS=true
|
81
|
+
|
82
|
+
# Configuración TLS de Redis
|
83
|
+
REDIS_CA_CERT_PATH=/ruta/a/certificados
|
84
|
+
REDIS_CA_CERT_NAME=redis-ca.crt
|
85
|
+
REDIS_TLS_MIN_VERSION=TLSv1.2
|
86
|
+
REDIS_TLS_MAX_VERSION=TLSv1.3
|
87
|
+
REDIS_VERIFY_MODE=peer
|
88
|
+
|
89
|
+
# Configuración de cache
|
90
|
+
JWKS_CACHE_TTL=3600
|
91
|
+
```
|
92
|
+
|
93
|
+
## Uso
|
94
|
+
|
95
|
+
### Validación Básica de Tokens
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
validator = JwtAuthCognito::JwtValidator.new
|
99
|
+
|
100
|
+
# Validar cualquier token JWT
|
101
|
+
result = validator.validate_token(jwt_token)
|
102
|
+
|
103
|
+
if result[:valid]
|
104
|
+
puts "¡Token válido!"
|
105
|
+
puts "ID de Usuario: #{result[:sub]}"
|
106
|
+
puts "Nombre de Usuario: #{result[:username]}"
|
107
|
+
puts "Tipo de Token: #{result[:token_use]}"
|
108
|
+
else
|
109
|
+
puts "Token inválido: #{result[:error]}"
|
110
|
+
end
|
111
|
+
```
|
112
|
+
|
113
|
+
### Validar Tipos Específicos de Token
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
# Validar específicamente access token
|
117
|
+
result = validator.validate_access_token(jwt_token)
|
118
|
+
|
119
|
+
# Validar específicamente ID token
|
120
|
+
result = validator.validate_id_token(jwt_token)
|
121
|
+
```
|
122
|
+
|
123
|
+
### Opciones Avanzadas de Validación
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
# Validar con requisitos personalizados
|
127
|
+
result = validator.validate_token(jwt_token, {
|
128
|
+
user_id: 'id-usuario-esperado',
|
129
|
+
client_id: 'client-id-esperado',
|
130
|
+
token_use: 'access',
|
131
|
+
required_scopes: ['read', 'write']
|
132
|
+
})
|
133
|
+
```
|
134
|
+
|
135
|
+
### Blacklist de Tokens
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
# Revocar un token específico
|
139
|
+
validator.revoke_token(jwt_token, user_id: 'usuario-123')
|
140
|
+
|
141
|
+
# Revocar todos los tokens de un usuario
|
142
|
+
validator.revoke_user_tokens('usuario-123')
|
143
|
+
|
144
|
+
# Verificar si un token está en blacklist
|
145
|
+
blacklisted = validator.validate_token(jwt_token)
|
146
|
+
# Retornará { valid: false, error: "Token has been revoked" }
|
147
|
+
```
|
148
|
+
|
149
|
+
### Validación en Lote
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
tokens = [token1, token2, token3]
|
153
|
+
results = validator.validate_multiple_tokens(tokens)
|
154
|
+
|
155
|
+
results.each_with_index do |result, index|
|
156
|
+
puts "Token #{index + 1}: #{result[:valid] ? 'Válido' : result[:error]}"
|
157
|
+
end
|
158
|
+
```
|
159
|
+
|
160
|
+
### Métodos Utilitarios
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
# Extraer token del header Authorization
|
164
|
+
token = validator.extract_token_from_header(request.headers['Authorization'])
|
165
|
+
|
166
|
+
# Obtener información del token
|
167
|
+
info = validator.get_token_info(token)
|
168
|
+
puts "Usuario: #{info[:username]}"
|
169
|
+
puts "Expira en: #{info[:expires_at]}"
|
170
|
+
puts "Tiene client secret: #{info[:has_client_secret]}"
|
171
|
+
|
172
|
+
# Verificar expiración
|
173
|
+
expired = validator.is_token_expired?(token)
|
174
|
+
|
175
|
+
# Obtener tiempo hasta expiración
|
176
|
+
seconds_to_expiry = validator.get_time_to_expiry(token)
|
177
|
+
|
178
|
+
# Calcular secret hash (para operaciones de Cognito)
|
179
|
+
# Útil si necesitas hacer operaciones directas con Cognito SDK
|
180
|
+
secret_hash = validator.calculate_secret_hash('username_or_email')
|
181
|
+
```
|
182
|
+
|
183
|
+
### Uso Directo de Servicios
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
# Usar servicios directamente para mayor control
|
187
|
+
blacklist_service = JwtAuthCognito::TokenBlacklistService.new
|
188
|
+
jwks_service = JwtAuthCognito::JwksService.new
|
189
|
+
|
190
|
+
# Agregar token a blacklist con TTL personalizado
|
191
|
+
blacklist_service.add_to_blacklist(token, user_id: 'usuario-123')
|
192
|
+
|
193
|
+
# Validar con JWKS
|
194
|
+
result = jwks_service.validate_token_with_jwks(token)
|
195
|
+
```
|
196
|
+
|
197
|
+
## Modos de Validación
|
198
|
+
|
199
|
+
### Modo Seguro (Producción)
|
200
|
+
- Validación completa de firma JWKS
|
201
|
+
- Recuperación automática y cache de claves públicas
|
202
|
+
- Validación completa de claims
|
203
|
+
- Recomendado para entornos de producción
|
204
|
+
|
205
|
+
### Modo Básico (Desarrollo)
|
206
|
+
- Solo validación de estructura del token
|
207
|
+
- Sin verificación de firma
|
208
|
+
- Validación más rápida para desarrollo/testing
|
209
|
+
- **NO recomendado para producción**
|
210
|
+
|
211
|
+
```ruby
|
212
|
+
# Configurar modo de validación
|
213
|
+
JwtAuthCognito.configure do |config|
|
214
|
+
config.validation_mode = :basic # Para desarrollo
|
215
|
+
config.validation_mode = :secure # Para producción
|
216
|
+
end
|
217
|
+
```
|
218
|
+
|
219
|
+
## Manejo de Errores
|
220
|
+
|
221
|
+
La gema proporciona tipos de error específicos:
|
222
|
+
|
223
|
+
- `JwtAuthCognito::ValidationError`: Fallas de validación de tokens
|
224
|
+
- `JwtAuthCognito::TokenExpiredError`: Token expirado
|
225
|
+
- `JwtAuthCognito::TokenRevokedError`: Token revocado
|
226
|
+
- `JwtAuthCognito::BlacklistError`: Fallas en operaciones de Redis/blacklist
|
227
|
+
- `JwtAuthCognito::ConfigurationError`: Problemas de configuración
|
228
|
+
- `JwtAuthCognito::JWKSError`: Errores de JWKS
|
229
|
+
- `JwtAuthCognito::RedisConnectionError`: Problemas de conexión a Redis
|
230
|
+
|
231
|
+
```ruby
|
232
|
+
begin
|
233
|
+
result = validator.validate_token(token)
|
234
|
+
rescue JwtAuthCognito::TokenExpiredError => e
|
235
|
+
puts "Token expirado: #{e.message}"
|
236
|
+
rescue JwtAuthCognito::BlacklistError => e
|
237
|
+
puts "Error de blacklist: #{e.message}"
|
238
|
+
end
|
239
|
+
```
|
240
|
+
|
241
|
+
## Integración con Rails
|
242
|
+
|
243
|
+
### Ejemplo de Middleware
|
244
|
+
|
245
|
+
```ruby
|
246
|
+
class JwtAuthenticationMiddleware
|
247
|
+
def initialize(app)
|
248
|
+
@app = app
|
249
|
+
@validator = JwtAuthCognito::JwtValidator.new
|
250
|
+
end
|
251
|
+
|
252
|
+
def call(env)
|
253
|
+
request = Rack::Request.new(env)
|
254
|
+
token = @validator.extract_token_from_header(request.get_header('HTTP_AUTHORIZATION'))
|
255
|
+
|
256
|
+
if token
|
257
|
+
result = @validator.validate_access_token(token)
|
258
|
+
if result[:valid]
|
259
|
+
env['current_user_id'] = result[:sub]
|
260
|
+
env['current_username'] = result[:username]
|
261
|
+
else
|
262
|
+
return unauthorized_response(result[:error])
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
@app.call(env)
|
267
|
+
end
|
268
|
+
|
269
|
+
private
|
270
|
+
|
271
|
+
def unauthorized_response(error)
|
272
|
+
[401, { 'Content-Type' => 'application/json' },
|
273
|
+
[{ error: 'No Autorizado', message: error }.to_json]]
|
274
|
+
end
|
275
|
+
end
|
276
|
+
```
|
277
|
+
|
278
|
+
### Helper para Controladores
|
279
|
+
|
280
|
+
```ruby
|
281
|
+
module JwtAuthHelper
|
282
|
+
extend ActiveSupport::Concern
|
283
|
+
|
284
|
+
included do
|
285
|
+
before_action :authenticate_jwt_token!
|
286
|
+
end
|
287
|
+
|
288
|
+
private
|
289
|
+
|
290
|
+
def authenticate_jwt_token!
|
291
|
+
token = jwt_validator.extract_token_from_header(request.headers['Authorization'])
|
292
|
+
return render_unauthorized('Token faltante') unless token
|
293
|
+
|
294
|
+
result = jwt_validator.validate_access_token(token)
|
295
|
+
|
296
|
+
if result[:valid]
|
297
|
+
@current_user_id = result[:sub]
|
298
|
+
@current_username = result[:username]
|
299
|
+
else
|
300
|
+
render_unauthorized(result[:error])
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
def jwt_validator
|
305
|
+
@jwt_validator ||= JwtAuthCognito::JwtValidator.new
|
306
|
+
end
|
307
|
+
|
308
|
+
def render_unauthorized(message)
|
309
|
+
render json: { error: 'No Autorizado', message: message }, status: :unauthorized
|
310
|
+
end
|
311
|
+
end
|
312
|
+
```
|
313
|
+
|
314
|
+
### Integración con Llegando-Neo
|
315
|
+
|
316
|
+
```ruby
|
317
|
+
# En config/initializers/jwt_auth_cognito.rb
|
318
|
+
JwtAuthCognito.configure do |config|
|
319
|
+
# Reutilizar configuración existente de Redis
|
320
|
+
redis_config = Rails.application.config_for(:redis)
|
321
|
+
config.redis_host = redis_config[:host]
|
322
|
+
config.redis_port = redis_config[:port]
|
323
|
+
config.redis_password = redis_config[:password]
|
324
|
+
config.redis_ssl = redis_config[:ssl]
|
325
|
+
|
326
|
+
# Configuración de Cognito desde secrets
|
327
|
+
config.cognito_user_pool_id = Rails.application.secrets.cognito_user_pool_id
|
328
|
+
config.cognito_region = Rails.application.secrets.cognito_region
|
329
|
+
config.cognito_client_id = Rails.application.secrets.cognito_client_id
|
330
|
+
|
331
|
+
config.validation_mode = Rails.env.production? ? :secure : :basic
|
332
|
+
end
|
333
|
+
```
|
334
|
+
|
335
|
+
## Compatibilidad
|
336
|
+
|
337
|
+
- **Ruby**: >= 2.7.0 (Compatible con llegando-neo Ruby 2.7.5)
|
338
|
+
- **Rails**: >= 5.0 (Compatible con llegando-neo Rails 5.2.6)
|
339
|
+
- **Redis**: >= 4.2.5 (Compatible con llegando-neo redis >= 4.2.5)
|
340
|
+
|
341
|
+
## Desarrollo
|
342
|
+
|
343
|
+
Después de clonar el repositorio, ejecuta `bin/setup` para instalar dependencias. Luego, ejecuta `rake spec` para correr las pruebas. También puedes ejecutar `bin/console` para una consola interactiva que te permitirá experimentar.
|
344
|
+
|
345
|
+
Para instalar esta gema localmente, ejecuta `bundle exec rake install`. Para liberar una nueva versión, actualiza el número de versión en `version.rb`, y luego ejecuta `bundle exec rake release`.
|
346
|
+
|
347
|
+
## Contribución
|
348
|
+
|
349
|
+
Los reportes de bugs y pull requests son bienvenidos en GitHub.
|
350
|
+
|
351
|
+
## Licencia
|
352
|
+
|
353
|
+
La gema está disponible como código abierto bajo los términos de la [Licencia MIT](https://opensource.org/licenses/MIT).
|
354
|
+
|
355
|
+
## Generación Automática de Configuración
|
356
|
+
|
357
|
+
### Usando el generador de Rails
|
358
|
+
|
359
|
+
```bash
|
360
|
+
# Generar configuración automáticamente
|
361
|
+
rails generate jwt_auth_cognito:install
|
362
|
+
```
|
363
|
+
|
364
|
+
### Usando tareas Rake
|
365
|
+
|
366
|
+
```bash
|
367
|
+
# Instalar configuración
|
368
|
+
rake jwt_auth_cognito:install
|
369
|
+
|
370
|
+
# Ver configuración actual
|
371
|
+
rake jwt_auth_cognito:config
|
372
|
+
|
373
|
+
# Probar conexiones
|
374
|
+
rake jwt_auth_cognito:test_cognito
|
375
|
+
rake jwt_auth_cognito:test_redis
|
376
|
+
|
377
|
+
# Limpiar blacklist
|
378
|
+
rake jwt_auth_cognito:clear_blacklist
|
379
|
+
```
|
380
|
+
|
381
|
+
Esto generará automáticamente:
|
382
|
+
- `config/initializers/jwt_auth_cognito.rb` - Archivo de configuración
|
383
|
+
- `.env.example` - Variables de entorno de ejemplo
|
384
|
+
- Configuración optimizada para tu proyecto Rails
|
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/jwt_auth_cognito/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "jwt_auth_cognito"
|
7
|
+
spec.version = JwtAuthCognito::VERSION
|
8
|
+
spec.authors = ["The Optimal"]
|
9
|
+
spec.email = ["contact@theoptimal.com"]
|
10
|
+
|
11
|
+
spec.summary = "Validación offline de JWT con AWS Cognito y blacklist Redis"
|
12
|
+
spec.description = "Gema Ruby para validar tokens JWT de AWS Cognito de forma offline con funcionalidad de blacklist basada en Redis y soporte TLS completo"
|
13
|
+
spec.homepage = "https://github.com/theoptimal/jwt-auth-cognito"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.required_ruby_version = ">= 2.7.0"
|
16
|
+
|
17
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
18
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
19
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
20
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
21
|
+
spec.metadata["documentation_uri"] = "#{spec.homepage}/blob/main/README.md"
|
22
|
+
spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/issues"
|
23
|
+
|
24
|
+
# Specify which files should be added to the gem when it is released.
|
25
|
+
spec.files = Dir.chdir(__dir__) do
|
26
|
+
files = Dir.glob("lib/**/*.{rb,rake,erb}") +
|
27
|
+
Dir.glob("*.{md,txt,yml}") +
|
28
|
+
["Gemfile", "Rakefile", "jwt_auth_cognito.gemspec"]
|
29
|
+
|
30
|
+
# Add optional files if they exist
|
31
|
+
files << ".rspec" if File.exist?(".rspec")
|
32
|
+
files << ".rubocop.yml" if File.exist?(".rubocop.yml")
|
33
|
+
|
34
|
+
files
|
35
|
+
end
|
36
|
+
spec.bindir = "exe"
|
37
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
38
|
+
spec.require_paths = ["lib"]
|
39
|
+
|
40
|
+
# Dependencies - compatible with llegando-neo (Ruby 2.7.5, Rails 5.2.6)
|
41
|
+
spec.add_dependency "jwt", "~> 2.0"
|
42
|
+
spec.add_dependency "redis", ">= 4.2.5", "< 6.0" # Compatible with llegando-neo redis version
|
43
|
+
spec.add_dependency "openssl", ">= 2.1.0" # For TLS support
|
44
|
+
spec.add_dependency "json", "~> 2.0"
|
45
|
+
|
46
|
+
# Development dependencies
|
47
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
48
|
+
spec.add_development_dependency "bundler", ">= 1.17", "< 3.0"
|
49
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
50
|
+
spec.add_development_dependency "rubocop", "~> 1.0"
|
51
|
+
spec.add_development_dependency "webmock", "~> 3.0"
|
52
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails/generators'
|
4
|
+
|
5
|
+
module JwtAuthCognito
|
6
|
+
module Generators
|
7
|
+
class InstallGenerator < Rails::Generators::Base
|
8
|
+
desc "Genera el archivo de configuración inicial para jwt_auth_cognito"
|
9
|
+
|
10
|
+
source_root File.expand_path('templates', __dir__)
|
11
|
+
|
12
|
+
def create_initializer
|
13
|
+
say "Creando archivo de configuración jwt_auth_cognito..."
|
14
|
+
template 'jwt_auth_cognito.rb.erb', 'config/initializers/jwt_auth_cognito.rb'
|
15
|
+
say "✓ Archivo de configuración creado en config/initializers/jwt_auth_cognito.rb", :green
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_env_example
|
19
|
+
if File.exist?('.env.example')
|
20
|
+
append_to_file '.env.example', env_variables
|
21
|
+
say "✓ Variables de entorno agregadas a .env.example", :green
|
22
|
+
else
|
23
|
+
create_file '.env.example', env_variables
|
24
|
+
say "✓ Archivo .env.example creado con variables de entorno", :green
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def show_next_steps
|
29
|
+
say "\n" + "="*60, :blue
|
30
|
+
say "🎉 ¡Configuración de jwt_auth_cognito completada!", :green
|
31
|
+
say "="*60, :blue
|
32
|
+
say "\n📋 PRÓXIMOS PASOS:", :yellow
|
33
|
+
say "\n1. Configura las variables de entorno:", :cyan
|
34
|
+
say " - Copia .env.example a .env (si usas dotenv)"
|
35
|
+
say " - Configura las variables de AWS Cognito y Redis"
|
36
|
+
|
37
|
+
say "\n2. Variables de entorno requeridas:", :cyan
|
38
|
+
say " COGNITO_USER_POOL_ID=us-east-1_abcdef123"
|
39
|
+
say " COGNITO_REGION=us-east-1"
|
40
|
+
say " COGNITO_CLIENT_ID=tu-client-id"
|
41
|
+
|
42
|
+
say "\n3. Variables de entorno opcionales:", :cyan
|
43
|
+
say " COGNITO_CLIENT_SECRET=tu-client-secret (para mayor seguridad)"
|
44
|
+
say " REDIS_TLS=true (para Redis con TLS)"
|
45
|
+
say " REDIS_CA_CERT_PATH=/ruta/a/certificados"
|
46
|
+
|
47
|
+
say "\n4. Reinicia tu aplicación Rails", :cyan
|
48
|
+
|
49
|
+
say "\n📖 Documentación completa:", :yellow
|
50
|
+
say " Revisa el README.md de la gema para ejemplos de uso"
|
51
|
+
say "\n" + "="*60, :blue
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def env_variables
|
57
|
+
<<~ENV_VARS
|
58
|
+
|
59
|
+
# JWT Auth Cognito - Configuración
|
60
|
+
COGNITO_USER_POOL_ID=us-east-1_tu_user_pool_id
|
61
|
+
COGNITO_REGION=us-east-1
|
62
|
+
COGNITO_CLIENT_ID=tu_cognito_client_id
|
63
|
+
COGNITO_CLIENT_SECRET=tu_cognito_client_secret
|
64
|
+
|
65
|
+
# Redis para blacklist de tokens
|
66
|
+
REDIS_HOST=localhost
|
67
|
+
REDIS_PORT=6379
|
68
|
+
REDIS_PASSWORD=tu_redis_password
|
69
|
+
REDIS_DB=0
|
70
|
+
REDIS_TLS=false
|
71
|
+
|
72
|
+
# Redis TLS (para producción)
|
73
|
+
REDIS_CA_CERT_PATH=/ruta/a/certificados
|
74
|
+
REDIS_CA_CERT_NAME=redis-ca.crt
|
75
|
+
REDIS_TLS_MIN_VERSION=TLSv1.2
|
76
|
+
REDIS_TLS_MAX_VERSION=TLSv1.3
|
77
|
+
|
78
|
+
# Configuración adicional
|
79
|
+
JWKS_CACHE_TTL=3600
|
80
|
+
ENV_VARS
|
81
|
+
end
|
82
|
+
|
83
|
+
def rails_env_production?
|
84
|
+
defined?(Rails) && Rails.env.production?
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Configuración de JWT Auth Cognito
|
4
|
+
# Generado automáticamente - puedes modificar según tus necesidades
|
5
|
+
|
6
|
+
JwtAuthCognito.configure do |config|
|
7
|
+
# =============================================================================
|
8
|
+
# CONFIGURACIÓN DE AWS COGNITO
|
9
|
+
# =============================================================================
|
10
|
+
|
11
|
+
# User Pool ID de tu Cognito (requerido)
|
12
|
+
config.cognito_user_pool_id = ENV['COGNITO_USER_POOL_ID']
|
13
|
+
|
14
|
+
# Región de AWS donde está tu Cognito (requerido)
|
15
|
+
config.cognito_region = ENV['COGNITO_REGION'] || ENV['AWS_REGION'] || 'us-east-1'
|
16
|
+
|
17
|
+
# Client ID de tu aplicación en Cognito (requerido para validación de audiencia)
|
18
|
+
config.cognito_client_id = ENV['COGNITO_CLIENT_ID']
|
19
|
+
|
20
|
+
# Client Secret de tu aplicación en Cognito (opcional, para mayor seguridad)
|
21
|
+
# Si tu User Pool App Client está configurado con client secret, esto es requerido
|
22
|
+
config.cognito_client_secret = ENV['COGNITO_CLIENT_SECRET']
|
23
|
+
|
24
|
+
# =============================================================================
|
25
|
+
# CONFIGURACIÓN DE REDIS (para blacklist de tokens)
|
26
|
+
# =============================================================================
|
27
|
+
|
28
|
+
<% if defined?(Rails) && File.exist?(Rails.root.join('config', 'redis.yml')) -%>
|
29
|
+
# Integración con configuración existente de Redis
|
30
|
+
begin
|
31
|
+
redis_config = Rails.application.config_for(:redis)
|
32
|
+
config.redis_host = redis_config[:host] || ENV['REDIS_HOST'] || 'localhost'
|
33
|
+
config.redis_port = redis_config[:port] || ENV['REDIS_PORT']&.to_i || 6379
|
34
|
+
config.redis_password = redis_config[:password] || ENV['REDIS_PASSWORD']
|
35
|
+
config.redis_db = redis_config[:db] || ENV['REDIS_DB']&.to_i || 0
|
36
|
+
config.redis_ssl = redis_config[:ssl] || ENV['REDIS_TLS'] == 'true'
|
37
|
+
rescue StandardError
|
38
|
+
# Fallback si config/redis.yml no existe o tiene problemas
|
39
|
+
<% end -%>
|
40
|
+
config.redis_host = ENV['REDIS_HOST'] || 'localhost'
|
41
|
+
config.redis_port = ENV['REDIS_PORT']&.to_i || 6379
|
42
|
+
config.redis_password = ENV['REDIS_PASSWORD']
|
43
|
+
config.redis_db = ENV['REDIS_DB']&.to_i || 0
|
44
|
+
config.redis_ssl = ENV['REDIS_TLS'] == 'true'
|
45
|
+
<% if defined?(Rails) && File.exist?(Rails.root.join('config', 'redis.yml')) -%>
|
46
|
+
end
|
47
|
+
<% end -%>
|
48
|
+
|
49
|
+
# Configuración de timeouts para Redis
|
50
|
+
config.redis_timeout = ENV['REDIS_TIMEOUT']&.to_i || 5
|
51
|
+
config.redis_connect_timeout = ENV['REDIS_CONNECT_TIMEOUT']&.to_i || 10
|
52
|
+
config.redis_read_timeout = ENV['REDIS_READ_TIMEOUT']&.to_i || 10
|
53
|
+
|
54
|
+
# =============================================================================
|
55
|
+
# CONFIGURACIÓN TLS PARA REDIS (PRODUCCIÓN)
|
56
|
+
# =============================================================================
|
57
|
+
|
58
|
+
# Configuración de certificados CA para Redis TLS
|
59
|
+
config.redis_ca_cert_path = ENV['REDIS_CA_CERT_PATH']
|
60
|
+
config.redis_ca_cert_name = ENV['REDIS_CA_CERT_NAME']
|
61
|
+
|
62
|
+
# Control de versiones TLS
|
63
|
+
config.redis_tls_min_version = ENV['REDIS_TLS_MIN_VERSION'] || 'TLSv1.2'
|
64
|
+
config.redis_tls_max_version = ENV['REDIS_TLS_MAX_VERSION'] || 'TLSv1.3'
|
65
|
+
config.redis_verify_mode = ENV['REDIS_VERIFY_MODE'] || 'peer'
|
66
|
+
|
67
|
+
# =============================================================================
|
68
|
+
# CONFIGURACIÓN DE VALIDACIÓN
|
69
|
+
# =============================================================================
|
70
|
+
|
71
|
+
# Modo de validación: :secure (producción) o :basic (desarrollo)
|
72
|
+
# - :secure: Validación completa con JWKS y verificación de firma
|
73
|
+
# - :basic: Validación básica sin verificación de firma (solo desarrollo)
|
74
|
+
<% if defined?(Rails) -%>
|
75
|
+
config.validation_mode = Rails.env.production? ? :secure : :basic
|
76
|
+
<% else -%>
|
77
|
+
config.validation_mode = ENV['RAILS_ENV'] == 'production' ? :secure : :basic
|
78
|
+
<% end -%>
|
79
|
+
|
80
|
+
# Tiempo de cache para claves JWKS (en segundos)
|
81
|
+
config.jwks_cache_ttl = ENV['JWKS_CACHE_TTL']&.to_i || 3600 # 1 hora
|
82
|
+
end
|
83
|
+
|
84
|
+
# =============================================================================
|
85
|
+
# CONFIGURACIÓN ADICIONAL (OPCIONAL)
|
86
|
+
# =============================================================================
|
87
|
+
|
88
|
+
# Ejemplo de middleware para proteger rutas API
|
89
|
+
# Descomenta y modifica según tus necesidades:
|
90
|
+
#
|
91
|
+
# class JwtCognitoMiddleware
|
92
|
+
# def initialize(app)
|
93
|
+
# @app = app
|
94
|
+
# @validator = JwtAuthCognito::JwtValidator.new
|
95
|
+
# end
|
96
|
+
#
|
97
|
+
# def call(env)
|
98
|
+
# request = Rack::Request.new(env)
|
99
|
+
#
|
100
|
+
# # Solo validar en rutas API que requieren autenticación
|
101
|
+
# if request.path.start_with?('/api/secure/')
|
102
|
+
# token = @validator.extract_token_from_header(request.get_header('HTTP_AUTHORIZATION'))
|
103
|
+
#
|
104
|
+
# if token
|
105
|
+
# result = @validator.validate_access_token(token)
|
106
|
+
# if result[:valid]
|
107
|
+
# env['current_user_id'] = result[:sub]
|
108
|
+
# env['current_username'] = result[:username]
|
109
|
+
# else
|
110
|
+
# return unauthorized_response(result[:error])
|
111
|
+
# end
|
112
|
+
# else
|
113
|
+
# return unauthorized_response('Token de autorización requerido')
|
114
|
+
# end
|
115
|
+
# end
|
116
|
+
#
|
117
|
+
# @app.call(env)
|
118
|
+
# end
|
119
|
+
#
|
120
|
+
# private
|
121
|
+
#
|
122
|
+
# def unauthorized_response(error)
|
123
|
+
# body = { error: 'No autorizado', message: error }.to_json
|
124
|
+
# [401, { 'Content-Type' => 'application/json' }, [body]]
|
125
|
+
# end
|
126
|
+
# end
|
127
|
+
#
|
128
|
+
# # Agregar el middleware a tu aplicación Rails:
|
129
|
+
# Rails.application.config.middleware.use JwtCognitoMiddleware
|