rodauth-oauth 0.7.3 → 0.9.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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -418
  3. data/README.md +30 -390
  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/doc/release_notes/0_9_0.md +56 -0
  28. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize.html.erb +50 -0
  29. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/device_search.html.erb +11 -0
  30. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/device_verification.html.erb +20 -0
  31. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/new_oauth_application.html.erb +55 -0
  32. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application.html.erb +29 -0
  33. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application_oauth_tokens.html.erb +39 -0
  34. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_applications.html.erb +30 -0
  35. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_tokens.html.erb +35 -0
  36. data/lib/generators/rodauth/oauth/templates/db/migrate/create_rodauth_oauth.rb +21 -1
  37. data/lib/rodauth/features/oauth.rb +3 -1418
  38. data/lib/rodauth/features/oauth_application_management.rb +225 -0
  39. data/lib/rodauth/features/oauth_assertion_base.rb +96 -0
  40. data/lib/rodauth/features/oauth_authorization_code_grant.rb +252 -0
  41. data/lib/rodauth/features/oauth_authorization_server.rb +0 -0
  42. data/lib/rodauth/features/oauth_base.rb +771 -0
  43. data/lib/rodauth/features/oauth_client_credentials_grant.rb +33 -0
  44. data/lib/rodauth/features/oauth_device_grant.rb +220 -0
  45. data/lib/rodauth/features/oauth_dynamic_client_registration.rb +252 -0
  46. data/lib/rodauth/features/oauth_http_mac.rb +3 -21
  47. data/lib/rodauth/features/oauth_implicit_grant.rb +59 -0
  48. data/lib/rodauth/features/oauth_jwt.rb +276 -100
  49. data/lib/rodauth/features/oauth_jwt_bearer_grant.rb +59 -0
  50. data/lib/rodauth/features/oauth_management_base.rb +68 -0
  51. data/lib/rodauth/features/oauth_pkce.rb +98 -0
  52. data/lib/rodauth/features/oauth_resource_server.rb +21 -0
  53. data/lib/rodauth/features/oauth_saml_bearer_grant.rb +102 -0
  54. data/lib/rodauth/features/oauth_token_introspection.rb +108 -0
  55. data/lib/rodauth/features/oauth_token_management.rb +79 -0
  56. data/lib/rodauth/features/oauth_token_revocation.rb +109 -0
  57. data/lib/rodauth/features/oidc.rb +36 -6
  58. data/lib/rodauth/features/oidc_dynamic_client_registration.rb +147 -0
  59. data/lib/rodauth/oauth/database_extensions.rb +15 -2
  60. data/lib/rodauth/oauth/jwe_extensions.rb +64 -0
  61. data/lib/rodauth/oauth/refinements.rb +48 -0
  62. data/lib/rodauth/oauth/ttl_store.rb +9 -3
  63. data/lib/rodauth/oauth/version.rb +1 -1
  64. data/locales/en.yml +33 -12
  65. data/templates/authorize.str +57 -8
  66. data/templates/client_secret_field.str +2 -2
  67. data/templates/description_field.str +1 -1
  68. data/templates/device_search.str +11 -0
  69. data/templates/device_verification.str +24 -0
  70. data/templates/homepage_url_field.str +2 -2
  71. data/templates/jwks_field.str +4 -0
  72. data/templates/jwt_public_key_field.str +4 -0
  73. data/templates/name_field.str +1 -1
  74. data/templates/new_oauth_application.str +9 -0
  75. data/templates/oauth_application.str +7 -3
  76. data/templates/oauth_application_oauth_tokens.str +52 -0
  77. data/templates/oauth_applications.str +3 -2
  78. data/templates/oauth_tokens.str +10 -11
  79. data/templates/redirect_uri_field.str +2 -2
  80. metadata +84 -4
  81. data/lib/rodauth/features/oauth_saml.rb +0 -104
