keycloak-api-rails 1.1.0 → 1.1.1
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/.github/workflows/release.yml +52 -0
- data/.gitignore +1 -0
- data/CHANGELOG.md +5 -0
- data/README.md +63 -20
- data/Rakefile +6 -0
- data/keycloak-api-rails.gemspec +2 -0
- data/lib/keycloak-api-rails/testing.rb +118 -0
- data/lib/keycloak-api-rails/version.rb +1 -1
- data/lib/keycloak-api-rails.rb +8 -0
- data/spec/keycloak-api-rails/service_spec.rb +6 -0
- data/spec/keycloak-api-rails/testing_spec.rb +102 -0
- metadata +20 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1f53bfe86f8f72911bac95192b9a21fc0cf1ec01868e00f2cdd8edfcc57811e3
|
|
4
|
+
data.tar.gz: 2ad644c2561e191e9dd858055a9442df69fff0af02d078360164f1d41283f633
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: da17e6a76b47460200ea397c58346dbdd43975f7aecec34e973b77ad15ebd12739d2c5d533064af24204d5be7bd4cc0f3c74b3df98e6d2623289a0d66f1398ad
|
|
7
|
+
data.tar.gz: 1f1f0ed249c2963fec160fdbdb6910a8387024f54d9956c35729fb5438037b541989046aee9dab2936dcc585d8f5e9ca6cd1b3247c20fb4dcea62ff7e2fae253
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Publishing is triggered by pushing a version tag, e.g.:
|
|
4
|
+
# git tag -a v1.2.0 -m "Version 1.2.0" && git push origin v1.2.0
|
|
5
|
+
#
|
|
6
|
+
# No RubyGems API key is stored in this repository: the gem is pushed through
|
|
7
|
+
# RubyGems' Trusted Publishing, which exchanges a short-lived GitHub OIDC token
|
|
8
|
+
# for a scoped RubyGems credential. This requires a trusted publisher declared on
|
|
9
|
+
# https://rubygems.org/gems/keycloak-api-rails pointing to this repository, this
|
|
10
|
+
# workflow file name and the 'release' environment.
|
|
11
|
+
on:
|
|
12
|
+
push:
|
|
13
|
+
tags: [ "v*" ]
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
release:
|
|
20
|
+
name: Push gem to RubyGems.org
|
|
21
|
+
# Pinned rather than 'ubuntu-latest', for the same reason as in ci.yml.
|
|
22
|
+
runs-on: ubuntu-24.04
|
|
23
|
+
|
|
24
|
+
environment: release
|
|
25
|
+
|
|
26
|
+
permissions:
|
|
27
|
+
contents: write # 'rake release' pushes the version tag when it does not exist yet
|
|
28
|
+
id-token: write # mandatory to request the OIDC token used by trusted publishing
|
|
29
|
+
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@v4
|
|
32
|
+
|
|
33
|
+
- name: Set up Ruby
|
|
34
|
+
uses: ruby/setup-ruby@v1
|
|
35
|
+
with:
|
|
36
|
+
ruby-version: '3.4'
|
|
37
|
+
bundler-cache: true
|
|
38
|
+
|
|
39
|
+
- name: Check that the tag matches KeycloakApiRails::VERSION
|
|
40
|
+
run: |
|
|
41
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
42
|
+
version="$(ruby -Ilib -r keycloak-api-rails/version -e 'print KeycloakApiRails::VERSION')"
|
|
43
|
+
if [ "$tag" != "$version" ]; then
|
|
44
|
+
echo "::error::Tag '${GITHUB_REF_NAME}' does not match KeycloakApiRails::VERSION '${version}'"
|
|
45
|
+
exit 1
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
- name: Run tests
|
|
49
|
+
run: bundle exec rspec
|
|
50
|
+
|
|
51
|
+
# Builds the gem, pushes it to RubyGems.org and waits for it to be available.
|
|
52
|
+
- uses: rubygems/release-gem@v1
|
data/.gitignore
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
log/*.log
|
|
3
3
|
.byebug_history
|
|
4
4
|
*.gem
|
|
5
|
+
pkg/
|
|
5
6
|
# This gem supports several Ruby versions, each of them resolving a different set of
|
|
6
7
|
# dependencies (Ruby 2.7 caps Rails to 7.1, Ruby 3.1 to 7.2, Ruby 3.2+ resolves Rails 8.1).
|
|
7
8
|
# A single committed lockfile cannot satisfy all of them.
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,11 @@ 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
|
+
## [1.1.1] - 2026-07-31
|
|
9
|
+
|
|
10
|
+
* New test helpers, in `keycloak-api-rails/testing`: `KeycloakApiRails::Testing.stub_public_keys!` validates the tokens forged by `keycloak_token` and `keycloak_auth_headers`, so that controller and request tests can be authenticated without a Keycloak server, and without the boilerplate the README used to describe. This file is not loaded by `keycloak-api-rails` and has to be required explicitly
|
|
11
|
+
* Publish the gem to RubyGems from Github Actions when a `v*` tag is pushed, using RubyGems' Trusted Publishing
|
|
12
|
+
|
|
8
13
|
## [1.1.0] - 2026-07-31
|
|
9
14
|
|
|
10
15
|
* Dependencies: Upgrade test dependency `timecop` to `0.9.11`
|
data/README.md
CHANGED
|
@@ -18,7 +18,7 @@ the recommended path.
|
|
|
18
18
|
## Install
|
|
19
19
|
|
|
20
20
|
```ruby
|
|
21
|
-
gem "keycloak-api-rails", "1.1.
|
|
21
|
+
gem "keycloak-api-rails", "1.1.1"
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
## Token validation
|
|
@@ -191,36 +191,60 @@ end
|
|
|
191
191
|
|
|
192
192
|
## Writing integration tests
|
|
193
193
|
|
|
194
|
-
If you want to write controller tests in your codebase and that Keycloak is configured for these controllers,
|
|
194
|
+
If you want to write controller or request tests in your codebase and that Keycloak is configured for these controllers,
|
|
195
|
+
this gem provides the helpers that forge valid tokens, so that no Keycloak server is required.
|
|
196
|
+
These helpers are not loaded by `keycloak-api-rails`: they have to be required explicitly, so that nothing they define ends up in a production process.
|
|
195
197
|
These lines are based on tests written using `rspec`.
|
|
196
198
|
|
|
197
|
-
* First, create a private key. This key should be created once per test suite for performance matters.
|
|
198
199
|
```ruby
|
|
199
|
-
|
|
200
|
-
|
|
200
|
+
# spec/rails_helper.rb
|
|
201
|
+
require "keycloak-api-rails/testing"
|
|
202
|
+
|
|
203
|
+
RSpec.configure do |config|
|
|
204
|
+
config.include KeycloakApiRails::Testing::Helpers
|
|
205
|
+
config.before(:suite) { KeycloakApiRails::Testing.stub_public_keys! }
|
|
201
206
|
end
|
|
202
207
|
```
|
|
203
|
-
|
|
208
|
+
|
|
209
|
+
`stub_public_keys!` makes the library validate the tokens forged by these helpers, instead of fetching public keys from a Keycloak server.
|
|
210
|
+
`server_url` and `realm_id` therefore do not have to be configured in the test environment. Requests can then be authenticated with:
|
|
211
|
+
|
|
204
212
|
```ruby
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
sub: user.keycloak_id,
|
|
210
|
-
}
|
|
211
|
-
token = JSON::JWT.new(claims)
|
|
212
|
-
token.kid = "default"
|
|
213
|
-
token.sign($private_key, :RS256).to_s
|
|
213
|
+
it "returns the current user" do
|
|
214
|
+
get "/me", headers: keycloak_auth_headers(sub: user.keycloak_id)
|
|
215
|
+
|
|
216
|
+
expect(response).to have_http_status(:ok)
|
|
214
217
|
end
|
|
215
218
|
```
|
|
216
|
-
|
|
219
|
+
|
|
220
|
+
`keycloak_auth_headers` returns an `Authorization` header, and `keycloak_token` returns the token alone.
|
|
221
|
+
Both accept the same optional parameters, one for each claim this library reads:
|
|
222
|
+
|
|
223
|
+
| Parameter | Claim | Default |
|
|
224
|
+
| --- | --- | --- |
|
|
225
|
+
| `sub` | `sub` | a random UUID |
|
|
226
|
+
| `email` | `email` | none |
|
|
227
|
+
| `locale` | `locale` | none |
|
|
228
|
+
| `authorized_party` | `azp` | none |
|
|
229
|
+
| `roles` | `realm_access.roles` | none |
|
|
230
|
+
| `resource_roles` | `resource_access` | none |
|
|
231
|
+
| `issued_at` | `iat` | now |
|
|
232
|
+
| `expires_at` | `exp` | one hour after `issued_at` |
|
|
233
|
+
| `claims` | any other claim | none |
|
|
234
|
+
|
|
235
|
+
For instance, to authenticate a request as a user having the realm role `admin`, the role `reader` on the client `a-client`,
|
|
236
|
+
and a `tenant_id` attribute declared in `config.custom_attributes`:
|
|
237
|
+
|
|
217
238
|
```ruby
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
239
|
+
headers = keycloak_auth_headers(sub: user.keycloak_id,
|
|
240
|
+
roles: ["admin"],
|
|
241
|
+
resource_roles: { "a-client" => ["reader"] },
|
|
242
|
+
claims: { "tenant_id" => tenant.id })
|
|
222
243
|
```
|
|
223
244
|
|
|
245
|
+
An expired token is forged by passing an `expires_at` in the past, which is how a `401` can be tested.
|
|
246
|
+
Assigning `KeycloakApiRails.public_key_resolver = nil` restores the regular resolver.
|
|
247
|
+
|
|
224
248
|
## How to execute library tests
|
|
225
249
|
|
|
226
250
|
From the `keycloak-rails-api` directory:
|
|
@@ -230,6 +254,25 @@ From the `keycloak-rails-api` directory:
|
|
|
230
254
|
$ docker run -v `pwd`:/usr/src/app/ keycloak-rails-api:test bundle exec rspec spec
|
|
231
255
|
```
|
|
232
256
|
|
|
257
|
+
## How to release a new version
|
|
258
|
+
|
|
259
|
+
Releases are published to [RubyGems](https://rubygems.org/gems/keycloak-api-rails) by GitHub Actions
|
|
260
|
+
(`.github/workflows/release.yml`), through [Trusted Publishing](https://guides.rubygems.org/trusted-publishing/):
|
|
261
|
+
no API key is stored in this repository, the workflow exchanges a short-lived GitHub OIDC token for a
|
|
262
|
+
scoped RubyGems credential.
|
|
263
|
+
|
|
264
|
+
1. Update `KeycloakApiRails::VERSION` in `lib/keycloak-api-rails/version.rb` and the `CHANGELOG.md`
|
|
265
|
+
2. Commit and push these changes to `main`
|
|
266
|
+
3. Tag the commit and push the tag:
|
|
267
|
+
|
|
268
|
+
```
|
|
269
|
+
$ git tag -a v1.2.0 -m "Version 1.2.0"
|
|
270
|
+
$ git push origin v1.2.0
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
The workflow then checks that the tag matches `KeycloakApiRails::VERSION`, runs the tests, builds the gem
|
|
274
|
+
and pushes it. It only publishes tags starting with `v`.
|
|
275
|
+
|
|
233
276
|
## Next developments
|
|
234
277
|
|
|
235
278
|
* Manage multiple realms
|
data/Rakefile
ADDED
data/keycloak-api-rails.gemspec
CHANGED
|
@@ -22,6 +22,8 @@ Gem::Specification.new do |spec|
|
|
|
22
22
|
|
|
23
23
|
spec.add_development_dependency "rspec", "3.13.2"
|
|
24
24
|
spec.add_development_dependency "timecop", "0.9.11"
|
|
25
|
+
# Required by 'rake release', which builds and publishes the gem from the CI.
|
|
26
|
+
spec.add_development_dependency "rake", ">= 13.0"
|
|
25
27
|
# Not pinned to an exact version: byebug 12 requires Ruby >= 3.1, byebug 13 requires Ruby >= 3.2.
|
|
26
28
|
# Older Rubies resolve to byebug 11.
|
|
27
29
|
spec.add_development_dependency "byebug", ">= 11.1.3"
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Test helpers for the applications that authenticate their requests with this library.
|
|
2
|
+
#
|
|
3
|
+
# This file is *not* loaded by "keycloak-api-rails": it has to be required explicitly, so that
|
|
4
|
+
# nothing it defines can end up in a production process.
|
|
5
|
+
#
|
|
6
|
+
# # spec/rails_helper.rb
|
|
7
|
+
# require "keycloak-api-rails/testing"
|
|
8
|
+
#
|
|
9
|
+
# RSpec.configure do |config|
|
|
10
|
+
# config.include KeycloakApiRails::Testing::Helpers
|
|
11
|
+
# config.before(:suite) { KeycloakApiRails::Testing.stub_public_keys! }
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# Requests can then be authenticated without a running Keycloak, and without configuring
|
|
15
|
+
# 'server_url' or 'realm_id':
|
|
16
|
+
#
|
|
17
|
+
# get "/me", headers: keycloak_auth_headers(sub: user.keycloak_id, roles: ["admin"])
|
|
18
|
+
|
|
19
|
+
require_relative "../keycloak-api-rails"
|
|
20
|
+
require "securerandom"
|
|
21
|
+
|
|
22
|
+
module KeycloakApiRails
|
|
23
|
+
module Testing
|
|
24
|
+
|
|
25
|
+
KEY_SIZE = 2048
|
|
26
|
+
KEY_ID = "keycloak-api-rails-testing"
|
|
27
|
+
ALGORITHM = :RS256
|
|
28
|
+
DEFAULT_VALIDITY_IN_SECONDS = 3600
|
|
29
|
+
|
|
30
|
+
# Returns the public keys forged by this module, instead of fetching them from a Keycloak server.
|
|
31
|
+
class PublicKeyResolver
|
|
32
|
+
def initialize(public_keys)
|
|
33
|
+
@public_keys = public_keys
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def find_public_keys
|
|
37
|
+
@public_keys
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
class << self
|
|
42
|
+
# The RSA key pair used to sign the tokens forged by this module. It is generated once per
|
|
43
|
+
# process: generating a key is by far the slowest operation of a test suite that uses tokens.
|
|
44
|
+
def private_key
|
|
45
|
+
@private_key ||= OpenSSL::PKey::RSA.generate(KEY_SIZE)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def signing_key
|
|
49
|
+
@signing_key ||= JSON::JWK.new(private_key, kid: KEY_ID)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def public_keys
|
|
53
|
+
@public_keys ||= JSON::JWK::Set.new(JSON::JWK.new(private_key.public_key, kid: KEY_ID))
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Makes the library validate the tokens forged by this module. Assigning
|
|
57
|
+
# 'KeycloakApiRails.public_key_resolver = nil' restores the regular resolver.
|
|
58
|
+
def stub_public_keys!
|
|
59
|
+
KeycloakApiRails.public_key_resolver = PublicKeyResolver.new(public_keys)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Forges a signed token, similar to the ones emitted by Keycloak. Every claim this library
|
|
63
|
+
# reads has a dedicated parameter; any other claim can be passed through 'claims', which is
|
|
64
|
+
# also how the attributes declared in 'config.custom_attributes' are set.
|
|
65
|
+
def token_for(sub: SecureRandom.uuid,
|
|
66
|
+
email: nil,
|
|
67
|
+
locale: nil,
|
|
68
|
+
authorized_party: nil,
|
|
69
|
+
roles: [],
|
|
70
|
+
resource_roles: {},
|
|
71
|
+
issued_at: Time.now,
|
|
72
|
+
expires_at: nil,
|
|
73
|
+
claims: {})
|
|
74
|
+
payload = {
|
|
75
|
+
"sub" => sub,
|
|
76
|
+
"email" => email,
|
|
77
|
+
"locale" => locale,
|
|
78
|
+
"azp" => authorized_party,
|
|
79
|
+
"iat" => issued_at.to_i,
|
|
80
|
+
"exp" => (expires_at || (issued_at.to_i + DEFAULT_VALIDITY_IN_SECONDS)).to_i
|
|
81
|
+
}.reject { |_, value| value.nil? }
|
|
82
|
+
|
|
83
|
+
payload["realm_access"] = { "roles" => roles.map(&:to_s) } unless roles.nil? || roles.empty?
|
|
84
|
+
payload["resource_access"] = build_resource_access(resource_roles) unless resource_roles.nil? || resource_roles.empty?
|
|
85
|
+
|
|
86
|
+
claims.each { |name, value| payload[name.to_s] = value }
|
|
87
|
+
|
|
88
|
+
JSON::JWT.new(payload).sign(signing_key, ALGORITHM).to_s
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# The 'Authorization' header a client would send, e.g. { "Authorization" => "Bearer eyJ0..." }.
|
|
92
|
+
# It accepts the same parameters as '.token_for'.
|
|
93
|
+
def auth_headers_for(**options)
|
|
94
|
+
{ "Authorization" => "Bearer #{token_for(**options)}" }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private
|
|
98
|
+
|
|
99
|
+
def build_resource_access(resource_roles)
|
|
100
|
+
resource_roles.inject({}) do |resource_access, (name, roles)|
|
|
101
|
+
resource_access[name.to_s] = { "roles" => Array(roles).map(&:to_s) }
|
|
102
|
+
resource_access
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Meant to be included in a test suite, e.g. 'config.include KeycloakApiRails::Testing::Helpers'.
|
|
108
|
+
module Helpers
|
|
109
|
+
def keycloak_token(**options)
|
|
110
|
+
KeycloakApiRails::Testing.token_for(**options)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def keycloak_auth_headers(**options)
|
|
114
|
+
KeycloakApiRails::Testing.auth_headers_for(**options)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
data/lib/keycloak-api-rails.rb
CHANGED
|
@@ -33,6 +33,14 @@ module KeycloakApiRails
|
|
|
33
33
|
@public_key_resolver ||= PublicKeyCachedResolver.from_configuration(http_client, config)
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
# Mainly used by "keycloak-api-rails/testing" to validate tokens without a Keycloak server.
|
|
37
|
+
# Assigning nil restores the regular resolver. The memoized service is discarded, since it holds
|
|
38
|
+
# a reference to the resolver that is being replaced.
|
|
39
|
+
def self.public_key_resolver=(resolver)
|
|
40
|
+
@public_key_resolver = resolver
|
|
41
|
+
@service = nil
|
|
42
|
+
end
|
|
43
|
+
|
|
36
44
|
def self.service
|
|
37
45
|
@service ||= KeycloakApiRails::Service.new(public_key_resolver)
|
|
38
46
|
end
|
|
@@ -205,6 +205,12 @@ RSpec.describe KeycloakApiRails::Service do
|
|
|
205
205
|
@result = service2.need_middleware_authentication?(method, path, headers)
|
|
206
206
|
end
|
|
207
207
|
|
|
208
|
+
# The configuration is global: leaving 'opt_in' enabled would disable the authentication
|
|
209
|
+
# of every example running afterwards.
|
|
210
|
+
after do
|
|
211
|
+
KeycloakApiRails.config.opt_in = false
|
|
212
|
+
end
|
|
213
|
+
|
|
208
214
|
it "should return false" do
|
|
209
215
|
expect(@result).to be false
|
|
210
216
|
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
require_relative "../../lib/keycloak-api-rails/testing"
|
|
2
|
+
|
|
3
|
+
RSpec.describe KeycloakApiRails::Testing do
|
|
4
|
+
include KeycloakApiRails::Testing::Helpers
|
|
5
|
+
|
|
6
|
+
# The tokens are validated by the real middleware and the real service, so that these examples
|
|
7
|
+
# fail if what this module forges stops being what the library expects.
|
|
8
|
+
let(:authenticated_env) { {} }
|
|
9
|
+
let(:app) do
|
|
10
|
+
downstream = lambda do |env|
|
|
11
|
+
authenticated_env.replace(env)
|
|
12
|
+
[200, {}, ["OK"]]
|
|
13
|
+
end
|
|
14
|
+
KeycloakApiRails::Middleware.new(downstream)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def get(headers)
|
|
18
|
+
app.call({
|
|
19
|
+
"REQUEST_METHOD" => "GET",
|
|
20
|
+
"PATH_INFO" => "/me",
|
|
21
|
+
"REQUEST_URI" => "http://example.org/me",
|
|
22
|
+
"HTTP_AUTHORIZATION" => headers["Authorization"]
|
|
23
|
+
})
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
before(:each) do
|
|
27
|
+
# The middleware only authenticates a request when the configuration tells it to, and the
|
|
28
|
+
# configuration is global: these examples cannot rely on what the other ones leave behind.
|
|
29
|
+
KeycloakApiRails.config.opt_in = false
|
|
30
|
+
KeycloakApiRails.config.skip_paths = {}
|
|
31
|
+
KeycloakApiRails::Testing.stub_public_keys!
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
after(:each) do
|
|
35
|
+
KeycloakApiRails.public_key_resolver = nil
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe ".stub_public_keys!" do
|
|
39
|
+
it "should validate the tokens forged by this module" do
|
|
40
|
+
status, _headers, _body = get(keycloak_auth_headers)
|
|
41
|
+
|
|
42
|
+
expect(status).to eq(200)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "should not require a Keycloak server to be configured" do
|
|
46
|
+
expect(KeycloakApiRails.config.server_url).to be_nil
|
|
47
|
+
expect(KeycloakApiRails.config.realm_id).to be_nil
|
|
48
|
+
|
|
49
|
+
status, _headers, _body = get(keycloak_auth_headers)
|
|
50
|
+
|
|
51
|
+
expect(status).to eq(200)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe ".token_for" do
|
|
56
|
+
it "should authenticate the given user" do
|
|
57
|
+
get(keycloak_auth_headers(sub: "a-keycloak-id"))
|
|
58
|
+
|
|
59
|
+
expect(KeycloakApiRails::Helper.current_user_id(authenticated_env)).to eq("a-keycloak-id")
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "should generate a different user for each token" do
|
|
63
|
+
first_token = JSON::JWT.decode(keycloak_token, KeycloakApiRails::Testing.public_keys)
|
|
64
|
+
second_token = JSON::JWT.decode(keycloak_token, KeycloakApiRails::Testing.public_keys)
|
|
65
|
+
|
|
66
|
+
expect(first_token["sub"]).not_to eq(second_token["sub"])
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it "should expose every claim read by the library" do
|
|
70
|
+
get(keycloak_auth_headers(sub: "a-keycloak-id",
|
|
71
|
+
email: "an-email@keycloak.io",
|
|
72
|
+
locale: "fr",
|
|
73
|
+
authorized_party: "a-client",
|
|
74
|
+
roles: ["admin", "user"],
|
|
75
|
+
resource_roles: { "a-client" => ["reader"] }))
|
|
76
|
+
|
|
77
|
+
expect(KeycloakApiRails::Helper.current_user_id(authenticated_env)).to eq("a-keycloak-id")
|
|
78
|
+
expect(KeycloakApiRails::Helper.current_user_email(authenticated_env)).to eq("an-email@keycloak.io")
|
|
79
|
+
expect(KeycloakApiRails::Helper.current_user_locale(authenticated_env)).to eq("fr")
|
|
80
|
+
expect(KeycloakApiRails::Helper.current_authorized_party(authenticated_env)).to eq("a-client")
|
|
81
|
+
expect(KeycloakApiRails::Helper.current_user_roles(authenticated_env)).to eq(["admin", "user"])
|
|
82
|
+
expect(KeycloakApiRails::Helper.current_resource_roles(authenticated_env)).to eq({ "a-client" => ["reader"] })
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it "should expose the claims declared as custom attributes" do
|
|
86
|
+
KeycloakApiRails.config.custom_attributes = ["tenant_id"]
|
|
87
|
+
|
|
88
|
+
get(keycloak_auth_headers(claims: { "tenant_id" => "a-tenant" }))
|
|
89
|
+
|
|
90
|
+
expect(KeycloakApiRails::Helper.current_user_custom_attributes(authenticated_env)).to eq({ "tenant_id" => "a-tenant" })
|
|
91
|
+
ensure
|
|
92
|
+
KeycloakApiRails.config.custom_attributes = []
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "should forge a token that is expired when 'expires_at' is in the past" do
|
|
96
|
+
status, _headers, body = get(keycloak_auth_headers(issued_at: Time.now - 7200, expires_at: Time.now - 3600))
|
|
97
|
+
|
|
98
|
+
expect(status).to eq(401)
|
|
99
|
+
expect(body.first).to include("JWT token is expired")
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: keycloak-api-rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.1.
|
|
4
|
+
version: 1.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Lorent Lempereur
|
|
@@ -65,6 +65,20 @@ dependencies:
|
|
|
65
65
|
- - '='
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
67
|
version: 0.9.11
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: rake
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '13.0'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '13.0'
|
|
68
82
|
- !ruby/object:Gem::Dependency
|
|
69
83
|
name: byebug
|
|
70
84
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -87,6 +101,7 @@ extensions: []
|
|
|
87
101
|
extra_rdoc_files: []
|
|
88
102
|
files:
|
|
89
103
|
- ".github/workflows/ci.yml"
|
|
104
|
+
- ".github/workflows/release.yml"
|
|
90
105
|
- ".gitignore"
|
|
91
106
|
- ".rspec"
|
|
92
107
|
- CHANGELOG.md
|
|
@@ -94,6 +109,7 @@ files:
|
|
|
94
109
|
- Gemfile
|
|
95
110
|
- MIT-LICENSE
|
|
96
111
|
- README.md
|
|
112
|
+
- Rakefile
|
|
97
113
|
- keycloak-api-rails.gemspec
|
|
98
114
|
- lib/keycloak-api-rails.rb
|
|
99
115
|
- lib/keycloak-api-rails/authentication.rb
|
|
@@ -105,6 +121,7 @@ files:
|
|
|
105
121
|
- lib/keycloak-api-rails/public_key_resolver.rb
|
|
106
122
|
- lib/keycloak-api-rails/railtie.rb
|
|
107
123
|
- lib/keycloak-api-rails/service.rb
|
|
124
|
+
- lib/keycloak-api-rails/testing.rb
|
|
108
125
|
- lib/keycloak-api-rails/token_error.rb
|
|
109
126
|
- lib/keycloak-api-rails/version.rb
|
|
110
127
|
- spec/keycloak-api-rails/activesupport_free_spec.rb
|
|
@@ -113,6 +130,7 @@ files:
|
|
|
113
130
|
- spec/keycloak-api-rails/helper_spec.rb
|
|
114
131
|
- spec/keycloak-api-rails/public_key_cached_resolver_spec.rb
|
|
115
132
|
- spec/keycloak-api-rails/service_spec.rb
|
|
133
|
+
- spec/keycloak-api-rails/testing_spec.rb
|
|
116
134
|
- spec/spec_helper.rb
|
|
117
135
|
- spec/support/activesupport_free_probe.rb
|
|
118
136
|
- spec/support/public_key_cached_resolver_stub.rb
|
|
@@ -136,7 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
136
154
|
- !ruby/object:Gem::Version
|
|
137
155
|
version: '0'
|
|
138
156
|
requirements: []
|
|
139
|
-
rubygems_version:
|
|
157
|
+
rubygems_version: 3.6.9
|
|
140
158
|
specification_version: 4
|
|
141
159
|
summary: Rails middleware that validates Authorization token emitted by Keycloak
|
|
142
160
|
test_files: []
|