rodauth-oauth 1.0.0.pre.beta1 → 1.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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -1
  3. data/MIGRATION-GUIDE-v1.md +12 -0
  4. data/README.md +30 -15
  5. data/doc/release_notes/0_1_0.md +2 -2
  6. data/doc/release_notes/0_2_0.md +1 -1
  7. data/doc/release_notes/0_3_0.md +1 -1
  8. data/doc/release_notes/0_5_0.md +2 -2
  9. data/doc/release_notes/0_8_0.md +2 -2
  10. data/doc/release_notes/1_0_0.md +79 -0
  11. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize.html.erb +19 -7
  12. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize_error.erb +10 -0
  13. data/lib/generators/rodauth/oauth/templates/db/migrate/create_rodauth_oauth.rb +54 -43
  14. data/lib/rodauth/features/oauth_application_management.rb +2 -2
  15. data/lib/rodauth/features/oauth_authorization_code_grant.rb +31 -7
  16. data/lib/rodauth/features/oauth_authorize_base.rb +32 -10
  17. data/lib/rodauth/features/oauth_base.rb +36 -16
  18. data/lib/rodauth/features/oauth_dynamic_client_registration.rb +7 -4
  19. data/lib/rodauth/features/oauth_implicit_grant.rb +16 -5
  20. data/lib/rodauth/features/oauth_jwt.rb +3 -3
  21. data/lib/rodauth/features/oauth_jwt_base.rb +29 -6
  22. data/lib/rodauth/features/oauth_jwt_bearer_grant.rb +7 -4
  23. data/lib/rodauth/features/oauth_jwt_secured_authorization_request.rb +64 -10
  24. data/lib/rodauth/features/oauth_resource_indicators.rb +0 -4
  25. data/lib/rodauth/features/oauth_resource_server.rb +3 -3
  26. data/lib/rodauth/features/oauth_saml_bearer_grant.rb +2 -0
  27. data/lib/rodauth/features/oidc.rb +263 -187
  28. data/lib/rodauth/features/oidc_dynamic_client_registration.rb +65 -25
  29. data/lib/rodauth/features/oidc_rp_initiated_logout.rb +118 -0
  30. data/lib/rodauth/oauth/http_extensions.rb +15 -2
  31. data/lib/rodauth/oauth/ttl_store.rb +2 -0
  32. data/lib/rodauth/oauth/version.rb +1 -1
  33. data/locales/en.yml +4 -1
  34. data/locales/pt.yml +4 -1
  35. data/templates/authorize.str +17 -10
  36. data/templates/authorize_error.str +12 -0
  37. metadata +15 -12
  38. data/doc/release_notes/1_0_0_beta1.md +0 -38
@@ -10,6 +10,7 @@ module Rodauth
10
10
  after "authorize"
11
11
 
12
12
  view "authorize", "Authorize", "authorize"
13
+ view "authorize_error", "Authorize Error", "authorize_error"
13
14
 
14
15
  button "Authorize", "oauth_authorize"
15
16
  button "Back to Client Application", "oauth_authorize_post"
@@ -23,6 +24,8 @@ module Rodauth
23
24
  translatable_method :oauth_applications_contacts_label, "Contacts"
24
25
  translatable_method :oauth_applications_tos_uri_label, "Terms of service URL"
25
26
  translatable_method :oauth_applications_policy_uri_label, "Policy URL"
27
+ translatable_method :oauth_unsupported_response_type_message, "Unsupported response type"
28
+ translatable_method :oauth_authorize_parameter_required, "Invalid or missing '%<parameter>s'"
26
29
 
27
30
  # /authorize
28
31
  auth_server_route(:authorize) do |r|
@@ -63,9 +66,17 @@ module Rodauth
63
66
  private
64
67
 
65
68
  def validate_authorize_params
66
- redirect_response_error("invalid_request", request.referer || default_redirect) unless oauth_application && check_valid_redirect_uri?
69
+ redirect_authorize_error("client_id") unless oauth_application
67
70
 
