Class: JwtAuthCognito::JwtValidator
- Inherits:
-
Object
- Object
- JwtAuthCognito::JwtValidator
- Defined in:
- lib/jwt_auth_cognito/jwt_validator.rb
Class Method Summary collapse
-
.create_cognito_validator(config = nil) ⇒ Object
Create a convenience factory method.
Instance Method Summary collapse
-
#calculate_secret_hash(identifier) ⇒ Object
Calculate secret hash for Cognito operations (when client secret is configured).
-
#check_permissions(user_id, app_id, org_id, permissions_to_check) ⇒ Object
Bulk permission check.
- #decode_token(token) ⇒ Object
- #extract_api_key_from_header(api_key_header) ⇒ Object
- #extract_api_key_from_headers(headers) ⇒ Object
-
#extract_token_from_header(authorization_header) ⇒ Object
Utility methods inspired by Node.js package.
- #get_time_to_expiry(token) ⇒ Object
- #get_token_info(token) ⇒ Object
-
#has_client_secret? ⇒ Boolean
Check if client secret is configured.
-
#has_permission?(user_id, app_id, org_id, permission) ⇒ Boolean
Returns true if the user has the given permission in the specified app/org context.
-
#has_permission_from_token?(token, app_id, org_id, permission) ⇒ Boolean
Validates a Bearer token and checks a single permission in one call.
-
#initialize(config = JwtAuthCognito.configuration) ⇒ JwtValidator
constructor
A new instance of JwtValidator.
- #initialize! ⇒ Object
- #is_token_expired?(token) ⇒ Boolean
-
#resolve_effective_permissions_for(user_id, app_id, org_id) ⇒ Object
Exposes effective permissions via the user data service.
- #revoke_token(token, user_id: nil) ⇒ Object
- #revoke_user_tokens(user_id) ⇒ Object
-
#validate(token, options = {}) ⇒ Object
Main validation method - use this for most cases Intelligently validates tokens with all features: - JWT validation (basic or secure) - API key validation (if provided) - Blacklist checking - Automatic appId verification - User data enrichment (if enabled).
- #validate_access_token(token) ⇒ Object
-
#validate_enriched(token, api_key = nil, options = {}) ⇒ Object
Get enriched validation (user data included) Use this when you need user permissions, organizations, apps.
- #validate_id_token(token) ⇒ Object
- #validate_multiple_tokens(tokens) ⇒ Object
-
#validate_token(token, options = {}) ⇒ Object
Quick validation for simple use cases Just validates the JWT token (includes blacklist check).
-
#validate_with_api_key(token, api_key, options = {}) ⇒ Object
Validate with API key (automatic appId verification) Use this when you have an API key and want automatic security.
-
#validate_with_app_access(token, api_key, options = {}) ⇒ Object
Validate with strict appId requirement Use this when you MUST ensure user has access to a specific app.
Constructor Details
#initialize(config = JwtAuthCognito.configuration) ⇒ JwtValidator
Returns a new instance of JwtValidator.
7 8 9 10 11 12 13 14 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 7 def initialize(config = JwtAuthCognito.configuration) @config = config @jwks_service = JwksService.new(config) @blacklist_service = TokenBlacklistService.new(config) @api_key_validator = config.enable_api_key_validation ? ApiKeyValidator.new(config) : nil @user_data_service = config.enable_user_data_retrieval ? UserDataService.new(nil, config.user_data_config) : nil @initialized = false end |
Class Method Details
.create_cognito_validator(config = nil) ⇒ Object
Create a convenience factory method
295 296 297 298 299 300 301 302 303 304 305 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 295 def self.create_cognito_validator(config = nil) if config old_config = JwtAuthCognito.configuration JwtAuthCognito.configure { |_c| config } validator = new JwtAuthCognito.instance_variable_set(:@configuration, old_config) validator else new end end |
Instance Method Details
#calculate_secret_hash(identifier) ⇒ Object
Calculate secret hash for Cognito operations (when client secret is configured)
218 219 220 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 218 def calculate_secret_hash(identifier) @config.calculate_secret_hash(identifier) end |
#check_permissions(user_id, app_id, org_id, permissions_to_check) ⇒ Object
Bulk permission check. Returns a hash with :allowed, :denied, :has_all, :has_any.
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 238 def (user_id, app_id, org_id, ) effective = (user_id, app_id, org_id) || [] allowed = [] denied = [] Array().each do |perm| if PermissionChecker.(perm, effective) allowed << perm else denied << perm end end { allowed: allowed, denied: denied, has_all: denied.empty?, has_any: allowed.any? } end |
#decode_token(token) ⇒ Object
193 194 195 196 197 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 193 def decode_token(token) JWT.decode(token, nil, false).first rescue JWT::DecodeError => e { error: "Failed to decode token: #{e.}" } end |
#extract_api_key_from_header(api_key_header) ⇒ Object
170 171 172 173 174 175 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 170 def extract_api_key_from_header(api_key_header) # Support common API key header formats return nil unless api_key_header api_key_header.strip end |
#extract_api_key_from_headers(headers) ⇒ Object
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 177 def extract_api_key_from_headers(headers) # Check various common API key header names (case insensitive) api_key_headers = %w[x-api-key X-API-Key X-API-KEY X-Api-Key] api_key_headers.each do |header_name| # Convert headers to a case-insensitive hash for lookup header_key = headers.keys.find { |key| key.downcase == header_name.downcase } next unless header_key value = headers[header_key] return extract_api_key_from_header(value) if value end nil end |
#extract_token_from_header(authorization_header) ⇒ Object
Utility methods inspired by Node.js package
163 164 165 166 167 168 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 163 def extract_token_from_header() return nil unless match = .match(/\ABearer (.+)\z/) match ? match[1] : nil end |
#get_time_to_expiry(token) ⇒ Object
283 284 285 286 287 288 289 290 291 292 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 283 def get_time_to_expiry(token) payload = decode_token(token) return nil if payload.is_a?(Hash) && payload[:error] exp = payload['exp'] return nil unless exp seconds = exp - Time.now.to_i seconds.positive? ? seconds : 0 end |
#get_token_info(token) ⇒ Object
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 199 def get_token_info(token) payload = decode_token(token) return payload if payload.is_a?(Hash) && payload[:error] { sub: payload['sub'], username: payload['cognito:username'] || payload['username'], email: payload['email'], token_use: payload['token_use'], client_id: payload['aud'], issued_at: payload['iat'] ? Time.at(payload['iat']) : nil, expires_at: payload['exp'] ? Time.at(payload['exp']) : nil, not_before: payload['nbf'] ? Time.at(payload['nbf']) : nil, jti: payload['jti'], has_client_secret: @config.has_client_secret? } end |
#has_client_secret? ⇒ Boolean
Check if client secret is configured
223 224 225 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 223 def has_client_secret? @config.has_client_secret? end |
#has_permission?(user_id, app_id, org_id, permission) ⇒ Boolean
Returns true if the user has the given permission in the specified app/org context.
230 231 232 233 234 235 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 230 def (user_id, app_id, org_id, ) = (user_id, app_id, org_id) return false unless PermissionChecker.(, ) end |
#has_permission_from_token?(token, app_id, org_id, permission) ⇒ Boolean
Validates a Bearer token and checks a single permission in one call.
256 257 258 259 260 261 262 263 264 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 256 def (token, app_id, org_id, ) result = validate_token(token) return false unless result[:valid] user_id = result[:payload]&.dig('sub') return false unless user_id (user_id, app_id, org_id, ) end |
#initialize! ⇒ Object
16 17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 16 def initialize! return if @initialized begin @jwks_service.initialize! @blacklist_service.initialize! if @blacklist_service.respond_to?(:initialize!) @user_data_service&.initialize! @initialized = true rescue StandardError => e ErrorUtils.log_error(e, 'JWT Validator initialization failed') raise JwtAuthCognito::ConfigurationError, ErrorUtils::JWT_ERROR_MESSAGES['INITIALIZATION_FAILED'] end end |
#is_token_expired?(token) ⇒ Boolean
273 274 275 276 277 278 279 280 281 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 273 def is_token_expired?(token) payload = decode_token(token) return true if payload.is_a?(Hash) && payload[:error] exp = payload['exp'] return false unless exp Time.now.to_i >= exp end |
#resolve_effective_permissions_for(user_id, app_id, org_id) ⇒ Object
Exposes effective permissions via the user data service.
267 268 269 270 271 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 267 def (user_id, app_id, org_id) return nil unless @user_data_service @user_data_service.(user_id, app_id, org_id) end |
#revoke_token(token, user_id: nil) ⇒ Object
154 155 156 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 154 def revoke_token(token, user_id: nil) @blacklist_service.add_to_blacklist(token, user_id: user_id) end |
#revoke_user_tokens(user_id) ⇒ Object
158 159 160 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 158 def revoke_user_tokens(user_id) @blacklist_service.invalidate_user_tokens(user_id) end |
#validate(token, options = {}) ⇒ Object
Main validation method - use this for most cases Intelligently validates tokens with all features:
-
JWT validation (basic or secure)
-
API key validation (if provided)
-
Blacklist checking
-
Automatic appId verification
-
User data enrichment (if enabled)
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 39 def validate(token, = {}) @config.validate! api_key = [:api_key] force_secure = [:force_secure] || false enrich_user_data = .fetch(:enrich_user_data, true) require_app_access = [:require_app_access] || false # Step 1: Validate API key if provided api_key_data = nil if api_key && @config.enable_api_key_validation && @api_key_validator api_key_result = @api_key_validator.validate_api_key(api_key) return { valid: false, error: api_key_result[:error] || 'API key validation failed' } unless api_key_result[:valid] api_key_data = api_key_result[:key_data] end # Step 2: Check blacklist first return { valid: false, error: 'Token has been revoked' } if @blacklist_service.is_blacklisted?(token) # Step 3: Validate JWT token validation_mode = force_secure ? :secure : @config.validation_mode token_result = case validation_mode when :secure validate_token_secure(token, ) when :basic validate_token_basic(token, ) else raise ConfigurationError, "Invalid validation_mode: #{validation_mode}" end return token_result unless token_result[:valid] && token_result[:payload] # Step 4: Verify appId access if API key has one if api_key_data app_validation = verify_app_access(api_key_data, token_result[:payload], require_app_access) return app_validation unless app_validation[:valid] end # Step 5: Enrich with user data if requested enriched_result = token_result.dup enriched_result[:api_key] = api_key_data if api_key_data if enrich_user_data && @config.enable_user_data_retrieval && @user_data_service user_id = token_result[:payload]['sub'] if user_id begin user_data = @user_data_service.get_comprehensive_user_data(user_id) enriched_result[:user_permissions] = user_data['permissions'] enriched_result[:user_organizations] = user_data['organizations'] enriched_result[:applications] = user_data['applications'] rescue StandardError => e ErrorUtils.log_error(e, 'User data retrieval failed') # Continue with basic validation if user data service fails end end end enriched_result end |
#validate_access_token(token) ⇒ Object
134 135 136 137 138 139 140 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 134 def validate_access_token(token) result = validate_token(token) return { valid: false, error: 'Token is not an access token' } if result[:valid] && result[:payload]['token_use'] != 'access' result end |
#validate_enriched(token, api_key = nil, options = {}) ⇒ Object
Get enriched validation (user data included) Use this when you need user permissions, organizations, apps
130 131 132 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 130 def validate_enriched(token, api_key = nil, = {}) validate(token, .merge(api_key: api_key, enrich_user_data: true)) end |
#validate_id_token(token) ⇒ Object
142 143 144 145 146 147 148 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 142 def validate_id_token(token) result = validate_token(token) return { valid: false, error: 'Token is not an ID token' } if result[:valid] && result[:payload]['token_use'] != 'id' result end |
#validate_multiple_tokens(tokens) ⇒ Object
150 151 152 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 150 def validate_multiple_tokens(tokens) tokens.map { |token| validate_token(token) } end |
#validate_token(token, options = {}) ⇒ Object
Quick validation for simple use cases Just validates the JWT token (includes blacklist check)
104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 104 def validate_token(token, = {}) result = validate(token, .merge(enrich_user_data: false)) { valid: result[:valid], payload: result[:payload], sub: result[:sub], username: result[:username], token_use: result[:token_use], error: result[:error] } end |
#validate_with_api_key(token, api_key, options = {}) ⇒ Object
Validate with API key (automatic appId verification) Use this when you have an API key and want automatic security
118 119 120 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 118 def validate_with_api_key(token, api_key, = {}) validate(token, .merge(api_key: api_key)) end |
#validate_with_app_access(token, api_key, options = {}) ⇒ Object
Validate with strict appId requirement Use this when you MUST ensure user has access to a specific app
124 125 126 |
# File 'lib/jwt_auth_cognito/jwt_validator.rb', line 124 def validate_with_app_access(token, api_key, = {}) validate(token, .merge(api_key: api_key, require_app_access: true)) end |