doorkeeper-openid_connect 1.7.1 → 1.8.0.pre.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -0
  3. data/README.md +40 -0
  4. data/app/controllers/doorkeeper/authorizations_controller.rb +1 -1
  5. data/app/controllers/doorkeeper/openid_connect/discovery_controller.rb +52 -26
  6. data/app/controllers/doorkeeper/openid_connect/userinfo_controller.rb +5 -1
  7. data/config/locales/en.yml +1 -0
  8. data/lib/doorkeeper/oauth/id_token_request.rb +7 -1
  9. data/lib/doorkeeper/oauth/id_token_response.rb +7 -7
  10. data/lib/doorkeeper/oauth/id_token_token_request.rb +2 -0
  11. data/lib/doorkeeper/oauth/id_token_token_response.rb +3 -3
  12. data/lib/doorkeeper/openid_connect.rb +21 -2
  13. data/lib/doorkeeper/openid_connect/claims/aggregated_claim.rb +2 -0
  14. data/lib/doorkeeper/openid_connect/claims/claim.rb +6 -4
  15. data/lib/doorkeeper/openid_connect/claims/distributed_claim.rb +2 -0
  16. data/lib/doorkeeper/openid_connect/claims/normal_claim.rb +2 -0
  17. data/lib/doorkeeper/openid_connect/claims_builder.rb +3 -1
  18. data/lib/doorkeeper/openid_connect/config.rb +24 -10
  19. data/lib/doorkeeper/openid_connect/engine.rb +2 -0
  20. data/lib/doorkeeper/openid_connect/errors.rb +2 -1
  21. data/lib/doorkeeper/openid_connect/helpers/controller.rb +52 -26
  22. data/lib/doorkeeper/openid_connect/id_token.rb +8 -2
  23. data/lib/doorkeeper/openid_connect/id_token_token.rb +2 -0
  24. data/lib/doorkeeper/openid_connect/oauth/authorization/code.rb +25 -8
  25. data/lib/doorkeeper/openid_connect/oauth/authorization_code_request.rb +4 -2
  26. data/lib/doorkeeper/openid_connect/oauth/password_access_token_request.rb +3 -1
  27. data/lib/doorkeeper/openid_connect/oauth/pre_authorization.rb +12 -14
  28. data/lib/doorkeeper/openid_connect/oauth/token_response.rb +3 -1
  29. data/lib/doorkeeper/openid_connect/orm/active_record.rb +2 -0
  30. data/lib/doorkeeper/openid_connect/orm/active_record/access_grant.rb +3 -1
  31. data/lib/doorkeeper/openid_connect/orm/active_record/request.rb +5 -3
  32. data/lib/doorkeeper/openid_connect/rails/routes.rb +3 -1
  33. data/lib/doorkeeper/openid_connect/rails/routes/mapper.rb +2 -0
  34. data/lib/doorkeeper/openid_connect/rails/routes/mapping.rb +2 -0
  35. data/lib/doorkeeper/openid_connect/user_info.rb +2 -0
  36. data/lib/doorkeeper/openid_connect/version.rb +3 -1
  37. data/lib/doorkeeper/request/id_token.rb +2 -0
  38. data/lib/doorkeeper/request/id_token_token.rb +2 -0
  39. data/lib/generators/doorkeeper/openid_connect/install_generator.rb +4 -2
  40. data/lib/generators/doorkeeper/openid_connect/migration_generator.rb +3 -1
  41. data/lib/generators/doorkeeper/openid_connect/templates/initializer.rb +22 -6
  42. data/lib/generators/doorkeeper/openid_connect/templates/migration.rb.erb +1 -1
  43. metadata +26 -36
  44. data/.gitignore +0 -8
  45. data/.ruby-version +0 -1
  46. data/.travis.yml +0 -27
  47. data/CONTRIBUTING.md +0 -45
  48. data/Gemfile +0 -8
  49. data/Rakefile +0 -24
  50. data/bin/console +0 -9
  51. data/bin/setup +0 -8
  52. data/doorkeeper-openid_connect.gemspec +0 -32
  53. data/lib/doorkeeper/openid_connect/response_types_config.rb +0 -17
@@ -1,15 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OpenidConnect
3
5
  def self.configure(&block)
4
6
  if Doorkeeper.configuration.orm != :active_record
