rodauth-oauth 0.9.0 → 0.9.3

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: 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