rodauth-oauth 1.3.1 → 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ac42aa0fb7d65030b403957a408d6d5b1f999614c957ec8176d814030ddb9381
4
- data.tar.gz: f67cf98dfdb162d1e015d3930b569756f2a25737a64e68ed36fcf31e2d672be2
3
+ metadata.gz: bf721a3632883e34bff44e33ca84c9a5aa2248748ccecf52c180edd25086abad
4
+ data.tar.gz: 59966edc773dbee470be46770532ee9159dfe370d346eca936a2ebc48a8fd224
5
5
  SHA512:
6
- metadata.gz: e86da1d43f30dfb18e1ae530dc5b1cc9cf69903d32a39bade2dc5881075140d79ef5644159ff40b1406b58dfb12197c79d13f683f20b431330d02d452b3cf87e
7
- data.tar.gz: ad35b6bc881f1e22f7cb7227115daee9556ebeada9e10b21e8ecfeb9dc0e7a51ddd606ef481f1125064e49d6ee39076bd8bc2043d58d0d103df6a27813a022db
6
+ metadata.gz: 6855059fe2d8225e4f3650dca01ad4ff3d82ad4e42623b43e5ea1d42ef3dbec317bf9488a2b1598556c135f9b9bf1d45ac41512d7347e3c5c55b7c5e8bd63305
7
+ data.tar.gz: 9a564b8ad6812841f4a2737ee263af188e5231c49b8b475f0c575a5b0496aa6f534709552845caaaacbb73dbe1afabb9079a17265683be5df73f62075a78896e
data/README.md CHANGED
@@ -90,8 +90,8 @@ Or install it yourself as:
90
90
 
91
91
  ## Articles
92
92
 
