rodauth-oauth 1.0.0.pre.beta1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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,