rodauth-oauth 0.9.0 → 0.9.3

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: ae63d25ed38845cc8186a0484cf7a223f4939b2695d61b58bc2df7d3aec1af0c
4
- data.tar.gz: bb0361efcde688c2dda720825513af035a7befcb36460c11d6d80226e8791bbe
3
+ metadata.gz: 79fa7abfcf7ea1c0a2594f92bfd7a59e53769dec5a7221a22f740b37471e6e07
4
+ data.tar.gz: 9f7e8ce4370d44d985940d46d569e3192c35e53cb6e3841094517e64258b2b1a
5
5
  SHA512:
6
- metadata.gz: 28a740ca518ec609cfcbffc61fbaa8f71fb049a8221ccb7858b9357b8f4eee9b3fe263b1a2747f1839677f07d05f3a2cf8c2c4370d1ae8c1b3fe4c33d54012c1
7
- data.tar.gz: ad1d38778909f1f7bb8d6e69fbb44177c02eb96c2d40a94e54e0a1221efc871b7fc0e5c364845b38b5a7bb35d33610a9fdcddbb2e95f18be47a6b039f986ed9f
6
+ metadata.gz: 520a85416c418f93615a471133201c7ec6dd5c16ff9d697a6ca87b609084b58c7b9799c5d3c7a8d02a4fc68ca9750c9ef41dd07dde17c1e1153f0cec8e58f3c1
7
+ data.tar.gz: 8e7ebc28ba158f43b5226a2de98fc76de8c81e8a8c87dbaa95a69050eed96793e487666389b60be6694437c298fdf23f183a1df098983eeec5f5549df0321c7c
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Rodauth::Oauth
2
2
 