68
- redirect_response_error("invalid_request") unless check_valid_response_type?
71
+ redirect_uris = oauth_application[oauth_applications_redirect_uri_column].split(" ")
72
+
73
+ if (redirect_uri = param_or_nil("redirect_uri"))
74
+ redirect_authorize_error("redirect_uri") unless redirect_uris.include?(redirect_uri)
75
+ elsif redirect_uris.size > 1
76
+ redirect_authorize_error("redirect_uri")
77
+ end
78
+
79
+ redirect_response_error("unsupported_response_type") unless check_valid_response_type?
69
80
 
70
81
  redirect_response_error("invalid_request") unless check_valid_access_type? && check_valid_approval_prompt?
71
82
 
@@ -78,10 +89,6 @@ module Rodauth
78
89
  false
79
90
  end
80
91
 
81
- def check_valid_redirect_uri?
82
- oauth_application[oauth_applications_redirect_uri_column].split(" ").include?(redirect_uri)
83
- end
84
-
85
92
  ACCESS_TYPES = %w[offline online].freeze
86
93
 
87
94
  def check_valid_access_type?
@@ -117,6 +124,21 @@ module Rodauth
117
124
  request.env["REQUEST_METHOD"] = "POST"
118
125
  end
119
126
 
127
+ def redirect_authorize_error(parameter, referer = request.referer || default_redirect)
128
+ error_message = oauth_authorize_parameter_required(parameter: parameter)
129
+
130
+ if accepts_json?
131
+ status_code = oauth_invalid_response_status
132
+
133
+ throw_json_response_error(status_code, "invalid_request", error_message)
134
+ else
135
+ scope.instance_variable_set(:@error, error_message)
136
+ scope.instance_variable_set(:@back_url, referer)
137
+
138
+ return_response(authorize_error_view)
139
+ end
140
+ end
141
+
120
142
  def authorization_required
121
143
  if accepts_json?
122
144
  throw_json_response_error(oauth_authorization_required_error_status, "invalid_client")
@@ -140,10 +162,10 @@ module Rodauth
140
162
  end
141
163
 
142
164
  def create_oauth_grant(create_params = {})
143
- create_params[oauth_grants_oauth_application_id_column] = oauth_application[oauth_applications_id_column]
144
- create_params[oauth_grants_redirect_uri_column] = redirect_uri
145
- create_params[oauth_grants_expires_in_column] = Sequel.date_add(Sequel::CURRENT_TIMESTAMP, seconds: oauth_grant_expires_in)
146
- create_params[oauth_grants_scopes_column] = scopes.join(oauth_scope_separator)
165
+ create_params[oauth_grants_oauth_application_id_column] ||= oauth_application[oauth_applications_id_column]
166
+ create_params[oauth_grants_redirect_uri_column] ||= redirect_uri
167
+ create_params[oauth_grants_expires_in_column] ||= Sequel.date_add(Sequel::CURRENT_TIMESTAMP, seconds: oauth_grant_expires_in)
168
+ create_params[oauth_grants_scopes_column] ||= scopes.join(oauth_scope_separator)
147
169
 
148
170
  if use_oauth_access_type? && (access_type = param_or_nil("access_type"))
149
171
  create_params[oauth_grants_access_type_column] = access_type
@@ -3,6 +3,8 @@
3
3
  require "time"
4
4
  require "base64"
5
5
  require "securerandom"
6
+ require "cgi"
7
+ require "digest/sha2"
6
8
  require "rodauth/version"
7
9
  require "rodauth/oauth"
8
10
  require "rodauth/oauth/database_extensions"
@@ -17,8 +19,6 @@ module Rodauth
17
19
  auth_value_methods(:http_request)
18
20
  auth_value_methods(:http_request_cache)
19
21
 
20
- SCOPES = %w[profile.read].freeze
21
-
22
22
  before "token"
23
23
 
24
24
  error_flash "Please authorize to continue", "require_authorization"
@@ -82,7 +82,7 @@ module Rodauth
82
82
  auth_value_method :oauth_already_in_use_response_status, 409
83
83
 
84
84
  # Feature options
85
- auth_value_method :oauth_application_scopes, SCOPES
85
+ auth_value_method :oauth_application_scopes, []
86
86
  auth_value_method :oauth_token_type, "bearer"
87
87
  auth_value_method :oauth_refresh_token_protection_policy, "rotation" # can be: none, sender_constrained, rotation
88
88
 
