Class: Verikloak::Middleware

Inherits:
Object
  • Object
show all
Includes:
MiddlewareAudienceResolution, MiddlewareConfiguration, MiddlewareDecoderCache, MiddlewareErrorMapping, MiddlewareJwksRefresh, MiddlewareTokenVerification, SkipPathMatcher
Defined in:
lib/verikloak/middleware.rb

Overview

Rack middleware that verifies incoming JWT access tokens (Keycloak) using OpenID Connect discovery and JWKs. On success, it populates:

  • env['verikloak.token'] — the raw JWT string
  • env['verikloak.user'] — the decoded JWT claims Hash

Failures are converted to JSON error responses with appropriate status codes.

Constant Summary collapse

DEFAULT_REALM =
'verikloak'
DEFAULT_TOKEN_ENV_KEY =
'verikloak.token'
DEFAULT_USER_ENV_KEY =
'verikloak.user'
DEFAULT_DECODER_CACHE_LIMIT =

rubocop:disable Metrics/ParameterLists

128
DEFAULT_JWKS_REFRESH_INTERVAL =
60
MAX_TOKEN_BYTES =

Maximum token size in bytes to prevent DoS via oversized JWTs. Aligned with BFF's BFF::Constants::MAX_TOKEN_BYTES.

8192

Constants included from MiddlewareJwksRefresh

Verikloak::MiddlewareJwksRefresh::FORCED_REFRESH_MIN_INTERVAL

Constants included from MiddlewareErrorMapping

Verikloak::MiddlewareErrorMapping::AUTH_ERROR_CODES, Verikloak::MiddlewareErrorMapping::INFRA_ERROR_CODES

Instance Method Summary collapse

Constructor Details

#initialize(app, discovery_url:, audience:, issuer: nil, skip_paths: [], discovery: nil, jwks_cache: nil, connection: nil, leeway: Verikloak::TokenDecoder::DEFAULT_LEEWAY, token_verify_options: {}, decoder_cache_limit: DEFAULT_DECODER_CACHE_LIMIT, jwks_refresh_interval: DEFAULT_JWKS_REFRESH_INTERVAL, token_env_key: DEFAULT_TOKEN_ENV_KEY, user_env_key: DEFAULT_USER_ENV_KEY, realm: DEFAULT_REALM, logger: nil, allow_http: false) ⇒ Middleware

Returns a new instance of Middleware.



736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
# File 'lib/verikloak/middleware.rb', line 736

def initialize(app,
               discovery_url:,
               audience:,
               issuer: nil,
               skip_paths: [],
               discovery: nil,
               jwks_cache: nil,
               connection: nil,
               leeway: Verikloak::TokenDecoder::DEFAULT_LEEWAY,
               token_verify_options: {},
               decoder_cache_limit: DEFAULT_DECODER_CACHE_LIMIT,
               jwks_refresh_interval: DEFAULT_JWKS_REFRESH_INTERVAL,
               token_env_key: DEFAULT_TOKEN_ENV_KEY,
               user_env_key: DEFAULT_USER_ENV_KEY,
               realm: DEFAULT_REALM,
               logger: nil,
               allow_http: false)
  @app             = app
  @connection      = connection || Verikloak::HTTP.default_connection
  @audience_source = audience
  @discovery       = discovery || Discovery.new(discovery_url: discovery_url, connection: @connection,
                                                allow_http: allow_http)
  @jwks_cache      = jwks_cache
  @allow_http      = allow_http
  @leeway = leeway
  @token_verify_options = token_verify_options || {}
  @decoder_cache_limit = normalize_decoder_cache_limit(decoder_cache_limit)
  @jwks_refresh_interval = normalize_jwks_refresh_interval(jwks_refresh_interval)
  @last_jwks_refresh_at = nil
  # Optional user-configured issuer (overrides discovery issuer when provided)
  @configured_issuer = issuer
  # Effective issuer; may be nil initially and set via discovery if not configured
  @issuer        = @configured_issuer
  @mutex         = Mutex.new
  @decoder_cache = {}
  @decoder_cache_order = []
  @last_cached_keys_id = nil
  @keys_digest_memo = nil
  @token_env_key = normalize_env_key(token_env_key, 'token_env_key')
  @user_env_key  = normalize_env_key(user_env_key, 'user_env_key')
  @realm         = normalize_realm(realm)
  @logger        = logger

  compile_skip_paths(skip_paths)
end

Instance Method Details

#call(env) ⇒ Array(Integer, Hash, Array<String>)

Rack entrypoint.

Parameters:

  • env (Hash)

    Rack environment

Returns:

  • (Array(Integer, Hash, Array<String>))

    standard Rack response triple



787
788
789
790
791
792
793
794
795
796
797
798
799
# File 'lib/verikloak/middleware.rb', line 787

def call(env)
  path = env['PATH_INFO']
  return @app.call(env) if skip?(path)

  token = extract_token(env)
  handle_request(env, token)
rescue Verikloak::Error => e
  code, status = map_error(e)
  error_response(code, e.message, status)
rescue StandardError => e
  log_internal_error(e)
  error_response('internal_server_error', 'An unexpected error occurred', 500)
end