rodauth-oauth 0.10.4 → 1.0.0.pre.beta1

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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/MIGRATION-GUIDE-v1.md +286 -0
  3. data/README.md +22 -30
  4. data/doc/release_notes/1_0_0_beta1.md +38 -0
  5. data/lib/generators/rodauth/oauth/install_generator.rb +0 -1
  6. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize.html.erb +4 -6
  7. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/device_search.html.erb +1 -1
  8. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/device_verification.html.erb +2 -2
  9. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/new_oauth_application.html.erb +1 -6
  10. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application.html.erb +0 -2
  11. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application_oauth_grants.html.erb +41 -0
  12. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_applications.html.erb +2 -2
  13. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_grants.html.erb +37 -0
  14. data/lib/generators/rodauth/oauth/templates/db/migrate/create_rodauth_oauth.rb +18 -29
  15. data/lib/rodauth/features/oauth_application_management.rb +59 -72
  16. data/lib/rodauth/features/oauth_assertion_base.rb +19 -23
  17. data/lib/rodauth/features/oauth_authorization_code_grant.rb +35 -88
  18. data/lib/rodauth/features/oauth_authorize_base.rb +103 -20
  19. data/lib/rodauth/features/oauth_base.rb +365 -302
  20. data/lib/rodauth/features/oauth_client_credentials_grant.rb +20 -18
  21. data/lib/rodauth/features/{oauth_device_grant.rb → oauth_device_code_grant.rb} +62 -73
  22. data/lib/rodauth/features/oauth_dynamic_client_registration.rb +46 -28
  23. data/lib/rodauth/features/oauth_grant_management.rb +70 -0
  24. data/lib/rodauth/features/oauth_implicit_grant.rb +25 -24
  25. data/lib/rodauth/features/oauth_jwt.rb +52 -688
  26. data/lib/rodauth/features/oauth_jwt_base.rb +435 -0
  27. data/lib/rodauth/features/oauth_jwt_bearer_grant.rb +45 -17
  28. data/lib/rodauth/features/oauth_jwt_jwks.rb +47 -0
  29. data/lib/rodauth/features/oauth_jwt_secured_authorization_request.rb +62 -0
  30. data/lib/rodauth/features/oauth_management_base.rb +2 -0
  31. data/lib/rodauth/features/oauth_pkce.rb +22 -26
  32. data/lib/rodauth/features/oauth_resource_indicators.rb +33 -21
  33. data/lib/rodauth/features/oauth_resource_server.rb +59 -0
  34. data/lib/rodauth/features/oauth_saml_bearer_grant.rb +5 -1
  35. data/lib/rodauth/features/oauth_token_introspection.rb +76 -46
  36. data/lib/rodauth/features/oauth_token_revocation.rb +46 -33
  37. data/lib/rodauth/features/oidc.rb +188 -95
  38. data/lib/rodauth/features/oidc_dynamic_client_registration.rb +89 -53
  39. data/lib/rodauth/oauth/database_extensions.rb +8 -6
  40. data/lib/rodauth/oauth/http_extensions.rb +61 -0
  41. data/lib/rodauth/oauth/railtie.rb +20 -0
  42. data/lib/rodauth/oauth/version.rb +1 -1
  43. data/lib/rodauth/oauth.rb +29 -1
  44. data/locales/en.yml +32 -22
  45. data/locales/pt.yml +32 -22
  46. data/templates/authorize.str +19 -24
  47. data/templates/device_search.str +1 -1
  48. data/templates/device_verification.str +2 -2
  49. data/templates/jwks_field.str +1 -0
  50. data/templates/new_oauth_application.str +1 -2
  51. data/templates/oauth_application.str +2 -2
  52. data/templates/oauth_application_oauth_grants.str +54 -0
  53. data/templates/oauth_applications.str +2 -2
  54. data/templates/oauth_grants.str +52 -0
  55. metadata +20 -16
  56. data/lib/generators/rodauth/oauth/templates/app/models/oauth_token.rb +0 -4
  57. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application_oauth_tokens.html.erb +0 -39
  58. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_tokens.html.erb +0 -35
  59. data/lib/rodauth/features/oauth.rb +0 -9
  60. data/lib/rodauth/features/oauth_http_mac.rb +0 -86
  61. data/lib/rodauth/features/oauth_token_management.rb +0 -81
  62. data/lib/rodauth/oauth/refinements.rb +0 -48
  63. data/templates/jwt_public_key_field.str +0 -4
  64. data/templates/oauth_application_oauth_tokens.str +0 -52
  65. data/templates/oauth_tokens.str +0 -50
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "rodauth/oauth"
4
+
3
5
  module Rodauth
