hydra-keycloak-client 0.1.12 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 90747753193ef584afd8c70fdb15dd9f38fc097500e6ab748a8690869d02ee9e
4
- data.tar.gz: 8ffbb7be26674cd909e70115a9954a1956197cde46eba192792e6353038ab985
3
+ metadata.gz: 0df63eb8a97b5a549f867fa0de5fe449c9791ca811195c1aba5610fb54a278e6
4
+ data.tar.gz: 05d90289f5a8dce6618664a44ba1e7f0b1cd4d901b09895d52b43ef7fac8666c
5
5
  SHA512:
6
- metadata.gz: 7e2ecf0a2b3cb6edfb676a3bd9944ead397640c2811a7fa5c9e743e6f15eed60aafa1460ae2973b4d01682472f57ca57b3b7715557e2a93966022e9e62b93a63
7
- data.tar.gz: ede2dd403ed0049bcd2cc6dd536d99a0b8823299d9387c12bbc9f2eb31c6955ef001f47a5106793aefe52e4943a7ca3a988a5b8b985acb50f7cda60f9da88522
6
+ metadata.gz: e00caef745d78165d825b25a69b7afcb578dda19e9bccd543a56c3640ad5be1f9d70e402d977d38594821c4d2a9a988f32dec05cdc5748dcdf58b834b090afbe
7
+ data.tar.gz: 919a92716cbf26f5e8f94b973fe5023d350c298bd541caa50e81d4ec824b59c789fd0fa52480d65e9c98e8405d92317b19090467102815725abbc62c42ba99cd
data/.gitignore CHANGED
@@ -1 +1,3 @@
1
1
  .rspec_status
2
+ coverage
3
+ .bundle
data/Gemfile CHANGED
@@ -11,5 +11,7 @@ gem 'rubocop', '~> 1.26'
11
11
 
12
12
  gem 'pry'
13
13
 
14
+ gem 'dalli', require: false, group: :test
15
+ gem 'redis', require: false, group: :test
14
16
  gem 'simplecov', require: false, group: :test
15
17
  gem 'simplecov-cobertura', require: false, group: :test
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- hydra-keycloak-client (0.1.11)
4
+ hydra-keycloak-client (0.1.14)
5
5
  dry-auto_inject
6
6
  dry-container
7
7
  dry-monads
@@ -15,6 +15,8 @@ GEM
15
15
  ast (2.4.2)
16
16
  coderay (1.1.3)
17
17
  concurrent-ruby (1.1.10)
18
+ connection_pool (2.3.0)
19
+ dalli (3.2.3)
18
20
  diff-lcs (1.4.4)
19
21
  docile (1.4.0)
20
22
  dry-auto_inject (0.9.0)
@@ -22,12 +24,11 @@ GEM
22
24
  dry-configurable (0.15.0)
23
25
  concurrent-ruby (~> 1.0)
24
26
  dry-core (~> 0.6)
25
- dry-container (0.9.0)
27
+ dry-container (0.10.1)
26
28
  concurrent-ruby (~> 1.0)
27
- dry-configurable (~> 0.13, >= 0.13.0)
28
- dry-core (0.7.1)
29
+ dry-core (0.8.1)
29
30
  concurrent-ruby (~> 1.0)
30
- dry-inflector (0.2.1)
31
+ dry-inflector (0.3.0)
31
32
  dry-initializer (3.1.1)
32
33
  dry-logic (1.2.0)
33
34
  concurrent-ruby (~> 1.0)
@@ -63,6 +64,10 @@ GEM
63
64
  method_source (~> 1.0)
64
65
  rainbow (3.1.1)
65
66
  rake (12.3.3)
67
+ redis (5.0.5)
68
+ redis-client (>= 0.9.0)
69
+ redis-client (0.11.2)
70
+ connection_pool
66
71
  regexp_parser (2.2.1)
67
72
  rexml (3.2.5)
68
73
  rspec (3.10.0)
@@ -105,13 +110,15 @@ PLATFORMS
105
110
  ruby
106
111
 
107
112
  DEPENDENCIES
113
+ dalli
108
114
  hydra-keycloak-client!
109
115
  pry
110
116
  rake (~> 12.0)