@@ -228,13 +228,20 @@ module Rodauth
228
228
  end
229
229
 
230
230
  def fetch_access_token
231
- value = request.env["HTTP_AUTHORIZATION"]
231
+ if (token = request.params["access_token"])
232
+ if request.post? && !(request.content_type.start_with?("application/x-www-form-urlencoded") &&
233
+ request.params.size == 1)
234
+ return
235
+ end
236
+ else
237
+ value = request.env["HTTP_AUTHORIZATION"]
232
238
 
233
- return unless value && !value.empty?
239
+ return unless value && !value.empty?
234
240
 
235
- scheme, token = value.split(" ", 2)
241
+ scheme, token = value.split(" ", 2)
236
242
 
237
- return unless scheme.downcase == oauth_token_type
243
+ return unless scheme.downcase == oauth_token_type
244
+ end
238
245
 
239
246
  return if token.nil? || token.empty?
240
247
 
@@ -245,11 +252,11 @@ module Rodauth
245
252
  return @authorization_token if defined?(@authorization_token)
246
253
 
247
254
  # check if there is a token
248
- bearer_token = fetch_access_token
255
+ access_token = fetch_access_token
249
256
 
250
- return unless bearer_token
257
+ return unless access_token
251
258
 
252
- @authorization_token = oauth_grant_by_token(bearer_token)
259
+ @authorization_token = oauth_grant_by_token(access_token)
253
260
  end
254
261
 
255
262
  def require_oauth_authorization(*scopes)
@@ -758,22 +765,31 @@ module Rodauth
758
765
  query_params = []
759
766
 
760
767
  query_params << if respond_to?(:"oauth_#{error_code}_error_code")
761
- "error=#{send(:"oauth_#{error_code}_error_code")}"
768
+ ["error", send(:"oauth_#{error_code}_error_code")]
762
769
  else
763
- "error=#{error_code}"
770
+ ["error", error_code]
764
771
  end
765
772
 
766
773
  if respond_to?(:"oauth_#{error_code}_message")
767
774
  message = send(:"oauth_#{error_code}_message")
768
- query_params << ["error_description=#{CGI.escape(message)}"]
775
+ query_params << ["error_description", CGI.escape(message)]
769
776
  end
770
777
 
771
- query_params << redirect_url.query if redirect_url.query
772
- redirect_url.query = query_params.join("&")
773
- redirect(redirect_url.to_s)
778
+ state = param_or_nil("state")
779
+
780
+ query_params << ["state", state] if state
781
+
782
+ _redirect_response_error(redirect_url, query_params)
774
783
  end
775
784
  end
776
785
 
786
+ def _redirect_response_error(redirect_url, query_params)
787
+ query_params = query_params.map { |k, v| "#{k}=#{v}" }
788
+ query_params << redirect_url.query if redirect_url.query
789
+ redirect_url.query = query_params.join("&")
790
+ redirect(redirect_url.to_s)
791
+ end
792
+
777
793
  def json_response_success(body, cache = false)
778
794
  response.status = 200
779
795
  response["Content-Type"] ||= json_response_content_type
@@ -835,6 +851,10 @@ module Rodauth
835
851
  URI::DEFAULT_PARSER.make_regexp(oauth_valid_uri_schemes).match?(uri)
836
852
  end
837
853
 
854
+ def check_valid_no_fragment_uri?(uri)
855
+ check_valid_uri?(uri) && URI.parse(uri).fragment.nil?
856
+ end
857
+
838
858
  # Resource server mode
839
859
 
840
860
  def authorization_server_metadata
@@ -8,7 +8,7 @@ module Rodauth
8
8
 
9
9
  before "register"
10
10
 
11
- auth_value_method :oauth_client_registration_required_params, %w[redirect_uris client_name client_uri]
11
+ auth_value_method :oauth_client_registration_required_params, %w[redirect_uris client_name]
12
12
 
13
13
  PROTECTED_APPLICATION_ATTRIBUTES = %w[account_id client_id].freeze
14
14
 
@@ -68,7 +68,10 @@ module Rodauth
68
68
  when "redirect_uris"
69
69
  if value.is_a?(Array)
70
70
  value = value.each do |uri|