4
6
  Feature.define(:oauth_authorize_base, :OauthAuthorizeBase) do
5
7
  depends :oauth_base
@@ -12,17 +14,20 @@ module Rodauth
12
14
  button "Authorize", "oauth_authorize"
13
15
  button "Back to Client Application", "oauth_authorize_post"
14
16
 
15
- translatable_method :oauth_tokens_scopes_label, "Scopes"
17
+ auth_value_method :use_oauth_access_type?, false
18
+
19
+ auth_value_method :oauth_grants_access_type_column, :access_type
20
+
21
+ translatable_method :authorize_page_lead, "The application %<name>s would like to access your data"
22
+ translatable_method :oauth_grants_scopes_label, "Scopes"
16
23
  translatable_method :oauth_applications_contacts_label, "Contacts"
17
24
  translatable_method :oauth_applications_tos_uri_label, "Terms of service URL"
18
25
  translatable_method :oauth_applications_policy_uri_label, "Policy URL"
19
26
 
20
27
  # /authorize
21
- route(:authorize) do |r|
22
- next unless is_authorization_server?
23
-
24
- before_authorize_route
28
+ auth_server_route(:authorize) do |r|
25
29
  require_authorizable_account
30
+ before_authorize_route
26
31
 
27
32
  validate_authorize_params
28
33
 
@@ -49,6 +54,12 @@ module Rodauth
49
54
  end
50
55
  end
51
56
 
57
+ def authorize_scopes
58
+ scopes || begin
59
+ oauth_application[oauth_applications_scopes_column].split(oauth_scope_separator)
60
+ end
61
+ end
62
+
52
63
  private
53
64
 
54
65
  def validate_authorize_params
@@ -56,7 +67,11 @@ module Rodauth
56
67
 
57
68
  redirect_response_error("invalid_request") unless check_valid_response_type?
58
69
 
59
- redirect_response_error("invalid_scope") unless check_valid_scopes?
70
+ redirect_response_error("invalid_request") unless check_valid_access_type? && check_valid_approval_prompt?
71
+
72
+ try_approval_prompt if use_oauth_access_type? && request.get?
73
+
74
+ redirect_response_error("invalid_scope") if (request.post? || param_or_nil("scope")) && !check_valid_scopes?
60
75
  end
61
76
 
62
77
  def check_valid_response_type?
@@ -67,9 +82,44 @@ module Rodauth
67
82
  oauth_application[oauth_applications_redirect_uri_column].split(" ").include?(redirect_uri)
68
83
  end
69
84
 
85
+ ACCESS_TYPES = %w[offline online].freeze
86
+
87
+ def check_valid_access_type?
88
+ return true unless use_oauth_access_type?
89
+
90
+ access_type = param_or_nil("access_type")
91
+ !access_type || ACCESS_TYPES.include?(access_type)
92
+ end
93
+
94
+ APPROVAL_PROMPTS = %w[force auto].freeze
95
+
96
+ def check_valid_approval_prompt?
97
+ return true unless use_oauth_access_type?
98
+
99
+ approval_prompt = param_or_nil("approval_prompt")
100
+ !approval_prompt || APPROVAL_PROMPTS.include?(approval_prompt)
101
+ end
102
+
103
+ def try_approval_prompt
104
+ approval_prompt = param_or_nil("approval_prompt")
105
+
106
+ return unless approval_prompt && approval_prompt == "auto"
107
+
108
+ return if db[oauth_grants_table].where(
109
+ oauth_grants_account_id_column => account_id,
110
+ oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column],
111
+ oauth_grants_redirect_uri_column => redirect_uri,
112
+ oauth_grants_scopes_column => scopes.join(oauth_scope_separator),
113
+ oauth_grants_access_type_column => "online"
114
+ ).count.zero?
115
+
116
+ # if there's a previous oauth grant for the params combo, it means that this user has approved before.
117
+ request.env["REQUEST_METHOD"] = "POST"
118
+ end
119
+
70
120
  def authorization_required
