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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1 -424
- data/README.md +30 -390
- data/doc/release_notes/0_0_1.md +3 -0
- data/doc/release_notes/0_0_2.md +15 -0
- data/doc/release_notes/0_0_3.md +31 -0
- data/doc/release_notes/0_0_4.md +36 -0
- data/doc/release_notes/0_0_5.md +36 -0
- data/doc/release_notes/0_0_6.md +21 -0
- data/doc/release_notes/0_1_0.md +44 -0
- data/doc/release_notes/0_2_0.md +43 -0
- data/doc/release_notes/0_3_0.md +28 -0
- data/doc/release_notes/0_4_0.md +18 -0
- data/doc/release_notes/0_4_1.md +9 -0
- data/doc/release_notes/0_4_2.md +5 -0
- data/doc/release_notes/0_4_3.md +3 -0
- data/doc/release_notes/0_5_0.md +11 -0
- data/doc/release_notes/0_5_1.md +13 -0
- data/doc/release_notes/0_6_0.md +9 -0
- data/doc/release_notes/0_6_1.md +6 -0
- data/doc/release_notes/0_7_0.md +20 -0
- data/doc/release_notes/0_7_1.md +10 -0
- data/doc/release_notes/0_7_2.md +21 -0
- data/doc/release_notes/0_7_3.md +10 -0
- data/doc/release_notes/0_7_4.md +5 -0
- data/doc/release_notes/0_8_0.md +37 -0
- data/doc/release_notes/0_9_0.md +56 -0
- data/doc/release_notes/0_9_1.md +9 -0
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize.html.erb +25 -4
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/device_search.html.erb +11 -0
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/device_verification.html.erb +20 -0
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/new_oauth_application.html.erb +27 -10
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application.html.erb +17 -5
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application_oauth_tokens.html.erb +39 -0
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_applications.html.erb +6 -5
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_tokens.html.erb +12 -15
- data/lib/generators/rodauth/oauth/templates/db/migrate/create_rodauth_oauth.rb +21 -1
- data/lib/rodauth/features/oauth.rb +3 -1418
- data/lib/rodauth/features/oauth_application_management.rb +225 -0
- data/lib/rodauth/features/oauth_assertion_base.rb +96 -0
- data/lib/rodauth/features/oauth_authorization_code_grant.rb +252 -0
- data/lib/rodauth/features/oauth_authorization_server.rb +0 -0
- data/lib/rodauth/features/oauth_base.rb +778 -0
- data/lib/rodauth/features/oauth_client_credentials_grant.rb +33 -0
- data/lib/rodauth/features/oauth_device_grant.rb +220 -0
- data/lib/rodauth/features/oauth_dynamic_client_registration.rb +252 -0
- data/lib/rodauth/features/oauth_http_mac.rb +3 -21
- data/lib/rodauth/features/oauth_implicit_grant.rb +59 -0
- data/lib/rodauth/features/oauth_jwt.rb +275 -100
- data/lib/rodauth/features/oauth_jwt_bearer_grant.rb +59 -0
- data/lib/rodauth/features/oauth_management_base.rb +68 -0
- data/lib/rodauth/features/oauth_pkce.rb +98 -0
- data/lib/rodauth/features/oauth_resource_server.rb +21 -0
- data/lib/rodauth/features/oauth_saml_bearer_grant.rb +102 -0
- data/lib/rodauth/features/oauth_token_introspection.rb +108 -0
- data/lib/rodauth/features/oauth_token_management.rb +79 -0
- data/lib/rodauth/features/oauth_token_revocation.rb +109 -0
- data/lib/rodauth/features/oidc.rb +38 -9
- data/lib/rodauth/features/oidc_dynamic_client_registration.rb +147 -0
- data/lib/rodauth/oauth/database_extensions.rb +15 -2
- data/lib/rodauth/oauth/jwe_extensions.rb +64 -0
- data/lib/rodauth/oauth/refinements.rb +48 -0
- data/lib/rodauth/oauth/ttl_store.rb +9 -3
- data/lib/rodauth/oauth/version.rb +1 -1
- data/locales/en.yml +33 -12
- data/templates/authorize.str +57 -8
- data/templates/client_secret_field.str +2 -2
- data/templates/description_field.str +1 -1
- data/templates/device_search.str +11 -0
- data/templates/device_verification.str +24 -0
- data/templates/homepage_url_field.str +2 -2
- data/templates/jwks_field.str +4 -0
- data/templates/jwt_public_key_field.str +4 -0
- data/templates/name_field.str +1 -1
- data/templates/new_oauth_application.str +9 -0
- data/templates/oauth_application.str +7 -3
- data/templates/oauth_application_oauth_tokens.str +52 -0
- data/templates/oauth_applications.str +3 -2
- data/templates/oauth_tokens.str +10 -11
- data/templates/redirect_uri_field.str +2 -2
- metadata +80 -3
- 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
|
File without changes
|