rodauth-oauth 1.6.6 → 1.6.7
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/doc/release_notes/1_6_7.md +17 -0
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application.html.erb +6 -0
- data/lib/generators/rodauth/oauth/templates/db/migrate/create_rodauth_oauth.rb +3 -1
- data/lib/rodauth/features/oauth_application_management.rb +22 -2
- data/lib/rodauth/features/oauth_authorize_base.rb +7 -0
- data/lib/rodauth/features/oauth_base.rb +42 -13
- data/lib/rodauth/features/oauth_device_code_grant.rb +5 -0
- data/lib/rodauth/features/oauth_dpop.rb +8 -0
- data/lib/rodauth/features/oauth_dynamic_client_registration.rb +38 -10
- data/lib/rodauth/features/oauth_grant_management.rb +6 -0
- data/lib/rodauth/features/oauth_jwt.rb +1 -1
- data/lib/rodauth/features/oauth_jwt_base.rb +8 -0
- data/lib/rodauth/features/oauth_jwt_bearer_grant.rb +8 -0
- data/lib/rodauth/features/oauth_jwt_jwks.rb +6 -0
- data/lib/rodauth/features/oauth_jwt_secured_authorization_request.rb +7 -1
- data/lib/rodauth/features/oauth_pushed_authorization_request.rb +6 -0
- data/lib/rodauth/features/oauth_resource_indicators.rb +7 -1
- data/lib/rodauth/features/oauth_resource_server.rb +1 -1
- data/lib/rodauth/features/oauth_saml_bearer_grant.rb +8 -3
- data/lib/rodauth/features/oauth_tls_client_auth.rb +14 -2
- data/lib/rodauth/features/oauth_token_revocation.rb +2 -1
- data/lib/rodauth/features/oidc.rb +25 -19
- data/lib/rodauth/features/oidc_frontchannel_logout.rb +10 -2
- data/lib/rodauth/features/oidc_rp_initiated_logout.rb +8 -2
- data/lib/rodauth/features/oidc_self_issued.rb +1 -1
- data/lib/rodauth/oauth/database_extensions.rb +1 -2
- data/lib/rodauth/oauth/version.rb +1 -1
- data/locales/en.yml +4 -0
- data/locales/pt.yml +4 -0
- data/templates/oauth_application.str +4 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 47de1ced2a621692058d69609ccb69999440ac8ca3de7f7a43f8522a81b93a20
|
|
4
|
+
data.tar.gz: 4822470b6f682648a772b8d7244000a68557875ee12ebd2126033f58128d9241
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 29604d9791ebdab3ea7a85a8ad05cc1ff32aaf8b73aad85b367be9cdd2ab5f199540d25b179147ef5346da7d709fcbf470aac05db6b903d80c483ce47214d399
|
|
7
|
+
data.tar.gz: 7d853b0d1a139fe2d10ec2d865493c8bdf9db313df79e4778e44943900f5634eabb03ac851a65f4f0bc31708014b037116ac46bf150b9fed8bee93fab5f9909c
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
### 1.6.7
|
|
2
|
+
|
|
3
|
+
#### Improvements
|
|
4
|
+
|
|
5
|
+
* Some new capabilities were built into `rodauth` recently, which `rodauth-oauth` now taps into:
|
|
6
|
+
* plugins are now object shape friendly, by defining ivars using `rodauth`'s `:uses_instance_variables` auth method (introduced in v2.44)
|
|
7
|
+
* `:oidc` plugin `get_oidc_param` and `get_additional_param` now have a default implementation, which raises an error (eliminates warnings).
|
|
8
|
+
* `only_json?` auth method is no longer defined (unless the `:jwt` plugin is used).
|
|
9
|
+
* some internal methods were now moved to private, instead of being needlessly exposed (eliminates security warnings).
|
|
10
|
+
* a new auth method, `confidential?(oauth_application)`, was added to the `:oauth_base` plugin, which is now used internally to check in some key flows whether the OAuth client application is a public or a confidential client, as it's defined in the OAuth RFC:
|
|
11
|
+
* when using the `:oauth_dynamic_client_registration` plugin, a client secret won't be generated for public clients (same logic will be applied for client registration management endpoints).
|
|
12
|
+
* when using the `:oauth_application_management` plugin, default templates will include a client type column, and omit the client secret for public clients, where they would previously.
|
|
13
|
+
|
|
14
|
+
#### Bugfixes
|
|
15
|
+
|
|
16
|
+
* do not render null fields in the payload of the oauth server metadata endpoint (RFC 8414 section 2 requires omission of undefined values, so clients can apply the recommended defaults).
|
|
17
|
+
* fixed regexp used for json requests (same fix as in [rodauth](https://github.com/jeremyevans/rodauth/commit/3e0d7ab2d49a5733d1afcaaf1062b8a8258aa57a))
|
|
@@ -7,8 +7,14 @@
|
|
|
7
7
|
<dd><%= oauth_application[rodauth.oauth_applications_description_column] %></dd>
|
|
8
8
|
<dt><%= rodauth.oauth_applications_homepage_url_label %>: </dt>
|
|
9
9
|
<dd><%= oauth_application[rodauth.oauth_applications_homepage_url_column] %></dd>
|
|
10
|
+
<dt><%= rodauth.oauth_applications_client_type_label %>: </dt>
|
|
11
|
+
<dd><%= rodauth.oauth_application_client_type_label(oauth_application) %></dd>
|
|
10
12
|
<dt><%= rodauth.oauth_applications_client_id_label %>: </dt>
|
|
11
13
|
<dd><%= oauth_application[rodauth.oauth_applications_client_id_column] %></dd>
|
|
14
|
+
<% if rodauth.confidential?(oauth_application) %>
|
|
15
|
+
<dt><%= rodauth.oauth_applications_client_secret_label %>: </dt>
|
|
16
|
+
<dd><%= oauth_application[rodauth.oauth_applications_client_secret_column] %></dd>
|
|
17
|
+
<% end %>
|
|
12
18
|
<dt><%= rodauth.oauth_applications_redirect_uri_label %>: </dt>
|
|
13
19
|
<dd><%= oauth_application[rodauth.oauth_applications_redirect_uri_column] %></dd>
|
|
14
20
|
<dt><%= rodauth.oauth_applications_scopes_label %>: </dt>
|
|
@@ -8,7 +8,9 @@ class CreateRodauthOauth < ActiveRecord::Migration<%= migration_version %>
|
|
|
8
8
|
t.string :homepage_url, null: true
|
|
9
9
|
t.string :redirect_uri, null: false
|
|
10
10
|
t.string :client_id, null: false, index: { unique: true }
|
|
11
|
-
|
|
11
|
+
# change `:null` contraints to false if you want to support confidential clients only, and
|
|
12
|
+
# no scheme relying on JWT private keys advertised by the JWKs uri.
|
|
13
|
+
t.string :client_secret, null: true, index: { unique: true }
|
|
12
14
|
t.string :registration_access_token, null: true
|
|
13
15
|
t.string :scopes, null: false
|
|
14
16
|
t.datetime :created_at, null: false, default: -> { "CURRENT_TIMESTAMP(6)" }
|
|
@@ -35,8 +35,11 @@ module Rodauth
|
|
|
35
35
|
translatable_method :oauth_applications_jwks_uri_label, "JSON Web Keys URI"
|
|
36
36
|
translatable_method :oauth_applications_homepage_url_label, "Homepage URL"
|
|
37
37
|
translatable_method :oauth_applications_redirect_uri_label, "Redirect URI"
|
|
38
|
-
translatable_method :oauth_applications_client_secret_label, "Client Secret"
|
|
39
38
|
translatable_method :oauth_applications_client_id_label, "Client ID"
|
|
39
|
+
translatable_method :oauth_applications_client_secret_label, "Client Secret"
|
|
40
|
+
translatable_method :oauth_applications_client_type_label, "Client Type"
|
|
41
|
+
translatable_method :oauth_applications_confidential_label, "Confidential"
|
|
42
|
+
translatable_method :oauth_applications_public_label, "Public"
|
|
40
43
|
|
|
41
44
|
%w[type token refresh_token expires_in revoked_at].each do |param|
|
|
42
45
|
translatable_method :"oauth_grants_#{param}_label", param.gsub("_", " ").capitalize
|
|
@@ -58,9 +61,18 @@ module Rodauth
|
|
|
58
61
|
translatable_method :oauth_no_grants_text, "No oauth grants yet!"
|
|
59
62
|
|
|
60
63
|
auth_methods(
|
|
61
|
-
:oauth_application_path
|
|
64
|
+
:oauth_application_path,
|
|
65
|
+
:oauth_application_client_type_label
|
|
62
66
|
)
|
|
63
67
|
|
|
68
|
+
if respond_to?(:uses_instance_variables)
|
|
69
|
+
uses_instance_variables(
|
|
70
|
+
:@oauth_applications,
|
|
71
|
+
:@oauth_grants,
|
|
72
|
+
:@field_errors
|
|
73
|
+
)
|
|
74
|
+
end
|
|
75
|
+
|
|
64
76
|
def oauth_applications_path(opts = {})
|
|
65
77
|
route_path(oauth_applications_route, opts)
|
|
66
78
|
end
|
|
@@ -69,6 +81,14 @@ module Rodauth
|
|
|
69
81
|
"#{oauth_applications_path}/#{id}"
|
|
70
82
|
end
|
|
71
83
|
|
|
84
|
+
def oauth_application_client_type_label(oauth_application)
|
|
85
|
+
if confidential?(oauth_application)
|
|
86
|
+
oauth_applications_confidential_label
|
|
87
|
+
else
|
|
88
|
+
oauth_applications_public_label
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
72
92
|
# /oauth-applications routes
|
|
73
93
|
def load_oauth_application_management_routes
|
|
74
94
|
request.on(oauth_applications_route) do
|
|
@@ -34,6 +34,13 @@ module Rodauth
|
|
|
34
34
|
:authorize_form_params
|
|
35
35
|
)
|
|
36
36
|
|
|
37
|
+
if respond_to?(:uses_instance_variables)
|
|
38
|
+
uses_instance_variables(
|
|
39
|
+
:@error,
|
|
40
|
+
:@back_url
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
37
44
|
OAUTH_ACCESS_TYPES = %w[offline online].freeze
|
|
38
45
|
|
|
39
46
|
OAUTH_APPROVAL_PROMPTS = %w[force auto].freeze
|
|
@@ -76,7 +76,7 @@ module Rodauth
|
|
|
76
76
|
account_id
|
|
77
77
|
name description scopes
|
|
78
78
|
client_id client_secret
|
|
79
|
-
homepage_url redirect_uri
|
|
79
|
+
confidential homepage_url redirect_uri
|
|
80
80
|
token_endpoint_auth_method grant_types response_types response_modes
|
|
81
81
|
logo_uri tos_uri policy_uri jwks jwks_uri
|
|
82
82
|
contacts software_id software_version
|
|
@@ -107,9 +107,7 @@ module Rodauth
|
|
|
107
107
|
|
|
108
108
|
auth_value_method :is_authorization_server?, true
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
auth_value_method :json_request_regexp, %r{\bapplication/(?:vnd\.api\+)?json\b}i
|
|
110
|
+
auth_value_method :json_request_regexp, %r{\Aapplication/(?:vnd\.api\+)?json\b}i
|
|
113
111
|
|
|
114
112
|
# METADATA
|
|
115
113
|
auth_value_method :oauth_metadata_service_documentation, nil
|
|
@@ -130,9 +128,18 @@ module Rodauth
|
|
|
130
128
|
:oauth_unique_id_generator,
|
|
131
129
|
:require_authorizable_account,
|
|
132
130
|
:oauth_account_ds,
|
|
133
|
-
:oauth_application_ds
|
|
131
|
+
:oauth_application_ds,
|
|
132
|
+
:confidential?
|
|
134
133
|
)
|
|
135
134
|
|
|
135
|
+
if respond_to?(:uses_instance_variables)
|
|
136
|
+
uses_instance_variables(
|
|
137
|
+
:@json_request,
|
|
138
|
+
:@oauth_application,
|
|
139
|
+
:@authorization_token
|
|
140
|
+
)
|
|
141
|
+
end
|
|
142
|
+
|
|
136
143
|
# /token
|
|
137
144
|
auth_server_route(:token) do |r|
|
|
138
145
|
require_oauth_application
|
|
@@ -233,7 +240,7 @@ module Rodauth
|
|
|
233
240
|
end
|
|
234
241
|
|
|
235
242
|
def oauth_application
|
|
236
|
-
return @oauth_application if
|
|
243
|
+
return @oauth_application if @oauth_application
|
|
237
244
|
|
|
238
245
|
client_id = param_or_nil("client_id")
|
|
239
246
|
|
|
@@ -242,6 +249,21 @@ module Rodauth
|
|
|
242
249
|
@oauth_application = db[oauth_applications_table].filter(oauth_applications_client_id_column => client_id).first
|
|
243
250
|
end
|
|
244
251
|
|
|
252
|
+
def confidential?(oauth_application)
|
|
253
|
+
token_endpoint_auth_method = oauth_application[oauth_applications_token_endpoint_auth_method_column]
|
|
254
|
+
|
|
255
|
+
# https://datatracker.ietf.org/doc/html/rfc7591#section-2
|
|
256
|
+
# "none": The client is a public client as defined in OAuth 2.0, Section 2.1, and does not have a client secret.
|
|
257
|
+
return false if token_endpoint_auth_method == "none"
|
|
258
|
+
|
|
259
|
+
oauth_confidential_token_endpoint_auth_methods.include?(token_endpoint_auth_method) ||
|
|
260
|
+
!(
|
|
261
|
+
# not exclusively registering implicit grant
|
|
262
|
+
Array(oauth_application[oauth_applications_grant_types_column]).include?("implicit") &&
|
|
263
|
+
Array(oauth_application[oauth_applications_response_types_column]).include?("token")
|
|
264
|
+
)
|
|
265
|
+
end
|
|
266
|
+
|
|
245
267
|
def fetch_access_token
|
|
246
268
|
if (token = request.params["access_token"])
|
|
247
269
|
if request.post? && !(request.content_type.start_with?("application/x-www-form-urlencoded") &&
|
|
@@ -270,7 +292,7 @@ module Rodauth
|
|
|
270
292
|
end
|
|
271
293
|
|
|
272
294
|
def authorization_token
|
|
273
|
-
return @authorization_token if
|
|
295
|
+
return @authorization_token if @authorization_token
|
|
274
296
|
|
|
275
297
|
# check if there is a token
|
|
276
298
|
access_token = fetch_access_token
|
|
@@ -329,6 +351,10 @@ module Rodauth
|
|
|
329
351
|
|
|
330
352
|
private
|
|
331
353
|
|
|
354
|
+
def oauth_confidential_token_endpoint_auth_methods
|
|
355
|
+
%w[client_secret_basic client_secret_post]
|
|
356
|
+
end
|
|
357
|
+
|
|
332
358
|
def oauth_account_ds(account_id)
|
|
333
359
|
account_ds(account_id)
|
|
334
360
|
end
|
|
@@ -763,19 +789,22 @@ module Rodauth
|
|
|
763
789
|
issuer = base_url
|
|
764
790
|
issuer += "/#{path}" if path
|
|
765
791
|
|
|
766
|
-
{
|
|
792
|
+
payload = {
|
|
767
793
|
issuer: issuer,
|
|
768
794
|
token_endpoint: token_url,
|
|
769
795
|
scopes_supported: oauth_application_scopes,
|
|
770
796
|
response_types_supported: oauth_response_types_supported,
|
|
771
797
|
response_modes_supported: oauth_response_modes_supported,
|
|
772
798
|
grant_types_supported: oauth_grant_types_supported,
|
|
773
|
-
token_endpoint_auth_methods_supported: oauth_token_endpoint_auth_methods_supported
|
|
774
|
-
service_documentation: oauth_metadata_service_documentation,
|
|
775
|
-
ui_locales_supported: oauth_metadata_ui_locales_supported,
|
|
776
|
-
op_policy_uri: oauth_metadata_op_policy_uri,
|
|
777
|
-
op_tos_uri: oauth_metadata_op_tos_uri
|
|
799
|
+
token_endpoint_auth_methods_supported: oauth_token_endpoint_auth_methods_supported
|
|
778
800
|
}
|
|
801
|
+
|
|
802
|
+
# RFC 8414, section 2: unused optional metadata parameters must be omitted, not null
|
|
803
|
+
payload[:service_documentation] = oauth_metadata_service_documentation if oauth_metadata_service_documentation
|
|
804
|
+
payload[:ui_locales_supported] = oauth_metadata_ui_locales_supported if oauth_metadata_ui_locales_supported
|
|
805
|
+
payload[:op_policy_uri] = oauth_metadata_op_policy_uri if oauth_metadata_op_policy_uri
|
|
806
|
+
payload[:op_tos_uri] = oauth_metadata_op_tos_uri if oauth_metadata_op_tos_uri
|
|
807
|
+
payload
|
|
779
808
|
end
|
|
780
809
|
|
|
781
810
|
def redirect_response_error(error_code, message = nil)
|
|
@@ -54,6 +54,14 @@ module Rodauth
|
|
|
54
54
|
|
|
55
55
|
auth_methods(:validate_dpop_proof_usage)
|
|
56
56
|
|
|
57
|
+
if respond_to?(:uses_instance_variables)
|
|
58
|
+
uses_instance_variables(
|
|
59
|
+
:@dpop_access_token,
|
|
60
|
+
:@dpop_claims,
|
|
61
|
+
:@dpop_thumbprint
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
|
|
57
65
|
def require_oauth_authorization(*scopes)
|
|
58
66
|
@dpop_access_token = fetch_access_token_from_authorization_header("dpop")
|
|
59
67
|
|
|
@@ -12,10 +12,21 @@ module Rodauth
|
|
|
12
12
|
auth_value_method :oauth_applications_registration_access_token_column, :registration_access_token
|
|
13
13
|
auth_value_method :registration_client_uri_route, "register"
|
|
14
14
|
|
|
15
|
+
translatable_method :register_no_client_secret_if_public, "Client secret is not allowed for a public client"
|
|
16
|
+
|
|
17
|
+
if respond_to?(:uses_instance_variables)
|
|
18
|
+
uses_instance_variables(
|
|
19
|
+
:@oauth_application_params,
|
|
20
|
+
:@oauth_application_unrecognized_params
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
|
|
15
24
|
PROTECTED_APPLICATION_CREATE_ATTRIBUTES = %w[account_id client_id].freeze
|
|
16
25
|
|
|
17
|
-
PROTECTED_APPLICATION_UPDATE_ATTRIBUTES = %w[
|
|
18
|
-
|
|
26
|
+
PROTECTED_APPLICATION_UPDATE_ATTRIBUTES = %w[
|
|
27
|
+
account_id client_id registration_access_token
|
|
28
|
+
registration_client_uri client_secret_expires_at client_id_issued_at
|
|
29
|
+
].freeze
|
|
19
30
|
|
|
20
31
|
HIDDEN_APPLICATION_ATTRIBUTES = %w[id account_id client_id client_secret client_secret_hash].freeze
|
|
21
32
|
|
|
@@ -39,15 +50,24 @@ module Rodauth
|
|
|
39
50
|
json_response_oauth_application(oauth_application)
|
|
40
51
|
end
|
|
41
52
|
request.on method: :put do
|
|
42
|
-
|
|
53
|
+
params = request.params
|
|
54
|
+
|
|
55
|
+
validate_client_registration_params(params, PROTECTED_APPLICATION_UPDATE_ATTRIBUTES)
|
|
43
56
|
|
|
44
|
-
if
|
|
57
|
+
if params.key?("client_secret") && !secret_matches?(oauth_application, params["client_secret"])
|
|
45
58
|
# if the client includes the "client_secret" field in the request, the value of this field MUST match the currently
|
|
46
59
|
# issued client secret for that client. The client MUST NOT be allowed to overwrite its existing client secret with
|
|
47
60
|
# its own chosen value.
|
|
48
61
|
authorization_required
|
|
49
62
|
end
|
|
50
63
|
|
|
64
|
+
if params.key?("client_secret")
|
|
65
|
+
next_oauth_application = oauth_application.merge(@oauth_application_params)
|
|
66
|
+
unless confidential?(next_oauth_application)
|
|
67
|
+
register_throw_json_response_error("invalid_client_metadata", register_no_client_secret_if_public)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
51
71
|
oauth_application = transaction do
|
|
52
72
|
applications_ds = db[oauth_applications_table]
|
|
53
73
|
.where(oauth_applications_client_id_column => client_id)
|
|
@@ -108,7 +128,7 @@ module Rodauth
|
|
|
108
128
|
|
|
109
129
|
def _before_register
|
|
110
130
|
raise %{dynamic client registration requires authentication.
|
|
111
|
-
Override
|
|
131
|
+
Override `before_register` to perform it.
|
|
112
132
|
example:
|
|
113
133
|
|
|
114
134
|
before_register do
|
|
@@ -324,6 +344,7 @@ module Rodauth
|
|
|
324
344
|
return_params["response_types"] = %w[code]
|
|
325
345
|
"code"
|
|
326
346
|
end
|
|
347
|
+
|
|
327
348
|
rescue_from_uniqueness_error do
|
|
328
349
|
initialize_create_params(create_params, return_params)
|
|
329
350
|
create_params.delete_if { |k, _| !application_columns.include?(k) }
|
|
@@ -341,15 +362,22 @@ module Rodauth
|
|
|
341
362
|
# "client_id_issued_at" is a number, expressed as seconds since 1970-01-01T00:00:00Z.
|
|
342
363
|
return_params["client_id_issued_at"] = Time.now.to_i
|
|
343
364
|
|
|
344
|
-
if create_params.key?(oauth_applications_client_secret_column)
|
|
345
|
-
|
|
346
|
-
|
|
365
|
+
if create_params.key?(oauth_applications_client_secret_column) && !confidential?(create_params)
|
|
366
|
+
register_throw_json_response_error("invalid_client_metadata", register_no_client_secret_if_public)
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
return_params.delete("client_secret")
|
|
370
|
+
|
|
371
|
+
unless (client_secret = create_params.key?(oauth_applications_client_secret_column))
|
|
372
|
+
# do not generate a secret for a confidential client
|
|
373
|
+
return unless confidential?(create_params)
|
|
374
|
+
|
|
347
375
|
client_secret = oauth_unique_id_generator
|
|
348
|
-
set_client_secret(create_params, client_secret)
|
|
349
376
|
return_params["client_secret"] = client_secret
|
|
350
377
|
return_params["client_secret_expires_at"] = 0
|
|
351
|
-
|
|
352
378
|
end
|
|
379
|
+
|
|
380
|
+
set_client_secret(create_params, client_secret)
|
|
353
381
|
end
|
|
354
382
|
|
|
355
383
|
def register_throw_json_response_error(code, message)
|
|
@@ -51,7 +51,7 @@ module Rodauth
|
|
|
51
51
|
def authorization_token
|
|
52
52
|
return super unless oauth_jwt_access_tokens
|
|
53
53
|
|
|
54
|
-
return @authorization_token if
|
|
54
|
+
return @authorization_token if @authorization_token
|
|
55
55
|
|
|
56
56
|
@authorization_token = decode_access_token
|
|
57
57
|
end
|
|
@@ -33,6 +33,14 @@ module Rodauth
|
|
|
33
33
|
:resource_owner_params_from_jwt_claims
|
|
34
34
|
)
|
|
35
35
|
|
|
36
|
+
if respond_to?(:uses_instance_variables)
|
|
37
|
+
uses_instance_variables(
|
|
38
|
+
:@oauth_jwt_issuer,
|
|
39
|
+
:@oauth_jwt_audience,
|
|
40
|
+
:@_jwt_key
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
36
44
|
private
|
|
37
45
|
|
|
38
46
|
def oauth_jwt_issuer
|
|
@@ -28,6 +28,14 @@ module Rodauth
|
|
|
28
28
|
|
|
29
29
|
private
|
|
30
30
|
|
|
31
|
+
def oauth_confidential_token_endpoint_auth_methods
|
|
32
|
+
if oauth_applications_client_secret_hash_column.nil?
|
|
33
|
+
super | %w[client_secret_jwt private_key_jwt urn:ietf:params:oauth:client-assertion-type:jwt-bearer]
|
|
34
|
+
else
|
|
35
|
+
super | %w[private_key_jwt]
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
31
39
|
def require_oauth_application_from_jwt_bearer_assertion_issuer(assertion)
|
|
32
40
|
claims = jwt_assertion(assertion)
|
|
33
41
|
|
|
@@ -23,6 +23,12 @@ module Rodauth
|
|
|
23
23
|
|
|
24
24
|
auth_value_method :max_param_bytesize, nil if Rodauth::VERSION >= "2.26.0"
|
|
25
25
|
|
|
26
|
+
if respond_to?(:uses_instance_variables)
|
|
27
|
+
uses_instance_variables(
|
|
28
|
+
:@require_signed_request_object
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
26
32
|
private
|
|
27
33
|
|
|
28
34
|
# /authorize
|
|
@@ -93,7 +99,7 @@ module Rodauth
|
|
|
93
99
|
end
|
|
94
100
|
|
|
95
101
|
def require_signed_request_object?
|
|
96
|
-
return @require_signed_request_object if
|
|
102
|
+
return @require_signed_request_object if @require_signed_request_object
|
|
97
103
|
|
|
98
104
|
@require_signed_request_object = (oauth_application[oauth_applications_require_signed_request_object_column] if oauth_application)
|
|
99
105
|
@require_signed_request_object = oauth_require_signed_request_object if @require_signed_request_object.nil?
|
|
@@ -19,6 +19,12 @@ module Rodauth
|
|
|
19
19
|
auth_value_method :"oauth_pushed_authorization_requests_#{column}_column", column
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
+
if respond_to?(:uses_instance_variables)
|
|
23
|
+
uses_instance_variables(
|
|
24
|
+
:@require_signed_request_object
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
22
28
|
# /par
|
|
23
29
|
auth_server_route(:par) do |r|
|
|
24
30
|
require_oauth_application
|
|
@@ -8,8 +8,14 @@ module Rodauth
|
|
|
8
8
|
|
|
9
9
|
auth_value_method :oauth_grants_resource_column, :resource
|
|
10
10
|
|
|
11
|
+
if respond_to?(:uses_instance_variables)
|
|
12
|
+
uses_instance_variables(
|
|
13
|
+
:@resource_indicators
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
|
|
11
17
|
def resource_indicators
|
|
12
|
-
return @resource_indicators if
|
|
18
|
+
return @resource_indicators if @resource_indicators
|
|
13
19
|
|
|
14
20
|
resources = param_or_nil("resource")
|
|
15
21
|
|
|
@@ -34,6 +34,13 @@ module Rodauth
|
|
|
34
34
|
:account_from_saml2_bearer_assertion
|
|
35
35
|
)
|
|
36
36
|
|
|
37
|
+
if respond_to?(:uses_instance_variables)
|
|
38
|
+
uses_instance_variables(
|
|
39
|
+
:@saml_settings,
|
|
40
|
+
:@assertion
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
37
44
|
def oauth_grant_types_supported
|
|
38
45
|
super | %w[urn:ietf:params:oauth:grant-type:saml2-bearer]
|
|
39
46
|
end
|
|
@@ -98,9 +105,8 @@ module Rodauth
|
|
|
98
105
|
settings
|
|
99
106
|
end
|
|
100
107
|
|
|
101
|
-
# rubocop:disable Naming/MemoizedInstanceVariableName
|
|
102
108
|
def parse_saml_assertion(assertion)
|
|
103
|
-
return @assertion if
|
|
109
|
+
return @assertion if @assertion
|
|
104
110
|
|
|
105
111
|
response = OneLogin::RubySaml::Response.new(assertion)
|
|
106
112
|
|
|
@@ -129,7 +135,6 @@ module Rodauth
|
|
|
129
135
|
|
|
130
136
|
@assertion = response
|
|
131
137
|
end
|
|
132
|
-
# rubocop:enable Naming/MemoizedInstanceVariableName
|
|
133
138
|
|
|
134
139
|
def oauth_server_metadata_body(*)
|
|
135
140
|
super.tap do |data|
|
|
@@ -21,12 +21,24 @@ module Rodauth
|
|
|
21
21
|
|
|
22
22
|
auth_value_method :oauth_grants_certificate_thumbprint_column, :certificate_thumbprint
|
|
23
23
|
|
|
24
|
+
if respond_to?(:uses_instance_variables)
|
|
25
|
+
uses_instance_variables(
|
|
26
|
+
:@client_certificate,
|
|
27
|
+
:@certificate,
|
|
28
|
+
:@client_certificate_sans
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
24
32
|
def oauth_token_endpoint_auth_methods_supported
|
|
25
33
|
super | %w[tls_client_auth self_signed_tls_client_auth]
|
|
26
34
|
end
|
|
27
35
|
|
|
28
36
|
private
|
|
29
37
|
|
|
38
|
+
def oauth_confidential_token_endpoint_auth_methods
|
|
39
|
+
super | %w[tls_client_auth self_signed_tls_client_auth]
|
|
40
|
+
end
|
|
41
|
+
|
|
30
42
|
def validate_token_params
|
|
31
43
|
# For all requests to the authorization server utilizing mutual-TLS client authentication,
|
|
32
44
|
# the client MUST include the client_id parameter
|
|
@@ -124,7 +136,7 @@ module Rodauth
|
|
|
124
136
|
end
|
|
125
137
|
|
|
126
138
|
def client_certificate
|
|
127
|
-
return @client_certificate if
|
|
139
|
+
return @client_certificate if @client_certificate
|
|
128
140
|
|
|
129
141
|
unless (pem_cert = request.env["SSL_CLIENT_CERT"] || request.env["HTTP_SSL_CLIENT_CERT"] || request.env["HTTP_X_SSL_CLIENT_CERT"])
|
|
130
142
|
return
|
|
@@ -136,7 +148,7 @@ module Rodauth
|
|
|
136
148
|
end
|
|
137
149
|
|
|
138
150
|
def client_certificate_sans
|
|
139
|
-
return @client_certificate_sans if
|
|
151
|
+
return @client_certificate_sans if @client_certificate_sans
|
|
140
152
|
|
|
141
153
|
@client_certificate_sans =
|
|
142
154
|
if client_certificate
|
|
@@ -117,7 +117,8 @@ module Rodauth
|
|
|
117
117
|
def oauth_server_metadata_body(*)
|
|
118
118
|
super.tap do |data|
|
|
119
119
|
data[:revocation_endpoint] = revoke_url
|
|
120
|
-
|
|
120
|
+
# revocation_endpoint_auth_methods_supported must be absent: it defaults to client_secret_basic
|
|
121
|
+
data.delete(:revocation_endpoint_auth_methods_supported)
|
|
121
122
|
end
|
|
122
123
|
end
|
|
123
124
|
end
|
|
@@ -115,6 +115,12 @@ module Rodauth
|
|
|
115
115
|
:json_webfinger_payload
|
|
116
116
|
)
|
|
117
117
|
|
|
118
|
+
if respond_to?(:uses_instance_variables)
|
|
119
|
+
uses_instance_variables(
|
|
120
|
+
:@acr
|
|
121
|
+
)
|
|
122
|
+
end
|
|
123
|
+
|
|
118
124
|
# /userinfo
|
|
119
125
|
auth_server_route(:userinfo) do |r|
|
|
120
126
|
r.on method: %i[get post] do
|
|
@@ -221,6 +227,14 @@ module Rodauth
|
|
|
221
227
|
end
|
|
222
228
|
end
|
|
223
229
|
|
|
230
|
+
def get_oidc_param(_, _)
|
|
231
|
+
raise NotImplementedError, "`get_oidc_param(account, claim)` auth method must be implemented to use oidc scopes."
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def get_additional_param(_, _)
|
|
235
|
+
raise NotImplementedError, "`get_oidc_param(account, claim)` auth method must be implemented to use oidc scopes."
|
|
236
|
+
end
|
|
237
|
+
|
|
224
238
|
def oauth_response_types_supported
|
|
225
239
|
grant_types = oauth_grant_types_supported
|
|
226
240
|
oidc_response_types = %w[id_token none]
|
|
@@ -626,35 +640,27 @@ module Rodauth
|
|
|
626
640
|
claims_locales = claims_locales.split(" ").map(&:to_sym) if claims_locales
|
|
627
641
|
|
|
628
642
|
unless oidc_scopes.empty?
|
|
629
|
-
|
|
630
|
-
get_oidc_param = proxy_get_param(:get_oidc_param, claims, claims_locales, additional_claims_info)
|
|
643
|
+
get_oidc_param = proxy_get_param(:get_oidc_param, claims, claims_locales, additional_claims_info)
|
|
631
644
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
645
|
+
oidc_scopes.each do |scope|
|
|
646
|
+
scope_claims = claims
|
|
647
|
+
params = scopes_by_claim[scope]
|
|
648
|
+
params = params.empty? ? OIDC_SCOPES_MAP[scope] : (OIDC_SCOPES_MAP[scope] & params)
|
|
636
649
|
|
|
637
|
-
|
|
650
|
+
scope_claims = (claims["address"] = {}) if scope == "address"
|
|
638
651
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
end
|
|
652
|
+
params.each do |param|
|
|
653
|
+
get_oidc_param[account, param, scope_claims]
|
|
642
654
|
end
|
|
643
|
-
else
|
|
644
|
-
warn "`get_oidc_param(account, claim)` must be implemented to use oidc scopes."
|
|
645
655
|
end
|
|
646
656
|
end
|
|
647
657
|
|
|
648
658
|
return if additional_scopes.empty?
|
|
649
659
|
|
|
650
|
-
|
|
651
|
-
get_additional_param = proxy_get_param(:get_additional_param, claims, claims_locales, additional_claims_info)
|
|
660
|
+
get_additional_param = proxy_get_param(:get_additional_param, claims, claims_locales, additional_claims_info)
|
|
652
661
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
end
|
|
656
|
-
else
|
|
657
|
-
warn "`get_additional_param(account, claim)` must be implemented to use oidc scopes."
|
|
662
|
+
additional_scopes.each do |scope|
|
|
663
|
+
get_additional_param[account, scope.to_sym]
|
|
658
664
|
end
|
|
659
665
|
end
|
|
660
666
|
|
|
@@ -22,6 +22,14 @@ module Rodauth
|
|
|
22
22
|
auth_value_method :oauth_applications_frontchannel_logout_uri_column, :frontchannel_logout_uri
|
|
23
23
|
auth_value_method :oauth_applications_frontchannel_logout_session_required_column, :frontchannel_logout_session_required
|
|
24
24
|
|
|
25
|
+
if respond_to?(:uses_instance_variables)
|
|
26
|
+
uses_instance_variables(
|
|
27
|
+
:@visited_sites,
|
|
28
|
+
:@frontchannel_logout_redirect,
|
|
29
|
+
:@frontchannel_logout_urls
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
|
|
25
33
|
attr_reader :frontchannel_logout_urls
|
|
26
34
|
|
|
27
35
|
attr_reader :frontchannel_logout_redirect
|
|
@@ -32,6 +40,8 @@ module Rodauth
|
|
|
32
40
|
super
|
|
33
41
|
end
|
|
34
42
|
|
|
43
|
+
private
|
|
44
|
+
|
|
35
45
|
def _logout_response
|
|
36
46
|
visited_sites = @visited_sites
|
|
37
47
|
|
|
@@ -71,8 +81,6 @@ module Rodauth
|
|
|
71
81
|
return_response frontchannel_logout_view
|
|
72
82
|
end
|
|
73
83
|
|
|
74
|
-
private
|
|
75
|
-
|
|
76
84
|
def generate_frontchannel_logout_urls(visited_sites, logout_urls)
|
|
77
85
|
@frontchannel_logout_urls = logout_urls.flat_map do |client_id, logout_url|
|
|
78
86
|
next unless logout_url
|
|
@@ -13,6 +13,12 @@ module Rodauth
|
|
|
13
13
|
|
|
14
14
|
attr_reader :oidc_logout_redirect
|
|
15
15
|
|
|
16
|
+
if respond_to?(:uses_instance_variables)
|
|
17
|
+
uses_instance_variables(
|
|
18
|
+
:@oidc_logout_redirect
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
|
|
16
22
|
# /oidc-logout
|
|
17
23
|
auth_server_route(:oidc_logout) do |r|
|
|
18
24
|
require_authorizable_account
|
|
@@ -109,12 +115,12 @@ module Rodauth
|
|
|
109
115
|
end
|
|
110
116
|
end
|
|
111
117
|
|
|
118
|
+
private
|
|
119
|
+
|
|
112
120
|
def _oidc_logout_response
|
|
113
121
|
redirect(oidc_logout_redirect)
|
|
114
122
|
end
|
|
115
123
|
|
|
116
|
-
private
|
|
117
|
-
|
|
118
124
|
# Logout
|
|
119
125
|
|
|
120
126
|
def validate_oidc_logout_params
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Rodauth
|
|
4
4
|
module OAuth
|
|
5
|
-
# rubocop:disable Naming/MethodName
|
|
5
|
+
# rubocop:disable-next Naming/MethodName
|
|
6
6
|
def self.ExtendDatabase(db)
|
|
7
7
|
Module.new do
|
|
8
8
|
dataset = db.dataset
|
|
@@ -87,6 +87,5 @@ module Rodauth
|
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
89
|
end
|
|
90
|
-
# rubocop:enable Naming/MethodName
|
|
91
90
|
end
|
|
92
91
|
end
|
data/locales/en.yml
CHANGED
|
@@ -40,6 +40,9 @@ en:
|
|
|
40
40
|
oauth_applications_redirect_uri_label: "Redirect URL"
|
|
41
41
|
oauth_applications_client_secret_label: "Client Secret"
|
|
42
42
|
oauth_applications_client_id_label: "Client ID"
|
|
43
|
+
oauth_applications_client_type_label: "Client Type"
|
|
44
|
+
oauth_applications_confidential_label: "Confidential"
|
|
45
|
+
oauth_applications_public_label: "Public"
|
|
43
46
|
oauth_no_applications_text: "No oauth applications yet!"
|
|
44
47
|
oauth_grant_user_code_label: "User code"
|
|
45
48
|
oauth_grant_user_jws_jwk_label: "JSON Web Keys"
|
|
@@ -87,3 +90,4 @@ en:
|
|
|
87
90
|
oauth_use_dpop_nonce_message: "DPoP nonce is required"
|
|
88
91
|
oauth_access_token_dpop_bound_message: "DPoP bound access token requires DPoP proof"
|
|
89
92
|
oauth_multiple_auth_methods_message: "Multiple methods used to include access token"
|
|
93
|
+
register_no_client_secret_if_public: "Client secret is not allowed for a public OAuth application"
|
data/locales/pt.yml
CHANGED
|
@@ -40,6 +40,9 @@ pt:
|
|
|
40
40
|
oauth_applications_redirect_uri_label: "URL para redireccionamento"
|
|
41
41
|
oauth_applications_client_secret_label: "Segredo de cliente"
|
|
42
42
|
oauth_applications_client_id_label: "ID do cliente"
|
|
43
|
+
oauth_applications_client_type_label: "Tipo de cliente"
|
|
44
|
+
oauth_applications_confidential_label: "Confidencial"
|
|
45
|
+
oauth_applications_public_label: "Público"
|
|
43
46
|
oauth_no_applications_text: "Nenhum aplicativo OAuth ainda!"
|
|
44
47
|
oauth_grant_user_code_label: "Código do usuário"
|
|
45
48
|
oauth_grant_user_jws_jwk_label: "Chaves JSON Web"
|
|
@@ -77,3 +80,4 @@ pt:
|
|
|
77
80
|
oauth_saml_assertion_single_issuer_message: "A asserção SAML tem que ter um único emissor"
|
|
78
81
|
oauth_saml_settings_not_found_message: "Nenhuma configuração SAML encontrada para o emissor"
|
|
79
82
|
oauth_invalid_id_token_hint_message: "ID token hint inválido"
|
|
83
|
+
register_no_client_secret_if_public: "Segredo de cliente não é permitido em um aplicativo OAuth público"
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
<div id="oauth-application">
|
|
2
2
|
<dl>
|
|
3
|
+
<dt class="confidential">#{rodauth.oauth_applications_client_type_label}</dt>
|
|
4
|
+
<dd class="confidential">#{rodauth.oauth_application_client_type_label(@oauth_application)}</dd>
|
|
3
5
|
#{
|
|
4
|
-
params = [*rodauth.oauth_application_required_params, "client_id"
|
|
6
|
+
params = [*rodauth.oauth_application_required_params, "client_id"]
|
|
7
|
+
params << "client_secret" if rodauth.confidential?(@oauth_application)
|
|
5
8
|
if rodauth.features.include?(:oauth_jwt)
|
|
6
9
|
params += %w[jwks]
|
|
7
10
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rodauth-oauth
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.6.
|
|
4
|
+
version: 1.6.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tiago Cardoso
|
|
@@ -94,6 +94,7 @@ extra_rdoc_files:
|
|
|
94
94
|
- doc/release_notes/1_6_4.md
|
|
95
95
|
- doc/release_notes/1_6_5.md
|
|
96
96
|
- doc/release_notes/1_6_6.md
|
|
97
|
+
- doc/release_notes/1_6_7.md
|
|
97
98
|
files:
|
|
98
99
|
- CHANGELOG.md
|
|
99
100
|
- LICENSE.txt
|
|
@@ -146,6 +147,7 @@ files:
|
|
|
146
147
|
- doc/release_notes/1_6_4.md
|
|
147
148
|
- doc/release_notes/1_6_5.md
|
|
148
149
|
- doc/release_notes/1_6_6.md
|
|
150
|
+
- doc/release_notes/1_6_7.md
|
|
149
151
|
- lib/generators/rodauth/oauth/install_generator.rb
|
|
150
152
|
- lib/generators/rodauth/oauth/templates/app/models/oauth_application.rb
|
|
151
153
|
- lib/generators/rodauth/oauth/templates/app/models/oauth_grant.rb
|