Class: JwtAuthCognito::RedisService
- Inherits:
-
Object
- Object
- JwtAuthCognito::RedisService
- Defined in:
- lib/jwt_auth_cognito/redis_service.rb
Constant Summary collapse
- BLACKLIST_PREFIX =
auth-service revokes tokens under ‘revoked:32 chars of token)` (see tokenBlacklist.ts#getTokenHash). This prefix and the id derivation in #generate_token_id MUST match it exactly, otherwise revocations performed by auth-service (/auth/logout) are looked up under a key that never exists and are silently missed. The Node validator uses this same scheme.
'revoked:'- USER_TOKENS_PREFIX =
'user_tokens:'
Class Method Summary collapse
-
.pool(config, factory) ⇒ Object
Builds (once) and returns the shared pool.
-
.reset_pool! ⇒ Object
Drops the shared pool.
Instance Method Summary collapse
- #clear_revoked_tokens ⇒ Object
- #generate_token_id(token) ⇒ Object
- #get(key) ⇒ Object
-
#initialize(config = JwtAuthCognito.configuration) ⇒ RedisService
constructor
A new instance of RedisService.
- #invalidate_user_tokens(user_id) ⇒ Object
- #is_token_revoked?(token_id) ⇒ Boolean
- #save_revoked_token(token_id, ttl = nil) ⇒ Object
- #set(key, value, ttl = nil) ⇒ Object
- #track_user_token(user_id, token_id, ttl = nil) ⇒ Object
Constructor Details
#initialize(config = JwtAuthCognito.configuration) ⇒ RedisService
Returns a new instance of RedisService.
42 43 44 |
# File 'lib/jwt_auth_cognito/redis_service.rb', line 42 def initialize(config = JwtAuthCognito.configuration) @config = config end |
Class Method Details
.pool(config, factory) ⇒ Object
Builds (once) and returns the shared pool. The factory is a callable that produces a connected Redis client; it runs lazily as the pool grows.
27 28 29 30 31 32 |
# File 'lib/jwt_auth_cognito/redis_service.rb', line 27 def pool(config, factory) @pool ||= ConnectionPool.new( size: config.redis_pool_size, timeout: config.redis_pool_timeout ) { factory.call } end |
.reset_pool! ⇒ Object
Drops the shared pool. Call after forking (Puma/Unicorn ‘after_fork`, Sidekiq) so each child builds its own connections instead of reusing parent sockets. Also used to isolate tests.
37 38 39 |
# File 'lib/jwt_auth_cognito/redis_service.rb', line 37 def reset_pool! @pool = nil end |
Instance Method Details
#clear_revoked_tokens ⇒ Object
74 75 76 77 78 79 80 81 82 |
# File 'lib/jwt_auth_cognito/redis_service.rb', line 74 def clear_revoked_tokens with_redis do |redis| keys = redis.keys("#{BLACKLIST_PREFIX}*") redis.del(*keys) if keys.any? keys.length end rescue Redis::BaseError => e raise BlacklistError, "Failed to clear revoked tokens: #{e.}" end |
#generate_token_id(token) ⇒ Object
117 118 119 120 121 122 123 124 125 |
# File 'lib/jwt_auth_cognito/redis_service.rb', line 117 def generate_token_id(token) # Must match auth-service's key derivation (tokenBlacklist.ts#getTokenHash): # base64 of the last 32 characters of the raw token. Using the JTI or a SHA256 # here would look up a key auth-service never writes, so revocations would be # missed. The Node validator (token-blacklist-service.ts#getTokenHash) is identical. suffix = token.to_s suffix = suffix[-32..] || suffix Base64.strict_encode64(suffix) end |
#get(key) ⇒ Object
127 128 129 130 131 |
# File 'lib/jwt_auth_cognito/redis_service.rb', line 127 def get(key) with_redis { |redis| redis.get(key) } rescue Redis::BaseError => e raise BlacklistError, "Failed to get key '#{key}': #{e.}" end |
#invalidate_user_tokens(user_id) ⇒ Object
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/jwt_auth_cognito/redis_service.rb', line 84 def invalidate_user_tokens(user_id) user_key = "#{USER_TOKENS_PREFIX}#{user_id}" with_redis do |redis| token_ids = redis.smembers(user_key) # Persist a revocation marker for each tracked token, then clear the set. # Inlined (instead of calling #save_revoked_token) to reuse this single # pooled connection and avoid a nested pool checkout. token_ids.each { |token_id| redis.set("#{BLACKLIST_PREFIX}#{token_id}", 'revoked') } redis.del(user_key) token_ids.length end rescue Redis::BaseError => e raise BlacklistError, "Failed to invalidate user tokens: #{e.}" end |
#is_token_revoked?(token_id) ⇒ Boolean
62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/jwt_auth_cognito/redis_service.rb', line 62 def is_token_revoked?(token_id) key = "#{BLACKLIST_PREFIX}#{token_id}" result = with_redis { |redis| redis.exists?(key) } result.is_a?(Integer) ? result.positive? : result rescue StandardError # Fail-open graceful degradation: if Redis is unreachable or the pool is # exhausted, do not block validation. Mirrors the Node validator and # auth-service (RedisService.isTokenRevoked), which also treat errors as # "not revoked" so an infrastructure issue never locks every user out. false end |
#save_revoked_token(token_id, ttl = nil) ⇒ Object
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/jwt_auth_cognito/redis_service.rb', line 46 def save_revoked_token(token_id, ttl = nil) key = "#{BLACKLIST_PREFIX}#{token_id}" with_redis do |redis| if ttl redis.setex(key, ttl, 'revoked') else redis.set(key, 'revoked') end end true rescue Redis::BaseError => e raise BlacklistError, "Failed to save revoked token: #{e.}" end |
#set(key, value, ttl = nil) ⇒ Object
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/jwt_auth_cognito/redis_service.rb', line 133 def set(key, value, ttl = nil) with_redis do |redis| # Only attach an expiry for a positive TTL. A nil or non-positive ttl (note: # 0 is truthy in Ruby) means a persistent key — `SETEX key 0` is rejected by # Redis with "ERR invalid expire time", so fall back to a plain SET. if ttl&.positive? redis.setex(key, ttl, value) else redis.set(key, value) end end true rescue Redis::BaseError => e raise BlacklistError, "Failed to set key '#{key}': #{e.}" end |
#track_user_token(user_id, token_id, ttl = nil) ⇒ Object
102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/jwt_auth_cognito/redis_service.rb', line 102 def track_user_token(user_id, token_id, ttl = nil) user_key = "#{USER_TOKENS_PREFIX}#{user_id}" with_redis do |redis| redis.sadd(user_key, token_id) # Set expiration on the user's token set redis.expire(user_key, ttl) if ttl end true rescue Redis::BaseError # Non-critical operation, log but don't fail false end |