Class: ApiKeys::Services::Authenticator

Inherits:
Object
  • Object
show all
Extended by:
Logging
Defined in:
lib/api_keys/services/authenticator.rb

Overview

Authenticates an incoming request by extracting and verifying an API key.

Defined Under Namespace

Classes: Result

Constant Summary collapse

MAX_TOKEN_BYTESIZE =
512
MAX_BCRYPT_CANDIDATES =
32
MAX_KNOWN_PREFIXES =
1_024
TOKEN_CACHE_NAMESPACE =
"api_keys:v2:token"
KNOWN_PREFIXES_CACHE_KEY =
"api_keys:v2:known_prefixes"

Class Method Summary collapse

Class Method Details

.call(request) ⇒ ApiKeys::Services::Authenticator::Result

Authenticates the request.

Parameters:

  • request (ActionDispatch::Request)

    The incoming request object.

Returns:



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
99
100
101
102
103
104
105
106
# File 'lib/api_keys/services/authenticator.rb', line 45

def self.call(request)
  request_uuid = request.uuid if request.respond_to?(:uuid)
  log_debug "[ApiKeys Auth] Authentication started for request #{request_uuid || '[unknown]'}"
  config = ApiKeys.configuration

  # === HTTPS Check (Production Only) ===
  if production_environment? && config.https_only_production
    unless secure_request?(request)
      warning_message = "[ApiKeys Security] API key authentication attempted over insecure HTTP connection in production."
      log_warn warning_message
      if config.https_strict_mode
        log_warn "[ApiKeys Security] Strict mode enabled: Aborting authentication."
        return Result.failure(error_code: :insecure_connection, message: "API requests must be made over HTTPS in production.")
      end
    end
  end
  # === End HTTPS Check ===

  token = extract_token(request, config)

  unless token
    log_debug "[ApiKeys Auth] Token extraction failed."
    return Result.failure(error_code: :missing_token, message: "API token is missing")
  end

  unless valid_token?(token)
    log_debug "[ApiKeys Auth] Rejected a malformed API token."
    return Result.failure(error_code: :invalid_token, message: "API token is invalid")
  end

  log_debug "[ApiKeys Auth] Token extracted successfully. Verifying..."
  # Pass the original token AND config to find_and_verify_key
  api_key = find_and_verify_key(token, config)

  result = if (configuration_failure = check_key_type_configuration(api_key, config) ||
                                          check_environment_configuration(api_key, config))
             configuration_failure
           elsif api_key&.active?
             log_debug "[ApiKeys Auth] Verification successful. Key ID: #{api_key.id}"

             # Check environment isolation if enabled
             env_check_result = check_environment_isolation(api_key, config)
             if env_check_result
               env_check_result  # Return failure result
             else
               # TODO: Optionally update last_used_at and requests_count
               Result.success(api_key)
             end
           elsif api_key&.revoked?
             log_debug "[ApiKeys Auth] Verification failed: Key revoked. Key ID: #{api_key.id}"
             Result.failure(error_code: :revoked_key, message: "API key has been revoked")
           elsif api_key&.expired?
             log_debug "[ApiKeys Auth] Verification failed: Key expired. Key ID: #{api_key.id}"
             Result.failure(error_code: :expired_key, message: "API key has expired")
           else # Not found, mismatch, or inactive
             log_debug "[ApiKeys Auth] Verification failed: Token invalid or key not found."
             Result.failure(error_code: :invalid_token, message: "API token is invalid")
           end

  log_debug "[ApiKeys Auth] Authentication finished. Success: #{result.success?}; error code: #{result.error_code || 'none'}"
  result
end

.clear_known_prefixes_cacheObject



278
279
280
281
282
283
284
285
# File 'lib/api_keys/services/authenticator.rb', line 278

def self.clear_known_prefixes_cache
  cache = rails_cache
  return unless cache

  cache.delete(KNOWN_PREFIXES_CACHE_KEY)
rescue StandardError => error
  log_warn "[ApiKeys Auth] Cache delete failed (#{error.class}); continuing safely."
end