keycloak_oauth 0.1.10 → 2.0.0
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/README.md +23 -10
- data/app/controllers/keycloak_oauth/callbacks_controller.rb +2 -4
- data/app/services/keycloak_oauth/authentication_service.rb +12 -19
- data/app/services/keycloak_oauth/authentication_service_base.rb +42 -0
- data/app/services/keycloak_oauth/{post_token_service.rb → post_authorization_code_service.rb} +1 -1
- data/app/services/keycloak_oauth/post_refresh_token_service.rb +39 -0
- data/app/services/keycloak_oauth/refresh_authentication_service.rb +21 -0
- data/lib/keycloak_oauth/version.rb +1 -1
- metadata +65 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: baa20f03dc097cf9b5f18e60f1cd860e3590fa2eafe097989d39d1b29690dd57
|
4
|
+
data.tar.gz: 477b1887a6020aa95bd7e680bf1abe508ad90b99cc480606fdd5daec81adf210
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a5b936cdf70211176742efcad5d1b799d6c72ecc1047c141e07c55ddb6c1b33985879d018616f30ab6ce7bb9b4e2be970977c4a1bcecb417bcf0ebd9e0787d68
|
7
|
+
data.tar.gz: 6f99f5fd86bff651312a0477ca719fb227ae1b5a960c9ebf066cb7e3a2d7a150f1d42a69c0df25efc6ccd78fd733ab220982e053e73202dfadf87d9268c8a1e5
|
data/README.md
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# KeycloakOauth
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
3
|
+
[Keycloak](https://www.keycloak.org) integration for Ruby on Rails.
|
6
4
|
|
7
5
|
## Installation
|
8
6
|
|
@@ -24,6 +22,12 @@ Or install it yourself as:
|
|
24
22
|
|
25
23
|
### Using `keycloak_oauth` in a Ruby on Rails app
|
26
24
|
|
25
|
+
Unless you plan to overwrite the oauth callback controller (see further below), mount the Rails engine into your application by specifying the following in `config/routes.rb`
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
mount KeycloakOauth::Engine => "/keycloak_oauth"
|
29
|
+
```
|
30
|
+
|
27
31
|
The configuration must be defined in the app by initialising the relevant attributes within a configuration block. For example, you could add an initializer script called `keycloak_oauth.rb` holding the following code:
|
28
32
|
|
29
33
|
```ruby
|
@@ -38,13 +42,22 @@ end
|
|
38
42
|
This then allows you to access the `KeycloakOauth` APIs:
|
39
43
|
`KeycloakOauth.connection.authorization_endpoint`
|
40
44
|
|
41
|
-
|
45
|
+
Ensure you have `default_url_options` set. Here
|
46
|
+
is an example of `default_url_options` appropriate for a development environment
|
47
|
+
in `config/environments/development.rb`:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
|
51
|
+
```
|
52
|
+
|
53
|
+
You can allow the user to log in with Keycloak by adding a link that points to `KeycloakOauth.connection.authorization_endpoint`:
|
42
54
|
e.g.
|
43
|
-
`<%= link_to 'Login with Keycloak', KeycloakOauth.connection.authorization_endpoint %>`
|
55
|
+
`<%= link_to 'Login with Keycloak', KeycloakOauth.connection.authorization_endpoint(options: { redirect_uri: keycloak_oauth.oauth2_url }) %>`
|
56
|
+
|
57
|
+
Once authentication is performed, the access and refresh tokens are stored in the session and can be used in your app as wished. As the session can become larger than we can store in a cookie (`CookieOverflow` exception), we recommend to use [activerecord-session_store](https://github.com/rails/activerecord-session_store).
|
44
58
|
|
45
|
-
|
59
|
+
### Customising redirect URIs
|
46
60
|
|
47
|
-
***Customising redirect URIs***
|
48
61
|
There are situations where you would want to customise the oauth2 route (e.g. to use a localised version of the callback URL).
|
49
62
|
In this case, you can do the following:
|
50
63
|
- add a controller to your app: e.g. `CallbackOverrides`
|
@@ -64,7 +77,7 @@ If you need the user to be redirected to something other than the root path, you
|
|
64
77
|
3. In the method, perform whatever logic you need to return the right path e.g.
|
65
78
|
```ruby
|
66
79
|
def after_sign_in_path
|
67
|
-
my_custom_path
|
80
|
+
main_app.my_custom_path
|
68
81
|
end
|
69
82
|
```
|
70
83
|
4. Tell the gem where you've overridden the paths by setting the following config in your configuration initializer file:
|
@@ -112,7 +125,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
112
125
|
|
113
126
|
## Contributing
|
114
127
|
|
115
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
128
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/simplificator/keycloak_oauth. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/simplificator/keycloak_oauth/blob/master/CODE_OF_CONDUCT.md).
|
116
129
|
|
117
130
|
|
118
131
|
## License
|
@@ -121,4 +134,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
121
134
|
|
122
135
|
## Code of Conduct
|
123
136
|
|
124
|
-
Everyone interacting in the KeycloakOauth project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
137
|
+
Everyone interacting in the KeycloakOauth project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/simplificator/keycloak_oauth/blob/master/CODE_OF_CONDUCT.md).
|
@@ -12,7 +12,7 @@ module KeycloakOauth
|
|
12
12
|
authentication_service.authenticate
|
13
13
|
map_authenticatable_if_implemented(session)
|
14
14
|
|
15
|
-
redirect_to self.class.method_defined?(:after_sign_in_path) ? after_sign_in_path(request) :
|
15
|
+
redirect_to self.class.method_defined?(:after_sign_in_path) ? after_sign_in_path(request) : main_app.root_path
|
16
16
|
end
|
17
17
|
|
18
18
|
private
|
@@ -24,8 +24,6 @@ module KeycloakOauth
|
|
24
24
|
def map_authenticatable_if_implemented(request)
|
25
25
|
if self.class.method_defined?(:map_authenticatable)
|
26
26
|
map_authenticatable(request)
|
27
|
-
else
|
28
|
-
raise NotImplementedError.new('User mapping must be handled by the host app. See README for more information.')
|
29
27
|
end
|
30
28
|
end
|
31
29
|
|
@@ -36,7 +34,7 @@ module KeycloakOauth
|
|
36
34
|
main_app.url_for(only_path: false, overwrite_params: nil)
|
37
35
|
rescue ActionController::UrlGenerationError
|
38
36
|
# If the host app does not override the oauth2 path, use the engine's path.
|
39
|
-
|
37
|
+
oauth2_url
|
40
38
|
end
|
41
39
|
end
|
42
40
|
end
|
@@ -1,31 +1,24 @@
|
|
1
1
|
module KeycloakOauth
|
2
|
-
class AuthenticationService
|
3
|
-
ACCESS_TOKEN_KEY = 'access_token'.freeze
|
4
|
-
REFRESH_TOKEN_KEY = 'refresh_token'.freeze
|
2
|
+
class AuthenticationService < AuthenticationServiceBase
|
5
3
|
|
6
|
-
attr_reader :
|
4
|
+
attr_reader :authentication_params
|
7
5
|
|
8
|
-
def initialize(
|
6
|
+
def initialize(session:, authentication_params:)
|
9
7
|
@authentication_params = authentication_params
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
def authenticate
|
14
|
-
post_token_service = KeycloakOauth::PostTokenService.new(
|
15
|
-
connection: KeycloakOauth.connection,
|
16
|
-
request_params: authentication_params
|
17
|
-
)
|
18
|
-
post_token_service.perform
|
19
|
-
store_credentials(post_token_service)
|
8
|
+
super session: session
|
20
9
|
end
|
21
10
|
|
22
11
|
private
|
23
12
|
|
24
|
-
def
|
25
|
-
|
13
|
+
def post_service_name
|
14
|
+
KeycloakOauth::PostAuthorizationCodeService
|
15
|
+
end
|
26
16
|
|
27
|
-
|
28
|
-
|
17
|
+
def post_service_arguments
|
18
|
+
{
|
19
|
+
connection: KeycloakOauth.connection,
|
20
|
+
request_params: authentication_params
|
21
|
+
}
|
29
22
|
end
|
30
23
|
end
|
31
24
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module KeycloakOauth
|
2
|
+
class AuthenticationServiceBase
|
3
|
+
ACCESS_TOKEN_KEY = 'access_token'.freeze
|
4
|
+
ACCESS_TOKEN_EXPIRES_IN = 'expires_in'.freeze
|
5
|
+
REFRESH_TOKEN_KEY = 'refresh_token'.freeze
|
6
|
+
REFRESH_TOKEN_EXPIRES_IN = 'refresh_expires_in'.freeze
|
7
|
+
|
8
|
+
attr_reader :session
|
9
|
+
|
10
|
+
def initialize(session:)
|
11
|
+
@session = session
|
12
|
+
end
|
13
|
+
|
14
|
+
def authenticate
|
15
|
+
request_time = Time.zone.now
|
16
|
+
post_token_service = post_service_name.new(**post_service_arguments)
|
17
|
+
post_token_service.perform
|
18
|
+
store_credentials_in_session(post_token_service, request_time)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def post_service_name
|
24
|
+
raise "Implement in subclass"
|
25
|
+
end
|
26
|
+
|
27
|
+
def post_service_arguments
|
28
|
+
raise "Implement in subclass"
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def store_credentials_in_session(post_token_service, request_time)
|
33
|
+
response_hash = post_token_service.parsed_response_body
|
34
|
+
|
35
|
+
session[:access_token] = response_hash[ACCESS_TOKEN_KEY]
|
36
|
+
session[:refresh_token] = response_hash[REFRESH_TOKEN_KEY]
|
37
|
+
|
38
|
+
session[:access_token_expires_at] = request_time + response_hash[ACCESS_TOKEN_EXPIRES_IN].to_i.seconds
|
39
|
+
session[:refresh_token_expires_at] = request_time + response_hash[REFRESH_TOKEN_EXPIRES_IN].to_i.seconds
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/app/services/keycloak_oauth/{post_token_service.rb → post_authorization_code_service.rb}
RENAMED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'net/http'
|
2
2
|
|
3
3
|
module KeycloakOauth
|
4
|
-
class
|
4
|
+
class PostAuthorizationCodeService < KeycloakOauth::AuthorizableService
|
5
5
|
DEFAULT_GRANT_TYPE = 'authorization_code'.freeze
|
6
6
|
|
7
7
|
attr_reader :request_params, :connection
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
module KeycloakOauth
|
4
|
+
class PostRefreshTokenService < KeycloakOauth::AuthorizableService
|
5
|
+
DEFAULT_GRANT_TYPE = 'refresh_token'.freeze
|
6
|
+
|
7
|
+
def initialize(connection:, refresh_token:)
|
8
|
+
@connection = connection
|
9
|
+
@refresh_token = refresh_token
|
10
|
+
end
|
11
|
+
|
12
|
+
def send_request
|
13
|
+
post_token
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
attr_reader :connection, :refresh_token
|
19
|
+
|
20
|
+
def post_token
|
21
|
+
uri = URI.parse(connection.authentication_endpoint)
|
22
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
23
|
+
request = Net::HTTP::Post.new(uri)
|
24
|
+
request.set_content_type(CONTENT_TYPE_X_WWW_FORM_URLENCODED)
|
25
|
+
request.set_form_data(token_request_params)
|
26
|
+
http.request(request)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def token_request_params
|
31
|
+
{
|
32
|
+
client_id: connection.client_id,
|
33
|
+
client_secret: connection.client_secret,
|
34
|
+
grant_type: DEFAULT_GRANT_TYPE,
|
35
|
+
refresh_token: refresh_token
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module KeycloakOauth
|
2
|
+
class RefreshAuthenticationService < AuthenticationServiceBase
|
3
|
+
|
4
|
+
def initialize(session:)
|
5
|
+
super
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def post_service_name
|
11
|
+
KeycloakOauth::PostRefreshTokenService
|
12
|
+
end
|
13
|
+
|
14
|
+
def post_service_arguments
|
15
|
+
{
|
16
|
+
connection: KeycloakOauth.connection,
|
17
|
+
refresh_token: session[:refresh_token]
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
metadata
CHANGED
@@ -1,35 +1,71 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: keycloak_oauth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- simplificator
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-06-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 6.0.3
|
20
17
|
- - ">="
|
21
18
|
- !ruby/object:Gem::Version
|
22
|
-
version: 6.0
|
19
|
+
version: '6.0'
|
23
20
|
type: :runtime
|
24
21
|
prerelease: false
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
26
23
|
requirements:
|
27
|
-
- - "
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '6.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activerecord-session_store
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: appraisal
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
28
53
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: combustion
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
30
59
|
- - ">="
|
31
60
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
33
69
|
- !ruby/object:Gem::Dependency
|
34
70
|
name: rspec-rails
|
35
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,6 +94,20 @@ dependencies:
|
|
58
94
|
- - ">="
|
59
95
|
- !ruby/object:Gem::Version
|
60
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: timecop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
61
111
|
description:
|
62
112
|
email:
|
63
113
|
- dev@simplificator.com
|
@@ -69,12 +119,15 @@ files:
|
|
69
119
|
- Rakefile
|
70
120
|
- app/controllers/keycloak_oauth/callbacks_controller.rb
|
71
121
|
- app/services/keycloak_oauth/authentication_service.rb
|
122
|
+
- app/services/keycloak_oauth/authentication_service_base.rb
|
72
123
|
- app/services/keycloak_oauth/authorizable_service.rb
|
73
124
|
- app/services/keycloak_oauth/get_users_service.rb
|
74
125
|
- app/services/keycloak_oauth/logout_service.rb
|
75
|
-
- app/services/keycloak_oauth/
|
126
|
+
- app/services/keycloak_oauth/post_authorization_code_service.rb
|
127
|
+
- app/services/keycloak_oauth/post_refresh_token_service.rb
|
76
128
|
- app/services/keycloak_oauth/post_users_service.rb
|
77
129
|
- app/services/keycloak_oauth/put_execute_actions_email_service.rb
|
130
|
+
- app/services/keycloak_oauth/refresh_authentication_service.rb
|
78
131
|
- app/services/keycloak_oauth/user_info_retrieval_service.rb
|
79
132
|
- config/routes.rb
|
80
133
|
- lib/keycloak_oauth.rb
|
@@ -98,14 +151,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
98
151
|
requirements:
|
99
152
|
- - ">="
|
100
153
|
- !ruby/object:Gem::Version
|
101
|
-
version: 2.
|
154
|
+
version: 2.6.0
|
102
155
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
156
|
requirements:
|
104
157
|
- - ">="
|
105
158
|
- !ruby/object:Gem::Version
|
106
159
|
version: '0'
|
107
160
|
requirements: []
|
108
|
-
rubygems_version: 3.1.
|
161
|
+
rubygems_version: 3.1.6
|
109
162
|
signing_key:
|
110
163
|
specification_version: 4
|
111
164
|
summary: Implementing OAuth with Keycloak in Ruby
|