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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1 -424
- data/README.md +26 -389
- 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/lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize.html.erb +3 -3
- 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 +22 -10
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application.html.erb +11 -5
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application_oauth_tokens.html.erb +38 -0
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_applications.html.erb +5 -5
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_tokens.html.erb +11 -15
- data/lib/generators/rodauth/oauth/templates/db/migrate/create_rodauth_oauth.rb +9 -1
- data/lib/rodauth/features/oauth.rb +3 -1418
- data/lib/rodauth/features/oauth_application_management.rb +209 -0
- data/lib/rodauth/features/oauth_assertion_base.rb +96 -0
- data/lib/rodauth/features/oauth_authorization_code_grant.rb +249 -0
- data/lib/rodauth/features/oauth_authorization_server.rb +0 -0
- data/lib/rodauth/features/oauth_base.rb +735 -0
- data/lib/rodauth/features/oauth_device_grant.rb +221 -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 +37 -60
- data/lib/rodauth/features/oauth_jwt_bearer_grant.rb +59 -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 +77 -0
- data/lib/rodauth/features/oauth_token_revocation.rb +109 -0
- data/lib/rodauth/features/oidc.rb +4 -3
- data/lib/rodauth/oauth/database_extensions.rb +15 -2
- data/lib/rodauth/oauth/refinements.rb +48 -0
- data/lib/rodauth/oauth/version.rb +1 -1
- data/locales/en.yml +28 -12
- data/templates/authorize.str +7 -7
- 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/jws_jwk_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 +51 -0
- data/templates/oauth_applications.str +2 -2
- data/templates/oauth_tokens.str +9 -11
- data/templates/redirect_uri_field.str +2 -2
- metadata +71 -3
- 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
|
File without changes
|