jwt_auth_cognito 0.1.1 → 1.0.0.pre.beta.1
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 +4 -4
- data/.rubocop.yml +78 -0
- data/BITBUCKET-DEPLOYMENT.md +290 -0
- data/CHANGELOG.md +65 -0
- data/CLAUDE.md +189 -9
- data/Gemfile +5 -5
- data/README.md +147 -1
- data/Rakefile +108 -5
- data/VERSIONING.md +244 -0
- data/bitbucket-pipelines.yml +273 -0
- data/jwt_auth_cognito.gemspec +42 -39
- data/lib/generators/jwt_auth_cognito/install_generator.rb +25 -25
- data/lib/jwt_auth_cognito/api_key_validator.rb +79 -0
- data/lib/jwt_auth_cognito/configuration.rb +38 -21
- data/lib/jwt_auth_cognito/error_utils.rb +110 -0
- data/lib/jwt_auth_cognito/jwks_service.rb +46 -50
- data/lib/jwt_auth_cognito/jwt_validator.rb +319 -92
- data/lib/jwt_auth_cognito/railtie.rb +3 -3
- data/lib/jwt_auth_cognito/redis_service.rb +90 -51
- data/lib/jwt_auth_cognito/ssm_service.rb +109 -0
- data/lib/jwt_auth_cognito/token_blacklist_service.rb +10 -12
- data/lib/jwt_auth_cognito/user_data_service.rb +332 -0
- data/lib/jwt_auth_cognito/version.rb +2 -2
- data/lib/jwt_auth_cognito.rb +42 -10
- data/lib/tasks/jwt_auth_cognito.rake +69 -70
- metadata +63 -26
@@ -0,0 +1,332 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module JwtAuthCognito
|
6
|
+
class UserDataService
|
7
|
+
DEFAULT_CACHE_TTL = 300 # 5 minutes
|
8
|
+
|
9
|
+
def initialize(redis_service = nil, config = {})
|
10
|
+
@redis_service = redis_service || RedisService.new
|
11
|
+
@config = {
|
12
|
+
enable_user_data_retrieval: true,
|
13
|
+
include_applications: true,
|
14
|
+
include_organizations: true,
|
15
|
+
include_roles: true,
|
16
|
+
include_effective_permissions: false,
|
17
|
+
cache_timeout: DEFAULT_CACHE_TTL
|
18
|
+
}.merge(config)
|
19
|
+
|
20
|
+
@cache = {}
|
21
|
+
@cache_timestamps = {}
|
22
|
+
@stats = {
|
23
|
+
service: 'UserDataService',
|
24
|
+
connection_status: 'not-initialized',
|
25
|
+
initialized: false,
|
26
|
+
cache_hits: 0,
|
27
|
+
cache_misses: 0
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize!
|
32
|
+
@redis_service.initialize! unless @redis_service.connected?
|
33
|
+
@stats[:initialized] = true
|
34
|
+
@stats[:connection_status] = 'connected'
|
35
|
+
puts '✅ UserDataService initialized successfully'
|
36
|
+
rescue StandardError => e
|
37
|
+
@stats[:initialized] = false
|
38
|
+
@stats[:connection_status] = 'error'
|
39
|
+
@stats[:error] = e.message
|
40
|
+
puts "❌ UserDataService initialization failed: #{e.message}"
|
41
|
+
raise e
|
42
|
+
end
|
43
|
+
|
44
|
+
def get_user_permissions(user_id)
|
45
|
+
return nil unless @config[:enable_user_data_retrieval]
|
46
|
+
|
47
|
+
cache_key = "user_permissions:#{user_id}"
|
48
|
+
|
49
|
+
# Check cache first
|
50
|
+
cached_data = get_from_cache(cache_key)
|
51
|
+
if cached_data
|
52
|
+
@stats[:cache_hits] += 1
|
53
|
+
return cached_data
|
54
|
+
end
|
55
|
+
|
56
|
+
@stats[:cache_misses] += 1
|
57
|
+
|
58
|
+
begin
|
59
|
+
redis_key = "user:permissions:#{user_id}"
|
60
|
+
data = @redis_service.get(redis_key)
|
61
|
+
|
62
|
+
return nil unless data
|
63
|
+
|
64
|
+
permissions = JSON.parse(data)
|
65
|
+
set_in_cache(cache_key, permissions)
|
66
|
+
|
67
|
+
permissions
|
68
|
+
rescue StandardError => e
|
69
|
+
puts "Error fetching user permissions for #{user_id}: #{e.message}"
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def get_application(app_id)
|
75
|
+
return nil unless @config[:include_applications]
|
76
|
+
|
77
|
+
cache_key = "app:#{app_id}"
|
78
|
+
|
79
|
+
# Check cache first
|
80
|
+
cached_data = get_from_cache(cache_key)
|
81
|
+
if cached_data
|
82
|
+
@stats[:cache_hits] += 1
|
83
|
+
return cached_data
|
84
|
+
end
|
85
|
+
|
86
|
+
@stats[:cache_misses] += 1
|
87
|
+
|
88
|
+
begin
|
89
|
+
redis_key = "app:#{app_id}"
|
90
|
+
data = @redis_service.get(redis_key)
|
91
|
+
|
92
|
+
return nil unless data
|
93
|
+
|
94
|
+
application = JSON.parse(data)
|
95
|
+
set_in_cache(cache_key, application)
|
96
|
+
|
97
|
+
application
|
98
|
+
rescue StandardError => e
|
99
|
+
puts "Error fetching application #{app_id}: #{e.message}"
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def get_organization(app_id, organization_id)
|
105
|
+
return nil unless @config[:include_organizations]
|
106
|
+
|
107
|
+
cache_key = "org:#{app_id}:#{organization_id}"
|
108
|
+
|
109
|
+
# Check cache first
|
110
|
+
cached_data = get_from_cache(cache_key)
|
111
|
+
if cached_data
|
112
|
+
@stats[:cache_hits] += 1
|
113
|
+
return cached_data
|
114
|
+
end
|
115
|
+
|
116
|
+
@stats[:cache_misses] += 1
|
117
|
+
|
118
|
+
begin
|
119
|
+
redis_key = "org:#{app_id}:#{organization_id}"
|
120
|
+
data = @redis_service.get(redis_key)
|
121
|
+
|
122
|
+
return nil unless data
|
123
|
+
|
124
|
+
organization = JSON.parse(data)
|
125
|
+
set_in_cache(cache_key, organization)
|
126
|
+
|
127
|
+
organization
|
128
|
+
rescue StandardError => e
|
129
|
+
puts "Error fetching organization #{app_id}:#{organization_id}: #{e.message}"
|
130
|
+
nil
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def get_app_roles(app_id, organization_id)
|
135
|
+
return nil unless @config[:include_roles]
|
136
|
+
|
137
|
+
cache_key = "app_roles:#{app_id}:#{organization_id}"
|
138
|
+
|
139
|
+
# Check cache first
|
140
|
+
cached_data = get_from_cache(cache_key)
|
141
|
+
if cached_data
|
142
|
+
@stats[:cache_hits] += 1
|
143
|
+
return cached_data
|
144
|
+
end
|
145
|
+
|
146
|
+
@stats[:cache_misses] += 1
|
147
|
+
|
148
|
+
begin
|
149
|
+
redis_key = "app:roles:#{app_id}:#{organization_id}"
|
150
|
+
data = @redis_service.get(redis_key)
|
151
|
+
|
152
|
+
return nil unless data
|
153
|
+
|
154
|
+
roles = JSON.parse(data)
|
155
|
+
set_in_cache(cache_key, roles)
|
156
|
+
|
157
|
+
roles
|
158
|
+
rescue StandardError => e
|
159
|
+
puts "Error fetching app roles #{app_id}:#{organization_id}: #{e.message}"
|
160
|
+
nil
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def get_app_schema(app_id)
|
165
|
+
cache_key = "app_schema:#{app_id}"
|
166
|
+
|
167
|
+
# Check cache first
|
168
|
+
cached_data = get_from_cache(cache_key)
|
169
|
+
if cached_data
|
170
|
+
@stats[:cache_hits] += 1
|
171
|
+
return cached_data
|
172
|
+
end
|
173
|
+
|
174
|
+
@stats[:cache_misses] += 1
|
175
|
+
|
176
|
+
begin
|
177
|
+
redis_key = 'app-schemas'
|
178
|
+
data = @redis_service.get(redis_key)
|
179
|
+
|
180
|
+
return nil unless data
|
181
|
+
|
182
|
+
schemas = JSON.parse(data)
|
183
|
+
schema = schemas[app_id]
|
184
|
+
|
185
|
+
return nil unless schema
|
186
|
+
|
187
|
+
set_in_cache(cache_key, schema)
|
188
|
+
schema
|
189
|
+
rescue StandardError => e
|
190
|
+
puts "Error fetching app schema for #{app_id}: #{e.message}"
|
191
|
+
nil
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def get_effective_permissions(user_id, app_id, organization_id)
|
196
|
+
return nil unless @config[:include_effective_permissions]
|
197
|
+
|
198
|
+
cache_key = "effective_permissions:#{user_id}:#{app_id}:#{organization_id}"
|
199
|
+
|
200
|
+
# Check cache first
|
201
|
+
cached_data = get_from_cache(cache_key)
|
202
|
+
if cached_data
|
203
|
+
@stats[:cache_hits] += 1
|
204
|
+
return cached_data
|
205
|
+
end
|
206
|
+
|
207
|
+
@stats[:cache_misses] += 1
|
208
|
+
|
209
|
+
begin
|
210
|
+
redis_key = "permissions:cache:#{user_id}:#{app_id}:#{organization_id}"
|
211
|
+
data = @redis_service.get(redis_key)
|
212
|
+
|
213
|
+
return nil unless data
|
214
|
+
|
215
|
+
effective_permissions = JSON.parse(data)
|
216
|
+
|
217
|
+
# Cache with shorter TTL for permission cache
|
218
|
+
set_in_cache(cache_key, effective_permissions, (@config[:cache_timeout] / 2).to_i)
|
219
|
+
|
220
|
+
effective_permissions
|
221
|
+
rescue StandardError => e
|
222
|
+
puts "Error fetching effective permissions #{user_id}:#{app_id}:#{organization_id}: #{e.message}"
|
223
|
+
nil
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def get_user_organizations(user_id)
|
228
|
+
permissions = get_user_permissions(user_id)
|
229
|
+
return [] unless permissions&.dig('permissions')
|
230
|
+
|
231
|
+
user_organizations = []
|
232
|
+
|
233
|
+
permissions['permissions'].each do |app_id, orgs|
|
234
|
+
orgs.each do |organization_id, org_data|
|
235
|
+
next unless org_data['status'] == 'active'
|
236
|
+
|
237
|
+
effective_permissions = nil
|
238
|
+
effective_permissions = get_effective_permissions(user_id, app_id, organization_id) if @config[:include_effective_permissions]
|
239
|
+
|
240
|
+
user_org = {
|
241
|
+
'appId' => app_id,
|
242
|
+
'organizationId' => organization_id,
|
243
|
+
'roles' => org_data['roles'],
|
244
|
+
'status' => org_data['status']
|
245
|
+
}
|
246
|
+
|
247
|
+
user_org['effectivePermissions'] = effective_permissions if effective_permissions
|
248
|
+
|
249
|
+
user_organizations << user_org
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
user_organizations
|
254
|
+
end
|
255
|
+
|
256
|
+
def get_user_applications(user_id)
|
257
|
+
user_organizations = get_user_organizations(user_id)
|
258
|
+
unique_app_ids = user_organizations.map { |org| org['appId'] }.uniq
|
259
|
+
|
260
|
+
applications = []
|
261
|
+
unique_app_ids.each do |app_id|
|
262
|
+
app = get_application(app_id)
|
263
|
+
applications << app if app && app['isActive']
|
264
|
+
end
|
265
|
+
|
266
|
+
applications
|
267
|
+
end
|
268
|
+
|
269
|
+
def get_comprehensive_user_data(user_id)
|
270
|
+
permissions = get_user_permissions(user_id)
|
271
|
+
organizations = get_user_organizations(user_id)
|
272
|
+
applications = get_user_applications(user_id)
|
273
|
+
|
274
|
+
{
|
275
|
+
'permissions' => permissions,
|
276
|
+
'organizations' => organizations,
|
277
|
+
'applications' => applications
|
278
|
+
}
|
279
|
+
end
|
280
|
+
|
281
|
+
def clear_user_cache(user_id)
|
282
|
+
keys_to_remove = @cache.keys.select { |key| key.include?(user_id) }
|
283
|
+
keys_to_remove.each do |key|
|
284
|
+
@cache.delete(key)
|
285
|
+
@cache_timestamps.delete(key)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
def clear_all_cache
|
290
|
+
@cache.clear
|
291
|
+
@cache_timestamps.clear
|
292
|
+
end
|
293
|
+
|
294
|
+
def stats
|
295
|
+
@stats.dup
|
296
|
+
end
|
297
|
+
|
298
|
+
def initialized?
|
299
|
+
@stats[:initialized] && @redis_service.connected?
|
300
|
+
end
|
301
|
+
|
302
|
+
def shutdown
|
303
|
+
clear_all_cache
|
304
|
+
@redis_service.disconnect if @redis_service.respond_to?(:disconnect)
|
305
|
+
@stats[:initialized] = false
|
306
|
+
@stats[:connection_status] = 'disconnected'
|
307
|
+
end
|
308
|
+
|
309
|
+
private
|
310
|
+
|
311
|
+
def get_from_cache(key)
|
312
|
+
return nil unless @cache.key?(key)
|
313
|
+
|
314
|
+
timestamp = @cache_timestamps[key]
|
315
|
+
return nil unless timestamp
|
316
|
+
|
317
|
+
# Check if cache entry has expired
|
318
|
+
if Time.now.to_i - timestamp > @config[:cache_timeout]
|
319
|
+
@cache.delete(key)
|
320
|
+
@cache_timestamps.delete(key)
|
321
|
+
return nil
|
322
|
+
end
|
323
|
+
|
324
|
+
@cache[key]
|
325
|
+
end
|
326
|
+
|
327
|
+
def set_in_cache(key, value, _ttl = nil)
|
328
|
+
@cache[key] = value
|
329
|
+
@cache_timestamps[key] = Time.now.to_i
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
data/lib/jwt_auth_cognito.rb
CHANGED
@@ -1,18 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative
|
6
|
-
require_relative
|
7
|
-
require_relative
|
8
|
-
require_relative
|
3
|
+
require_relative 'jwt_auth_cognito/version'
|
4
|
+
require_relative 'jwt_auth_cognito/configuration'
|
5
|
+
require_relative 'jwt_auth_cognito/ssm_service'
|
6
|
+
require_relative 'jwt_auth_cognito/jwks_service'
|
7
|
+
require_relative 'jwt_auth_cognito/redis_service'
|
8
|
+
require_relative 'jwt_auth_cognito/token_blacklist_service'
|
9
|
+
require_relative 'jwt_auth_cognito/api_key_validator'
|
10
|
+
require_relative 'jwt_auth_cognito/user_data_service'
|
11
|
+
require_relative 'jwt_auth_cognito/error_utils'
|
12
|
+
require_relative 'jwt_auth_cognito/jwt_validator'
|
9
13
|
|
10
14
|
module JwtAuthCognito
|
11
15
|
class Error < StandardError; end
|
12
16
|
class ValidationError < Error; end
|
13
17
|
class BlacklistError < Error; end
|
14
18
|
class ConfigurationError < Error; end
|
15
|
-
|
19
|
+
|
16
20
|
# Specific JWT error types (matching Node.js implementation)
|
17
21
|
class TokenExpiredError < ValidationError; end
|
18
22
|
class TokenNotActiveError < ValidationError; end
|
@@ -33,9 +37,37 @@ module JwtAuthCognito
|
|
33
37
|
def self.reset_configuration!
|
34
38
|
@configuration = Configuration.new
|
35
39
|
end
|
40
|
+
|
41
|
+
# Convenience factory method to create a Cognito validator
|
42
|
+
def self.create_cognito_validator(region:, user_pool_id:, client_id: nil, client_secret: nil, redis_config: {},
|
43
|
+
enable_api_key_validation: false, enable_user_data_retrieval: false)
|
44
|
+
old_config = configuration.dup
|
45
|
+
|
46
|
+
configure do |config|
|
47
|
+
config.cognito_region = region
|
48
|
+
config.cognito_user_pool_id = user_pool_id
|
49
|
+
config.cognito_client_id = client_id if client_id
|
50
|
+
config.cognito_client_secret = client_secret if client_secret
|
51
|
+
config.enable_api_key_validation = enable_api_key_validation
|
52
|
+
config.enable_user_data_retrieval = enable_user_data_retrieval
|
53
|
+
|
54
|
+
# Apply Redis configuration
|
55
|
+
config.redis_host = redis_config[:host] if redis_config[:host]
|
56
|
+
config.redis_port = redis_config[:port] if redis_config[:port]
|
57
|
+
config.redis_password = redis_config[:password] if redis_config[:password]
|
58
|
+
config.redis_ssl = redis_config[:tls] if redis_config.key?(:tls)
|
59
|
+
config.redis_ca_cert_path = redis_config[:ca_cert_path] if redis_config[:ca_cert_path]
|
60
|
+
config.redis_ca_cert_name = redis_config[:ca_cert_name] if redis_config[:ca_cert_name]
|
61
|
+
end
|
62
|
+
|
63
|
+
validator = JwtValidator.new
|
64
|
+
|
65
|
+
# Restore original configuration
|
66
|
+
@configuration = old_config
|
67
|
+
|
68
|
+
validator
|
69
|
+
end
|
36
70
|
end
|
37
71
|
|
38
72
|
# Cargar tareas Rake si estamos en Rails
|
39
|
-
if defined?(Rails::Railtie)
|
40
|
-
require_relative "jwt_auth_cognito/railtie"
|
41
|
-
end
|
73
|
+
require_relative 'jwt_auth_cognito/railtie' if defined?(Rails::Railtie)
|
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
namespace :jwt_auth_cognito do
|
4
|
-
desc
|
4
|
+
desc 'Genera archivo de configuración para jwt_auth_cognito'
|
5
5
|
task :install do
|
6
|
-
puts
|
7
|
-
|
6
|
+
puts '🚀 Instalando jwt_auth_cognito...'
|
7
|
+
|
8
8
|
# Verificar si estamos en una aplicación Rails
|
9
9
|
unless defined?(Rails)
|
10
|
-
puts
|
10
|
+
puts '❌ Error: Este comando debe ejecutarse desde una aplicación Rails'
|
11
11
|
exit 1
|
12
12
|
end
|
13
13
|
|
@@ -20,10 +20,10 @@ namespace :jwt_auth_cognito do
|
|
20
20
|
# Generar archivo de configuración
|
21
21
|
if File.exist?(config_file)
|
22
22
|
print "El archivo #{config_file} ya existe. ¿Sobrescribir? (y/N): "
|
23
|
-
response =
|
24
|
-
|
23
|
+
response = $stdin.gets.chomp.downcase
|
24
|
+
|
25
25
|
unless %w[y yes sí si].include?(response)
|
26
|
-
puts
|
26
|
+
puts '⏭️ Instalación cancelada'
|
27
27
|
exit 0
|
28
28
|
end
|
29
29
|
end
|
@@ -36,107 +36,106 @@ namespace :jwt_auth_cognito do
|
|
36
36
|
|
37
37
|
# Generar/actualizar .env.example
|
38
38
|
env_content = generate_env_content
|
39
|
-
|
39
|
+
|
40
40
|
if File.exist?(env_file)
|
41
41
|
existing_content = File.read(env_file)
|
42
|
-
|
43
|
-
|
44
|
-
puts "✅ Variables agregadas a .env.example"
|
42
|
+
if existing_content.include?('COGNITO_USER_POOL_ID')
|
43
|
+
puts 'ℹ️ Variables ya existen en .env.example'
|
45
44
|
else
|
46
|
-
|
45
|
+
File.write(env_file, "#{existing_content}\n#{env_content}")
|
46
|
+
puts '✅ Variables agregadas a .env.example'
|
47
47
|
end
|
48
48
|
else
|
49
49
|
File.write(env_file, env_content)
|
50
|
-
puts
|
50
|
+
puts '✅ Archivo .env.example creado'
|
51
51
|
end
|
52
52
|
|
53
53
|
show_next_steps
|
54
54
|
end
|
55
55
|
|
56
|
-
desc
|
56
|
+
desc 'Muestra la configuración actual de jwt_auth_cognito'
|
57
57
|
task :config do
|
58
|
-
puts
|
59
|
-
puts
|
60
|
-
|
58
|
+
puts '📋 Configuración actual de jwt_auth_cognito:'
|
59
|
+
puts '=' * 50
|
60
|
+
|
61
61
|
config = JwtAuthCognito.configuration
|
62
|
-
|
63
|
-
puts
|
62
|
+
|
63
|
+
puts '🏛️ AWS Cognito:'
|
64
64
|
puts " Region: #{config.cognito_region || '❌ NO CONFIGURADO'}"
|
65
65
|
puts " User Pool ID: #{config.cognito_user_pool_id || '❌ NO CONFIGURADO'}"
|
66
66
|
puts " Client ID: #{config.cognito_client_id || '❌ NO CONFIGURADO'}"
|
67
67
|
puts " Client Secret: #{config.has_client_secret? ? '✅ CONFIGURADO' : '⚠️ NO CONFIGURADO'}"
|
68
|
-
|
68
|
+
|
69
69
|
puts "\n🗄️ Redis:"
|
70
70
|
puts " Host: #{config.redis_host}"
|
71
71
|
puts " Puerto: #{config.redis_port}"
|
72
72
|
puts " Base de datos: #{config.redis_db}"
|
73
73
|
puts " TLS: #{config.redis_ssl ? '✅ HABILITADO' : '❌ DESHABILITADO'}"
|
74
74
|
puts " Password: #{config.redis_password ? '✅ CONFIGURADO' : '❌ NO CONFIGURADO'}"
|
75
|
-
|
75
|
+
|
76
76
|
puts "\n⚙️ Configuración:"
|
77
77
|
puts " Modo de validación: #{config.validation_mode}"
|
78
78
|
puts " Cache JWKS TTL: #{config.jwks_cache_ttl}s"
|
79
79
|
puts " Entorno: #{config.environment}"
|
80
|
-
|
80
|
+
|
81
81
|
puts "\n🔍 URLs:"
|
82
82
|
begin
|
83
83
|
puts " JWKS URL: #{config.jwks_url}"
|
84
84
|
puts " Cognito Issuer: #{config.cognito_issuer}"
|
85
|
-
rescue => e
|
85
|
+
rescue StandardError => e
|
86
86
|
puts " ❌ Error generando URLs: #{e.message}"
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
-
desc
|
90
|
+
desc 'Prueba la conexión a Redis'
|
91
91
|
task :test_redis do
|
92
|
-
puts
|
93
|
-
|
92
|
+
puts '🔧 Probando conexión a Redis...'
|
93
|
+
|
94
94
|
begin
|
95
95
|
redis_service = JwtAuthCognito::RedisService.new
|
96
96
|
redis_service.send(:connect_redis)
|
97
|
-
puts
|
98
|
-
rescue => e
|
97
|
+
puts '✅ Conexión a Redis exitosa'
|
98
|
+
rescue StandardError => e
|
99
99
|
puts "❌ Error conectando a Redis: #{e.message}"
|
100
100
|
puts "\n💡 Verifica tu configuración de Redis en las variables de entorno"
|
101
101
|
end
|
102
102
|
end
|
103
103
|
|
104
|
-
desc
|
104
|
+
desc 'Prueba la configuración de Cognito'
|
105
105
|
task :test_cognito do
|
106
|
-
puts
|
107
|
-
|
106
|
+
puts '🔧 Probando configuración de Cognito...'
|
107
|
+
|
108
108
|
config = JwtAuthCognito.configuration
|
109
|
-
|
109
|
+
|
110
110
|
begin
|
111
111
|
config.validate!
|
112
|
-
puts
|
113
|
-
|
112
|
+
puts '✅ Configuración básica de Cognito válida'
|
113
|
+
|
114
114
|
# Probar JWKS
|
115
115
|
jwks_service = JwtAuthCognito::JwksService.new
|
116
116
|
jwks_service.send(:fetch_jwks)
|
117
|
-
puts
|
118
|
-
|
119
|
-
rescue => e
|
117
|
+
puts '✅ Conexión a JWKS exitosa'
|
118
|
+
rescue StandardError => e
|
120
119
|
puts "❌ Error en configuración de Cognito: #{e.message}"
|
121
120
|
puts "\n💡 Verifica tus variables de entorno de Cognito"
|
122
121
|
end
|
123
122
|
end
|
124
123
|
|
125
|
-
desc
|
124
|
+
desc 'Limpia todos los tokens de la blacklist'
|
126
125
|
task :clear_blacklist do
|
127
|
-
print
|
128
|
-
response =
|
129
|
-
|
126
|
+
print '⚠️ ¿Estás seguro de que quieres limpiar toda la blacklist? (y/N): '
|
127
|
+
response = $stdin.gets.chomp.downcase
|
128
|
+
|
130
129
|
if %w[y yes sí si].include?(response)
|
131
130
|
begin
|
132
131
|
blacklist_service = JwtAuthCognito::TokenBlacklistService.new
|
133
132
|
count = blacklist_service.clear_all_blacklisted_tokens
|
134
133
|
puts "✅ #{count} tokens eliminados de la blacklist"
|
135
|
-
rescue => e
|
134
|
+
rescue StandardError => e
|
136
135
|
puts "❌ Error limpiando blacklist: #{e.message}"
|
137
136
|
end
|
138
137
|
else
|
139
|
-
puts
|
138
|
+
puts '⏭️ Operación cancelada'
|
140
139
|
end
|
141
140
|
end
|
142
141
|
|
@@ -144,7 +143,7 @@ namespace :jwt_auth_cognito do
|
|
144
143
|
|
145
144
|
def generate_config_content
|
146
145
|
has_redis_config = defined?(Rails) && File.exist?(Rails.root.join('config', 'redis.yml'))
|
147
|
-
|
146
|
+
|
148
147
|
<<~CONFIG
|
149
148
|
# frozen_string_literal: true
|
150
149
|
|
@@ -155,16 +154,16 @@ namespace :jwt_auth_cognito do
|
|
155
154
|
# =============================================================================
|
156
155
|
# CONFIGURACIÓN DE AWS COGNITO
|
157
156
|
# =============================================================================
|
158
|
-
|
157
|
+
#{' '}
|
159
158
|
# User Pool ID de tu Cognito (requerido)
|
160
159
|
config.cognito_user_pool_id = ENV['COGNITO_USER_POOL_ID']
|
161
|
-
|
160
|
+
#{' '}
|
162
161
|
# Región de AWS donde está tu Cognito (requerido)
|
163
162
|
config.cognito_region = ENV['COGNITO_REGION'] || ENV['AWS_REGION'] || 'us-east-1'
|
164
|
-
|
163
|
+
#{' '}
|
165
164
|
# Client ID de tu aplicación en Cognito (requerido para validación de audiencia)
|
166
165
|
config.cognito_client_id = ENV['COGNITO_CLIENT_ID']
|
167
|
-
|
166
|
+
#{' '}
|
168
167
|
# Client Secret de tu aplicación en Cognito (opcional, para mayor seguridad)
|
169
168
|
# Si tu User Pool App Client está configurado con client secret, esto es requerido
|
170
169
|
config.cognito_client_secret = ENV['COGNITO_CLIENT_SECRET']
|
@@ -172,9 +171,9 @@ namespace :jwt_auth_cognito do
|
|
172
171
|
# =============================================================================
|
173
172
|
# CONFIGURACIÓN DE REDIS (para blacklist de tokens)
|
174
173
|
# =============================================================================
|
175
|
-
|
174
|
+
#{' '}
|
176
175
|
#{redis_config_section(has_redis_config)}
|
177
|
-
|
176
|
+
#{' '}
|
178
177
|
# Configuración de timeouts para Redis
|
179
178
|
config.redis_timeout = ENV['REDIS_TIMEOUT']&.to_i || 5
|
180
179
|
config.redis_connect_timeout = ENV['REDIS_CONNECT_TIMEOUT']&.to_i || 10
|
@@ -183,11 +182,11 @@ namespace :jwt_auth_cognito do
|
|
183
182
|
# =============================================================================
|
184
183
|
# CONFIGURACIÓN TLS PARA REDIS (PRODUCCIÓN)
|
185
184
|
# =============================================================================
|
186
|
-
|
185
|
+
#{' '}
|
187
186
|
# Configuración de certificados CA para Redis TLS
|
188
187
|
config.redis_ca_cert_path = ENV['REDIS_CA_CERT_PATH']
|
189
188
|
config.redis_ca_cert_name = ENV['REDIS_CA_CERT_NAME']
|
190
|
-
|
189
|
+
#{' '}
|
191
190
|
# Control de versiones TLS
|
192
191
|
config.redis_tls_min_version = ENV['REDIS_TLS_MIN_VERSION'] || 'TLSv1.2'
|
193
192
|
config.redis_tls_max_version = ENV['REDIS_TLS_MAX_VERSION'] || 'TLSv1.3'
|
@@ -196,10 +195,10 @@ namespace :jwt_auth_cognito do
|
|
196
195
|
# =============================================================================
|
197
196
|
# CONFIGURACIÓN DE VALIDACIÓN
|
198
197
|
# =============================================================================
|
199
|
-
|
198
|
+
#{' '}
|
200
199
|
# Modo de validación: :secure (producción) o :basic (desarrollo)
|
201
200
|
config.validation_mode = Rails.env.production? ? :secure : :basic
|
202
|
-
|
201
|
+
#{' '}
|
203
202
|
# Tiempo de cache para claves JWKS (en segundos)
|
204
203
|
config.jwks_cache_ttl = ENV['JWKS_CACHE_TTL']&.to_i || 3600 # 1 hora
|
205
204
|
end
|
@@ -265,26 +264,26 @@ namespace :jwt_auth_cognito do
|
|
265
264
|
end
|
266
265
|
|
267
266
|
def show_next_steps
|
268
|
-
puts "\n
|
269
|
-
puts
|
270
|
-
puts
|
267
|
+
puts "\n#{'=' * 60}"
|
268
|
+
puts '🎉 ¡Instalación de jwt_auth_cognito completada!'
|
269
|
+
puts '=' * 60
|
271
270
|
puts "\n📋 PRÓXIMOS PASOS:"
|
272
271
|
puts "\n1. 🔧 Configura las variables de entorno:"
|
273
|
-
puts
|
274
|
-
puts
|
275
|
-
|
272
|
+
puts ' - Edita .env (si usas dotenv) o configura variables del sistema'
|
273
|
+
puts ' - Como mínimo configura: COGNITO_USER_POOL_ID, COGNITO_REGION, COGNITO_CLIENT_ID'
|
274
|
+
|
276
275
|
puts "\n2. 🧪 Prueba la configuración:"
|
277
|
-
puts
|
278
|
-
puts
|
279
|
-
puts
|
280
|
-
|
276
|
+
puts ' rake jwt_auth_cognito:config # Ver configuración actual'
|
277
|
+
puts ' rake jwt_auth_cognito:test_cognito # Probar Cognito'
|
278
|
+
puts ' rake jwt_auth_cognito:test_redis # Probar Redis'
|
279
|
+
|
281
280
|
puts "\n3. 🚀 Reinicia tu aplicación Rails"
|
282
|
-
|
281
|
+
|
283
282
|
puts "\n4. 📖 Usa la gema en tu código:"
|
284
|
-
puts
|
285
|
-
puts
|
286
|
-
|
283
|
+
puts ' validator = JwtAuthCognito::JwtValidator.new'
|
284
|
+
puts ' result = validator.validate_token(jwt_token)'
|
285
|
+
|
287
286
|
puts "\n💡 Para más ejemplos, revisa el README de la gema"
|
288
|
-
puts
|
287
|
+
puts '=' * 60
|
289
288
|
end
|
290
|
-
end
|
289
|
+
end
|