71
121
  if accepts_json?
72
- throw_json_response_error(authorization_required_error_status, "invalid_client")
122
+ throw_json_response_error(oauth_authorization_required_error_status, "invalid_client")
73
123
  else
74
124
  set_redirect_error_flash(require_authorization_error_flash)
75
125
  redirect(authorize_path)
@@ -80,29 +130,62 @@ module Rodauth
80
130
 
81
131
  def authorize_response(params, mode); end
82
132
 
83
- def create_oauth_token_from_authorization_code(oauth_grant, create_params, should_generate_refresh_token = false)
84
- # revoke oauth grant
85
- db[oauth_grants_table].where(oauth_grants_id_column => oauth_grant[oauth_grants_id_column])
86
- .update(oauth_grants_revoked_at_column => Sequel::CURRENT_TIMESTAMP)
133
+ def create_token_from_authorization_code(grant_params, should_generate_refresh_token = !use_oauth_access_type?, oauth_grant: nil)
134
+ # fetch oauth grant
135
+ oauth_grant ||= valid_locked_oauth_grant(grant_params)
87
136
 
88
137
  should_generate_refresh_token ||= oauth_grant[oauth_grants_access_type_column] == "offline"
89
138
 
90
- generate_oauth_token(create_params, should_generate_refresh_token)
139
+ generate_token(oauth_grant, should_generate_refresh_token)
91
140
  end
92
141
 
93
142
  def create_oauth_grant(create_params = {})
94
- create_params.merge!(
95
- oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column],
96
- oauth_grants_redirect_uri_column => redirect_uri,
97
- oauth_grants_expires_in_column => Sequel.date_add(Sequel::CURRENT_TIMESTAMP, seconds: oauth_grant_expires_in),
98
- oauth_grants_scopes_column => scopes.join(oauth_scope_separator)
99
- )
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)
147
+
148
+ if use_oauth_access_type? && (access_type = param_or_nil("access_type"))
149
+ create_params[oauth_grants_access_type_column] = access_type
150
+ end
100
151
 
101
152
  ds = db[oauth_grants_table]
102
153
 
154
+ create_params[oauth_grants_code_column] = oauth_unique_id_generator
155
+
156
+ if oauth_reuse_access_token
157
+ unique_conds = Hash[oauth_grants_unique_columns.map { |column| [column, create_params[column]] }]
158
+ valid_grant = valid_oauth_grant_ds(unique_conds).select(oauth_grants_id_column).first
159
+ if valid_grant
160
+ create_params[oauth_grants_id_column] = valid_grant[oauth_grants_id_column]
161
+ rescue_from_uniqueness_error do
162
+ __insert_or_update_and_return__(
163
+ ds,
164
+ oauth_grants_id_column,
165
+ [oauth_grants_id_column],
166
+ create_params
167
+ )
168
+ end
169
+ return create_params[oauth_grants_code_column]
170
+ end
171
+ end
172
+
103
173
  rescue_from_uniqueness_error do
104
- create_params[oauth_grants_code_column] = oauth_unique_id_generator
105
- __insert_and_return__(ds, oauth_grants_id_column, create_params)
174
+ if __one_oauth_token_per_account
175
+ __insert_or_update_and_return__(
176
+ ds,
177
+ oauth_grants_id_column,
178
+ oauth_grants_unique_columns,
179
+ create_params,
180
+ nil,
181
+ {
182
+ oauth_grants_expires_in_column => Sequel.date_add(Sequel::CURRENT_TIMESTAMP, seconds: oauth_grant_expires_in),
183
+ oauth_grants_revoked_at_column => nil
184
+ }
185
+ )
186
+ else
187
+ __insert_and_return__(ds, oauth_grants_id_column, create_params)
188
+ end
106
189
  end
107
190
  create_params[oauth_grants_code_column]
108
191
  end