keycloak-api-rails 2.0.3 → 2.0.4
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 +4 -4
- data/CHANGELOG.md +7 -1
- data/README.md +18 -5
- data/lib/keycloak-api-rails/configuration.rb +2 -0
- data/lib/keycloak-api-rails/service.rb +3 -2
- data/lib/keycloak-api-rails/testing.rb +2 -1
- data/lib/keycloak-api-rails/version.rb +1 -1
- data/lib/keycloak-api-rails.rb +1 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e074e3c78c789139c8eeaf6a127dceaf0c7ff7235c1134f3fa0ea3c4149b277a
|
|
4
|
+
data.tar.gz: 6b4a5b9faaa16ab883a25c03e5eb31a0c8740f80b784812d6f47eb7aae117eb3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9472f08064d6a3e3ba64b781f089d2a42861b26323f9eba3a752ec63d41e54f32288b82ed0fa1b12e46418e9c1771351c7be8676476303162e88a28d676655bd
|
|
7
|
+
data.tar.gz: 0f106f193ceaa023611b9128910b5f944ebfe213c7b7fc1237f7a2e49c55c9b76564ac84ef080e3a4c36f48bcb463da434756d00af5f370cc07a5e8842c5c0f6
|
data/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,13 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [
|
|
8
|
+
## [2.0.4] - 2026-08-03
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
* New `issuer_url` option: the `iss` claim is checked against it instead of `server_url` when set, for setups where the API reaches Keycloak through a different address than the one Keycloak advertises to its clients (e.g. an internal Docker hostname vs. a public one).
|
|
13
|
+
|
|
14
|
+
## [2.0.3] - 2026-08-03
|
|
9
15
|
|
|
10
16
|
### Security
|
|
11
17
|
|
data/README.md
CHANGED
|
@@ -18,7 +18,7 @@ the recommended path.
|
|
|
18
18
|
## Install
|
|
19
19
|
|
|
20
20
|
```ruby
|
|
21
|
-
gem "keycloak-api-rails", "2.0.
|
|
21
|
+
gem "keycloak-api-rails", "2.0.4"
|
|
22
22
|
|
|
23
23
|
## Token validation
|
|
24
24
|
|
|
@@ -60,6 +60,7 @@ All options have a default value. However, all of them can be changed in your in
|
|
|
60
60
|
| Option | Default Value | Type | Required? | Description | Example |
|
|
61
61
|
| ---- | ----- | ------ | ----- | ------ | ----- |
|
|
62
62
|
| `server_url` | `nil`| String | Required | The base url where your Keycloak server is located, scheme included. Without one, the url cannot be parsed and no public key is ever downloaded | `https://keycloak.example.org` or `http://auth:8080` |
|
|
63
|
+
| `issuer_url` | `server_url`| String | Optional | The base url expected in a token's `iss` claim, when it differs from `server_url`. Needed when the API reaches Keycloak through a different address than the one Keycloak advertises to its clients, e.g. an internal Docker hostname vs. a public one | `https://keycloak.example.org` |
|
|
63
64
|
| `realm_id` | `nil`| String, Array or Proc | Required | Realm's name(s) (not id, actually). A single String, an Array of Strings for multiple tenants, or a Proc receiving the realm named by the token and answering whether it is allowed | `"master"`, `["tenant1", "tenant2"]` or `->(realm) { Tenant.exists?(name: realm) }` |
|
|
64
65
|
| `logger` | `Logger.new(STDOUT)`| Logger | Optional | The logger used by `keycloak-api-rails` | `Rails.logger` |
|
|
65
66
|
| `skip_paths` | `{}`| Hash of methods and paths regexp | Optional | Paths whose token must not be validated, matched against the URL-decoded path of the request. Each path must be a `Regexp`, anchored with `\A` and `\z`: a String is refused when the application boots, and `^` and `$` are warned about, for the reasons given below | `{ get: [/\A\/health\/.+/] }`|
|
|
@@ -136,14 +137,26 @@ Keycloak only adds an API to the `aud` claim of a token once that API is declare
|
|
|
136
137
|
`config.verify_not_before = true` additionally rejects a token whose `nbf` claim is in the future.
|
|
137
138
|
It is disabled by default because a clock skew between Keycloak and the API rejects valid tokens.
|
|
138
139
|
|
|
139
|
-
The `iss` claim is checked against `server_url` only when one is
|
|
140
|
+
The `iss` claim is checked against `issuer_url`, or `server_url` when `issuer_url` is not configured, only when one of the two is set. A test environment replacing the public key resolver, as `keycloak-api-rails/testing` does, accepts any issuer.
|
|
141
|
+
|
|
142
|
+
## When Keycloak is reached through a different address than its own
|
|
143
|
+
|
|
144
|
+
`server_url` is used both to download public keys and, absent `issuer_url`, to check the `iss` claim. Those are two different needs: the former must be an address the API can reach, the latter must match what Keycloak put in the token, which is whatever address its own clients used. Behind a reverse proxy, or from inside Docker, these are often not the same:
|
|
145
|
+
|
|
146
|
+
```ruby
|
|
147
|
+
KeycloakApiRails.configure do |config|
|
|
148
|
+
config.server_url = "http://keycloak:8080" # reachable from the API container
|
|
149
|
+
config.issuer_url = "https://keycloak.example.org" # what Keycloak's own hostname config puts in `iss`
|
|
150
|
+
config.realm_id = ENV["KEYCLOAK_REALM_ID"]
|
|
151
|
+
end
|
|
152
|
+
```
|
|
140
153
|
|
|
141
154
|
## Multi-tenancy (Multiple Realms)
|
|
142
155
|
|
|
143
156
|
The library natively supports multi-tenancy by validating tokens issued by multiple Keycloak realms.
|
|
144
157
|
Instead of a static string, you can configure `realm_id` with an Array or a Proc. The library will dynamically extract the realm from the token's `iss` claim, check if it is permitted, and fetch the appropriate public keys for that specific realm.
|
|
145
158
|
|
|
146
|
-
Those realms all live under the same `server_url
|
|
159
|
+
Those realms all live under the same `server_url` (or `issuer_url`, when configured): a token whose `iss` names another server is rejected, however allowed the realm it names.
|
|
147
160
|
Declaring `expected_audience` matters all the more here — without it, a token issued for any client of any allowed realm opens the API.
|
|
148
161
|
|
|
149
162
|
Using an Array of allowed realms:
|
|
@@ -344,8 +357,8 @@ scoped RubyGems credential.
|
|
|
344
357
|
3. Tag the commit and push the tag:
|
|
345
358
|
|
|
346
359
|
```
|
|
347
|
-
$ git tag -a v2.0.
|
|
348
|
-
$ git push origin v2.0.
|
|
360
|
+
$ git tag -a v2.0.4 -m "Version 2.0.4"
|
|
361
|
+
$ git push origin v2.0.4
|
|
349
362
|
```
|
|
350
363
|
|
|
351
364
|
The workflow then checks that the tag matches `KeycloakApiRails::VERSION`, runs the tests, builds the gem
|
|
@@ -7,6 +7,7 @@ module KeycloakApiRails
|
|
|
7
7
|
LOGGER_METHODS = [:debug, :info, :warn, :error].freeze
|
|
8
8
|
|
|
9
9
|
attr_accessor :server_url
|
|
10
|
+
attr_accessor :issuer_url
|
|
10
11
|
attr_accessor :realm_id
|
|
11
12
|
attr_accessor :skip_paths
|
|
12
13
|
attr_accessor :opt_in
|
|
@@ -27,6 +28,7 @@ module KeycloakApiRails
|
|
|
27
28
|
errors = []
|
|
28
29
|
|
|
29
30
|
errors.push("'server_url' must be a String or nil, got #{server_url.inspect}") unless server_url.nil? || server_url.is_a?(String)
|
|
31
|
+
errors.push("'issuer_url' must be a String or nil, got #{issuer_url.inspect}") unless issuer_url.nil? || issuer_url.is_a?(String)
|
|
30
32
|
errors.push("'realm_id' must be a String, an Array of Strings, a Proc, or nil, got #{realm_id.inspect}") unless valid_realm_id?(realm_id)
|
|
31
33
|
errors.push("'logger' must respond to #{LOGGER_METHODS.join(', ')}") unless LOGGER_METHODS.all? { |method| logger.respond_to?(method) }
|
|
32
34
|
errors.push("'opt_in' must be true or false, got #{opt_in.inspect}") unless boolean?(opt_in)
|
|
@@ -116,8 +116,9 @@ module KeycloakApiRails
|
|
|
116
116
|
raise TokenError.invalid_audience(token) unless audience_valid?(decoded_token)
|
|
117
117
|
raise TokenError.invalid_token_type(token) unless token_type_valid?(decoded_token)
|
|
118
118
|
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
issuer_url = KeycloakApiRails.config.issuer_url || KeycloakApiRails.config.server_url
|
|
120
|
+
if issuer_url
|
|
121
|
+
expected_iss = File.join(issuer_url.to_s, "realms", realm_id.to_s)
|
|
121
122
|
raise TokenError.invalid_realm(token) unless decoded_token["iss"] == expected_iss
|
|
122
123
|
end
|
|
123
124
|
end
|
|
@@ -90,7 +90,8 @@ module KeycloakApiRails
|
|
|
90
90
|
unless claims.key?(:iss) || claims.key?("iss")
|
|
91
91
|
config_realm_id = KeycloakApiRails.config.realm_id
|
|
92
92
|
realm_id = config_realm_id.is_a?(String) ? config_realm_id : "master"
|
|
93
|
-
|
|
93
|
+
issuer_url = KeycloakApiRails.config.issuer_url || KeycloakApiRails.config.server_url
|
|
94
|
+
payload["iss"] = File.join(issuer_url.to_s, "realms", realm_id)
|
|
94
95
|
end
|
|
95
96
|
|
|
96
97
|
claims.each { |name, value| payload[name.to_s] = value }
|
data/lib/keycloak-api-rails.rb
CHANGED