117
+ redis
111
118
  rspec (~> 3.0)
112
119
  rubocop (~> 1.26)
113
120
  simplecov
114
121
  simplecov-cobertura
115
122
 
116
123
  BUNDLED WITH
117
- 2.2.25
124
+ 2.3.5
data/README.md CHANGED
@@ -33,7 +33,7 @@ keycloack_client = Hydra::Keycloak::ClientCreator.call(
33
33
  logout_redirect: keycloak_config.fetch(:logout_redirect),
34
34
  store_client: 'redis',
35
35
  store_client_options: {redis_host: ENV['REDIS_HOST'], redis_port: ENV['REDIS_PORT']},
36
- scope: 'hoper_scope homs_scope'
36
+ scope: ['hoper_scope', 'homs_scope']
37
37
  })
38
38
  ```
39
39
 
@@ -12,16 +12,39 @@ module Hydra
12
12
  module Keycloak
13
13
  class ConfigurationError < ::StandardError; end
14
14
 
15
+ MEMCACHED_SCHEMA = Dry::Schema.JSON do
16
+ required(:memcached_host).filled(:string)
17
+ required(:memcached_port).filled(:integer)
18
+ required(:memcached_namespace).filled(:string)
19
+ end
20
+
21
+ REDIS_SCHEMA = Dry::Schema.JSON do
22
+ required(:redis_host).filled(:string)
23
+ required(:redis_port).filled(:integer)
24
+ end
25
+
26
+ CONFIG_SCHEMA = Dry::Schema.JSON do
27
+ required(:auth_server_url).filled(:string)
28
+ required(:realm).filled(:string)
29
+ required(:client_id).filled(:string)
30
+ required(:redirect_uri).filled(:string)
31
+ required(:secret).filled(:string)
32
+ required(:logout_redirect).filled(:string)
33
+ required(:store_client).value(included_in?: %w[redis memcached])
34
+ required(:store_client_options).hash(MEMCACHED_SCHEMA | REDIS_SCHEMA)
35
+ optional(:scope).array(:str?)
36
+ end
37
+
15
38
  class ClientCreator
16
39
  extend ::Hydra::Keycloak::Mixin
17
40
 
18
41
  class << self
19
42
  def call(config:)
20
- register_containers(validate_config(config))
43
+ register_containers(validate_config(config).to_h)
21
44
  end
22
45
 
23
46
  def validate_config(config)
24
- validated_config = config_schema.call(config)
47
+ validated_config = CONFIG_SCHEMA.call(config)
25
48
 
26
49
  if validated_config.failure?
27
50
  raise ConfigurationError, "Wrong configuration params: #{validated_config.errors(full: true).to_h}"
@@ -30,40 +53,8 @@ module Hydra
30
53
  validated_config
31
54
  end
32
55
 
33
- def config_schema
34
- memcached_schema = ::Hydra::Keycloak::ClientCreator.memcached_schema
35
- redis_schema = ::Hydra::Keycloak::ClientCreator.redis_schema
36
- Dry::Schema.JSON do
37
- required(:auth_server_url).filled(:string)
38
- required(:realm).filled(:string)
39
- required(:client_id).filled(:string)
40
- required(:redirect_uri).filled(:string)
41
- required(:secret).filled(:string)
42
- required(:logout_redirect).filled(:string)
43
- required(:store_client).value(included_in?: %w[redis memcached])
44
- required(:store_client_options).hash(memcached_schema | redis_schema)
45
- optional(:scope).array(:str?)
46
- end
47
- end
48
-
49
- def memcached_schema
50
- Dry::Schema.JSON do
51
- required(:memcached_host).filled(:string)
52
- required(:memcached_port).filled(:string)
53
- required(:memcached_namespace).filled(:string)
54
- end
55
- end
56
-
57
- def redis_schema
58
- Dry::Schema.JSON do
59
- required(:redis_host).filled(:string)
60
- required(:redis_port).filled(:string)
61
- end
62
- end
63
-
64
56
  def register_containers(validated_config)
65
57
  register_urls(validated_config)
66
- register_queries
67
58
  register_store_client(validated_config)
68
59
  register_store
69
60
  register_code_verifier
@@ -79,42 +70,43 @@ module Hydra
79
70
  end
80
71
  end
81
72
 
82
- def register_queries
83
- container.register :queries do
84
- require 'hydra/keycloak/queries/gateway'
73
+ def register_store_client(config)
74
+ case config.fetch(:store_client)
75
+ when 'redis'
76
+ register_redis_store(config.fetch(:store_client_options))
77
+ when 'memcached'
78
+ register_memcached_store(config.fetch(:store_client_options))
79
+ end
80
+ end
81
+
82
+ def register_redis_store(redis_host:, redis_port:)
83
+ require 'hydra/keycloak/store/adapters/redis'
85
84
 
86
- ::Hydra::Keycloak::Queries::Gateway.new
85
+ container.register :redis do
86
+ ::Redis.new(host: redis_host, port: redis_port)
87
+ end
88
+
89
+ container.register :store_client do
90
+ require 'hydra/keycloak/store/redis_client'
91
+
92
+ ::Hydra::Keycloak::Store::RedisClient.new
87
93
  end
88
94
  end
89
95
 
90
- def register_store_client(config)
91
- case config[:store_client]
92
- when 'redis'
93
- require 'hydra/keycloak/store/adapters/redis'
96
+ def register_memcached_store(memcached_host:, memcached_port:, memcached_namespace:)
97
+ require 'hydra/keycloak/store/adapters/memcached'
94
98
 
95
- container.register :redis do
96
- ::Redis.new(host: config[:redis_host], port: config[:redis_port])
97
- end
99
+ container.register :dalli do
100
+ ::Dalli::Client.new(
101
+ "#{memcached_host}:#{memcached_port}",
102
+ namespace: memcached_namespace
103
+ )
104
+ end
98
105
 
99
- container.register :store_client do
100
- require 'hydra/keycloak/store/redis_client'
106
+ container.register :store_client do
107
+ require 'hydra/keycloak/store/memcached_client'
101
108
 
102
- ::Hydra::Keycloak::Store::RedisClient.new
103
- end
104
- when 'memcached'
105
- require 'hydra/keycloak/store/adapters/memcached'
106
- container.register :dalli do
107
- ::Dalli::Client.new(
108
- "#{config[:store_client_options][:memcached_host]}:#{config[:store_client_options][:memcached_port]}",
109
- namespace: config[:store_client_options][:memcached_namespace]
110
- )
111
- end
112
-
113
- container.register :store_client do
114
- require 'hydra/keycloak/store/memcached_client'
115
-
116
- ::Hydra::Keycloak::Store::MemcachedClient.new
117
- end
109
+ ::Hydra::Keycloak::Store::MemcachedClient.new
118
110
  end
119
111
  end
120
112
 
@@ -135,7 +127,7 @@ module Hydra
135
127
  class Client
136
128
  extend ::Hydra::Keycloak::Mixin
137
129
  include ::Dry::Monads[:result, :do]
138
- inject['urls', 'queries', 'store', 'code_verifier']
130
+ inject['urls', 'tokens_repo', 'store', 'code_verifier']
139
131
 
140
132
  def auth_url
141
133
  code_verifier.generate
@@ -143,9 +135,23 @@ module Hydra
143
135
  end
144
136
 
145
137
  def authenticate!(auth_code)
146
- return Failure(status: 400, code: :auth_code_was_not_received) unless auth_code
138
+ tokens_repo.get_tokens(auth_code, code_verifier.value).fmap do |tokens|
139
+ access_token = tokens[:access_token]
140
+ id_token = tokens[:id_token]
141
+ refresh_token = tokens[:refresh_token]
142
+
143
+ session_state = access_token.session_state
144
+
145
+ save_token(session_state, 'access_token', access_token)
146
+ save_token(session_state, 'id_token', id_token)
147
+ save_token(session_state, 'refresh_token', refresh_token)
148
+
149
+ session_state
150
+ end
151
+ end
147
152
 
148
- queries.get_tokens(auth_code, code_verifier.value).fmap do |tokens|
153
+ def authenticate_by_password!(username, password)
154
+ tokens_repo.get_tokens_by_password(username, password).fmap do |tokens|
149
155
  access_token = tokens[:access_token]
150
156
  id_token = tokens[:id_token]
151
157
  refresh_token = tokens[:refresh_token]
@@ -180,7 +186,7 @@ module Hydra
180
186
  access_token = yield fetch_token(session_state, 'access_token')
181
187
  end
182
188
 
183
- queries.token_introspect(access_token.source)
189
+ tokens_repo.introspect_token(access_token.source)
184
190
  end
185
191
 
186
192
  def access_token_jti(session_state)
@@ -198,7 +204,7 @@ module Hydra
198
204
  end
199
205
 
200
206
  def introspect_token(token)
201
- queries.token_introspect(token)
207
+ tokens_repo.introspect_token(token)
202
208
  end
203
209
 
204
210
  private
@@ -229,7 +235,7 @@ module Hydra
229
235
 
230
236
  def refresh_tokens(session_state)
231
237
  refresh_token = yield fetch_token(session_state, 'refresh_token')
232
- new_tokens = yield queries.refresh_tokens(refresh_token.source)
238
+ new_tokens = yield tokens_repo.refresh_tokens(refresh_token.source)
233
239
 
234
240
  yield save_token(session_state, 'access_token', new_tokens[:access_token])
235
241
  yield save_token(session_state, 'id_token', new_tokens[:id_token])
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'base64'
3
4
  require 'digest'
4
5
  require 'securerandom'
5
6
 
@@ -26,7 +27,7 @@ module Hydra
26
27
 
27
28
  def _generate_pkce(code_verifier)
28
29
  # https://datatracker.ietf.org/doc/html/rfc7636#section-4.6
29
- Digest::SHA256.base64digest(code_verifier).tr('+/', '-_').tr('=', '')
30
+ Base64.urlsafe_encode64(Digest::SHA256.digest(code_verifier), padding: false)
30
31
  end
31
32
  end
32
33
  end
@@ -8,10 +8,22 @@ module Hydra
8
8
  class Container
9
9
  extend Dry::Container::Mixin
10
10
 
11
- register(:http_client) do
12
- require 'hydra/keycloak/queries/http_client'
11
+ register(:http) do
12
+ require 'net/http'
13
13
 
14
- ::Hydra::Keycloak::Queries::HttpClient.new
14
+ Net::HTTP
15
+ end
16
+
17
+ register(:tokens_gateway) do
18
+ require 'hydra/keycloak/tokens/gateway'
19
+
20
+ ::Hydra::Keycloak::Tokens::Gateway.new
21
+ end
22
+
23
+ register(:tokens_repo) do
24
+ require 'hydra/keycloak/tokens/repo'
25
+
26
+ ::Hydra::Keycloak::Tokens::Repo.new
15
27
  end
16
28
  end
17
29
 
@@ -3,13 +3,18 @@
3
3
  require 'net/http'
4
4
  require 'json'
5
5
  require 'dry/monads'
6
+ require 'hydra/keycloak/container'
6
7
 
7
8
  module Hydra
8
9
  module Keycloak
9
- module Queries
10
- class HttpClient
10
+ module Tokens
11
+ class Gateway
12
+ extend ::Hydra::Keycloak::Mixin
13
+
11
14
  include ::Dry::Monads[:result]
12
15
 
16
+ inject['http']
17
+
13
18
  NETWORK_ERRORS = [Timeout::Error,
14
19
  Errno::EINVAL,
15
20
  Errno::ECONNRESET,
@@ -19,8 +24,8 @@ module Hydra
19
24
  Net::HTTPHeaderSyntaxError,
20
25
  Net::ProtocolError].freeze
21
26
 
22
- def do_post_request(path, body)
23
- response = Net::HTTP.post_form(URI(path), **body)
27
+ def post(path, body)
28
+ response = http.post_form(URI(path), **body)
24
29
 
25
30
  if response.code == '200'
26
31
  json = JSON.parse(response.body)
@@ -7,15 +7,19 @@ require 'hydra/keycloak/token'
7
7
 
8
8
  module Hydra
9
9
  module Keycloak
10
- module Queries
11
- class Gateway
10
+ module Tokens
11
+ class Repo
12
12
  extend ::Hydra::Keycloak::Mixin
13
13
  include ::Dry::Monads[:result]
14
- inject['http_client', 'urls']
14
+ inject['tokens_gateway', 'urls']
15
15
 
16
16
  def get_tokens(auth_code, code_verifier)
17
- result = make_request(urls.token_endpoint,
18
- urls.token_request_body(auth_code, code_verifier))
17
+ return Failure(status: 400, code: :auth_code_was_not_received) unless auth_code
18
+
19
+ result = tokens_gateway.post(
20
+ urls.token_endpoint,
21
+ urls.auth_code_token_request_body(auth_code, code_verifier)
22
+ )
19
23
 
20
24
  result.fmap do |tokens|
21
25
  {
@@ -26,9 +30,28 @@ module Hydra
26
30
  end
27
31
  end
28
32
 
29
- def token_introspect(token)
30
- make_request(urls.introspection_endpoint,
31
- urls.introspection_request_body(token)).bind do |result|
33
+ def get_tokens_by_password(username, password)
34
+ return Failure(status: 400, code: :username_or_password_is_empty) if username.nil? || password.nil?
35
+
36
+ result = tokens_gateway.post(
37
+ urls.token_endpoint,
38
+ urls.password_token_request_body(username, password)
39
+ )
40
+
41
+ result.fmap do |tokens|
42
+ {
43
+ access_token: ::Hydra::Keycloak::Token.new(tokens['access_token']),
44
+ id_token: ::Hydra::Keycloak::Token.new(tokens['id_token']),
45
+ refresh_token: ::Hydra::Keycloak::Token.new(tokens['refresh_token'])
46
+ }
47
+ end
48
+ end
49
+
50
+ def introspect_token(token)
51
+ tokens_gateway.post(
52
+ urls.introspection_endpoint,
53
+ urls.introspection_request_body(token)
54
+ ).bind do |result|
32
55
  if result['active']
33
56
  Success(result)
34
57
  else
@@ -38,8 +61,10 @@ module Hydra
38
61
  end
39
62
 
40
63
  def refresh_tokens(refresh_token)
41
- make_request(urls.token_endpoint,
42
- urls.refresh_request_body(refresh_token)).bind do |result|
64
+ tokens_gateway.post(
65
+ urls.token_endpoint,
66
+ urls.refresh_request_body(refresh_token)
67
+ ).bind do |result|
43
68
  if result['error']
44
69
  Failure(status: 400, code: :token_refreshing_error)
45
70
  else
@@ -51,12 +76,6 @@ module Hydra
51
76
  end
52
77
  end
53
78
  end
54
-
55
- private
56
-
57
- def make_request(path, body)
58
- http_client.do_post_request(path, body)
59
- end
60
79
  end
61
80
  end
62
81
  end
@@ -10,22 +10,26 @@ module Hydra
10
10
  end
11
11
 
12
12
  def auth_url(code_challenge)
13
- "#{@config[:auth_server_url]}" \
14
- "realms/#{@config[:realm]}/protocol/openid-connect/auth/?" \
15
- 'response_type=code&' \
16
- "client_id=#{@config[:client_id]}&" \
17
- "redirect_uri=#{@config[:redirect_uri]}&" \
18
- "nonce=#{@config[:secret]}&" \
19
- "scope=#{scope}&" \
20
- "code_challenge=#{code_challenge}&" \
21
- 'code_challenge_method=S256'
13
+ URI(URI.join(@config[:auth_server_url], "realms/#{@config[:realm]}/protocol/openid-connect/auth")).tap do |uri|
14
+ uri.query = URI.encode_www_form(
15
+ {
16
+ response_type: 'code',
17
+ client_id: @config[:client_id],
18
+ redirect_uri: @config[:redirect_uri],
19
+ nonce: @config[:secret],
20
+ scope: scope,
21
+ code_challenge: code_challenge,
22
+ code_challenge_method: 'S256'
23
+ }
24
+ )
25
+ end.to_s
22
26
  end
23
27
 
24
28
  def token_endpoint
25
- "#{@config[:auth_server_url]}realms/#{@config[:realm]}/protocol/openid-connect/token"
29
+ URI.join(@config[:auth_server_url], "realms/#{@config[:realm]}/protocol/openid-connect/token")
26
30
  end
27
31
 
28
- def token_request_body(auth_code, code_verifier)
32
+ def auth_code_token_request_body(auth_code, code_verifier)
29
33
  {
30
34
  grant_type: 'authorization_code',
31
35
  code: auth_code,
@@ -36,8 +40,19 @@ module Hydra
36
40
  }
37
41
  end
38
42
 
43
+ def password_token_request_body(username, password)
44
+ {
45
+ grant_type: 'password',
46
+ username: username,
47
+ password: password,
48
+ scope: scope,
49
+ client_id: @config[:client_id],
50
+ client_secret: @config[:secret]
51
+ }
52
+ end
53
+
39
54
  def introspection_endpoint
40
- "#{@config[:auth_server_url]}realms/#{@config[:realm]}/protocol/openid-connect/token/introspect"
55
+ URI.join(@config[:auth_server_url], "realms/#{@config[:realm]}/protocol/openid-connect/token/introspect")
41
56
  end
42
57
 
43
58
  def introspection_request_body(token)
@@ -50,9 +65,9 @@ module Hydra
50
65
  end
51
66
 
52
67
  def end_session_url(id_token)
53
- "#{@config[:auth_server_url]}realms/#{@config[:realm]}/protocol/openid-connect/logout" \
54
- "?id_token_hint=#{id_token}" \
55
- "&post_logout_redirect_uri=#{@config[:logout_redirect]}"
68
+ URI.join(@config[:auth_server_url], "realms/#{@config[:realm]}/protocol/openid-connect/logout").tap do |uri|
69
+ uri.query = URI.encode_www_form(id_token_hint: id_token, post_logout_redirect_uri: @config[:logout_redirect])
70
+ end.to_s
56
71
  end
57
72
 
58
73
  def refresh_request_body(refresh_token)
@@ -68,7 +83,7 @@ module Hydra
68
83
  private
69
84
 
70
85
  def scope
71
- (DEFAULT_SCOPE + @config[:scope]).join('%20')
86
+ [*DEFAULT_SCOPE, *@config[:scope]].join(' ')
72
87
  end
73
88
  end
74
89
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Hydra
4
4
  module Keycloak
5
- VERSION = '0.1.12'
5
+ VERSION = '0.1.14'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hydra-keycloak-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.12
4
+ version: 0.1.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fedor Kosolapov
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-06-30 00:00:00.000000000 Z
11
+ date: 2022-12-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jwt
@@ -118,14 +118,14 @@ files:
118
118
  - lib/hydra/keycloak/client.rb
119
119
  - lib/hydra/keycloak/code_verifier.rb
120
120
  - lib/hydra/keycloak/container.rb
121
- - lib/hydra/keycloak/queries/gateway.rb
122
- - lib/hydra/keycloak/queries/http_client.rb
123
121
  - lib/hydra/keycloak/store/adapters/memcached.rb
124
122
  - lib/hydra/keycloak/store/adapters/redis.rb
125
123
  - lib/hydra/keycloak/store/gateway.rb
126
124
  - lib/hydra/keycloak/store/memcached_client.rb
127
125
  - lib/hydra/keycloak/store/redis_client.rb
128
126
  - lib/hydra/keycloak/token.rb
127
+ - lib/hydra/keycloak/tokens/gateway.rb
128
+ - lib/hydra/keycloak/tokens/repo.rb
129
129
  - lib/hydra/keycloak/urls.rb
130
130
  - lib/hydra/keycloak/version.rb
131
131
  - run_tests.sh
@@ -136,7 +136,7 @@ metadata:
136
136
  homepage_uri: https://github.com/hydra-billing/hydra-keycloak-client
137
137
  source_code_uri: https://github.com/hydra-billing/hydra-keycloak-client
138
138
  changelog_uri: https://github.com/hydra-billing/hydra-keycloak-client
139
- post_install_message:
139
+ post_install_message:
140
140
  rdoc_options: []
141
141
  require_paths:
142
142
  - lib
@@ -152,7 +152,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
152
152
  version: '0'
153
153
  requirements: []
154
154
  rubygems_version: 3.1.6
155
- signing_key:
155
+ signing_key:
156
156
  specification_version: 4
157
157
  summary: Keycloak client for SSO
158
158
  test_files: []