71
- register_throw_json_response_error("invalid_redirect_uri", register_invalid_uri_message(uri)) unless check_valid_uri?(uri)
71
+ unless check_valid_no_fragment_uri?(uri)
72
+ register_throw_json_response_error("invalid_redirect_uri",
73
+ register_invalid_uri_message(uri))
74
+ end
72
75
  end.join(" ")
73
76
  else
74
77
  register_throw_json_response_error("invalid_redirect_uri", register_invalid_uri_message(value))
@@ -76,7 +79,7 @@ module Rodauth
76
79
  key = oauth_applications_redirect_uri_column
77
80
  when "token_endpoint_auth_method"
78
81
  unless oauth_token_endpoint_auth_methods_supported.include?(value)
79
- register_throw_json_response_error("invalid_client_metadata", register_invalid_param_message(key))
82
+ register_throw_json_response_error("invalid_client_metadata", register_invalid_client_metadata_message(key, value))
80
83
  end
81
84
  # verify if in range
82
85
  key = oauth_applications_token_endpoint_auth_method_column
@@ -84,7 +87,7 @@ module Rodauth
84
87
  if value.is_a?(Array)
85
88
  value = value.each do |grant_type|
86
89
  unless oauth_grant_types_supported.include?(grant_type)
87
- register_throw_json_response_error("invalid_client_metadata", register_oauth_invalid_grant_type_message(grant_type))
90
+ register_throw_json_response_error("invalid_client_metadata", register_invalid_client_metadata_message(grant_type, value))
88
91
  end
89
92
  end.join(" ")
90
93
  else
@@ -28,23 +28,34 @@ module Rodauth
28
28
 
29
29
  redirect_response_error("invalid_request") unless supported_response_mode?(response_mode)
30
30
 
31
- response_params.replace(_do_authorize_token)
31
+ oauth_grant = _do_authorize_token
32
+
33
+ response_params.replace(json_access_token_payload(oauth_grant))
32
34
 
33
35
  response_params["state"] = param("state") if param_or_nil("state")
34
36
 
35
37
  [response_params, response_mode]
36
38
  end
37
39
 
38
- def _do_authorize_token
40
+ def _do_authorize_token(grant_params = {})
39
41
  grant_params = {
40
42
  oauth_grants_type_column => "implicit",
41
43
  oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column],
42
44
  oauth_grants_scopes_column => scopes,
43
45
  oauth_grants_account_id_column => account_id
44
- }
45
- oauth_grant = generate_token(grant_params, false)
46
+ }.merge(grant_params)
47
+
48
+ generate_token(grant_params, false)
49
+ end
46
50
 
47
- json_access_token_payload(oauth_grant)
51
+ def _redirect_response_error(redirect_url, query_params)
52
+ response_types = param("response_type").split(/ +/)
53
+
54
+ return super if response_types.empty? || response_types == %w[code]
55
+
56
+ query_params = query_params.map { |k, v| "#{k}=#{v}" }
57
+ redirect_url.fragment = query_params.join("&")
58
+ redirect(redirect_url.to_s)
48
59
  end
49
60
 
50
61
  def authorize_response(params, mode)
@@ -49,11 +49,11 @@ module Rodauth
49
49
  return @authorization_token if defined?(@authorization_token)
50
50
 
51
51
  @authorization_token = begin
52
- bearer_token = fetch_access_token
52
+ access_token = fetch_access_token
53
53
 
54
- return unless bearer_token
54
+ return unless access_token
55
55
 
56
- jwt_claims = jwt_decode(bearer_token)
56
+ jwt_claims = jwt_decode(access_token)
57
57
 
58
58
  return unless jwt_claims
59
59
 
@@ -63,7 +63,11 @@ module Rodauth
63
63
  end
64
64
 
65
65
  def jwt_subject(oauth_grant, client_application = oauth_application)
66
- oauth_grant[oauth_grants_account_id_column] || client_application[oauth_applications_client_id_column]
66
+ account_id = oauth_grant[oauth_grants_account_id_column]
67
+
68
+ return account_id.to_s if account_id
69
+
70
+ client_application[oauth_applications_client_id_column]
67
71
  end
68
72
 
69
73
  def oauth_server_metadata_body(path = nil)
@@ -207,14 +211,23 @@ module Rodauth
207
211
 
