Class: Verikloak::Middleware

Inherits:
Object
  • Object
show all
Includes:
MiddlewareAudienceResolution, MiddlewareConfiguration, MiddlewareDecoderCache, MiddlewareErrorMapping, 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` — the raw JWT string

  • env` — 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
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 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, 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.



578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
# File 'lib/verikloak/middleware.rb', line 578

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,
               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)
  # 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
  @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



625
626
627
628
629
630
631
632
633
634
635
636
637
# File 'lib/verikloak/middleware.rb', line 625

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