rodauth-oauth 0.7.4 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -424
  3. data/README.md +26 -389
  4. data/doc/release_notes/0_0_1.md +3 -0
  5. data/doc/release_notes/0_0_2.md +15 -0
  6. data/doc/release_notes/0_0_3.md +31 -0
  7. data/doc/release_notes/0_0_4.md +36 -0
  8. data/doc/release_notes/0_0_5.md +36 -0
  9. data/doc/release_notes/0_0_6.md +21 -0
  10. data/doc/release_notes/0_1_0.md +44 -0
  11. data/doc/release_notes/0_2_0.md +43 -0
  12. data/doc/release_notes/0_3_0.md +28 -0
  13. data/doc/release_notes/0_4_0.md +18 -0
  14. data/doc/release_notes/0_4_1.md +9 -0
  15. data/doc/release_notes/0_4_2.md +5 -0
  16. data/doc/release_notes/0_4_3.md +3 -0
  17. data/doc/release_notes/0_5_0.md +11 -0
  18. data/doc/release_notes/0_5_1.md +13 -0
  19. data/doc/release_notes/0_6_0.md +9 -0
  20. data/doc/release_notes/0_6_1.md +6 -0
  21. data/doc/release_notes/0_7_0.md +20 -0
  22. data/doc/release_notes/0_7_1.md +10 -0
  23. data/doc/release_notes/0_7_2.md +21 -0
  24. data/doc/release_notes/0_7_3.md +10 -0
  25. data/doc/release_notes/0_7_4.md +5 -0
  26. data/doc/release_notes/0_8_0.md +37 -0
  27. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize.html.erb +3 -3
  28. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/device_search.html.erb +11 -0
  29. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/device_verification.html.erb +20 -0
  30. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/new_oauth_application.html.erb +22 -10
  31. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application.html.erb +11 -5
  32. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application_oauth_tokens.html.erb +38 -0
  33. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_applications.html.erb +5 -5
  34. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_tokens.html.erb +11 -15
  35. data/lib/generators/rodauth/oauth/templates/db/migrate/create_rodauth_oauth.rb +9 -1
  36. data/lib/rodauth/features/oauth.rb +3 -1418
  37. data/lib/rodauth/features/oauth_application_management.rb +209 -0
  38. data/lib/rodauth/features/oauth_assertion_base.rb +96 -0
  39. data/lib/rodauth/features/oauth_authorization_code_grant.rb +249 -0
  40. data/lib/rodauth/features/oauth_authorization_server.rb +0 -0
  41. data/lib/rodauth/features/oauth_base.rb +735 -0
  42. data/lib/rodauth/features/oauth_device_grant.rb +221 -0
  43. data/lib/rodauth/features/oauth_http_mac.rb +3 -21
  44. data/lib/rodauth/features/oauth_implicit_grant.rb +59 -0
  45. data/lib/rodauth/features/oauth_jwt.rb +37 -60
  46. data/lib/rodauth/features/oauth_jwt_bearer_grant.rb +59 -0
  47. data/lib/rodauth/features/oauth_pkce.rb +98 -0
  48. data/lib/rodauth/features/oauth_resource_server.rb +21 -0
  49. data/lib/rodauth/features/oauth_saml_bearer_grant.rb +102 -0
  50. data/lib/rodauth/features/oauth_token_introspection.rb +108 -0
  51. data/lib/rodauth/features/oauth_token_management.rb +77 -0
  52. data/lib/rodauth/features/oauth_token_revocation.rb +109 -0
  53. data/lib/rodauth/features/oidc.rb +4 -3
  54. data/lib/rodauth/oauth/database_extensions.rb +15 -2
  55. data/lib/rodauth/oauth/refinements.rb +48 -0
  56. data/lib/rodauth/oauth/version.rb +1 -1
  57. data/locales/en.yml +28 -12
  58. data/templates/authorize.str +7 -7
  59. data/templates/client_secret_field.str +2 -2
  60. data/templates/description_field.str +1 -1
  61. data/templates/device_search.str +11 -0
  62. data/templates/device_verification.str +24 -0
  63. data/templates/homepage_url_field.str +2 -2
  64. data/templates/jws_jwk_field.str +4 -0
  65. data/templates/jwt_public_key_field.str +4 -0
  66. data/templates/name_field.str +1 -1
  67. data/templates/new_oauth_application.str +9 -0
  68. data/templates/oauth_application.str +7 -3
  69. data/templates/oauth_application_oauth_tokens.str +51 -0
  70. data/templates/oauth_applications.str +2 -2
  71. data/templates/oauth_tokens.str +9 -11
  72. data/templates/redirect_uri_field.str +2 -2
  73. metadata +71 -3
  74. data/lib/rodauth/features/oauth_saml.rb +0 -104