208
212
  claims = if is_authorization_server?
209
213
  if jwks
214
+ jwks = jwks[:keys] if jwks.is_a?(Hash)
215
+
210
216
  enc_algs = [jws_encryption_algorithm].compact
211
217
  enc_meths = [jws_encryption_method].compact
212
218
 
213
219
  sig_algs = jws_algorithm ? [jws_algorithm] : jwks.select { |k| k[:use] == "sig" }.map { |k| k[:alg] }
214
220
  sig_algs = sig_algs.compact.map(&:to_sym)
215
221
 
216
- jws = JSON::JWT.decode(token, JSON::JWK::Set.new({ keys: jwks }), enc_algs + sig_algs, enc_meths)
217
- jws = JSON::JWT.decode(jws.plain_text, JSON::JWK::Set.new({ keys: jwks }), sig_algs) if jws.is_a?(JSON::JWE)
222
+ # JWKs may be set up without a KID, when there's a single one
223
+ if jwks.size == 1 && !jwks[0][:kid]
224
+ key = jwks[0]
225
+ jwk_key = JSON::JWK.new(key)
226
+ jws = JSON::JWT.decode(token, jwk_key)
227
+ else
228
+ jws = JSON::JWT.decode(token, JSON::JWK::Set.new({ keys: jwks }), enc_algs + sig_algs, enc_meths)
229
+ jws = JSON::JWT.decode(jws.plain_text, JSON::JWK::Set.new({ keys: jwks }), sig_algs) if jws.is_a?(JSON::JWE)
230
+ end
218
231
  jws
219
232
  elsif jws_key
220
233
  JSON::JWT.decode(token, jws_key)
@@ -279,7 +292,7 @@ module Rodauth
279
292
  end
280
293
 
281
294
  def jwt_encode(payload,
282
- signing_algorithm: oauth_jwt_keys.keys.first)
295
+ signing_algorithm: oauth_jwt_keys.keys.first, **)
283
296
  headers = {}
284
297
 
285
298
  key = oauth_jwt_keys[signing_algorithm] || _jwt_key
@@ -368,8 +381,18 @@ module Rodauth
368
381
  # decode jwt
369
382
  claims = if is_authorization_server?
370
383
  if jwks
371
- algorithms = jws_algorithm ? [jws_algorithm] : jwks.select { |k| k[:use] == "sig" }.map { |k| k[:alg] }
372
- JWT.decode(token, nil, true, algorithms: algorithms, jwks: { keys: jwks }, **verify_claims_params).first
384
+ jwks = jwks[:keys] if jwks.is_a?(Hash)
385
+
386
+ # JWKs may be set up without a KID, when there's a single one
387
+ if jwks.size == 1 && !jwks[0][:kid]
388
+ key = jwks[0]
389
+ algo = key[:alg]
390
+ key = JWT::JWK.import(key).keypair
391
+ JWT.decode(token, key, true, algorithms: [algo], **verify_claims_params).first
392
+ else
393
+ algorithms = jws_algorithm ? [jws_algorithm] : jwks.select { |k| k[:use] == "sig" }.map { |k| k[:alg] }
394
+ JWT.decode(token, nil, true, algorithms: algorithms, jwks: { keys: jwks }, **verify_claims_params).first
395
+ end
373
396
  elsif jws_key
374
397
  JWT.decode(token, jws_key, true, algorithms: [jws_algorithm], **verify_claims_params).first
375
398
  end
@@ -6,6 +6,8 @@ module Rodauth
6
6
  Feature.define(:oauth_jwt_bearer_grant, :OauthJwtBearerGrant) do
7
7
  depends :oauth_assertion_base, :oauth_jwt
8
8
 
9
+ auth_value_method :max_param_bytesize, nil if Rodauth::VERSION >= "2.26.0"
10
+
9
11
  auth_value_methods(
10
12
  :require_oauth_application_from_jwt_bearer_assertion_issuer,
11
13
  :require_oauth_application_from_jwt_bearer_assertion_subject,
@@ -54,7 +56,7 @@ module Rodauth
54
56
 
55
57
  def require_oauth_application_from_client_secret_jwt(client_id, assertion, alg)
56
58
  oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column => client_id).first