5
- fail Errors::InvalidConfiguration, 'Doorkeeper OpenID Connect currently only supports the ActiveRecord ORM adapter'
7
+ raise Errors::InvalidConfiguration, 'Doorkeeper OpenID Connect currently only supports the ActiveRecord ORM adapter'
6
8
  end
7
9
 
8
10
  @config = Config::Builder.new(&block).build
9
11
  end
10
12
 
11
13
  def self.configuration
12
- @config || (fail Errors::MissingConfiguration)
14
+ @config || (raise Errors::MissingConfiguration)
13
15
  end
14
16
 
15
17
  class Config
@@ -23,12 +25,12 @@ module Doorkeeper
23
25
  @config
24
26
  end
25
27
 
26
- def jws_public_key(*args)
27
- puts "DEPRECATION WARNING: `jws_public_key` is not needed anymore and will be removed in a future version, please remove it from config/initializers/doorkeeper_openid_connect.rb"
28
+ def jws_public_key(*_args)
29
+ puts 'DEPRECATION WARNING: `jws_public_key` is not needed anymore and will be removed in a future version, please remove it from config/initializers/doorkeeper_openid_connect.rb'
28
30
  end
29
31
 
30
32
  def jws_private_key(*args)
31
- puts "DEPRECATION WARNING: `jws_private_key` has been replaced by `signing_key` and will be removed in a future version, please remove it from config/initializers/doorkeeper_openid_connect.rb"
33
+ puts 'DEPRECATION WARNING: `jws_private_key` has been replaced by `signing_key` and will be removed in a future version, please remove it from config/initializers/doorkeeper_openid_connect.rb'
32
34
  signing_key(*args)
33
35
  end
34
36
  end
@@ -71,7 +73,7 @@ module Doorkeeper
71
73
  value = if attribute_builder
72
74
  attribute_builder.new(&block).build
73
75
  else
74
- block ? block : args.first
76
+ block || args.first
75
77
  end
76
78
 
77
79
  @config.instance_variable_set(:"@#{attribute}", value)
@@ -102,19 +104,23 @@ module Doorkeeper
102
104
  option :subject_types_supported, default: [:public]
103
105
 
104
106
  option :resource_owner_from_access_token, default: lambda { |*_|
105
- fail Errors::InvalidConfiguration, I18n.translate('doorkeeper.openid_connect.errors.messages.resource_owner_from_access_token_not_configured')
107
+ raise Errors::InvalidConfiguration, I18n.translate('doorkeeper.openid_connect.errors.messages.resource_owner_from_access_token_not_configured')
106
108
  }
107
109
 
108
110
  option :auth_time_from_resource_owner, default: lambda { |*_|
109
- fail Errors::InvalidConfiguration, I18n.translate('doorkeeper.openid_connect.errors.messages.auth_time_from_resource_owner_not_configured')
111
+ raise Errors::InvalidConfiguration, I18n.translate('doorkeeper.openid_connect.errors.messages.auth_time_from_resource_owner_not_configured')
110
112
  }
111
113
 
112
114
  option :reauthenticate_resource_owner, default: lambda { |*_|
113
- fail Errors::InvalidConfiguration, I18n.translate('doorkeeper.openid_connect.errors.messages.reauthenticate_resource_owner_not_configured')
115
+ raise Errors::InvalidConfiguration, I18n.translate('doorkeeper.openid_connect.errors.messages.reauthenticate_resource_owner_not_configured')
116
+ }
117
+
118
+ option :select_account_for_resource_owner, default: lambda { |*_|
119
+ raise Errors::InvalidConfiguration, I18n.translate('doorkeeper.openid_connect.errors.messages.select_account_for_resource_owner_not_configured')
114
120
  }
115
121
 
116
122
  option :subject, default: lambda { |*_|
117
- fail Errors::InvalidConfiguration, I18n.translate('doorkeeper.openid_connect.errors.messages.subject_not_configured')
123
+ raise Errors::InvalidConfiguration, I18n.translate('doorkeeper.openid_connect.errors.messages.subject_not_configured')
118
124
  }
119
125
 
120
126
  option :expiration, default: 120
@@ -124,6 +130,14 @@ module Doorkeeper
124
130
  option :protocol, default: lambda { |*_|
125
131
  ::Rails.env.production? ? :https : :http
126
132
  }
