rodauth-oauth 1.6.3 → 1.6.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 99a936bfd8b23b07d5f7611c86d2098573bc83e8d9eb2dbf0531936385bc5e29
4
- data.tar.gz: 9fb05d801cda3f35e9a9a4326933d98c6ea20c304e80be014b0ade2d1009bf03
3
+ metadata.gz: cc6a614c797762b13c8b97dc51abf5191dc09ef919e27fd394f46504b62410c7
4
+ data.tar.gz: 3c6003d4131e03514a5bf8acd674cc1b7d8fcacd9eada7ab98044fc3f037fc95
5
5
  SHA512:
6
- metadata.gz: fcfa0988b7fe51d1455073913591850df119e98a7e38296c1b43bf509e9f6279094a538d13eb751d7e8ff3eac85d15678ecd7aa883c2c452818f13e13a9630e3
7
- data.tar.gz: e311c4bb35bfe6f131dbf0c79e7d0f9cb4d75fa5ba0d709c13fcdce7875c807553c5d46020300362952f576980034667dc7c97d27ed5bf4f7540d0874728b5f0
6
+ metadata.gz: 997c1e682fc4f482d0ea367a918c4db15130535294db13f19c52c558f152eca78fd197461d74ce269e77a56b36cf465b8b7e9d2424f3b999cb6491782c8d69f6
7
+ data.tar.gz: 2109d08b8911e33697e08e528899d45535de16c1cc994d663324c51161fed1f6e35386849413adb6dadbb526b2b75c64522865cbdf90798042ca8e68767362cf
data/README.md CHANGED
@@ -50,6 +50,7 @@ This gem implements the following RFCs and features of OAuth:
50
50
  * `oauth_assertion_base` - [Assertion Framework](https://datatracker.ietf.org/doc/html/rfc7521);
51
51
  * `oauth_saml_bearer_grant` - [SAML 2.0 Bearer Assertion](https://datatracker.ietf.org/doc/html/rfc7522);
52
52
  * `oauth_jwt_bearer_grant` - [JWT Bearer Assertion](https://datatracker.ietf.org/doc/html/rfc7523);
53
+ * `oauth_dpop` - [OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer (DPoP)](https://datatracker.ietf.org/doc/rfc9449/);
53
54
 
54
55
  * `oauth_dynamic_client_registration` - [Dynamic Client Registration Protocol](https://datatracker.ietf.org/doc/html/rfc7591) and [Dynamic Client Registration Management](https://www.rfc-editor.org/rfc/rfc7592);
55
56
  * OAuth application and token management dashboards;
@@ -67,7 +68,7 @@ It also implements several components of [OpenID Connect](https://openid.net/con
67
68
  * `oidc_frontchannel_logout` - [Frontchannel Logout](https://gitlab.com/os85/rodauth-oauth/-/wikis/Frontchannel-Logout);
68
69
  * `oidc_backchannel_logout` - [Backchannel Logout](https://gitlab.com/os85/rodauth-oauth/-/wikis/Backchannel-Logout);
69
70
 
70
- This gem supports also rails (through [rodauth-rails]((https://github.com/janko/rodauth-rails))).
71
+ This gem supports also rails (via [rodauth-rails]((https://github.com/janko/rodauth-rails)), which also dictates the versioning policy).
71
72
 
72
73
 
73
74
  ## Installation
@@ -0,0 +1,9 @@
1
+ ### 1.6.4
2
+
3
+ #### Bugfixes
4
+
5
+ * on `/userinfo` request, enforce existence of oauth grant before proceeding with the userinfo requests.
6
+
7
+ #### Chore
8
+
9
+ * linting + tweaks to example scripts.
@@ -0,0 +1,10 @@
1
+ ### 1.6.5
2
+
3
+ #### Bugfixes
4
+
5
+ * `:oauth_dynamic_client_registration` feature: scoping to the client being updated, preventing thst the resulting database operations touch every row in the `"oauth_applications"` table.
6
+ * `:json` feature: fixed implementation of `json_request?`.
7
+ * `json-jwt` support: set explicit `:skip_verification` parameter when the jwt decoding algorithm is `"none"`.
8
+ * rails generators: add missing uniqueness constraint to the index on the `"issuer"` column of the `"oauth_saml_settings"` table.
9
+ * rails generators: remove the `"jti"` column entry from the `"oauth_dpop_proofs"` table (already made the pkey in the `create_table` instruction).
10
+ * Make `password_hash` method public, which restores compatibility with `rodauth` v2.28 or more recent; `rodauth` **minimum supported version** becomes 2.28.0 as a result, which should not be perceived as a breaking change, as there are no other significant API changes, and ruby version support remains the same.
@@ -136,10 +136,10 @@ class CreateRodauthOauth < ActiveRecord::Migration<%= migration_version %>
136
136
  t.boolean :check_idp_cert_expiration, null: true
137
137
  t.text :name_identifier_format, null: true
138
138
  t.string :audience, null: true
139
- t.string :issuer, null: false, unique: true
139
+ t.string :issuer, null: false, index: { unique: true }
140
140
  end
141
141
 
142
- create_table :oauth_dpop_proofs, primary_key: :jti do |t|
142
+ create_table :oauth_dpop_proofs, id: false, primary_key: :jti do |t|
143
143
  t.string :jti, null: false
144
144
  t.datetime :first_use, null: false, default: -> { "CURRENT_TIMESTAMP(6)" }
145
145
  end
@@ -33,6 +33,10 @@ module Rodauth
33
33
  :oauth_grants_resource_owner_columns
34
34
  )
35
35
 
36
+ OAUTH_ACCESS_TYPES = %w[offline online].freeze
37
+
38
+ OAUTH_APPROVAL_PROMPTS = %w[force auto].freeze
39
+
36
40
  # /authorize
37
41
  auth_server_route(:authorize) do |r|
38
42
  require_authorizable_account
@@ -106,22 +110,18 @@ module Rodauth
106
110
  false
107
111
  end
108
112
 
109
- ACCESS_TYPES = %w[offline online].freeze
110
-
111
113
  def check_valid_access_type?
112
114
  return true unless use_oauth_access_type?
113
115
 
114
116
  access_type = param_or_nil("access_type")
115
- !access_type || ACCESS_TYPES.include?(access_type)
117
+ !access_type || OAUTH_ACCESS_TYPES.include?(access_type)
116
118
  end
117
119
 
118
- APPROVAL_PROMPTS = %w[force auto].freeze
119
-
120
120
  def check_valid_approval_prompt?
121
121
  return true unless use_oauth_access_type?
122
122
 
123
123
  approval_prompt = param_or_nil("approval_prompt")
124
- !approval_prompt || APPROVAL_PROMPTS.include?(approval_prompt)
124
+ !approval_prompt || OAUTH_APPROVAL_PROMPTS.include?(approval_prompt)
125
125
  end
126
126
 
127
127
  def resource_owner_params
@@ -142,7 +142,7 @@ module Rodauth
142
142
  oauth_grants_redirect_uri_column => redirect_uri,
143
143
  oauth_grants_scopes_column => scopes.join(oauth_scope_separator),
144
144
  oauth_grants_access_type_column => "online"
145
- ).count.zero?
145
+ ).none?
146
146
 
147
147
  # if there's a previous oauth grant for the params combo, it means that this user has approved before.
148
148
  request.env["REQUEST_METHOD"] = "POST"
@@ -191,10 +191,10 @@ module Rodauth
191
191
  (accept = request.env["HTTP_ACCEPT"]) && accept =~ json_request_regexp
192
192
  end
193
193
 
194
- # copied from the jwt feature
194
+ # copied from the json feature
195
195
  def json_request?
196
- return super if features.include?(:jsonn)
197
- return @json_request if defined?(@json_request)
196
+ return super if features.include?(:json)
197
+ return @json_request unless @json_request.nil?
198
198
 
199
199
  @json_request = request.content_type =~ json_request_regexp
200
200
  end
@@ -221,13 +221,11 @@ module Rodauth
221
221
  def oauth_application
222
222
  return @oauth_application if defined?(@oauth_application)
223
223
 
224
- @oauth_application = begin
225
- client_id = param_or_nil("client_id")
224
+ client_id = param_or_nil("client_id")
226
225
 
227
- return unless client_id
226
+ return unless client_id
228
227
 
229
- db[oauth_applications_table].filter(oauth_applications_client_id_column => client_id).first
230
- end
228
+ @oauth_application = db[oauth_applications_table].filter(oauth_applications_client_id_column => client_id).first
231
229
  end
232
230
 
233
231
  def fetch_access_token
@@ -309,6 +307,12 @@ module Rodauth
309
307
  self.class.send(:define_method, :__one_oauth_token_per_account) { one_oauth_token_per_account }
310
308
  end
311
309
 
310
+ def password_hash(password)
311
+ return super if features.include?(:login_password_requirements_base)
312
+
313
+ BCrypt::Password.create(password, cost: BCrypt::Engine::DEFAULT_COST)
314
+ end
315
+
312
316
  private
313
317
 
314
318
  def oauth_account_ds(account_id)
@@ -455,21 +459,15 @@ module Rodauth
455
459
  oauth_grant[oauth_grants_oauth_application_id_column] == oauth_application[oauth_applications_id_column]
456
460
  end
457
461
 
458
- def password_hash(password)
459
- return super if features.include?(:login_password_requirements_base)
460
-
461
- BCrypt::Password.create(password, cost: BCrypt::Engine::DEFAULT_COST)
462
- end
463
-
464
462
  def generate_token(grant_params = {}, should_generate_refresh_token = true)
465
- if grant_params[oauth_grants_id_column] && (oauth_reuse_access_token &&
466
- (
467
- if oauth_grants_token_hash_column
468
- grant_params[oauth_grants_token_hash_column]
469
- else
470
- grant_params[oauth_grants_token_column]
471
- end
472
- ))
463
+ if grant_params[oauth_grants_id_column] && oauth_reuse_access_token &&
464
+ (
465
+ if oauth_grants_token_hash_column
466
+ grant_params[oauth_grants_token_hash_column]
467
+ else
468
+ grant_params[oauth_grants_token_column]
469
+ end
470
+ )
473
471
  return grant_params
474
472
  end
475
473
 
@@ -18,7 +18,7 @@ module Rodauth
18
18
  request.on(registration_client_uri_route) do
19
19
  # CLIENT REGISTRATION URI
20
20
  request.on(String) do |client_id|
21
- (token = (v = request.env["HTTP_AUTHORIZATION"]) && v[/\A *Bearer (.*)\Z/, 1])
21
+ token = (v = request.env["HTTP_AUTHORIZATION"]) && v[/\A *Bearer (.*)\Z/, 1]
22
22
 
23
23
  next unless token
24
24
 
@@ -50,6 +50,7 @@ module Rodauth
50
50
 
51
51
  oauth_application = transaction do
52
52
  applications_ds = db[oauth_applications_table]
53
+ .where(oauth_applications_client_id_column => client_id)
53
54
  __update_and_return__(applications_ds, @oauth_application_params)
54
55
  end
55
56
  json_response_oauth_application(oauth_application)
@@ -259,21 +259,21 @@ module Rodauth
259
259
  elsif jws_key
260
260
  JSON::JWT.decode(token, jws_key)
261
261
  else
262
- JSON::JWT.decode(token, nil, jws_algorithm)
262
+ JSON::JWT.decode(token, (:skip_verification if jws_algorithm == "none"), jws_algorithm)
263
263
  end
264
264
  elsif (jwks = auth_server_jwks_set)
265
265
  JSON::JWT.decode(token, JSON::JWK::Set.new(jwks))
266
266
  end
267
267
 
268
268
  now = Time.now
269
- if verify_claims && (
270
- (!claims[:exp] || Time.at(claims[:exp]) < now) &&
271
- (claims[:nbf] && Time.at(claims[:nbf]) < now) &&
272
- (claims[:iat] && Time.at(claims[:iat]) < now) &&
273
- (verify_iss && claims[:iss] != oauth_jwt_issuer) &&
274
- (verify_aud && !verify_aud(claims[:aud], claims[:client_id])) &&
275
- (verify_jti && !verify_jti(claims[:jti], claims))
276
- )
269
+ if verify_claims &&
270
+ (!claims[:exp] || Time.at(claims[:exp]) < now) &&
271
+ claims[:nbf] && Time.at(claims[:nbf]) < now &&
272
+ claims[:iat] && Time.at(claims[:iat]) < now &&
273
+ verify_iss && claims[:iss] != oauth_jwt_issuer &&
274
+ verify_aud && !verify_aud(claims[:aud], claims[:client_id]) &&
275
+ verify_jti && !verify_jti(claims[:jti], claims)
276
+
277
277
  return
278
278
  end
279
279
 
@@ -347,7 +347,6 @@ module Rodauth
347
347
  def jwt_encode(payload,
348
348
  signing_algorithm: oauth_jwt_keys.keys.first,
349
349
  headers: {}, **)
350
-
351
350
  key = oauth_jwt_keys[signing_algorithm] || _jwt_key
352
351
  key = key.first if key.is_a?(Array)
353
352
 
@@ -475,7 +474,6 @@ module Rodauth
475
474
  jwe_key: oauth_jwt_jwe_keys[[jws_encryption_algorithm, jws_encryption_method]] || oauth_jwt_jwe_keys.values.first,
476
475
  **args
477
476
  )
478
-
479
477
  token = if jwks && jwks.any? { |k| k[:use] == "enc" }
480
478
  JWE.__rodauth_oauth_decrypt_from_jwks(token, jwks, alg: jws_encryption_algorithm, enc: jws_encryption_method)
481
479
  elsif jwe_key
@@ -138,21 +138,20 @@ module Rodauth
138
138
  def client_certificate_sans
139
139
  return @client_certificate_sans if defined?(@client_certificate_sans)
140
140
 
141
- @client_certificate_sans = begin
142
- return [] unless client_certificate
143
-
144
- san = client_certificate.extensions.find { |ext| ext.oid == "subjectAltName" }
145
-
146
- return [] unless san
147
-
148
- ostr = OpenSSL::ASN1.decode(san.to_der).value.last
149
-
150
- sans = OpenSSL::ASN1.decode(ostr.value)
151
-
152
- return [] unless sans
153
-
154
- sans.value
155
- end
141
+ @client_certificate_sans =
142
+ if client_certificate
143
+ if (san = client_certificate.extensions.find { |ext| ext.oid == "subjectAltName" })
144
+ ostr = OpenSSL::ASN1.decode(san.to_der).value.last
145
+
146
+ sans = OpenSSL::ASN1.decode(ostr.value)
147
+
148
+ sans ? sans.value : []
149
+ else
150
+ []
151
+ end
152
+ else
153
+ []
154
+ end
156
155
  end
157
156
 
158
157
  def distinguished_name_match?(sub1, sub2)
@@ -99,7 +99,7 @@ module Rodauth
99
99
  private
100
100
 
101
101
  def require_oauth_application_for_introspect
102
- (token = (v = request.env["HTTP_AUTHORIZATION"]) && v[/\A *Bearer (.*)\Z/, 1])
102
+ token = (v = request.env["HTTP_AUTHORIZATION"]) && v[/\A *Bearer (.*)\Z/, 1]
103
103
 
104
104
  return require_oauth_application unless token
105
105
 
@@ -144,7 +144,9 @@ module Rodauth
144
144
  **resource_owner_params_from_jwt_claims(claims)
145
145
  ).first
146
146
 
147
- claims_locales = oauth_grant[oauth_grants_claims_locales_column] if oauth_grant
147
+ throw_json_response_error(oauth_authorization_required_error_status, "invalid_token") unless oauth_grant
148
+
149
+ claims_locales = oauth_grant[oauth_grants_claims_locales_column]
148
150
 
149
151
  if (claims = oauth_grant[oauth_grants_claims_column])
150
152
  claims = JSON.parse(claims)
@@ -317,9 +319,8 @@ module Rodauth
317
319
  # MUST ignore the offline_access request unless the Client
318
320
  # is using a response_type value that would result in an
319
321
  # Authorization Code
320
- if sc && sc.include?("offline_access") && !(param_or_nil("prompt") == "consent" && (
321
- (response_type = param_or_nil("response_type")) && response_type.split(" ").include?("code")
322
- ))
322
+ if sc && sc.include?("offline_access") && !(param_or_nil("prompt") == "consent" &&
323
+ (response_type = param_or_nil("response_type")) && response_type.split(" ").include?("code"))
323
324
  sc.delete("offline_access")
324
325
 
325
326
  request.params["scope"] = sc.join(" ")
@@ -792,9 +793,7 @@ module Rodauth
792
793
  # Metadata
793
794
 
794
795
  def openid_configuration_body(path = nil)
795
- metadata = oauth_server_metadata_body(path).select do |k, _|
796
- VALID_METADATA_KEYS.include?(k)
797
- end
796
+ metadata = oauth_server_metadata_body(path).slice(*VALID_METADATA_KEYS)
798
797
 
799
798
  scope_claims = oauth_application_scopes.each_with_object([]) do |scope, claims|
800
799
  oidc, param = scope.split(".", 2)
@@ -873,7 +872,7 @@ module Rodauth
873
872
  return unless digest
874
873
 
875
874
  hash = digest.digest(hash)
876
- hash = hash[0...hash.size / 2]
875
+ hash = hash[0...(hash.size / 2)]
877
876
  Base64.urlsafe_encode64(hash).tr("=", "")
878
877
  end
879
878
  end
@@ -132,8 +132,8 @@ module Rodauth
132
132
  end
133
133
  end
134
134
 
135
- if features.include?(:oidc_rp_initiated_logout) && (defined?(oauth_applications_post_logout_redirect_uris_column) &&
136
- (value = @oauth_application_params[oauth_applications_post_logout_redirect_uris_column]))
135
+ if features.include?(:oidc_rp_initiated_logout) && defined?(oauth_applications_post_logout_redirect_uris_column) &&
136
+ (value = @oauth_application_params[oauth_applications_post_logout_redirect_uris_column])
137
137
  if value.is_a?(Array)
138
138
  @oauth_application_params[oauth_applications_post_logout_redirect_uris_column] = value.each do |redirect_uri|
139
139
  unless check_valid_uri?(redirect_uri)
@@ -23,7 +23,7 @@ module Rodauth
23
23
  catch_error do
24
24
  validate_oidc_logout_params
25
25
 
26
- claims = oauth_application = nil
26
+ claims = nil
27
27
 
28
28
  if (id_token_hint = param_or_nil("id_token_hint"))
29
29
  #
@@ -81,7 +81,7 @@ module Rodauth
81
81
  end
82
82
 
83
83
  def __insert_or_do_nothing_and_return__(dataset, pkey, unique_columns, params)
84
- find_params = params.select { |key, _| unique_columns.include?(key) }
84
+ find_params = params.slice(*unique_columns)
85
85
  dataset.where(find_params).first || __insert_and_return__(dataset, pkey, params)
86
86
  end
87
87
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Rodauth
4
4
  module OAuth
5
- VERSION = "1.6.3"
5
+ VERSION = "1.6.5"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rodauth-oauth
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.3
4
+ version: 1.6.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tiago Cardoso
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-07-26 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: base64
@@ -30,24 +29,24 @@ dependencies:
30
29
  requirements:
31
30
  - - "~>"
32
31
  - !ruby/object:Gem::Version
33
- version: '2.0'
32
+ version: '2.28'
34
33
  type: :runtime
35
34
  prerelease: false
36
35
  version_requirements: !ruby/object:Gem::Requirement
37
36
  requirements:
38
37
  - - "~>"
39
38
  - !ruby/object:Gem::Version
40
- version: '2.0'
39
+ version: '2.28'
41
40
  description: Implementation of the OAuth 2.0 protocol on top of rodauth.
42
41
  email:
43
42
  - cardoso_tiago@hotmail.com
44
43
  executables: []
45
44
  extensions: []
46
45
  extra_rdoc_files:
46
+ - CHANGELOG.md
47
47
  - LICENSE.txt
48
- - README.md
49
48
  - MIGRATION-GUIDE-v1.md
50
- - CHANGELOG.md
49
+ - README.md
51
50
  - doc/release_notes/0_0_1.md
52
51
  - doc/release_notes/0_0_2.md
53
52
  - doc/release_notes/0_0_3.md
@@ -92,6 +91,8 @@ extra_rdoc_files:
92
91
  - doc/release_notes/1_6_1.md
93
92
  - doc/release_notes/1_6_2.md
94
93
  - doc/release_notes/1_6_3.md
94
+ - doc/release_notes/1_6_4.md
95
+ - doc/release_notes/1_6_5.md
95
96
  files:
96
97
  - CHANGELOG.md
97
98
  - LICENSE.txt
@@ -141,6 +142,8 @@ files:
141
142
  - doc/release_notes/1_6_1.md
142
143
  - doc/release_notes/1_6_2.md
143
144
  - doc/release_notes/1_6_3.md
145
+ - doc/release_notes/1_6_4.md
146
+ - doc/release_notes/1_6_5.md
144
147
  - lib/generators/rodauth/oauth/install_generator.rb
145
148
  - lib/generators/rodauth/oauth/templates/app/models/oauth_application.rb
146
149
  - lib/generators/rodauth/oauth/templates/app/models/oauth_grant.rb
@@ -227,7 +230,6 @@ metadata:
227
230
  source_code_uri: https://gitlab.com/os85/rodauth-oauth
228
231
  changelog_uri: https://gitlab.com/os85/rodauth-oauth/-/blob/master/CHANGELOG.md
229
232
  rubygems_mfa_required: 'true'
230
- post_install_message:
231
233
  rdoc_options: []
232
234
  require_paths:
233
235
  - lib
@@ -242,8 +244,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
242
244
  - !ruby/object:Gem::Version
243
245
  version: '0'
244
246
  requirements: []
245
- rubygems_version: 3.5.3
246
- signing_key:
247
+ rubygems_version: 3.6.9
247
248
  specification_version: 4
248
249
  summary: Implementation of the OAuth 2.0 protocol on top of rodauth.
249
250
  test_files: []