57
- authorization_required unless supports_auth_method?(oauth_application, "client_secret_jwt")
59
+ authorization_required unless oauth_application && supports_auth_method?(oauth_application, "client_secret_jwt")
58
60
  client_secret = oauth_application[oauth_applications_client_secret_column]
59
61
  claims = jwt_assertion(assertion, jws_key: client_secret, jws_algorithm: alg)
60
62
  authorization_required unless claims && claims["iss"] == client_id
@@ -63,7 +65,7 @@ module Rodauth
63
65
 
64
66
  def require_oauth_application_from_private_key_jwt(client_id, assertion)
65
67
  oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column => client_id).first
66
- authorization_required unless supports_auth_method?(oauth_application, "private_key_jwt")
68
+ authorization_required unless oauth_application && supports_auth_method?(oauth_application, "private_key_jwt")
67
69
  jwks = oauth_application_jwks(oauth_application)
68
70
  claims = jwt_assertion(assertion, jwks: jwks)
69
71
  authorization_required unless claims
@@ -79,8 +81,9 @@ module Rodauth
79
81
  end
80
82
 
81
83
  def jwt_assertion(assertion, **kwargs)
82
- claims = jwt_decode(assertion, verify_iss: false, verify_aud: false, **kwargs)
83
- return unless verify_aud(request.url, claims["aud"])
84
+ claims = jwt_decode(assertion, verify_iss: false, verify_aud: false, verify_jti: false, **kwargs)
85
+
86
+ return unless claims && verify_aud(request.url, claims["aud"])
84
87
 
85
88
  claims
86
89
  end
@@ -4,31 +4,46 @@ require "rodauth/oauth"
4
4
 
5
5
  module Rodauth
6
6
  Feature.define(:oauth_jwt_secured_authorization_request, :OauthJwtSecuredAuthorizationRequest) do
7
+ ALLOWED_REQUEST_URI_CONTENT_TYPES = %w[application/jose application/oauth-authz-req+jwt].freeze
8
+
7
9
  depends :oauth_authorize_base, :oauth_jwt_base
8
10
 
11
+ auth_value_method :oauth_require_request_uri_registration, false
12
+ auth_value_method :oauth_request_object_signing_alg_allow_none, false
13
+
14
+ auth_value_method :oauth_applications_request_uris_column, :request_uris
15
+
9
16
  auth_value_method :oauth_applications_request_object_signing_alg_column, :request_object_signing_alg
10
17
  auth_value_method :oauth_applications_request_object_encryption_alg_column, :request_object_encryption_alg
11
18
  auth_value_method :oauth_applications_request_object_encryption_enc_column, :request_object_encryption_enc
12
19
 
13
- translatable_method :oauth_request_uri_not_supported_message, "request uri is unsupported"
14
20
  translatable_method :oauth_invalid_request_object_message, "request object is invalid"
15
21
 
22
+ auth_value_method :max_param_bytesize, nil if Rodauth::VERSION >= "2.26.0"
23
+
16
24
  private
17
25
 
18
26
  # /authorize
19
27
 
20
28
  def validate_authorize_params
21
- # TODO: add support for requst_uri
22
- redirect_response_error("request_uri_not_supported") if param_or_nil("request_uri")
23
-
24
29
  request_object = param_or_nil("request")
25
30
 
26
- return super unless request_object && oauth_application
31
+ request_uri = param_or_nil("request_uri")
27
32
 
28
- if (jwks = oauth_application_jwks(oauth_application))
29
- jwks = JSON.parse(jwks, symbolize_names: true) if jwks.is_a?(String)
30
- else
31
- redirect_response_error("invalid_request_object")
33
+ return super unless (request_object || request_uri) && oauth_application
34
+
35
+ if request_uri
36
+ request_uri = CGI.unescape(request_uri)
37
+
38
+ redirect_response_error("invalid_request_uri") unless supported_request_uri?(request_uri, oauth_application)
39
+
40
+ response = http_request(request_uri)
41
+
42
+ unless response.code.to_i == 200 && ALLOWED_REQUEST_URI_CONTENT_TYPES.include?(response["content-type"])
43
+ redirect_response_error("invalid_request_uri")
44
+ end
45
+
46
+ request_object = response.body
32
47
  end
33
48
 