133
+
134
+ option :end_session_endpoint, default: lambda { |*_|
135
+ nil
136
+ }
137
+
138
+ option :discovery_url_options, default: lambda { |*_|
139
+ {}
140
+ }
127
141
  end
128
142
  end
129
143
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OpenidConnect
3
5
  class Engine < ::Rails::Engine
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OpenidConnect
3
5
  module Errors
@@ -24,7 +26,6 @@ module Doorkeeper
24
26
  class LoginRequired < OpenidConnectError; end
25
27
  class ConsentRequired < OpenidConnectError; end
26
28
  class InteractionRequired < OpenidConnectError; end
27
- class AccountSelectionRequired < OpenidConnectError; end
28
29
  end
29
30
  end
30
31
  end
@@ -1,9 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OpenidConnect
3
5
  module Helpers
4
6
  module Controller
5
7
  private
6
8
 
9
+ # FIXME: remove after Doorkeeper will merge it
10
+ def current_resource_owner
11
+ return @current_resource_owner if defined?(@current_resource_owner)
12
+
13
+ super
14
+ end
15
+
7
16
  def authenticate_resource_owner!
8
17
  super.tap do |owner|
9
18
  next unless oidc_authorization_request?
@@ -11,8 +20,8 @@ module Doorkeeper
11
20
  handle_oidc_prompt_param!(owner)
12
21
  handle_oidc_max_age_param!(owner)
13
22
  end
14
- rescue Errors::OpenidConnectError => exception
15
- handle_oidc_error!(exception)
23
+ rescue Errors::OpenidConnectError => e
24
+ handle_oidc_error!(e)
16
25
  end
17
26
 
18
27
  def oidc_authorization_request?
@@ -30,26 +39,29 @@ module Doorkeeper
30
39
  @_response_body = nil
31
40
 
32
41
  error_response = if exception.type == :invalid_request
33
- ::Doorkeeper::OAuth::InvalidRequestResponse.new(
34
- name: exception.type,
35
- state: params[:state],
36
- redirect_uri: params[:redirect_uri],
37
- )
38
- else
39
- ::Doorkeeper::OAuth::ErrorResponse.new(
40
- name: exception.type,
41
- state: params[:state],
42
- redirect_uri: params[:redirect_uri],
43
- )
44
- end
42
+ ::Doorkeeper::OAuth::InvalidRequestResponse.new(
43
+ name: exception.type,
44
+ state: params[:state],
45
+ redirect_uri: params[:redirect_uri],
46
+ response_on_fragment: pre_auth.response_on_fragment?,
47
+ )
48
+ else
49
+ ::Doorkeeper::OAuth::ErrorResponse.new(
50
+ name: exception.type,
51
+ state: params[:state],
52
+ redirect_uri: params[:redirect_uri],
53
+ response_on_fragment: pre_auth.response_on_fragment?,
54
+ )
55
+ end
45
56
 
46
57
  response.headers.merge!(error_response.headers)
47
58
 
48
- if error_response.redirectable?
49
- render json: error_response.body, status: :found, location: error_response.redirect_uri
50
- else
51
- render json: error_response.body, status: error_response.status
52
- end
59
+ # NOTE: Assign error_response to @authorize_response then use redirect_or_render method that are defined at
60
+ # doorkeeper's authorizations_controller.
61
+ # - https://github.com/doorkeeper-gem/doorkeeper/blob/v5.5.0/app/controllers/doorkeeper/authorizations_controller.rb#L110
62
+ # - https://github.com/doorkeeper-gem/doorkeeper/blob/v5.5.0/app/controllers/doorkeeper/authorizations_controller.rb#L52
63
+ @authorize_response = error_response
64
+ redirect_or_render(@authorize_response)
53
65
  end
54
66
 
55
67
  def handle_oidc_prompt_param!(owner)
@@ -58,7 +70,7 @@ module Doorkeeper
58
70
  prompt_values.each do |prompt|
59
71
  case prompt
60
72
  when 'none'
61
- raise Errors::InvalidRequest if (prompt_values - [ 'none' ]).any?
73
+ raise Errors::InvalidRequest if (prompt_values - ['none']).any?
62
74
  raise Errors::LoginRequired unless owner
63
75
  raise Errors::ConsentRequired if oidc_consent_required?
64
76
  when 'login'
@@ -66,8 +78,7 @@ module Doorkeeper
66
78
  when 'consent'
67
79
  render :new