@@ -0,0 +1,209 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rodauth
4
+ Feature.define(:oauth_application_management, :OauthApplicationManagement) do
5
+ depends :oauth_base
6
+
7
+ before "create_oauth_application"
8
+ after "create_oauth_application"
9
+
10
+ error_flash "There was an error registering your oauth application", "create_oauth_application"
11
+ notice_flash "Your oauth application has been registered", "create_oauth_application"
12
+
13
+ view "oauth_applications", "Oauth Applications", "oauth_applications"
14
+ view "oauth_application", "Oauth Application", "oauth_application"
15
+ view "new_oauth_application", "New Oauth Application", "new_oauth_application"
16
+ view "oauth_application_oauth_tokens", "Oauth Application Tokens", "oauth_application_oauth_tokens"
17
+
18
+ auth_value_method :oauth_valid_uri_schemes, %w[https]
19
+
20
+ # Application
21
+ APPLICATION_REQUIRED_PARAMS = %w[name description scopes homepage_url redirect_uri client_secret].freeze
22
+ auth_value_method :oauth_application_required_params, APPLICATION_REQUIRED_PARAMS
23
+
24
+ (APPLICATION_REQUIRED_PARAMS + %w[client_id]).each do |param|
25
+ auth_value_method :"oauth_application_#{param}_param", param
26
+ configuration_module_eval do
27
+ define_method :"#{param}_label" do
28
+ warn "#{__method__} is deprecated, switch to oauth_applications_#{__method__}_label"
29
+ __send__(:"oauth_applications_#{param}_label")
30
+ end
31
+ end
32
+ end
33
+
34
+ translatable_method :oauth_applications_name_label, "Name"
35
+ translatable_method :oauth_applications_description_label, "Description"
36
+ translatable_method :oauth_applications_scopes_label, "Scopes"
37
+ translatable_method :oauth_applications_homepage_url_label, "Homepage URL"
38
+ translatable_method :oauth_applications_redirect_uri_label, "Redirect URI"
39
+ translatable_method :oauth_applications_client_secret_label, "Client Secret"
40
+ translatable_method :oauth_applications_client_id_label, "Client ID"
41
+ button "Register", "oauth_application"
42
+ button "Revoke", "oauth_token_revoke"
43
+
44
+ auth_value_method :oauth_applications_oauth_tokens_path, "oauth-tokens"
45
+ auth_value_method :oauth_applications_route, "oauth-applications"
46
+ auth_value_method :oauth_applications_id_pattern, Integer
47
+
48
+ translatable_method :invalid_url_message, "Invalid URL"
49
+ translatable_method :null_error_message, "is not filled"
50
+
51
+ def oauth_applications_path(opts = {})
52
+ route_path(oauth_applications_route, opts)
53
+ end
54
+
55
+ def oauth_applications_url(opts = {})
56
+ route_url(oauth_applications_route, opts)
57
+ end
58
+ auth_value_methods(
59
+ :oauth_application_path
60
+ )
61
+
62
+ def oauth_application_path(id)
63
+ "#{oauth_applications_path}/#{id}"
64
+ end
65
+
66
+ # /oauth-applications routes
67
+ def oauth_applications
68
+ request.on(oauth_applications_route) do
69
+ require_account
70
+
71
+ request.get "new" do
72
+ new_oauth_application_view
73
+ end
74
+
75
+ request.on(oauth_applications_id_pattern) do |id|
76
+ oauth_application = db[oauth_applications_table]
77
+ .where(oauth_applications_id_column => id)
78
+ .where(oauth_applications_account_id_column => account_id)
79
+ .first
80
+ next unless oauth_application
81
+
82
+ scope.instance_variable_set(:@oauth_application, oauth_application)
83
+
84
+ request.is do
85
+ request.get do
86
+ oauth_application_view
87
+ end
88
+ end
89
+
90
+ request.on(oauth_applications_oauth_tokens_path) do
91
+ oauth_tokens = db[oauth_tokens_table].where(oauth_tokens_oauth_application_id_column => id)
92
+ scope.instance_variable_set(:@oauth_tokens, oauth_tokens)
93
+ request.get do
94
+ oauth_application_oauth_tokens_view
95
+ end
96
+ end
97
+ end
98
+
99
+ request.get do
100
+ scope.instance_variable_set(:@oauth_applications, db[oauth_applications_table]
101
+ .where(oauth_applications_account_id_column => account_id))
102
+ oauth_applications_view
103
+ end
104
+
105
+ request.post do
106
+ catch_error do
107
+ validate_oauth_application_params
108
+
109
+ transaction do
110
+ before_create_oauth_application
111
+ id = create_oauth_application
112
+ after_create_oauth_application
113
+ set_notice_flash create_oauth_application_notice_flash
114
+ redirect "#{request.path}/#{id}"
115
+ end
116
+ end
117
+ set_error_flash create_oauth_application_error_flash
118
+ new_oauth_application_view
119
+ end
120
+ end
121
+ end
122
+
123
+ def check_csrf?
124
+ case request.path
125
+ when oauth_applications_path
126
+ only_json? ? false : super
127
+ else
128
+ super
129
+ end
130
+ end
131
+
132
+ private
133
+
134
+ def oauth_application_params
135
+ @oauth_application_params ||= oauth_application_required_params.each_with_object({}) do |param, params|
136
+ value = request.params[__send__(:"oauth_application_#{param}_param")]
137
+ if value && !value.empty?
138
+ params[param] = value
139
+ else
140
+ set_field_error(param, null_error_message)
141
+ end
142
+ end
143
+ end
144
+
145
+ def validate_oauth_application_params
146
+ oauth_application_params.each do |key, value|
147
+ if key == oauth_application_homepage_url_param
148
+
149
+ set_field_error(key, invalid_url_message) unless check_valid_uri?(value)
150
+
151
+ elsif key == oauth_application_redirect_uri_param
152
+
153
+ if value.respond_to?(:each)
154
+ value.each do |uri|
155
+ next if uri.empty?
156
+
157
+ set_field_error(key, invalid_url_message) unless check_valid_uri?(uri)
158
+ end
159
+ else
160
+ set_field_error(key, invalid_url_message) unless check_valid_uri?(value)
161
+ end
162
+ elsif key == oauth_application_scopes_param
163
+
164
+ value.each do |scope|
165
+ set_field_error(key, invalid_scope_message) unless oauth_application_scopes.include?(scope)
166
+ end
167
+ end
168
+ end
169
+
170
+ throw :rodauth_error if @field_errors && !@field_errors.empty?
171
+ end
172
+
173
+ def create_oauth_application
174
+ create_params = {
175
+ oauth_applications_account_id_column => account_id,
176
+ oauth_applications_name_column => oauth_application_params[oauth_application_name_param],
177
+ oauth_applications_description_column => oauth_application_params[oauth_application_description_param],
178
+ oauth_applications_scopes_column => oauth_application_params[oauth_application_scopes_param],
179
+ oauth_applications_homepage_url_column => oauth_application_params[oauth_application_homepage_url_param]
180
+ }
181
+
182
+ redirect_uris = oauth_application_params[oauth_application_redirect_uri_param]
183
+ redirect_uris = redirect_uris.to_a.reject(&:empty?).join(" ") if redirect_uris.respond_to?(:each)
184
+ create_params[oauth_applications_redirect_uri_column] = redirect_uris unless redirect_uris.empty?
185
+ # set client ID/secret pairs
186
+
187
+ create_params.merge! \
188
+ oauth_applications_client_secret_column => \
189
+ secret_hash(oauth_application_params[oauth_application_client_secret_param])
190
+
191
+ create_params[oauth_applications_scopes_column] = if create_params[oauth_applications_scopes_column]
192
+ create_params[oauth_applications_scopes_column].join(oauth_scope_separator)
193
+ else
194
+ oauth_application_default_scope
195
+ end
196
+
197
+ rescue_from_uniqueness_error do
198
+ create_params[oauth_applications_client_id_column] = oauth_unique_id_generator
199
+ db[oauth_applications_table].insert(create_params)
200
+ end
201
+ end
202
+
203
+ def oauth_server_metadata_body(*)
204
+ super.tap do |data|
205
+ data[:registration_endpoint] = oauth_applications_url
206
+ end
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,96 @@
1
+ # frozen-string-literal: true
2
+
3
+ require "rodauth/oauth/refinements"
4
+
5
+ module Rodauth
6
+ Feature.define(:oauth_assertion_base, :OauthAssertionBase) do
7
+ using PrefixExtensions
8
+
9
+ depends :oauth_base
10
+
11
+ auth_value_methods(
12
+ :assertion_grant_type?,
13
+ :client_assertion_type?,
14
+ :assertion_grant_type,
15
+ :client_assertion_type
16
+ )
17
+
18
+ private
19
+
20
+ def validate_oauth_token_params
21
+ return super unless assertion_grant_type?
22
+
23
+ redirect_response_error("invalid_grant") unless param_or_nil("assertion")
24
+ end
25
+
26
+ def require_oauth_application
27
+ if assertion_grant_type?
28
+ @oauth_application = __send__(:"require_oauth_application_from_#{assertion_grant_type}_assertion_issuer", param("assertion"))
29
+ elsif client_assertion_type?
30
+ @oauth_application = __send__(:"require_oauth_application_from_#{client_assertion_type}_assertion_subject",
31
+ param("client_assertion"))
32
+ else
33
+ return super
34
+ end
35
+
36
+ redirect_response_error("invalid_grant") unless @oauth_application
37
+
38
+ if client_assertion_type? &&
39
+ (client_id = param_or_nil("client_id")) &&
40
+ client_id != @oauth_application[oauth_applications_client_id_column]
41
+ # If present, the value of the
42
+ # "client_id" parameter MUST identify the same client as is
43
+ # identified by the client assertion.
44
+ redirect_response_error("invalid_grant")
45
+ end
46
+ end
47
+
48
+ def account_from_bearer_assertion_subject(subject)
49
+ __insert_or_do_nothing_and_return__(
50
+ db[accounts_table],
51
+ account_id_column,
52
+ [login_column],
53
+ login_column => subject
54
+ )
55
+ end
56
+
57
+ def create_oauth_token(grant_type)
58
+ return super unless assertion_grant_type?(grant_type)
59
+
60
+ account = __send__(:"account_from_#{assertion_grant_type}_assertion", param("assertion"))
61
+
62
+ redirect_response_error("invalid_grant") unless account
63
+
64
+ grant_scopes = if param_or_nil("scope")
65
+ redirect_response_error("invalid_grant") unless check_valid_scopes?
66
+ scopes
67
+ else
68
+ @oauth_application[oauth_applications_scopes_column]
69
+ end
70
+
71
+ create_params = {
72
+ oauth_tokens_account_id_column => account[account_id_column],
73
+ oauth_tokens_oauth_application_id_column => @oauth_application[oauth_applications_id_column],
74
+ oauth_tokens_scopes_column => grant_scopes
75
+ }
76
+
77
+ generate_oauth_token(create_params, false)
78
+ end
79
+
80
+ def assertion_grant_type?(grant_type = param("grant_type"))
81
+ grant_type.start_with?("urn:ietf:params:oauth:grant-type:")
82
+ end
83
+
84
+ def client_assertion_type?(client_assertion_type = param("client_assertion_type"))
85
+ client_assertion_type.start_with?("urn:ietf:params:oauth:client-assertion-type:")
86
+ end
87
+
88
+ def assertion_grant_type(grant_type = param("grant_type"))
89
+ grant_type.delete_prefix("urn:ietf:params:oauth:grant-type:").tr("-", "_")
90
+ end
91
+
92
+ def client_assertion_type(assertion_type = param("client_assertion_type"))
93
+ assertion_type.delete_prefix("urn:ietf:params:oauth:client-assertion-type:").tr("-", "_")
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,249 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rodauth
4
+ Feature.define(:oauth_authorization_code_grant, :OauthAuthorizationCodeGrant) do
5
+ depends :oauth_base
6
+
7
+ before "authorize"
8
+ after "authorize"
9
+
10
+ view "authorize", "Authorize", "authorize"
11
+
12
+ button "Authorize", "oauth_authorize"
13
+ button "Back to Client Application", "oauth_authorize_post"
14
+
15
+ auth_value_method :use_oauth_access_type?, true
16
+
17
+ # OAuth Grants
18
+ auth_value_method :oauth_grants_table, :oauth_grants
19
+ auth_value_method :oauth_grants_id_column, :id
20
+ %i[
21
+ account_id oauth_application_id
22
+ redirect_uri code scopes access_type
23
+ expires_in revoked_at
24
+ ].each do |column|
25
+ auth_value_method :"oauth_grants_#{column}_column", column
26
+ end
27
+
28
+ translatable_method :oauth_tokens_scopes_label, "Scopes"
29
+
30
+ # /authorize
31
+ route(:authorize) do |r|
32
+ next unless is_authorization_server?
33
+
34
+ before_authorize_route
35
+ require_authorizable_account
36
+
37
+ validate_oauth_grant_params
38
+ try_approval_prompt if use_oauth_access_type? && request.get?
39
+
40
+ r.get do
41
+ authorize_view
42
+ end
43
+
44
+ r.post do
45
+ params, mode = transaction do
46
+ before_authorize
47
+ do_authorize
48
+ end
49
+
50
+ authorize_response(params, mode)
51
+ end
52
+ end
53
+
54
+ def check_csrf?
55
+ case request.path
56
+ when authorize_path
57
+ only_json? ? false : super
58
+ else
59
+ super
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def validate_oauth_grant_params
66
+ redirect_response_error("invalid_request", request.referer || default_redirect) unless oauth_application && check_valid_redirect_uri?
67
+
68
+ unless oauth_application && check_valid_redirect_uri? && check_valid_access_type? &&
69
+ check_valid_approval_prompt? && check_valid_response_type?
70
+ redirect_response_error("invalid_request")
71
+ end
72
+ redirect_response_error("invalid_scope") unless check_valid_scopes?
73
+
74
+ return unless (response_mode = param_or_nil("response_mode")) && response_mode != "form_post"
75
+
76
+ redirect_response_error("invalid_request")
77
+ end
78
+
79
+ def validate_oauth_token_params
80
+ redirect_response_error("invalid_request") if param_or_nil("grant_type") == "authorization_code" && !param_or_nil("code")
81
+ super
82
+ end
83
+
84
+ def try_approval_prompt
85
+ approval_prompt = param_or_nil("approval_prompt")
86
+
87
+ return unless approval_prompt && approval_prompt == "auto"
88
+
89
+ return if db[oauth_grants_table].where(
90
+ oauth_grants_account_id_column => account_id,
91
+ oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column],
92
+ oauth_grants_redirect_uri_column => redirect_uri,
93
+ oauth_grants_scopes_column => scopes.join(oauth_scope_separator),
94
+ oauth_grants_access_type_column => "online"
95
+ ).count.zero?
96
+
97
+ # if there's a previous oauth grant for the params combo, it means that this user has approved before.
98
+ request.env["REQUEST_METHOD"] = "POST"
99
+ end
100
+
101
+ def create_oauth_grant(create_params = {})
102
+ create_params.merge!(
103
+ oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column],
104
+ oauth_grants_redirect_uri_column => redirect_uri,
105
+ oauth_grants_expires_in_column => Sequel.date_add(Sequel::CURRENT_TIMESTAMP, seconds: oauth_grant_expires_in),
106
+ oauth_grants_scopes_column => scopes.join(oauth_scope_separator)
107
+ )
108
+
109
+ # Access Type flow
110
+ if use_oauth_access_type? && (access_type = param_or_nil("access_type"))
111
+ create_params[oauth_grants_access_type_column] = access_type
112
+ end
113
+
114
+ ds = db[oauth_grants_table]
115
+
116
+ rescue_from_uniqueness_error do
117
+ create_params[oauth_grants_code_column] = oauth_unique_id_generator
118
+ __insert_and_return__(ds, oauth_grants_id_column, create_params)
119
+ end
120
+ create_params[oauth_grants_code_column]
121
+ end
122
+
123
+ def do_authorize(response_params = {}, response_mode = param_or_nil("response_mode"))
124
+ case param("response_type")
125
+
126
+ when "code"
127
+ response_mode ||= "query"
128
+ response_params.replace(_do_authorize_code)
129
+ when "none"
130
+ response_mode ||= "none"
131
+ when "", nil
132
+ response_mode ||= oauth_response_mode
133
+ response_params.replace(_do_authorize_code)
134
+ end
135
+
136
+ response_params["state"] = param("state") if param_or_nil("state")
137
+
138
+ [response_params, response_mode]
139
+ end
140
+
141
+ def _do_authorize_code
142
+ { "code" => create_oauth_grant(oauth_grants_account_id_column => account_id) }
143
+ end
144
+
145
+ def authorize_response(params, mode)
146
+ redirect_url = URI.parse(redirect_uri)
147
+ case mode
148
+ when "query"
149
+ params = params.map { |k, v| "#{k}=#{v}" }
150
+ params << redirect_url.query if redirect_url.query
151
+ redirect_url.query = params.join("&")
152
+ redirect(redirect_url.to_s)
153
+ when "form_post"
154
+ scope.view layout: false, inline: <<-FORM
155
+ <html>
156
+ <head><title>Authorized</title></head>
157
+ <body onload="javascript:document.forms[0].submit()">
158
+ <form method="post" action="#{redirect_uri}">
159
+ #{
160
+ params.map do |name, value|
161
+ "<input type=\"hidden\" name=\"#{name}\" value=\"#{scope.h(value)}\" />"
162
+ end.join
163
+ }
164
+ <input type="submit" class="btn btn-outline-primary" value="#{scope.h(oauth_authorize_post_button)}"/>
165
+ </form>
166
+ </body>
167
+ </html>
168
+ FORM
169
+ when "none"
170
+ redirect(redirect_url.to_s)
171
+ end
172
+ end
173
+
174
+ def create_oauth_token(grant_type)
175
+ return super unless grant_type == "authorization_code"
176
+
177
+ # fetch oauth grant
178
+ oauth_grant = db[oauth_grants_table].where(
179
+ oauth_grants_code_column => param("code"),
180
+ oauth_grants_redirect_uri_column => param("redirect_uri"),
181
+ oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column],
182
+ oauth_grants_revoked_at_column => nil
183
+ ).where(Sequel[oauth_grants_expires_in_column] >= Sequel::CURRENT_TIMESTAMP)
184
+ .for_update
185
+ .first
186
+
187
+ redirect_response_error("invalid_grant") unless oauth_grant
188
+
189
+ create_params = {
190
+ oauth_tokens_account_id_column => oauth_grant[oauth_grants_account_id_column],
191
+ oauth_tokens_oauth_application_id_column => oauth_grant[oauth_grants_oauth_application_id_column],
192
+ oauth_tokens_oauth_grant_id_column => oauth_grant[oauth_grants_id_column],
193
+ oauth_tokens_scopes_column => oauth_grant[oauth_grants_scopes_column]
194
+ }
195
+ create_oauth_token_from_authorization_code(oauth_grant, create_params)
196
+ end
197
+
198
+ def create_oauth_token_from_authorization_code(oauth_grant, create_params)
199
+ # revoke oauth grant
200
+ db[oauth_grants_table].where(oauth_grants_id_column => oauth_grant[oauth_grants_id_column])
201
+ .update(oauth_grants_revoked_at_column => Sequel::CURRENT_TIMESTAMP)
202
+
203
+ should_generate_refresh_token = !use_oauth_access_type? ||
204
+ oauth_grant[oauth_grants_access_type_column] == "offline"
205
+
206
+ generate_oauth_token(create_params, should_generate_refresh_token)
207
+ end
208
+
209
+ ACCESS_TYPES = %w[offline online].freeze
210
+
211
+ def check_valid_access_type?
212
+ return true unless use_oauth_access_type?
213
+
214
+ access_type = param_or_nil("access_type")
215
+ !access_type || ACCESS_TYPES.include?(access_type)
216
+ end
217
+
218
+ APPROVAL_PROMPTS = %w[force auto].freeze
219
+
220
+ def check_valid_approval_prompt?
221
+ return true unless use_oauth_access_type?
222
+
223
+ approval_prompt = param_or_nil("approval_prompt")
224
+ !approval_prompt || APPROVAL_PROMPTS.include?(approval_prompt)
225
+ end
226
+
227
+ def check_valid_response_type?
228
+ response_type = param_or_nil("response_type")
229
+
230
+ response_type.nil? || response_type == "code"
231
+ end
232
+
233
+ def check_valid_redirect_uri?
234
+ oauth_application[oauth_applications_redirect_uri_column].split(" ").include?(redirect_uri)
235
+ end
236
+
237
+ def oauth_server_metadata_body(*)
238
+ super.tap do |data|
239
+ data[:authorization_endpoint] = authorize_url
240
+ data[:response_types_supported] << "code"
241
+
242
+ data[:response_modes_supported] << "query"
243
+ data[:response_modes_supported] << "form_post"
244
+
245
+ data[:grant_types_supported] << "authorization_code"
246
+ end
247
+ end
248
+ end
249
+ end