3
- [![pipeline status](https://gitlab.com/honeyryderchuck/rodauth-oauth/badges/master/pipeline.svg)](https://gitlab.com/honeyryderchuck/rodauth-oauth/-/pipelines?page=1&ref=master)
3
+ [![Gem Version](https://badge.fury.io/rb/rodauth-oauth.svg)](http://rubygems.org/gems/rodauth-oauth)
4
+ [![pipeline status](https://gitlab.com/honeyryderchuck/rodauth-oauth/badges/master/pipeline.svg)](https://gitlab.com/honeyryderchuck/rodauth-oauth/pipelines?page=1&scope=all&ref=master)
4
5
  [![coverage report](https://gitlab.com/honeyryderchuck/rodauth-oauth/badges/master/coverage.svg?job=coverage)](https://honeyryderchuck.gitlab.io/rodauth-oauth/coverage/#_AllFiles)
5
6
 
6
7
  This is an extension to the `rodauth` gem which implements the [OAuth 2.0 framework](https://tools.ietf.org/html/rfc6749) for an authorization server.
@@ -73,7 +74,7 @@ Or install it yourself as:
73
74
 
74
75
  ## Usage
75
76
 
76
- This tutorial assumes you already read the documentation and know how to set up `rodauth`. After that, integrating `roda-auth` will look like:
77
+ This tutorial assumes you already read the documentation and know how to set up `rodauth`. After that, integrating `rodauth-oauth` will look like:
77
78
 
78
79
  ```ruby
79
80
  plugin :rodauth do
@@ -0,0 +1,9 @@
1
+ ### 0.9.1 (08/05/2022)
2
+
3
+ #### Improvements
4
+
5
+ Using `return_response`, introduced in `rodauth` v2.23, which accomplishes better integration with rails response logging mechanism when used under `rodauth-rails`.
6
+
7
+ #### Bugfixes
8
+
9
+ * Fixing namespacing issue which required anyone to have to `require "rodauth-oauth"` before loading it (no need to anymore).
@@ -0,0 +1,10 @@
1
+ ### 0.9.2 (11/05/2022)
2
+
3
+ #### Bugfixes
4
+
5
+ * Fixed remaining namespacing fix issues requiring usage of `require "rodauth-oauth"`.
6
+ * Fixed wrong expectation of database for resource-server mode when `:oauth_management_base` plugin was used.
7
+ * oidc: fixed incorrect grant creation flow whenn using `nonce` param.
8
+ * oidc: fixed jwt encoding regression when not setting encryption method/algorithmm for client applications.
9
+ * templates: added missing jwks field to the "New oauth application" form.
10
+ * Several fixes on the example OIDC applications, mostly around CSRF breakage when using latest version of `omniauth`.
@@ -0,0 +1,9 @@
1
+ ### 0.9.2 (30/05/2022)
2
+
3
+ #### Bugfixes
4
+
5
+ * `oauth_jwt`: new access tokens generated via the `"refresh_token"` grant type are now JWT (it was falling back to non JWT behaviour);
6
+ * `oidc`: a new `id_token` is now generated via the `"refresh_token"` grant type with "rotation" policy (it was being omitted from the response);
7
+ * `oidc`: fixing calculation of `"auth_time"` claim, which (as per RFC) needs to stay the same across first authentication and subsequent `"refresh_token"` requests;
8
+ * it requires a new db column (default: `"auth_time"`, datetime) in the `"oauth_tokens"` database;
9
+ * hash-column `"refresh_token"` will now expose the refresh token (instead of the hash column version) in the `"refresh_token"` grant type response payload (only happened in "non-rotation" refresh token mode).
@@ -77,6 +77,7 @@ class CreateRodauthOauth < ActiveRecord::Migration<%= migration_version %>
77
77
  t.datetime :created_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
78
78
  # uncomment to use OIDC nonce
79
79
  # t.string :nonce
80
+ # t.datetime :auth_time
80
81
  end
81
82
  end
82
83
  end
@@ -33,7 +33,7 @@ module Rodauth
33
33
 
34
34
  translatable_method :oauth_applications_name_label, "Name"
35
35
  translatable_method :oauth_applications_description_label, "Description"
36
- translatable_method :oauth_applications_scopes_label, "Scopes"
36
+ translatable_method :oauth_applications_scopes_label, "Default scopes"
37
37
  translatable_method :oauth_applications_contacts_label, "Contacts"
38
38
  translatable_method :oauth_applications_tos_uri_label, "Terms of service"
39
39
  translatable_method :oauth_applications_policy_uri_label, "Policy"
@@ -4,6 +4,8 @@ require "time"
4
4
  require "base64"
5
5
  require "securerandom"
6
6
  require "net/http"
7
+ require "rodauth/version"
8
+ require "rodauth/oauth/version"
7
9
  require "rodauth/oauth/ttl_store"
8
10
  require "rodauth/oauth/database_extensions"
9
11
  require "rodauth/oauth/refinements"
@@ -424,32 +426,40 @@ module Rodauth
424
426
  }.merge(params)
425
427
 
426
428
  rescue_from_uniqueness_error do
427
- token = oauth_unique_id_generator
429
+ access_token = _generate_access_token(create_params)
430
+ refresh_token = _generate_refresh_token(create_params) if should_generate_refresh_token
431
+ oauth_token = _store_oauth_token(create_params)
432
+ oauth_token[oauth_tokens_token_column] = access_token
433
+ oauth_token[oauth_tokens_refresh_token_column] = refresh_token if refresh_token
434
+ oauth_token
435
+ end
436
+ end
428
437
 
429
- if oauth_tokens_token_hash_column
430
- create_params[oauth_tokens_token_hash_column] = generate_token_hash(token)
431
- else
432
- create_params[oauth_tokens_token_column] = token
433
- end
438
+ def _generate_access_token(params = {})
439
+ token = oauth_unique_id_generator
434
440
 
435
- refresh_token = nil
436
- if should_generate_refresh_token
437
- refresh_token = oauth_unique_id_generator
441
+ if oauth_tokens_token_hash_column
442
+ params[oauth_tokens_token_hash_column] = generate_token_hash(token)
443
+ else
444
+ params[oauth_tokens_token_column] = token
445
+ end
438
446
 
439
- if oauth_tokens_refresh_token_hash_column
440
- create_params[oauth_tokens_refresh_token_hash_column] = generate_token_hash(refresh_token)
441
- else
442
- create_params[oauth_tokens_refresh_token_column] = refresh_token
443
- end
444
- end
445
- oauth_token = _generate_oauth_token(create_params)
446
- oauth_token[oauth_tokens_token_column] = token
447
- oauth_token[oauth_tokens_refresh_token_column] = refresh_token if refresh_token
448
- oauth_token
447
+ token
448
+ end
449
+
450
+ def _generate_refresh_token(params)
451
+ token = oauth_unique_id_generator
452
+
453
+ if oauth_tokens_refresh_token_hash_column
454
+ params[oauth_tokens_refresh_token_hash_column] = generate_token_hash(token)
455
+ else
456
+ params[oauth_tokens_refresh_token_column] = token
449
457
  end
458
+
459
+ token
450
460
  end
451
461
 
452
- def _generate_oauth_token(params = {})
462
+ def _store_oauth_token(params = {})
453
463
  ds = db[oauth_tokens_table]
454
464
 
455
465
  if __one_oauth_token_per_account
@@ -575,43 +585,24 @@ module Rodauth
575
585
 
576
586
  rescue_from_uniqueness_error do
577
587
  oauth_tokens_ds = db[oauth_tokens_table]
578
- token = oauth_unique_id_generator
588
+ access_token = _generate_access_token(update_params)
579
589
 
580
- if oauth_tokens_token_hash_column
581
- update_params[oauth_tokens_token_hash_column] = generate_token_hash(token)
590
+ if oauth_refresh_token_protection_policy == "rotation"
591
+ update_params = {
592
+ **update_params,
593
+ oauth_tokens_oauth_token_id_column => oauth_token[oauth_tokens_id_column],
594
+ oauth_tokens_account_id_column => oauth_token[oauth_tokens_account_id_column],
595
+ oauth_tokens_scopes_column => oauth_token[oauth_tokens_scopes_column]
596
+ }
597
+
598
+ refresh_token = _generate_refresh_token(update_params)
582
599
  else
583
- update_params[oauth_tokens_token_column] = token
600
+ refresh_token = param("refresh_token")
584
601
  end
602
+ oauth_token = __update_and_return__(oauth_tokens_ds, update_params)
585
603
 
586
- oauth_token = if oauth_refresh_token_protection_policy == "rotation"
587
- insert_params = {
588
- **update_params,
589
- oauth_tokens_oauth_token_id_column => oauth_token[oauth_tokens_id_column],
590
- oauth_tokens_scopes_column => oauth_token[oauth_tokens_scopes_column]
591
- }
592
-
593
- refresh_token = oauth_unique_id_generator
594
-
595
- if oauth_tokens_refresh_token_hash_column
596
- insert_params[oauth_tokens_refresh_token_hash_column] = generate_token_hash(refresh_token)
597
- else
598
- insert_params[oauth_tokens_refresh_token_column] = refresh_token
599
- end
600
-
601
- # revoke the refresh token
602
- oauth_tokens_ds.where(oauth_tokens_id_column => oauth_token[oauth_tokens_id_column])
603
- .update(oauth_tokens_revoked_at_column => Sequel::CURRENT_TIMESTAMP)
604
-
605
- insert_params[oauth_tokens_oauth_token_id_column] = oauth_token[oauth_tokens_id_column]
606
- __insert_and_return__(oauth_tokens_ds, oauth_tokens_id_column, insert_params)
607
- else
608
- # includes none
609
- ds = oauth_tokens_ds.where(oauth_tokens_id_column => oauth_token[oauth_tokens_id_column])
610
- __update_and_return__(ds, update_params)
611
- end
612
-
613
- oauth_token[oauth_tokens_token_column] = token
614
- oauth_token[oauth_tokens_refresh_token_column] = refresh_token if refresh_token
604
+ oauth_token[oauth_tokens_token_column] = access_token
605
+ oauth_token[oauth_tokens_refresh_token_column] = refresh_token
615
606
  oauth_token
616
607
  end
617
608
  end
@@ -687,8 +678,7 @@ module Rodauth
687
678
  response["Pragma"] = "no-cache"
688
679
  end
689
680
  json_payload = _json_response_body(body)
690
- response.write(json_payload)
691
- request.halt
681
+ return_response(json_payload)
692
682
  end
693
683
 
694
684
  def throw_json_response_error(status, error_code, message = nil)
@@ -703,8 +693,7 @@ module Rodauth
703
693
  json_payload = _json_response_body(payload)
704
694
  response["Content-Type"] ||= json_response_content_type
705
695
  response["WWW-Authenticate"] = oauth_token_type.upcase if status == 401
706
- response.write(json_payload)
707
- request.halt
696
+ return_response(json_payload)
708
697
  end
709
698
 
710
699
  unless method_defined?(:_json_response_body)
@@ -717,6 +706,13 @@ module Rodauth
717
706
  end
718
707
  end
719
708
 
709
+ if Gem::Version.new(Rodauth.version) < Gem::Version.new("2.23")
710
+ def return_response(body = nil)
711
+ response.write(body) if body
712
+ request.halt
713
+ end
714
+ end
715
+
720
716
  def authorization_required
721
717
  if accepts_json?
722
718
  throw_json_response_error(authorization_required_error_status, "invalid_client")
@@ -1,5 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
+ require "rodauth/oauth/version"
3
4
  require "rodauth/oauth/ttl_store"
4
5
 
5
6
  module Rodauth
@@ -38,6 +39,9 @@ module Rodauth
38
39
 
39
40
  translatable_method :oauth_applications_jwt_public_key_label, "Public key"
40
41
 
42
+ auth_value_method :oauth_application_jwt_public_key_param, "jwt_public_key"
43
+ auth_value_method :oauth_application_jwks_param, "jwks"
44
+
41
45
  auth_value_method :oauth_jwt_keys, {}
42
46
  auth_value_method :oauth_jwt_key, nil
43
47
  auth_value_method :oauth_jwt_public_key, nil
@@ -62,7 +66,6 @@ module Rodauth
62
66
  :jwt_encode,
63
67
  :jwt_decode,
64
68
  :jwks_set,
65
- :last_account_login_at,
66
69
  :generate_jti
67
70
  )
68
71
 
@@ -95,12 +98,6 @@ module Rodauth
95
98
 
96
99
  private
97
100
 
98
- unless method_defined?(:last_account_login_at)
99
- def last_account_login_at
100
- nil
101
- end
102
- end
103
-
104
101
  def issuer
105
102
  @issuer ||= oauth_jwt_token_issuer || authorization_server_url
106
103
  end
@@ -171,41 +168,38 @@ module Rodauth
171
168
 
172
169
  # /token
173
170
 
174
- def generate_oauth_token(params = {}, should_generate_refresh_token = true)
175
- create_params = {
176
- oauth_grants_expires_in_column => Sequel.date_add(Sequel::CURRENT_TIMESTAMP, seconds: oauth_token_expires_in)
177
- }.merge(params)
178
-
179
- oauth_token = rescue_from_uniqueness_error do
180
- if should_generate_refresh_token
181
- refresh_token = oauth_unique_id_generator
182
-
183
- if oauth_tokens_refresh_token_hash_column
184
- create_params[oauth_tokens_refresh_token_hash_column] = generate_token_hash(refresh_token)
185
- else
186
- create_params[oauth_tokens_refresh_token_column] = refresh_token
187
- end
188
- end
171
+ def create_oauth_token_from_token(oauth_token, update_params)
172
+ otoken = super
173
+ access_token = _generate_jwt_access_token(otoken)
174
+ otoken[oauth_tokens_token_column] = access_token
175
+ otoken
176
+ end
189
177
 
190
- _generate_oauth_token(create_params)
191
- end
178
+ def generate_oauth_token(params = {}, should_generate_refresh_token = true)
179
+ oauth_token = super
180
+ access_token = _generate_jwt_access_token(oauth_token)
181
+ oauth_token[oauth_tokens_token_column] = access_token
182
+ oauth_token
183
+ end
192
184
 
185
+ def _generate_jwt_access_token(oauth_token)
193
186
  claims = jwt_claims(oauth_token)
194
187
 
195
188
  # one of the points of using jwt is avoiding database lookups, so we put here all relevant
196
189
  # token data.
197
190
  claims[:scope] = oauth_token[oauth_tokens_scopes_column]
198
191
 
199
- token = jwt_encode(claims)
192
+ jwt_encode(claims)
193
+ end
200
194
 
201
- oauth_token[oauth_tokens_token_column] = token
202
- oauth_token
195
+ def _generate_access_token(*)
196
+ # no op
203
197
  end
204
198
 
205
199
  def jwt_claims(oauth_token)
206
200
  issued_at = Time.now.to_i
207
201
 
208
- claims = {
202
+ {
209
203
  iss: issuer, # issuer
210
204
  iat: issued_at, # issued at
211
205
  #
@@ -223,10 +217,6 @@ module Rodauth
223
217
  exp: issued_at + oauth_token_expires_in,
224
218
  aud: (oauth_jwt_audience || oauth_application[oauth_applications_client_id_column])
225
219
  }
226
-
227
- claims[:auth_time] = last_account_login_at.to_i if last_account_login_at
228
-
229
- claims
230
220
  end
231
221
 
232
222
  def jwt_subject(oauth_token)
@@ -714,8 +704,7 @@ module Rodauth
714
704
  response["Cache-Control"] = "no-store"
715
705
  response["Pragma"] = "no-cache"
716
706
  end
717
- response.write(jwt)
718
- request.halt
707
+ return_response(jwt)
719
708
  end
720
709
  end
721
710
  end
@@ -1,5 +1,6 @@
1
1
  # frozen-string-literal: true
2
2
 
3
+ require "rodauth/oauth/version"
3
4
  require "rodauth/oauth/ttl_store"
4
5
 
5
6
  module Rodauth
@@ -48,6 +48,10 @@ module Rodauth
48
48
 
49
49
  def post_configure
50
50
  super
51
+
52
+ # TODO: remove this in v1, when resource-server mode does not load all of the provider features.
53
+ return unless db
54
+
51
55
  db.extension :pagination
52
56
  end
53
57
 
@@ -74,6 +74,7 @@ module Rodauth
74
74
 
75
75
  auth_value_method :oauth_grants_nonce_column, :nonce
76
76
  auth_value_method :oauth_tokens_nonce_column, :nonce
77
+ auth_value_method :oauth_tokens_auth_time_column, :auth_time
77
78
 
78
79
  translatable_method :invalid_scope_message, "The Access Token expired"
79
80
 
@@ -120,7 +121,8 @@ module Rodauth
120
121
  jwks: oauth_application_jwks,
121
122
  encryption_algorithm: @oauth_application[oauth_applications_userinfo_encrypted_response_alg_column],
122
123
  encryption_method: @oauth_application[oauth_applications_userinfo_encrypted_response_enc_column]
123
- }
124
+ }.compact
125
+
124
126
  jwt = jwt_encode(
125
127
  oidc_claims,
126
128
  signing_algorithm: algo,
@@ -234,8 +236,7 @@ module Rodauth
234
236
  href: authorization_server_url
235
237
  }]
236
238
  })
237
- response.write(json_payload)
238
- request.halt
239
+ return_response(json_payload)
239
240
  end
240
241
  end
241
242
  end
@@ -316,7 +317,7 @@ module Rodauth
316
317
  def create_oauth_grant(create_params = {})
317
318
  return super unless (nonce = param_or_nil("nonce"))
318
319
 
319
- super(oauth_grants_nonce_column => nonce)
320
+ super(create_params.merge(oauth_grants_nonce_column => nonce))
320
321
  end
321
322
 
322
323
  def create_oauth_token_from_authorization_code(oauth_grant, create_params)
@@ -341,8 +342,7 @@ module Rodauth
341
342
 
342
343
  # Time when the End-User authentication occurred.
343
344
  #
344
- # Sounds like the same as issued at claim.
345
- id_token_claims[:auth_time] = id_token_claims[:iat]
345
+ id_token_claims[:auth_time] = oauth_token[oauth_tokens_auth_time_column].to_i
346
346
 
347
347
  account = db[accounts_table].where(account_id_column => oauth_token[oauth_tokens_account_id_column]).first
348
348
 
@@ -358,7 +358,8 @@ module Rodauth
358
358
  signing_algorithm: oauth_application[oauth_applications_id_token_signed_response_alg_column] || oauth_jwt_algorithm,
359
359
  encryption_algorithm: oauth_application[oauth_applications_id_token_encrypted_response_alg_column],
360
360
  encryption_method: oauth_application[oauth_applications_id_token_encrypted_response_enc_column]
361
- }
361
+ }.compact
362
+
362
363
  oauth_token[:id_token] = jwt_encode(id_token_claims, **params)
363
364
  end
364
365
 
@@ -487,7 +488,7 @@ module Rodauth
487
488
  end
488
489
  end
489
490
 
490
- scope_claims.unshift("auth_time") if last_account_login_at
491
+ scope_claims.unshift("auth_time")
491
492
 
492
493
  response_types_supported = metadata[:response_types_supported]
493
494
 
@@ -533,7 +534,7 @@ module Rodauth
533
534
  response["Access-Control-Allow-Methods"] = "GET, OPTIONS"
534
535
  response["Access-Control-Max-Age"] = "3600"
535
536
  response.status = 200
536
- request.halt
537
+ return_response
537
538
  end
538
539
  end
539
540
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Rodauth
4
4
  module OAuth
5
- VERSION = "0.9.0"
5
+ VERSION = "0.9.3"
6
6
  end
7
7
  end
data/locales/en.yml CHANGED
@@ -19,7 +19,7 @@ en:
19
19
  oauth_management_pagination_next_button: "Next"
20
20
  oauth_applications_name_label: "Name"
21
21
  oauth_applications_description_label: "Description"
22
- oauth_applications_scopes_label: "Scopes"
22
+ oauth_applications_scopes_label: "Default scopes"
23
23
  oauth_applications_contacts_label: "Contacts"
24
24
  oauth_applications_homepage_url_label: "Homepage URL"
25
25
  oauth_applications_tos_uri_label: "Terms of Service URL"
@@ -1,4 +1,4 @@
1
1
  <div class="form-group">
2
2
  <label for="name">#{rodauth.oauth_applications_jwks_label}#{rodauth.input_field_label_suffix}</label>
3
- #{rodauth.input_field_string(rodauth.oauth_application_jwks_param, "jwks", :type=>"text")}
3
+ <textarea id="jwks" class="form-control" name="#{rodauth.oauth_application_jwks_param}" rows="3"></textarea>
4
4
  </div>
@@ -1,4 +1,4 @@
1
1
  <div class="form-group">
2
2
  <label for="name">#{rodauth.oauth_applications_jwt_public_key_label}#{rodauth.input_field_label_suffix}</label>
3
- #{rodauth.input_field_string(rodauth.oauth_application_jwt_public_key_param, "jwt_public_key", :type=>"text")}
3
+ #{rodauth.input_field_string(rodauth.oauth_application_jwt_public_key_param, "jwt_public_key", :type=>"text", :required=>false)}
4
4
  </div>
@@ -11,7 +11,7 @@
11
11
  if rodauth.features.include?(:oauth_jwt)
12
12
  <<-HTML
13
13
  #{rodauth.render('jwt_public_key_field')}
14
- #{rodauth.render('jws_jwk_field')}
14
+ #{rodauth.render('jwks_field')}
15
15
  HTML
16
16
  end
17
17
  }
@@ -1,8 +1,9 @@
1
1
  <fieldset class="form-group">
2
+ <legend>#{rodauth.oauth_applications_scopes_label}</legend>
2
3
  #{
3
4
  rodauth.oauth_application_scopes.map do |scope|
4
- "<div class=\"form-check checkbox\">" +
5
- "<input id=\"#{scope}\" type=\"checkbox\" name=\"#{rodauth.oauth_application_scopes_param}[]\" value=\"#{scope}\">" +
5
+ "<div class=\"form-group form-check\">" +
6
+ "<input id=\"#{scope}\" type=\"checkbox\" class=\"form-check-input\" name=\"#{rodauth.oauth_application_scopes_param}[]\" value=\"#{scope}\">" +
6
7
  "<label for=\"#{scope}\">#{scope}</label>" +
7
8
  "</div>"
8
9
  end.join
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: 0.9.0
4
+ version: 0.9.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tiago Cardoso
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-04-19 00:00:00.000000000 Z
11
+ date: 2022-05-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rodauth
@@ -57,6 +57,9 @@ extra_rdoc_files:
57
57
  - doc/release_notes/0_7_4.md
58
58
  - doc/release_notes/0_8_0.md
59
59
  - doc/release_notes/0_9_0.md
60
+ - doc/release_notes/0_9_1.md
61
+ - doc/release_notes/0_9_2.md
62
+ - doc/release_notes/0_9_3.md
60
63
  files:
61
64
  - CHANGELOG.md
62
65
  - LICENSE.txt
@@ -85,6 +88,9 @@ files:
85
88
  - doc/release_notes/0_7_4.md
86
89
  - doc/release_notes/0_8_0.md
87
90
  - doc/release_notes/0_9_0.md
91
+ - doc/release_notes/0_9_1.md
92
+ - doc/release_notes/0_9_2.md
93
+ - doc/release_notes/0_9_3.md
88
94
  - lib/generators/rodauth/oauth/install_generator.rb
89
95
  - lib/generators/rodauth/oauth/templates/app/models/oauth_application.rb
90
96
  - lib/generators/rodauth/oauth/templates/app/models/oauth_grant.rb