68
80
  when 'select_account'
69
- # TODO: let the user implement this
70
- raise Errors::AccountSelectionRequired
81
+ select_account_for_oidc_resource_owner(owner)
71
82
  else
72
83
  raise Errors::InvalidRequest
73
84
  end
@@ -88,16 +99,21 @@ module Doorkeeper
88
99
  end
89
100
  end
90
101
 
91
- def reauthenticate_oidc_resource_owner(owner)
102
+ def return_without_oidc_prompt_param(prompt_value)
92
103
  return_to = URI.parse(request.path)
93
104
  return_to.query = request.query_parameters.tap do |params|
94
- params['prompt'] = params['prompt'].to_s.sub(/\blogin\s*\b/, '').strip
105
+ params['prompt'] = params['prompt'].to_s.sub(/\b#{prompt_value}\s*\b/, '').strip
95
106
  params.delete('prompt') if params['prompt'].blank?
96
107
  end.to_query
108
+ return_to.to_s
109
+ end
110
+
111
+ def reauthenticate_oidc_resource_owner(owner)
112
+ return_to = return_without_oidc_prompt_param('login')
97
113
 
98
114
  instance_exec(
99
115
  owner,
100
- return_to.to_s,
116
+ return_to,
101
117
  &Doorkeeper::OpenidConnect.configuration.reauthenticate_resource_owner
102
118
  )
103
119
 
@@ -107,9 +123,19 @@ module Doorkeeper
107
123
  def oidc_consent_required?
108
124
  !skip_authorization? && !matching_token?
109
125
  end
126
+
127
+ def select_account_for_oidc_resource_owner(owner)
128
+ return_to = return_without_oidc_prompt_param('select_account')
129
+
130
+ instance_exec(
131
+ owner,
132
+ return_to,
133
+ &Doorkeeper::OpenidConnect.configuration.select_account_for_resource_owner
134
+ )
135
+ end
110
136
  end
111
137
  end
112
138
  end
113
139
 
114
- Helpers::Controller.send :prepend, OpenidConnect::Helpers::Controller
140
+ Helpers::Controller.prepend OpenidConnect::Helpers::Controller
115
141
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OpenidConnect
3
5
  class IdToken
@@ -9,7 +11,7 @@ module Doorkeeper
9
11
  @access_token = access_token
10
12
  @nonce = nonce
11
13
  @resource_owner = Doorkeeper::OpenidConnect.configuration.resource_owner_from_access_token.call(access_token)
12
- @issued_at = Time.now
14
+ @issued_at = Time.zone.now
13
15
  end
14
16
 
15
17
  def claims
@@ -38,7 +40,11 @@ module Doorkeeper
38
40
  private
39
41
 
40
42
  def issuer
41
- Doorkeeper::OpenidConnect.configuration.issuer
43
+ if Doorkeeper::OpenidConnect.configuration.issuer.respond_to?(:call)
44
+ Doorkeeper::OpenidConnect.configuration.issuer.call(@resource_owner, @access_token.application).to_s
45
+ else
46
+ Doorkeeper::OpenidConnect.configuration.issuer
47
+ end
42
48
  end
43
49
 
44
50
  def subject
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OpenidConnect
3
5
  class IdTokenToken < IdToken
@@ -1,22 +1,39 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OpenidConnect
3
5
  module OAuth
4
6
  module Authorization
5
7
  module Code
6
- def issue_token
7
- super.tap do |access_grant|
8
- if pre_auth.nonce.present?
9
- ::Doorkeeper::OpenidConnect::Request.create!(
10
- access_grant: access_grant,
11
- nonce: pre_auth.nonce
12
- )
8
+ if Doorkeeper::OAuth::Authorization::Code.method_defined?(:issue_token!)
9
+ def issue_token!
10
+ super.tap do |access_grant|
11
+ create_openid_request(access_grant) if pre_auth.nonce.present?
12
+ end
13
+ end
14
+
15
+ alias issue_token issue_token!
16
+ else
17
+ # FIXME: drop this after dropping support of Doorkeeper < 5.4
18
+ def issue_token
19
+ super.tap do |access_grant|
20
+ create_openid_request(access_grant) if pre_auth.nonce.present?
13
21
  end
14
22
  end
15
23
  end
24
+
25
+ private
26
+
27
+ def create_openid_request(access_grant)
28
+ ::Doorkeeper::OpenidConnect::Request.create!(
29
+ access_grant: access_grant,
30
+ nonce: pre_auth.nonce
31
+ )
32
+ end
16
33
  end
17
34
  end
18
35
  end
19
36
  end
20
37
 
21
- OAuth::Authorization::Code.send :prepend, OpenidConnect::OAuth::Authorization::Code
38
+ OAuth::Authorization::Code.prepend OpenidConnect::OAuth::Authorization::Code
22
39
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OpenidConnect
3
5
  module OAuth
@@ -8,7 +10,7 @@ module Doorkeeper
8
10
  super
9
11
 
10
12
  nonce =
11
- if openid_request = grant.openid_request
13
+ if (openid_request = grant.openid_request)
12
14
  openid_request.destroy!
13
15
  openid_request.nonce
14
16
  end
@@ -20,5 +22,5 @@ module Doorkeeper
20
22
  end
21
23
  end
22
24
 
23
- OAuth::AuthorizationCodeRequest.send :prepend, OpenidConnect::OAuth::AuthorizationCodeRequest
25
+ OAuth::AuthorizationCodeRequest.prepend OpenidConnect::OAuth::AuthorizationCodeRequest
24
26
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OpenidConnect
3
5
  module OAuth
@@ -20,5 +22,5 @@ module Doorkeeper
20
22
  end
21
23
  end
22
24
 
23
- OAuth::PasswordAccessTokenRequest.send :prepend, OpenidConnect::OAuth::PasswordAccessTokenRequest
25
+ OAuth::PasswordAccessTokenRequest.prepend OpenidConnect::OAuth::PasswordAccessTokenRequest
24
26
  end
@@ -1,32 +1,30 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OpenidConnect
3
5
  module OAuth
4
6
  module PreAuthorization
5
7
  attr_reader :nonce
6
8
 
7
- def initialize(server, attrs = {})
9
+ def initialize(server, attrs = {}, resource_owner = nil)
8
10
  super
9
11
  @nonce = attrs[:nonce]
10
12
  end
11
13
 
12
- # This method will be updated when doorkeeper move to version > 5.2.2
13
- # TODO: delete this method and refactor response_on_fragment? method (below) when doorkeeper gem version constrains is > 5.2.2
14
- def error_response
15
- if error == :invalid_request
16
- Doorkeeper::OAuth::InvalidRequestResponse.from_request(self, response_on_fragment: response_on_fragment?)
17
- else
18
- Doorkeeper::OAuth::ErrorResponse.from_request(self, response_on_fragment: response_on_fragment?)
19
- end
20
- end
14
+ # NOTE: Auto get default response_mode of specified response_type if response_mode is not
15
+ # yet present. We can delete this method after Doorkeeper's minimize version support it.
16
+ def response_on_fragment?
17
+ return response_mode == 'fragment' if response_mode.present?
21
18
 
22
- private
19
+ grant_flow = server.authorization_response_flows.detect do |flow|
20
+ flow.matches_response_type?(response_type)
21
+ end
23
22
 
24
- def response_on_fragment?
25
- response_type == "token" || response_type == "id_token" || response_type == "id_token token"
23
+ grant_flow&.default_response_mode == 'fragment'
26
24
  end
27
25
  end
28
26
  end
29
27
  end
30
28
 
31
- OAuth::PreAuthorization.send :prepend, OpenidConnect::OAuth::PreAuthorization
29
+ OAuth::PreAuthorization.prepend OpenidConnect::OAuth::PreAuthorization
32
30
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OpenidConnect
3
5
  module OAuth
@@ -19,5 +21,5 @@ module Doorkeeper
19
21
  end
20
22
  end
21
23
 
22
- OAuth::TokenResponse.send :prepend, OpenidConnect::OAuth::TokenResponse
24
+ OAuth::TokenResponse.prepend OpenidConnect::OAuth::TokenResponse
23
25
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/lazy_load_hooks'
2
4
 
3
5
  module Doorkeeper
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Doorkeeper
2
4
  module OpenidConnect
3
5
  module AccessGrant
@@ -12,5 +14,5 @@ module Doorkeeper
12
14
  end
13
15
  end
14
16
 
15
- AccessGrant.send :prepend, OpenidConnect::AccessGrant
17
+ AccessGrant.prepend OpenidConnect::AccessGrant
16
18
  end