rodauth-oauth 0.7.4 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -424
  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/doc/release_notes/0_9_1.md +9 -0
  29. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize.html.erb +25 -4
  30. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/device_search.html.erb +11 -0
  31. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/device_verification.html.erb +20 -0
  32. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/new_oauth_application.html.erb +27 -10
  33. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application.html.erb +17 -5
  34. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application_oauth_tokens.html.erb +39 -0
  35. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_applications.html.erb +6 -5
  36. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_tokens.html.erb +12 -15
  37. data/lib/generators/rodauth/oauth/templates/db/migrate/create_rodauth_oauth.rb +21 -1
  38. data/lib/rodauth/features/oauth.rb +3 -1418
  39. data/lib/rodauth/features/oauth_application_management.rb +225 -0
  40. data/lib/rodauth/features/oauth_assertion_base.rb +96 -0
  41. data/lib/rodauth/features/oauth_authorization_code_grant.rb +252 -0
  42. data/lib/rodauth/features/oauth_authorization_server.rb +0 -0
  43. data/lib/rodauth/features/oauth_base.rb +778 -0
  44. data/lib/rodauth/features/oauth_client_credentials_grant.rb +33 -0
  45. data/lib/rodauth/features/oauth_device_grant.rb +220 -0
  46. data/lib/rodauth/features/oauth_dynamic_client_registration.rb +252 -0
  47. data/lib/rodauth/features/oauth_http_mac.rb +3 -21
  48. data/lib/rodauth/features/oauth_implicit_grant.rb +59 -0
  49. data/lib/rodauth/features/oauth_jwt.rb +275 -100
  50. data/lib/rodauth/features/oauth_jwt_bearer_grant.rb +59 -0
  51. data/lib/rodauth/features/oauth_management_base.rb +68 -0
  52. data/lib/rodauth/features/oauth_pkce.rb +98 -0
  53. data/lib/rodauth/features/oauth_resource_server.rb +21 -0
  54. data/lib/rodauth/features/oauth_saml_bearer_grant.rb +102 -0
  55. data/lib/rodauth/features/oauth_token_introspection.rb +108 -0
  56. data/lib/rodauth/features/oauth_token_management.rb +79 -0
  57. data/lib/rodauth/features/oauth_token_revocation.rb +109 -0
  58. data/lib/rodauth/features/oidc.rb +38 -9
  59. data/lib/rodauth/features/oidc_dynamic_client_registration.rb +147 -0
  60. data/lib/rodauth/oauth/database_extensions.rb +15 -2
  61. data/lib/rodauth/oauth/jwe_extensions.rb +64 -0
  62. data/lib/rodauth/oauth/refinements.rb +48 -0
  63. data/lib/rodauth/oauth/ttl_store.rb +9 -3
  64. data/lib/rodauth/oauth/version.rb +1 -1
  65. data/locales/en.yml +33 -12
  66. data/templates/authorize.str +57 -8
  67. data/templates/client_secret_field.str +2 -2
  68. data/templates/description_field.str +1 -1
  69. data/templates/device_search.str +11 -0
  70. data/templates/device_verification.str +24 -0
  71. data/templates/homepage_url_field.str +2 -2
  72. data/templates/jwks_field.str +4 -0
  73. data/templates/jwt_public_key_field.str +4 -0
  74. data/templates/name_field.str +1 -1
  75. data/templates/new_oauth_application.str +9 -0
  76. data/templates/oauth_application.str +7 -3
  77. data/templates/oauth_application_oauth_tokens.str +52 -0
  78. data/templates/oauth_applications.str +3 -2
  79. data/templates/oauth_tokens.str +10 -11
  80. data/templates/redirect_uri_field.str +2 -2
  81. metadata +80 -3
  82. 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