Class: KeycloakApiRails::Service

Inherits:
Object
  • Object
show all
Defined in:
lib/keycloak-api-rails/service.rb

Constant Summary collapse

REALM_NAME =
/\A(?!\.+\z)[A-Za-z0-9._~-]{1,128}\z/.freeze
SUPPORTED_ALGORITHMS =
%i[RS256 RS384 RS512 PS256 PS384 PS512 ES256 ES384 ES512].freeze

Instance Method Summary collapse

Constructor Details

#initialize(key_resolver) ⇒ Service

Returns a new instance of Service.



11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/keycloak-api-rails/service.rb', line 11

def initialize(key_resolver)
  configuration                          = KeycloakApiRails.config
  @key_resolver                          = key_resolver
  @skip_paths                            = normalize_skip_paths(configuration.skip_paths, configuration.logger)
  @opt_in                                = configuration.opt_in
  @token_expiration_tolerance_in_seconds = configuration.token_expiration_tolerance_in_seconds
  @expected_audiences                    = Array(configuration.expected_audience).map(&:to_s)
  @expected_token_type                   = configuration.expected_token_type
  @verify_not_before                     = configuration.verify_not_before
  @allow_token_in_query_string           = configuration.allow_token_in_query_string
  @allowed_algorithms                    = Array(configuration.allowed_algorithms).map(&:to_sym)
end

Instance Method Details

#decode_and_verify(token) ⇒ Object



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/keycloak-api-rails/service.rb', line 24

def decode_and_verify(token)
  raise TokenError.no_token(token) if token.nil? || token.empty?
  
  parts = token.to_s.split('.')
  raise TokenError.invalid_format(token) if parts.length < 3

  realm_id = extract_realm_from_token(token)
  raise TokenError.invalid_realm(token) unless realm_allowed?(realm_id)
  
  public_keys = @key_resolver.find_public_keys(realm_id)
  
  if public_keys.nil?
    raise MissingPublicKeysError, "No Keycloak public key is available to verify the token" 
  end

  decoded_token = decode(token, public_keys)
  verify_claims!(token, decoded_token, realm_id)
  decoded_token
end

#extract_realm_from_token(token) ⇒ Object



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/keycloak-api-rails/service.rb', line 44

def extract_realm_from_token(token)
  payload_segment = token.split('.', 3)[1]
  return nil unless payload_segment

  decoded_payload = Base64.urlsafe_decode64(payload_segment)
  parsed_payload  = JSON.parse(decoded_payload)

  if parsed_payload.is_a?(Hash)
    iss = parsed_payload['iss']
    return nil unless iss.is_a?(String)
  
    realm_id = iss.split('/').last
    if realm_id&.match?(REALM_NAME)
      realm_id 
    else
      nil
    end
  else
    nil
  end
rescue JSON::ParserError, ArgumentError
  nil
end

#need_middleware_authentication?(method, path, headers) ⇒ Boolean

Returns:

  • (Boolean)


93
94
95
# File 'lib/keycloak-api-rails/service.rb', line 93

def need_middleware_authentication?(method, path, headers)
  !is_preflight?(method, headers) && (!@opt_in && !should_skip?(method, path))
end

#read_token(uri, headers) ⇒ Object



82
83
84
85
86
87
88
89
90
91
# File 'lib/keycloak-api-rails/service.rb', line 82

def read_token(uri, headers)
  header_token = Helper.read_token_from_headers(headers)
  if !header_token.empty?
    header_token
  elsif @allow_token_in_query_string
    Helper.read_token_from_query_string(uri).to_s
  else
    ""
  end
end

#realm_allowed?(realm_id) ⇒ Boolean

Returns:

  • (Boolean)


68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/keycloak-api-rails/service.rb', line 68

def realm_allowed?(realm_id)
  config_realm_id = KeycloakApiRails.config.realm_id
  return true if config_realm_id.nil?
  return false if realm_id.nil?

  if config_realm_id.respond_to?(:call)
    config_realm_id.call(realm_id)
  elsif config_realm_id.is_a?(Array)
    config_realm_id.include?(realm_id)
  else
    config_realm_id == realm_id
  end
end