93
- * [How to use rodauth-oauth with rails and rodauth](https://honeyryderchuck.gitlab.io/httpx/2021/03/15/oidc-provider-on-rails-using-rodauth-oauth.html)
94
- * [How to use rodauth-oauth with rails and without rodauth](https://honeyryderchuck.gitlab.io/httpx/2021/09/08/using-rodauth-oauth-in-rails-without-rodauth-based-auth.html)
93
+ * [How to use rodauth-oauth with rails and rodauth](https://honeyryderchuck.gitlab.io/2021/03/15/oidc-provider-on-rails-using-rodauth-oauth.html)
94
+ * [How to use rodauth-oauth with rails and without rodauth](https://honeyryderchuck.gitlab.io/2021/09/08/using-rodauth-oauth-in-rails-without-rodauth-based-auth.html)
95
95
 
96
96
  ## Usage
97
97
 
@@ -0,0 +1,14 @@
1
+ ### 1.3.2 (27/07/2023)
2
+
3
+ #### Improvements
4
+
5
+ * `require_signed_request_object` option for JAR (`oauth_jwt_secured_authorization_request` plugin) is now supported:
6
+ * in the oauth server metadata endpoint
7
+ * as a plugin config option (`oauth_require_signed_request_object`, defaults to `false`)
8
+ * as a oauth dynamic registration endpoint param (`require_signed_request_object`, requires corresponding columnn)
9
+ * enforces JAR-based authorization, andd does not allow unsigned JAR JWTs, when turned on.
10
+
11
+ #### Bugfixes
12
+
13
+ * JWT decoding failed in circumstances where a declared encryption algo didn't have key/method declared.
14
+ * fix for when PAR (`oauth_pushed_authorization_request` feature) is used with JAR (`oauth_jwt_secured_authorization_request` plugin), and PAR `request_uri` param wasn't being removed when validating authorize request parameters, thereby making JAR logic evaluate it as a JAR `requuest_uri` (it is now correctly not taken into account in such a case);
@@ -46,6 +46,7 @@ class CreateRodauthOauth < ActiveRecord::Migration<%= migration_version %>
46
46
  t.string :request_object_encryption_alg, null: true
47
47
  t.string :request_object_encryption_enc, null: true
48
48
  t.string :request_uris, null: true
49
+ t.boolean :require_signed_request_object, null: true
49
50
  t.boolean :require_pushed_authorization_requests, null: false, default: false
50
51
 
51
52
  # :oauth_tls_client_auth
@@ -200,6 +200,14 @@ module Rodauth
200
200
  when "client_name"
201
201
  register_throw_json_response_error("invalid_client_metadata", register_invalid_param_message(value)) unless value.is_a?(String)
202
202
  key = oauth_applications_name_column
203
+ when "require_signed_request_object"
204
+ unless respond_to?(:oauth_applications_require_signed_request_object_column)
205
+ register_throw_json_response_error("invalid_client_metadata",
206
+ register_invalid_param_message(key))
207
+ end
208
+ request_params[key] = value = convert_to_boolean(key, value)
209
+
210
+ key = oauth_applications_require_signed_request_object_column
203
211
  when "require_pushed_authorization_requests"
204
212
  unless respond_to?(:oauth_applications_require_pushed_authorization_requests_column)
205
213
  register_throw_json_response_error("invalid_client_metadata",
@@ -189,14 +189,16 @@ module Rodauth
189
189
  jwt = jwt.sign(jwk, signing_algorithm)
190
190
  jwt.kid = jwk.thumbprint
191
191
 
192
+ return jwt.to_s unless encryption_algorithm && encryption_method
193
+
192
194
  if jwks && (jwk = jwks.find { |k| k[:use] == "enc" && k[:alg] == encryption_algorithm && k[:enc] == encryption_method })
193
195
  jwk = JSON::JWK.new(jwk)
194
196
  jwe = jwt.encrypt(jwk, encryption_algorithm.to_sym, encryption_method.to_sym)
195
197
  jwe.to_s
196
198
  elsif jwe_key
197
199
  jwe_key = jwe_key.first if jwe_key.is_a?(Array)
198
- algorithm = encryption_algorithm.to_sym if encryption_algorithm
199
- meth = encryption_method.to_sym if encryption_method
200
+ algorithm = encryption_algorithm.to_sym
201
+ meth = encryption_method.to_sym
200
202
  jwt.encrypt(jwe_key, algorithm, meth)
201
203
  else
202
204
  jwt.to_s
@@ -246,6 +248,8 @@ module Rodauth
246
248
  jws
247
249
  elsif jws_key
248
250
  JSON::JWT.decode(token, jws_key)
251
+ else
252
+ JSON::JWT.decode(token, nil, jws_algorithm)
249
253
  end
250
254
  elsif (jwks = auth_server_jwks_set)
251
255
  JSON::JWT.decode(token, JSON::JWK::Set.new(jwks))
@@ -428,6 +432,8 @@ module Rodauth
428
432
  end
429
433
  elsif jws_key
430
434
  JWT.decode(token, jws_key, true, algorithms: [jws_algorithm], **verify_claims_params).first
435
+ else
436
+ JWT.decode(token, jws_key, false, **verify_claims_params).first
431
437
  end
432
438
  elsif (jwks = auth_server_jwks_set)
433
439
  algorithms = jwks[:keys].select { |k| k[:use] == "sig" }.map { |k| k[:alg] }
@@ -9,13 +9,15 @@ module Rodauth
9
9
  depends :oauth_authorize_base, :oauth_jwt_base
10
10
 
11
11
  auth_value_method :oauth_require_request_uri_registration, false
12
+ auth_value_method :oauth_require_signed_request_object, false
12
13
  auth_value_method :oauth_request_object_signing_alg_allow_none, false
13
14
 
14
- auth_value_method :oauth_applications_request_uris_column, :request_uris
15
-
16
- auth_value_method :oauth_applications_request_object_signing_alg_column, :request_object_signing_alg
17
- auth_value_method :oauth_applications_request_object_encryption_alg_column, :request_object_encryption_alg
18
- auth_value_method :oauth_applications_request_object_encryption_enc_column, :request_object_encryption_enc
15
+ %i[
16
+ request_uris require_signed_request_object request_object_signing_alg
17
+ request_object_encryption_alg request_object_encryption_enc
18
+ ].each do |column|
19
+ auth_value_method :"oauth_applications_#{column}_column", column
20
+ end
19
21
 
20
22
  translatable_method :oauth_invalid_request_object_message, "request object is invalid"
21
23
 
@@ -30,7 +32,13 @@ module Rodauth
30
32
 
31
33
  request_uri = param_or_nil("request_uri")
32
34
 
33
- return super unless (request_object || request_uri) && oauth_application
35
+ unless (request_object || request_uri) && oauth_application
36
+ if request.path == authorize_path && request.get? && require_signed_request_object?
37
+ redirect_response_error("invalid_request_object")
38
+ end
39
+
40
+ return super
41
+ end
34
42
 
35
43
  if request_uri
36
44
  request_uri = CGI.unescape(request_uri)
@@ -84,6 +92,14 @@ module Rodauth
84
92
  request_uris.nil? || request_uris.split(oauth_scope_separator).one? { |uri| request_uri.start_with?(uri) }
85
93
  end
86
94
 
95
+ def require_signed_request_object?
96
+ return @require_signed_request_object if defined?(@require_signed_request_object)
97
+
98
+ @require_signed_request_object = (oauth_application[oauth_applications_require_signed_request_object_column] if oauth_application)
99
+ @require_signed_request_object = oauth_require_signed_request_object if @require_signed_request_object.nil?
100
+ @require_signed_request_object
101
+ end
102
+
87
103
  def decode_request_object(request_object)
88
104
  request_sig_enc_opts = {
89
105
  jws_algorithm: oauth_application[oauth_applications_request_object_signing_alg_column],
@@ -94,6 +110,8 @@ module Rodauth
94
110
  request_sig_enc_opts[:jws_algorithm] ||= "none" if oauth_request_object_signing_alg_allow_none
95
111
 
96
112
  if request_sig_enc_opts[:jws_algorithm] == "none"
113
+ redirect_response_error("invalid_request_object") if require_signed_request_object?
114
+
97
115
  jwks = nil
98
116
  elsif (jwks = oauth_application_jwks(oauth_application))
99
117
  jwks = JSON.parse(jwks, symbolize_names: true) if jwks.is_a?(String)
@@ -118,6 +136,7 @@ module Rodauth
118
136
  data[:request_parameter_supported] = true
119
137
  data[:request_uri_parameter_supported] = true
120
138
  data[:require_request_uri_registration] = oauth_require_request_uri_registration
139
+ data[:require_signed_request_object] = oauth_require_signed_request_object
121
140
  end
122
141
  end
123
142
  end
@@ -65,32 +65,37 @@ module Rodauth
65
65
  # The request_uri authorization request parameter is one exception, and it MUST NOT be provided.
66
66
  redirect_response_error("invalid_request") if param_or_nil("request_uri")
67
67
 
68
- if (request_object = param_or_nil("request")) && features.include?(:oauth_jwt_secured_authorization_request)
69
- claims = decode_request_object(request_object)
70
-
71
- # https://datatracker.ietf.org/doc/html/rfc9126#section-3-5.3
72
- # reject the request if the authenticated client_id does not match the client_id claim in the Request Object
73
- if (client_id = claims["client_id"]) && (client_id != oauth_application[oauth_applications_client_id_column])
74
- redirect_response_error("invalid_request_object")
75
- end
76
-
77
- # requiring the iss claim to match the client_id is at the discretion of the authorization server
78
- if oauth_require_pushed_authorization_request_iss_request_object &&
79
- (iss = claims.delete("iss")) &&
80
- iss != oauth_application[oauth_applications_client_id_column]
81
- redirect_response_error("invalid_request_object")
82
- end
83
-
84
- if (aud = claims.delete("aud")) && !verify_aud(aud, oauth_jwt_issuer)
68
+ if features.include?(:oauth_jwt_secured_authorization_request)
69
+
70
+ if (request_object = param_or_nil("request"))
71
+ claims = decode_request_object(request_object)
72
+
73
+ # https://datatracker.ietf.org/doc/html/rfc9126#section-3-5.3
74
+ # reject the request if the authenticated client_id does not match the client_id claim in the Request Object
75
+ if (client_id = claims["client_id"]) && (client_id != oauth_application[oauth_applications_client_id_column])
76
+ redirect_response_error("invalid_request_object")
77
+ end
78
+
79
+ # requiring the iss claim to match the client_id is at the discretion of the authorization server
80
+ if oauth_require_pushed_authorization_request_iss_request_object &&
81
+ (iss = claims.delete("iss")) &&
82
+ iss != oauth_application[oauth_applications_client_id_column]
83
+ redirect_response_error("invalid_request_object")
84
+ end
85
+
86
+ if (aud = claims.delete("aud")) && !verify_aud(aud, oauth_jwt_issuer)
87
+ redirect_response_error("invalid_request_object")
88
+ end
89
+
90
+ claims.delete("exp")
91
+ request.params.delete("request")
92
+
93
+ claims.each do |k, v|
94
+ request.params[k.to_s] = v
95
+ end
96
+ elsif require_signed_request_object?
85
97
  redirect_response_error("invalid_request_object")
86
98
  end
87
-
88
- claims.delete("exp")
89
- request.params.delete("request")
90
-
91
- claims.each do |k, v|
92
- request.params[k.to_s] = v
93
- end
94
99
  end
95
100
 
96
101
  validate_authorize_params
@@ -118,6 +123,10 @@ module Rodauth
118
123
  request.params[k.to_s] = v
119
124
  end
120
125
 
126
+ request.params.delete("request_uri")
127
+
128
+ # we're removing the request_uri here, so the checkup for signed reqest has to be invalidated.
129
+ @require_signed_request_object = false
121
130
  elsif oauth_require_pushed_authorization_requests ||
122
131
  (oauth_application && oauth_application[oauth_applications_require_pushed_authorization_requests_column])
123
132
  redirect_authorize_error("request_uri")
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Rodauth
4
4
  module OAuth
5
- VERSION = "1.3.1"
5
+ VERSION = "1.3.2"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rodauth-oauth
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tiago Cardoso
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-27 00:00:00.000000000 Z
11
+ date: 2023-07-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rodauth
@@ -71,6 +71,7 @@ extra_rdoc_files:
71
71
  - doc/release_notes/1_2_0.md
72
72
  - doc/release_notes/1_3_0.md
73
73
  - doc/release_notes/1_3_1.md
74
+ - doc/release_notes/1_3_2.md
74
75
  files:
75
76
  - CHANGELOG.md
76
77
  - LICENSE.txt
@@ -113,6 +114,7 @@ files:
113
114
  - doc/release_notes/1_2_0.md
114
115
  - doc/release_notes/1_3_0.md
115
116
  - doc/release_notes/1_3_1.md
117
+ - doc/release_notes/1_3_2.md
116
118
  - lib/generators/rodauth/oauth/install_generator.rb
117
119
  - lib/generators/rodauth/oauth/templates/app/models/oauth_application.rb
118
120
  - lib/generators/rodauth/oauth/templates/app/models/oauth_grant.rb