@@ -0,0 +1,225 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rodauth
4
+ Feature.define(:oauth_application_management, :OauthApplicationManagement) do
5
+ depends :oauth_management_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 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[description 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_contacts_label, "Contacts"
38
+ translatable_method :oauth_applications_tos_uri_label, "Terms of service"
39
+ translatable_method :oauth_applications_policy_uri_label, "Policy"
40
+ translatable_method :oauth_applications_jwks_label, "JSON Web Keys"
41
+ translatable_method :oauth_applications_jwks_uri_label, "JSON Web Keys URI"
42
+ translatable_method :oauth_applications_homepage_url_label, "Homepage URL"
43
+ translatable_method :oauth_applications_redirect_uri_label, "Redirect URI"
44
+ translatable_method :oauth_applications_client_secret_label, "Client Secret"
45
+ translatable_method :oauth_applications_client_id_label, "Client ID"
46
+ button "Register", "oauth_application"
47
+ button "Revoke", "oauth_token_revoke"
48
+
49
+ auth_value_method :oauth_applications_oauth_tokens_path, "oauth-tokens"
50
+ auth_value_method :oauth_applications_route, "oauth-applications"
51
+ auth_value_method :oauth_applications_per_page, 20
52
+ auth_value_method :oauth_applications_id_pattern, Integer
53
+ auth_value_method :oauth_tokens_per_page, 20
54
+
55
+ translatable_method :invalid_url_message, "Invalid URL"
56
+ translatable_method :null_error_message, "is not filled"
57
+
58
+ def oauth_applications_path(opts = {})
59
+ route_path(oauth_applications_route, opts)
60
+ end
61
+
62
+ def oauth_applications_url(opts = {})
63
+ route_url(oauth_applications_route, opts)
64
+ end
65
+ auth_value_methods(
66
+ :oauth_application_path
67
+ )
68
+
69
+ def oauth_application_path(id)
70
+ "#{oauth_applications_path}/#{id}"
71
+ end
72
+
73
+ # /oauth-applications routes
74
+ def oauth_applications
75
+ request.on(oauth_applications_route) do
76
+ require_account
77
+
78
+ request.get "new" do
79
+ new_oauth_application_view
80
+ end
81
+
82
+ request.on(oauth_applications_id_pattern) do |id|
83
+ oauth_application = db[oauth_applications_table]
84
+ .where(oauth_applications_id_column => id)
85
+ .where(oauth_applications_account_id_column => account_id)
86
+ .first
87
+ next unless oauth_application
88
+
89
+ scope.instance_variable_set(:@oauth_application, oauth_application)
90
+
91
+ request.is do
92
+ request.get do
93
+ oauth_application_view
94
+ end
95
+ end
96
+
97
+ request.on(oauth_applications_oauth_tokens_path) do
98
+ page = Integer(param_or_nil("page") || 1)
99
+ per_page = per_page_param(oauth_tokens_per_page)
100
+ oauth_tokens = db[oauth_tokens_table]
101
+ .where(oauth_tokens_oauth_application_id_column => id)
102
+ .order(Sequel.desc(oauth_tokens_id_column))
103
+ scope.instance_variable_set(:@oauth_tokens, oauth_tokens.paginate(page, per_page))
104
+ request.get do
105
+ oauth_application_oauth_tokens_view
106
+ end
107
+ end
108
+ end
109
+
110
+ request.get do
111
+ page = Integer(param_or_nil("page") || 1)
112
+ per_page = per_page_param(oauth_applications_per_page)
113
+ scope.instance_variable_set(:@oauth_applications, db[oauth_applications_table]
114
+ .where(oauth_applications_account_id_column => account_id)
115
+ .order(Sequel.desc(oauth_applications_id_column))
116
+ .paginate(page, per_page))
117
+
118
+ oauth_applications_view
119
+ end
120
+
121
+ request.post do
122
+ catch_error do
123
+ validate_oauth_application_params
124
+
125
+ transaction do
126
+ before_create_oauth_application
127
+ id = create_oauth_application
128
+ after_create_oauth_application
129
+ set_notice_flash create_oauth_application_notice_flash
130
+ redirect "#{request.path}/#{id}"
131
+ end
132
+ end
133
+ set_error_flash create_oauth_application_error_flash
134
+ new_oauth_application_view
135
+ end
136
+ end
137
+ end
138
+
139
+ def check_csrf?
140
+ case request.path
141
+ when oauth_applications_path
142
+ only_json? ? false : super
143
+ else
144
+ super
145
+ end
146
+ end
147
+
148
+ private
149
+
150
+ def oauth_application_params
151
+ @oauth_application_params ||= oauth_application_required_params.each_with_object({}) do |param, params|
152
+ value = request.params[__send__(:"oauth_application_#{param}_param")]
153
+ if value && !value.empty?
154
+ params[param] = value
155
+ else
156
+ set_field_error(param, null_error_message)
157
+ end
158
+ end
159
+ end
160
+
161
+ def validate_oauth_application_params
162
+ oauth_application_params.each do |key, value|
163
+ if key == oauth_application_homepage_url_param
164
+
165
+ set_field_error(key, invalid_url_message) unless check_valid_uri?(value)
166
+
167
+ elsif key == oauth_application_redirect_uri_param
168
+
169
+ if value.respond_to?(:each)
170
+ value.each do |uri|
171
+ next if uri.empty?
172
+
173
+ set_field_error(key, invalid_url_message) unless check_valid_uri?(uri)
174
+ end
175
+ else
176
+ set_field_error(key, invalid_url_message) unless check_valid_uri?(value)
177
+ end
178
+ elsif key == oauth_application_scopes_param
179
+
180
+ value.each do |scope|
181
+ set_field_error(key, invalid_scope_message) unless oauth_application_scopes.include?(scope)
182
+ end
183
+ end
184
+ end
185
+
186
+ throw :rodauth_error if @field_errors && !@field_errors.empty?
187
+ end
188
+
189
+ def create_oauth_application
190
+ create_params = {
191
+ oauth_applications_account_id_column => account_id,
192
+ oauth_applications_name_column => oauth_application_params[oauth_application_name_param],
193
+ oauth_applications_description_column => oauth_application_params[oauth_application_description_param],
194
+ oauth_applications_scopes_column => oauth_application_params[oauth_application_scopes_param],
195
+ oauth_applications_homepage_url_column => oauth_application_params[oauth_application_homepage_url_param]
196
+ }
197
+
198
+ redirect_uris = oauth_application_params[oauth_application_redirect_uri_param]
199
+ redirect_uris = redirect_uris.to_a.reject(&:empty?).join(" ") if redirect_uris.respond_to?(:each)
200
+ create_params[oauth_applications_redirect_uri_column] = redirect_uris unless redirect_uris.empty?
201
+ # set client ID/secret pairs
202
+
203
+ create_params.merge! \
204
+ oauth_applications_client_secret_column => \
205
+ secret_hash(oauth_application_params[oauth_application_client_secret_param])
206
+
207
+ create_params[oauth_applications_scopes_column] = if create_params[oauth_applications_scopes_column]
208
+ create_params[oauth_applications_scopes_column].join(oauth_scope_separator)
209
+ else
210
+ oauth_application_default_scope
211
+ end
212
+
213
+ rescue_from_uniqueness_error do
214
+ create_params[oauth_applications_client_id_column] = oauth_unique_id_generator
215
+ db[oauth_applications_table].insert(create_params)
216
+ end
217
+ end
218
+
219
+ def oauth_server_metadata_body(*)
220
+ super.tap do |data|
221
+ data[:registration_endpoint] = oauth_applications_url
222
+ end
223
+ end
224
+ end
225
+ 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) && supported_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,252 @@
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
+ translatable_method :oauth_applications_contacts_label, "Contacts"
30
+ translatable_method :oauth_applications_tos_uri_label, "Terms of service URL"
31
+ translatable_method :oauth_applications_policy_uri_label, "Policy URL"
32
+
33
+ # /authorize
34
+ route(:authorize) do |r|
35
+ next unless is_authorization_server?
36
+
37
+ before_authorize_route
38
+ require_authorizable_account
39
+
40
+ validate_oauth_grant_params
41
+ try_approval_prompt if use_oauth_access_type? && request.get?
42
+
43
+ r.get do
44
+ authorize_view
45
+ end
46
+
47
+ r.post do
48
+ params, mode = transaction do
49
+ before_authorize
50
+ do_authorize
51
+ end
52
+
53
+ authorize_response(params, mode)
54
+ end
55
+ end
56
+
57
+ def check_csrf?
58
+ case request.path
59
+ when authorize_path
60
+ only_json? ? false : super
61
+ else
62
+ super
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def validate_oauth_grant_params
69
+ redirect_response_error("invalid_request", request.referer || default_redirect) unless oauth_application && check_valid_redirect_uri?
70
+
71
+ unless oauth_application && check_valid_redirect_uri? && check_valid_access_type? &&
72
+ check_valid_approval_prompt? && check_valid_response_type?
73
+ redirect_response_error("invalid_request")
74
+ end
75
+ redirect_response_error("invalid_scope") unless check_valid_scopes?
76
+
77
+ return unless (response_mode = param_or_nil("response_mode")) && response_mode != "form_post"
78
+
79
+ redirect_response_error("invalid_request")
80
+ end
81
+
82
+ def validate_oauth_token_params
83
+ redirect_response_error("invalid_request") if param_or_nil("grant_type") == "authorization_code" && !param_or_nil("code")
84
+ super
85
+ end
86
+
87
+ def try_approval_prompt
88
+ approval_prompt = param_or_nil("approval_prompt")
89
+
90
+ return unless approval_prompt && approval_prompt == "auto"
91
+
92
+ return if db[oauth_grants_table].where(
93
+ oauth_grants_account_id_column => account_id,
94
+ oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column],
95
+ oauth_grants_redirect_uri_column => redirect_uri,
96
+ oauth_grants_scopes_column => scopes.join(oauth_scope_separator),
97
+ oauth_grants_access_type_column => "online"
98
+ ).count.zero?
99
+
100
+ # if there's a previous oauth grant for the params combo, it means that this user has approved before.
101
+ request.env["REQUEST_METHOD"] = "POST"
102
+ end
103
+
104
+ def create_oauth_grant(create_params = {})
105
+ create_params.merge!(
106
+ oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column],
107
+ oauth_grants_redirect_uri_column => redirect_uri,
108
+ oauth_grants_expires_in_column => Sequel.date_add(Sequel::CURRENT_TIMESTAMP, seconds: oauth_grant_expires_in),
109
+ oauth_grants_scopes_column => scopes.join(oauth_scope_separator)
110
+ )
111
+
112
+ # Access Type flow
113
+ if use_oauth_access_type? && (access_type = param_or_nil("access_type"))
114
+ create_params[oauth_grants_access_type_column] = access_type
115
+ end
116
+
117
+ ds = db[oauth_grants_table]
118
+
119
+ rescue_from_uniqueness_error do
120
+ create_params[oauth_grants_code_column] = oauth_unique_id_generator
121
+ __insert_and_return__(ds, oauth_grants_id_column, create_params)
122
+ end
123
+ create_params[oauth_grants_code_column]
124
+ end
125
+
126
+ def do_authorize(response_params = {}, response_mode = param_or_nil("response_mode"))
127
+ case param("response_type")
128
+
129
+ when "code"
130
+ response_mode ||= "query"
131
+ response_params.replace(_do_authorize_code)
132
+ when "none"
133
+ response_mode ||= "none"
134
+ when "", nil
135
+ response_mode ||= oauth_response_mode
136
+ response_params.replace(_do_authorize_code)
137
+ end
138
+
139
+ response_params["state"] = param("state") if param_or_nil("state")
140
+
141
+ [response_params, response_mode]
142
+ end
143
+
144
+ def _do_authorize_code
145
+ { "code" => create_oauth_grant(oauth_grants_account_id_column => account_id) }
146
+ end
147
+
148
+ def authorize_response(params, mode)
149
+ redirect_url = URI.parse(redirect_uri)
150
+ case mode
151
+ when "query"
152
+ params = params.map { |k, v| "#{k}=#{v}" }
153
+ params << redirect_url.query if redirect_url.query
154
+ redirect_url.query = params.join("&")
155
+ redirect(redirect_url.to_s)
156
+ when "form_post"
157
+ scope.view layout: false, inline: <<-FORM
158
+ <html>
159
+ <head><title>Authorized</title></head>
160
+ <body onload="javascript:document.forms[0].submit()">
161
+ <form method="post" action="#{redirect_uri}">
162
+ #{
163
+ params.map do |name, value|
164
+ "<input type=\"hidden\" name=\"#{name}\" value=\"#{scope.h(value)}\" />"
165
+ end.join
166
+ }
167
+ <input type="submit" class="btn btn-outline-primary" value="#{scope.h(oauth_authorize_post_button)}"/>
168
+ </form>
169
+ </body>
170
+ </html>
171
+ FORM
172
+ when "none"
173
+ redirect(redirect_url.to_s)
174
+ end
175
+ end
176
+
177
+ def create_oauth_token(grant_type)
178
+ return super unless supported_grant_type?(grant_type, "authorization_code")
179
+
180
+ # fetch oauth grant
181
+ oauth_grant = db[oauth_grants_table].where(
182
+ oauth_grants_code_column => param("code"),
183
+ oauth_grants_redirect_uri_column => param("redirect_uri"),
184
+ oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column],
185
+ oauth_grants_revoked_at_column => nil
186
+ ).where(Sequel[oauth_grants_expires_in_column] >= Sequel::CURRENT_TIMESTAMP)
187
+ .for_update
188
+ .first
189
+
190
+ redirect_response_error("invalid_grant") unless oauth_grant
191
+
192
+ create_params = {
193
+ oauth_tokens_account_id_column => oauth_grant[oauth_grants_account_id_column],
194
+ oauth_tokens_oauth_application_id_column => oauth_grant[oauth_grants_oauth_application_id_column],
195
+ oauth_tokens_oauth_grant_id_column => oauth_grant[oauth_grants_id_column],
196
+ oauth_tokens_scopes_column => oauth_grant[oauth_grants_scopes_column]
197
+ }
198
+ create_oauth_token_from_authorization_code(oauth_grant, create_params)
199
+ end
200
+
201
+ def create_oauth_token_from_authorization_code(oauth_grant, create_params)
202
+ # revoke oauth grant
203
+ db[oauth_grants_table].where(oauth_grants_id_column => oauth_grant[oauth_grants_id_column])
204
+ .update(oauth_grants_revoked_at_column => Sequel::CURRENT_TIMESTAMP)
205
+
206
+ should_generate_refresh_token = !use_oauth_access_type? ||
207
+ oauth_grant[oauth_grants_access_type_column] == "offline"
208
+
209
+ generate_oauth_token(create_params, should_generate_refresh_token)
210
+ end
211
+
212
+ ACCESS_TYPES = %w[offline online].freeze
213
+
214
+ def check_valid_access_type?
215
+ return true unless use_oauth_access_type?
216
+
217
+ access_type = param_or_nil("access_type")
218
+ !access_type || ACCESS_TYPES.include?(access_type)
219
+ end
220
+
221
+ APPROVAL_PROMPTS = %w[force auto].freeze
222
+
223
+ def check_valid_approval_prompt?
224
+ return true unless use_oauth_access_type?
225
+
226
+ approval_prompt = param_or_nil("approval_prompt")
227
+ !approval_prompt || APPROVAL_PROMPTS.include?(approval_prompt)
228
+ end
229
+
230
+ def check_valid_response_type?
231
+ response_type = param_or_nil("response_type")
232
+
233
+ response_type.nil? || response_type == "code"
234
+ end
235
+
236
+ def check_valid_redirect_uri?
237
+ oauth_application[oauth_applications_redirect_uri_column].split(" ").include?(redirect_uri)
238
+ end
239
+
240
+ def oauth_server_metadata_body(*)
241
+ super.tap do |data|
242
+ data[:authorization_endpoint] = authorize_url
243
+ data[:response_types_supported] << "code"
244
+
245
+ data[:response_modes_supported] << "query"
246
+ data[:response_modes_supported] << "form_post"
247
+
248
+ data[:grant_types_supported] << "authorization_code"
249
+ end
250
+ end
251
+ end
252
+ end