rodauth-oauth 0.7.4 → 0.8.0

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