34
49
  request_sig_enc_opts = {
@@ -37,10 +52,33 @@ module Rodauth
37
52
  jws_encryption_method: oauth_application[oauth_applications_request_object_encryption_enc_column]
38
53
  }.compact
39
54
 
40
- claims = jwt_decode(request_object, jwks: jwks, verify_jti: false, verify_aud: false, **request_sig_enc_opts)
55
+ request_sig_enc_opts[:jws_algorithm] ||= "none" if oauth_request_object_signing_alg_allow_none
56
+
57
+ if request_sig_enc_opts[:jws_algorithm] == "none"
58
+ jwks = nil
59
+ elsif (jwks = oauth_application_jwks(oauth_application))
60
+ jwks = JSON.parse(jwks, symbolize_names: true) if jwks.is_a?(String)
61
+ else
62
+ redirect_response_error("invalid_request_object")
63
+ end
64
+
65
+ claims = jwt_decode(request_object,
66
+ jwks: jwks,
67
+ verify_jti: false,
68
+ verify_iss: false,
69
+ verify_aud: false,
70
+ **request_sig_enc_opts)
41
71
 
42
72
  redirect_response_error("invalid_request_object") unless claims
43
73
 
74
+ if (iss = claims["iss"]) && (iss != oauth_application[oauth_applications_client_id_column])
75
+ redirect_response_error("invalid_request_object")
76
+ end
77
+
78
+ if (aud = claims["aud"]) && !verify_aud(aud, oauth_jwt_issuer)
79
+ redirect_response_error("invalid_request_object")
80
+ end
81
+
44
82
  # If signed, the Authorization Request
45
83
  # Object SHOULD contain the Claims "iss" (issuer) and "aud" (audience)
46
84
  # as members, with their semantics being the same as defined in the JWT
@@ -58,5 +96,21 @@ module Rodauth
58
96
 
59
97
  super
60
98
  end
99
+
100
+ def supported_request_uri?(request_uri, oauth_application)
101
+ return false unless check_valid_uri?(request_uri)
102
+
103
+ request_uris = oauth_application[oauth_applications_request_uris_column]
104
+
105
+ request_uris.nil? || request_uris.split(oauth_scope_separator).one? { |uri| request_uri.start_with?(uri) }
106
+ end
107
+
108
+ def oauth_server_metadata_body(*)
109
+ super.tap do |data|
110
+ data[:request_parameter_supported] = true
111
+ data[:request_uri_parameter_supported] = true
112
+ data[:require_request_uri_registration] = oauth_require_request_uri_registration
113
+ end
114
+ end
61
115
  end
62
116
  end
@@ -70,10 +70,6 @@ module Rodauth
70
70
  super(oauth_grant, update_params.merge(oauth_grants_resource_column => resource_indicators))
71
71
  end
72
72
 
73
- def check_valid_no_fragment_uri?(uri)
74
- check_valid_uri?(uri) && URI.parse(uri).fragment.nil?
75
- end
76
-
77
73
  module IndicatorAuthorizationCodeGrant
78
74
  private
79
75
 
@@ -16,12 +16,12 @@ module Rodauth
16
16
  return @authorization_token if defined?(@authorization_token)
17
17
 
18
18
  # check if there is a token
19
- bearer_token = fetch_access_token
19
+ access_token = fetch_access_token
20
20
 
21
- return unless bearer_token
21
+ return unless access_token
22
22
 
23
23
  # where in resource server, NOT the authorization server.
24
- payload = introspection_request("access_token", bearer_token)
24
+ payload = introspection_request("access_token", access_token)
25
25
 
26
26
  return unless payload["active"]
27
27
 
@@ -17,6 +17,8 @@ module Rodauth
17
17
  auth_value_method :oauth_saml_security_digest_method, XMLSecurity::Document::SHA1
18
18
  auth_value_method :oauth_saml_security_signature_method, XMLSecurity::Document::RSA_SHA1
19
19
 
20
+ auth_value_method :max_param_bytesize, nil if Rodauth::VERSION >= "2.26.0"
21
+
20
22
  auth_value_methods(
21
23
  :require_oauth_application_from_saml2_bearer_assertion_issuer,
22
24
  :require_oauth_application_from_saml2_bearer_assertion_subject,