rodauth-oauth 1.0.0.pre.beta1 → 1.0.0.pre.beta2
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/README.md +6 -5
- data/doc/release_notes/1_0_0_beta2.md +34 -0
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize.html.erb +19 -7
- data/lib/generators/rodauth/oauth/templates/db/migrate/create_rodauth_oauth.rb +54 -43
- data/lib/rodauth/features/oauth_application_management.rb +2 -2
- data/lib/rodauth/features/oauth_authorization_code_grant.rb +31 -6
- data/lib/rodauth/features/oauth_authorize_base.rb +16 -6
- data/lib/rodauth/features/oauth_base.rb +35 -16
- data/lib/rodauth/features/oauth_dynamic_client_registration.rb +7 -4
- data/lib/rodauth/features/oauth_implicit_grant.rb +6 -5
- data/lib/rodauth/features/oauth_jwt.rb +3 -3
- data/lib/rodauth/features/oauth_jwt_base.rb +29 -6
- data/lib/rodauth/features/oauth_jwt_bearer_grant.rb +7 -4
- data/lib/rodauth/features/oauth_jwt_secured_authorization_request.rb +64 -10
- data/lib/rodauth/features/oauth_resource_indicators.rb +0 -4
- data/lib/rodauth/features/oauth_resource_server.rb +3 -3
- data/lib/rodauth/features/oauth_saml_bearer_grant.rb +2 -0
- data/lib/rodauth/features/oidc.rb +231 -183
- data/lib/rodauth/features/oidc_dynamic_client_registration.rb +65 -25
- data/lib/rodauth/features/oidc_rp_initiated_logout.rb +115 -0
- data/lib/rodauth/oauth/http_extensions.rb +15 -2
- data/lib/rodauth/oauth/ttl_store.rb +2 -0
- data/lib/rodauth/oauth/version.rb +1 -1
- data/locales/en.yml +3 -1
- data/locales/pt.yml +3 -1
- data/templates/authorize.str +17 -10
- metadata +5 -2
@@ -17,6 +17,7 @@ module Rodauth
|
|
17
17
|
issuer
|
18
18
|
authorization_endpoint
|
19
19
|
end_session_endpoint
|
20
|
+
backchannel_logout_session_supported
|
20
21
|
token_endpoint
|
21
22
|
userinfo_endpoint
|
22
23
|
jwks_uri
|
@@ -74,10 +75,9 @@ module Rodauth
|
|
74
75
|
auth_value_method :"oauth_applications_#{column}_column", column
|
75
76
|
end
|
76
77
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
auth_value_method :oauth_grants_acr_column, :acr
|
78
|
+
%i[nonce acr claims_locales claims].each do |column|
|
79
|
+
auth_value_method :"oauth_grants_#{column}_column", column
|
80
|
+
end
|
81
81
|
|
82
82
|
auth_value_method :oauth_jwt_subject_type, "public" # fallback subject type: public, pairwise
|
83
83
|
auth_value_method :oauth_jwt_subject_secret, nil # salt for pairwise generation
|
@@ -88,12 +88,10 @@ module Rodauth
|
|
88
88
|
auth_value_method :oauth_prompt_login_cookie_options, {}.freeze
|
89
89
|
auth_value_method :oauth_prompt_login_interval, 5 * 60 * 60 # 5 minutes
|
90
90
|
|
91
|
-
# logout
|
92
|
-
auth_value_method :oauth_applications_post_logout_redirect_uri_column, :post_logout_redirect_uri
|
93
|
-
auth_value_method :use_rp_initiated_logout?, false
|
94
|
-
|
95
91
|
auth_value_methods(
|
92
|
+
:oauth_acr_values_supported,
|
96
93
|
:get_oidc_account_last_login_at,
|
94
|
+
:oidc_authorize_on_prompt_none?,
|
97
95
|
:get_oidc_param,
|
98
96
|
:get_additional_param,
|
99
97
|
:require_acr_value_phr,
|
@@ -122,11 +120,28 @@ module Rodauth
|
|
122
120
|
|
123
121
|
oidc_claims = { "sub" => claims["sub"] }
|
124
122
|
|
125
|
-
fill_with_account_claims(oidc_claims, account, oauth_scopes)
|
126
|
-
|
127
123
|
@oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column => claims["client_id"]).first
|
128
124
|
|
129
|
-
|
125
|
+
throw_json_response_error(oauth_authorization_required_error_status, "invalid_token") unless @oauth_application
|
126
|
+
|
127
|
+
oauth_grant = valid_oauth_grant_ds(
|
128
|
+
oauth_grants_oauth_application_id_column => @oauth_application[oauth_applications_id_column],
|
129
|
+
oauth_grants_account_id_column => account[account_id_column]
|
130
|
+
).first
|
131
|
+
|
132
|
+
claims_locales = oauth_grant[oauth_grants_claims_locales_column] if oauth_grant
|
133
|
+
|
134
|
+
if (claims = oauth_grant[oauth_grants_claims_column])
|
135
|
+
claims = JSON.parse(claims)
|
136
|
+
if (userinfo_essential_claims = claims["userinfo"])
|
137
|
+
oauth_scopes |= userinfo_essential_claims.to_a
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# 5.4 - The Claims requested by the profile, email, address, and phone scope values are returned from the UserInfo Endpoint
|
142
|
+
fill_with_account_claims(oidc_claims, account, oauth_scopes, claims_locales)
|
143
|
+
|
144
|
+
if (algo = @oauth_application[oauth_applications_userinfo_signed_response_alg_column])
|
130
145
|
params = {
|
131
146
|
jwks: oauth_application_jwks(@oauth_application),
|
132
147
|
encryption_algorithm: @oauth_application[oauth_applications_userinfo_encrypted_response_alg_column],
|
@@ -134,7 +149,12 @@ module Rodauth
|
|
134
149
|
}.compact
|
135
150
|
|
136
151
|
jwt = jwt_encode(
|
137
|
-
oidc_claims
|
152
|
+
oidc_claims.merge(
|
153
|
+
# If signed, the UserInfo Response SHOULD contain the Claims iss (issuer) and aud (audience) as members. The iss value
|
154
|
+
# SHOULD be the OP's Issuer Identifier URL. The aud value SHOULD be or include the RP's Client ID value.
|
155
|
+
iss: oauth_jwt_issuer,
|
156
|
+
aud: @oauth_application[oauth_applications_client_id_column]
|
157
|
+
),
|
138
158
|
signing_algorithm: algo,
|
139
159
|
**params
|
140
160
|
)
|
@@ -148,81 +168,6 @@ module Rodauth
|
|
148
168
|
end
|
149
169
|
end
|
150
170
|
|
151
|
-
# /oidc-logout
|
152
|
-
auth_server_route(:oidc_logout) do |r|
|
153
|
-
next unless use_rp_initiated_logout?
|
154
|
-
|
155
|
-
require_authorizable_account
|
156
|
-
before_oidc_logout_route
|
157
|
-
|
158
|
-
# OpenID Providers MUST support the use of the HTTP GET and POST methods
|
159
|
-
r.on method: %i[get post] do
|
160
|
-
catch_error do
|
161
|
-
validate_oidc_logout_params
|
162
|
-
|
163
|
-
#
|
164
|
-
# why this is done:
|
165
|
-
#
|
166
|
-
# we need to decode the id token in order to get the application, because, if the
|
167
|
-
# signing key is application-specific, we don't know how to verify the signature
|
168
|
-
# beforehand. Hence, we have to do it twice: decode-and-do-not-verify, initialize
|
169
|
-
# the @oauth_application, and then decode-and-verify.
|
170
|
-
#
|
171
|
-
claims = jwt_decode(param("id_token_hint"), verify_claims: false)
|
172
|
-
oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column => claims["client_id"]).first
|
173
|
-
oauth_grant = db[oauth_grants_table]
|
174
|
-
.where(
|
175
|
-
oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column],
|
176
|
-
oauth_grants_account_id_column => account_id
|
177
|
-
).first
|
178
|
-
|
179
|
-
# check whether ID token belongs to currently logged-in user
|
180
|
-
redirect_response_error("invalid_request") unless oauth_grant && claims["sub"] == jwt_subject(
|
181
|
-
oauth_grant, oauth_application
|
182
|
-
)
|
183
|
-
|
184
|
-
# When an id_token_hint parameter is present, the OP MUST validate that it was the issuer of the ID Token.
|
185
|
-
redirect_response_error("invalid_request") unless claims && claims["iss"] == oauth_jwt_issuer
|
186
|
-
|
187
|
-
# now let's logout from IdP
|
188
|
-
transaction do
|
189
|
-
before_logout
|
190
|
-
logout
|
191
|
-
after_logout
|
192
|
-
end
|
193
|
-
|
194
|
-
if (post_logout_redirect_uri = param_or_nil("post_logout_redirect_uri"))
|
195
|
-
catch(:default_logout_redirect) do
|
196
|
-
oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column => claims["client_id"]).first
|
197
|
-
|
198
|
-
throw(:default_logout_redirect) unless oauth_application
|
199
|
-
|
200
|
-
post_logout_redirect_uris = oauth_application[oauth_applications_post_logout_redirect_uri_column].split(" ")
|
201
|
-
|
202
|
-
throw(:default_logout_redirect) unless post_logout_redirect_uris.include?(post_logout_redirect_uri)
|
203
|
-
|
204
|
-
if (state = param_or_nil("state"))
|
205
|
-
post_logout_redirect_uri = URI(post_logout_redirect_uri)
|
206
|
-
params = ["state=#{state}"]
|
207
|
-
params << post_logout_redirect_uri.query if post_logout_redirect_uri.query
|
208
|
-
post_logout_redirect_uri.query = params.join("&")
|
209
|
-
post_logout_redirect_uri = post_logout_redirect_uri.to_s
|
210
|
-
end
|
211
|
-
|
212
|
-
redirect(post_logout_redirect_uri)
|
213
|
-
end
|
214
|
-
|
215
|
-
end
|
216
|
-
|
217
|
-
# regular logout procedure
|
218
|
-
set_notice_flash(logout_notice_flash)
|
219
|
-
redirect(logout_redirect)
|
220
|
-
end
|
221
|
-
|
222
|
-
redirect_response_error("invalid_request")
|
223
|
-
end
|
224
|
-
end
|
225
|
-
|
226
171
|
def load_openid_configuration_route(alt_issuer = nil)
|
227
172
|
request.on(".well-known/openid-configuration") do
|
228
173
|
allow_cors(request)
|
@@ -260,7 +205,11 @@ module Rodauth
|
|
260
205
|
end
|
261
206
|
|
262
207
|
def oauth_response_types_supported
|
263
|
-
|
208
|
+
grant_types = oauth_grant_types_supported
|
209
|
+
oidc_response_types = %w[id_token none]
|
210
|
+
oidc_response_types |= ["code id_token"] if grant_types.include?("authorization_code")
|
211
|
+
oidc_response_types |= ["code token", "id_token token", "code id_token token"] if grant_types.include?("implicit")
|
212
|
+
super | oidc_response_types
|
264
213
|
end
|
265
214
|
|
266
215
|
def current_oauth_account
|
@@ -284,18 +233,62 @@ module Rodauth
|
|
284
233
|
end
|
285
234
|
end
|
286
235
|
|
236
|
+
def oauth_acr_values_supported
|
237
|
+
acr_values = []
|
238
|
+
acr_values << "phrh" if features.include?(:webauthn_login)
|
239
|
+
acr_values << "phr" if respond_to?(:require_two_factor_authenticated)
|
240
|
+
acr_values
|
241
|
+
end
|
242
|
+
|
243
|
+
def oidc_authorize_on_prompt_none?(_account)
|
244
|
+
false
|
245
|
+
end
|
246
|
+
|
287
247
|
def validate_authorize_params
|
288
|
-
|
248
|
+
if (max_age = param_or_nil("max_age"))
|
289
249
|
|
290
|
-
|
250
|
+
max_age = Integer(max_age)
|
291
251
|
|
292
|
-
|
252
|
+
redirect_response_error("invalid_request") unless max_age.positive?
|
293
253
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
254
|
+
if Time.now - get_oidc_account_last_login_at(session_value) > max_age
|
255
|
+
# force user to re-login
|
256
|
+
clear_session
|
257
|
+
set_session_value(login_redirect_session_key, request.fullpath)
|
258
|
+
redirect require_login_redirect
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
if (claims = param_or_nil("claims"))
|
263
|
+
# The value is a JSON object listing the requested Claims.
|
264
|
+
claims = JSON.parse(claims)
|
265
|
+
|
266
|
+
claims.each do |_, individual_claims|
|
267
|
+
redirect_response_error("invalid_request") unless individual_claims.is_a?(Hash)
|
268
|
+
|
269
|
+
individual_claims.each do |_, claim|
|
270
|
+
redirect_response_error("invalid_request") unless claim.nil? || individual_claims.is_a?(Hash)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
sc = scopes
|
276
|
+
|
277
|
+
if sc && sc.include?("offline_access")
|
278
|
+
|
279
|
+
sc.delete("offline_access")
|
280
|
+
|
281
|
+
# MUST ensure that the prompt parameter contains consent
|
282
|
+
# MUST ignore the offline_access request unless the Client
|
283
|
+
# is using a response_type value that would result in an
|
284
|
+
# Authorization Code
|
285
|
+
if param_or_nil("prompt") == "consent" && (
|
286
|
+
(response_type = param_or_nil("response_type")) && response_type.split(" ").include?("code")
|
287
|
+
)
|
288
|
+
request.params["access_type"] = "offline"
|
289
|
+
end
|
290
|
+
|
291
|
+
request.params["scope"] = sc.join(" ")
|
299
292
|
end
|
300
293
|
|
301
294
|
super
|
@@ -304,7 +297,7 @@ module Rodauth
|
|
304
297
|
def require_authorizable_account
|
305
298
|
try_prompt
|
306
299
|
super
|
307
|
-
try_acr_values
|
300
|
+
@acr = try_acr_values
|
308
301
|
end
|
309
302
|
|
310
303
|
def get_oidc_account_last_login_at(account_id)
|
@@ -348,22 +341,18 @@ module Rodauth
|
|
348
341
|
|
349
342
|
case prompt
|
350
343
|
when "none"
|
344
|
+
return unless request.get?
|
345
|
+
|
351
346
|
redirect_response_error("login_required") unless logged_in?
|
352
347
|
|
353
348
|
require_account
|
354
349
|
|
355
|
-
|
356
|
-
oauth_grants_account_id_column => account_id,
|
357
|
-
oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column],
|
358
|
-
oauth_grants_redirect_uri_column => redirect_uri,
|
359
|
-
oauth_grants_scopes_column => scopes.join(oauth_scope_separator),
|
360
|
-
oauth_grants_access_type_column => "online"
|
361
|
-
).count.zero?
|
362
|
-
redirect_response_error("consent_required")
|
363
|
-
end
|
350
|
+
redirect_response_error("interaction_required") unless oidc_authorize_on_prompt_none?(account_from_session)
|
364
351
|
|
365
352
|
request.env["REQUEST_METHOD"] = "POST"
|
366
353
|
when "login"
|
354
|
+
return unless request.get?
|
355
|
+
|
367
356
|
if logged_in? && request.cookies[oauth_prompt_login_cookie_key] == "login"
|
368
357
|
::Rack::Utils.delete_cookie_header!(response.headers, oauth_prompt_login_cookie_key, oauth_prompt_login_cookie_options)
|
369
358
|
return
|
@@ -380,18 +369,17 @@ module Rodauth
|
|
380
369
|
|
381
370
|
redirect require_login_redirect
|
382
371
|
when "consent"
|
372
|
+
return unless request.post?
|
373
|
+
|
383
374
|
require_account
|
384
375
|
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
oauth_grants_scopes_column => scopes.join(oauth_scope_separator),
|
390
|
-
oauth_grants_access_type_column => "online"
|
391
|
-
).count.zero?
|
392
|
-
redirect_response_error("consent_required")
|
393
|
-
end
|
376
|
+
sc = scopes || []
|
377
|
+
|
378
|
+
redirect_response_error("consent_required") if sc.empty?
|
379
|
+
|
394
380
|
when "select-account"
|
381
|
+
return unless request.get?
|
382
|
+
|
395
383
|
# only works if select_account plugin is available
|
396
384
|
require_select_account if respond_to?(:require_select_account)
|
397
385
|
else
|
@@ -403,44 +391,68 @@ module Rodauth
|
|
403
391
|
return unless (acr_values = param_or_nil("acr_values"))
|
404
392
|
|
405
393
|
acr_values.split(" ").each do |acr_value|
|
394
|
+
next unless oauth_acr_values_supported.include?(acr_value)
|
395
|
+
|
406
396
|
case acr_value
|
407
|
-
when "phr"
|
408
|
-
|
397
|
+
when "phr"
|
398
|
+
return acr_value if require_acr_value_phr
|
399
|
+
when "phrh"
|
400
|
+
return acr_value if require_acr_value_phrh
|
409
401
|
else
|
410
|
-
require_acr_value(acr_value)
|
402
|
+
return acr_value if require_acr_value(acr_value)
|
411
403
|
end
|
412
404
|
end
|
405
|
+
|
406
|
+
nil
|
413
407
|
end
|
414
408
|
|
415
409
|
def require_acr_value_phr
|
416
|
-
return unless respond_to?(:require_two_factor_authenticated)
|
410
|
+
return false unless respond_to?(:require_two_factor_authenticated)
|
417
411
|
|
418
412
|
require_two_factor_authenticated
|
413
|
+
true
|
419
414
|
end
|
420
415
|
|
421
416
|
def require_acr_value_phrh
|
417
|
+
return false unless features.include?(:webauthn_login)
|
418
|
+
|
422
419
|
require_acr_value_phr && two_factor_login_type_match?("webauthn")
|
423
420
|
end
|
424
421
|
|
425
|
-
def require_acr_value(_acr)
|
422
|
+
def require_acr_value(_acr)
|
423
|
+
true
|
424
|
+
end
|
426
425
|
|
427
426
|
def create_oauth_grant(create_params = {})
|
428
|
-
|
429
|
-
create_params[oauth_grants_nonce_column] = nonce
|
430
|
-
end
|
431
|
-
if (acr = param_or_nil("acr"))
|
432
|
-
create_params[oauth_grants_acr_column] = acr
|
433
|
-
end
|
427
|
+
create_params.replace(oidc_grant_params.merge(create_params))
|
434
428
|
super
|
435
429
|
end
|
436
430
|
|
431
|
+
def create_oauth_grant_with_token(create_params = {})
|
432
|
+
create_params[oauth_grants_type_column] = "hybrid"
|
433
|
+
create_params[oauth_grants_account_id_column] = account_id
|
434
|
+
create_params[oauth_grants_expires_in_column] = Sequel.date_add(Sequel::CURRENT_TIMESTAMP, seconds: oauth_access_token_expires_in)
|
435
|
+
authorization_code = create_oauth_grant(create_params)
|
436
|
+
access_token = if oauth_jwt_access_tokens
|
437
|
+
_generate_jwt_access_token(create_params)
|
438
|
+
else
|
439
|
+
oauth_grant = valid_oauth_grant_ds.where(oauth_grants_code_column => authorization_code).first
|
440
|
+
_generate_access_token(oauth_grant)
|
441
|
+
end
|
442
|
+
|
443
|
+
{
|
444
|
+
"code" => authorization_code,
|
445
|
+
**json_access_token_payload(oauth_grants_token_column => access_token)
|
446
|
+
}
|
447
|
+
end
|
448
|
+
|
437
449
|
def create_token(*)
|
438
450
|
oauth_grant = super
|
439
451
|
generate_id_token(oauth_grant)
|
440
452
|
oauth_grant
|
441
453
|
end
|
442
454
|
|
443
|
-
def generate_id_token(oauth_grant)
|
455
|
+
def generate_id_token(oauth_grant, include_claims = false)
|
444
456
|
oauth_scopes = oauth_grant[oauth_grants_scopes_column].split(oauth_scope_separator)
|
445
457
|
|
446
458
|
return unless oauth_scopes.include?("openid")
|
@@ -461,7 +473,18 @@ module Rodauth
|
|
461
473
|
# who just authorized its generation.
|
462
474
|
return unless account
|
463
475
|
|
464
|
-
|
476
|
+
if (claims = oauth_grant[oauth_grants_claims_column])
|
477
|
+
claims = JSON.parse(claims)
|
478
|
+
if (id_token_essential_claims = claims["id_token"])
|
479
|
+
oauth_scopes |= id_token_essential_claims.to_a
|
480
|
+
|
481
|
+
include_claims = true
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
# 5.4 - However, when no Access Token is issued (which is the case for the response_type value id_token),
|
486
|
+
# the resulting Claims are returned in the ID Token.
|
487
|
+
fill_with_account_claims(id_token_claims, account, oauth_scopes, param_or_nil("claims_locales")) if include_claims
|
465
488
|
|
466
489
|
params = {
|
467
490
|
jwks: oauth_application_jwks(oauth_application),
|
@@ -477,11 +500,31 @@ module Rodauth
|
|
477
500
|
end
|
478
501
|
|
479
502
|
# aka fill_with_standard_claims
|
480
|
-
def fill_with_account_claims(claims, account, scopes)
|
503
|
+
def fill_with_account_claims(claims, account, scopes, claims_locales)
|
504
|
+
additional_claims_info = {}
|
505
|
+
|
481
506
|
scopes_by_claim = scopes.each_with_object({}) do |scope, by_oidc|
|
482
507
|
next if scope == "openid"
|
483
508
|
|
484
|
-
|
509
|
+
if scope.is_a?(Array)
|
510
|
+
# essential claims
|
511
|
+
param, additional_info = scope
|
512
|
+
|
513
|
+
param = param.to_sym
|
514
|
+
|
515
|
+
oidc, = OIDC_SCOPES_MAP.find do |_, oidc_scopes|
|
516
|
+
oidc_scopes.include?(param)
|
517
|
+
end || param.to_s
|
518
|
+
|
519
|
+
param = nil if oidc == param.to_s
|
520
|
+
|
521
|
+
additional_claims_info[param] = additional_info
|
522
|
+
else
|
523
|
+
|
524
|
+
oidc, param = scope.split(".", 2)
|
525
|
+
|
526
|
+
param = param.to_sym if param
|
527
|
+
end
|
485
528
|
|
486
529
|
by_oidc[oidc] ||= []
|
487
530
|
|
@@ -490,13 +533,11 @@ module Rodauth
|
|
490
533
|
|
491
534
|
oidc_scopes, additional_scopes = scopes_by_claim.keys.partition { |key| OIDC_SCOPES_MAP.key?(key) }
|
492
535
|
|
493
|
-
|
494
|
-
claims_locales = claims_locales.split(" ").map(&:to_sym)
|
495
|
-
end
|
536
|
+
claims_locales = claims_locales.split(" ").map(&:to_sym) if claims_locales
|
496
537
|
|
497
538
|
unless oidc_scopes.empty?
|
498
539
|
if respond_to?(:get_oidc_param)
|
499
|
-
get_oidc_param = proxy_get_param(:get_oidc_param, claims, claims_locales)
|
540
|
+
get_oidc_param = proxy_get_param(:get_oidc_param, claims, claims_locales, additional_claims_info)
|
500
541
|
|
501
542
|
oidc_scopes.each do |scope|
|
502
543
|
scope_claims = claims
|
@@ -517,7 +558,7 @@ module Rodauth
|
|
517
558
|
return if additional_scopes.empty?
|
518
559
|
|
519
560
|
if respond_to?(:get_additional_param)
|
520
|
-
get_additional_param = proxy_get_param(:get_additional_param, claims, claims_locales)
|
561
|
+
get_additional_param = proxy_get_param(:get_additional_param, claims, claims_locales, additional_claims_info)
|
521
562
|
|
522
563
|
additional_scopes.each do |scope|
|
523
564
|
get_additional_param[account, scope.to_sym]
|
@@ -527,23 +568,36 @@ module Rodauth
|
|
527
568
|
end
|
528
569
|
end
|
529
570
|
|
530
|
-
def proxy_get_param(get_param_func, claims, claims_locales)
|
571
|
+
def proxy_get_param(get_param_func, claims, claims_locales, additional_claims_info)
|
531
572
|
meth = method(get_param_func)
|
532
573
|
if meth.arity == 2
|
533
|
-
|
574
|
+
lambda do |account, param, cl = claims|
|
575
|
+
additional_info = additional_claims_info[param] || EMPTY_HASH
|
576
|
+
value = additional_info["value"] || meth[account, param]
|
577
|
+
value = nil if additional_info["values"] && additional_info["values"].include?(value)
|
578
|
+
cl[param] = value if value
|
579
|
+
end
|
534
580
|
elsif claims_locales.nil?
|
535
|
-
|
581
|
+
lambda do |account, param, cl = claims|
|
582
|
+
additional_info = additional_claims_info[param] || EMPTY_HASH
|
583
|
+
value = additional_info["value"] || meth[account, param, nil]
|
584
|
+
value = nil if additional_info["values"] && additional_info["values"].include?(value)
|
585
|
+
cl[param] = value if value
|
586
|
+
end
|
536
587
|
else
|
537
588
|
lambda do |account, param, cl = claims|
|
538
589
|
claims_values = claims_locales.map do |locale|
|
539
|
-
|
540
|
-
|
590
|
+
additional_info = additional_claims_info[param] || EMPTY_HASH
|
591
|
+
value = additional_info["value"] || meth[account, param, locale]
|
592
|
+
value = nil if additional_info["values"] && additional_info["values"].include?(value)
|
593
|
+
value
|
594
|
+
end.compact
|
541
595
|
|
542
596
|
if claims_values.uniq.size == 1
|
543
597
|
cl[param] = claims_values.first
|
544
598
|
else
|
545
599
|
claims_locales.zip(claims_values).each do |locale, value|
|
546
|
-
cl["#{param}##{locale}"] = value
|
600
|
+
cl["#{param}##{locale}"] = value if value
|
547
601
|
end
|
548
602
|
end
|
549
603
|
end
|
@@ -580,23 +634,39 @@ module Rodauth
|
|
580
634
|
end
|
581
635
|
|
582
636
|
def do_authorize(response_params = {}, response_mode = param_or_nil("response_mode"))
|
583
|
-
|
637
|
+
response_type = param("response_type")
|
638
|
+
case response_type
|
584
639
|
when "id_token"
|
585
|
-
|
640
|
+
grant_params = oidc_grant_params
|
641
|
+
generate_id_token(grant_params, true)
|
642
|
+
response_params.replace("id_token" => grant_params[:id_token])
|
586
643
|
when "code token"
|
587
644
|
redirect_response_error("invalid_request") unless supports_token_response_type?
|
588
645
|
|
589
|
-
response_params.replace(
|
646
|
+
response_params.replace(create_oauth_grant_with_token)
|
590
647
|
when "code id_token"
|
591
|
-
|
648
|
+
params = _do_authorize_code
|
649
|
+
oauth_grant = valid_oauth_grant_ds.where(oauth_grants_code_column => params["code"]).first
|
650
|
+
generate_id_token(oauth_grant)
|
651
|
+
response_params.replace(
|
652
|
+
"id_token" => oauth_grant[:id_token],
|
653
|
+
"code" => params["code"]
|
654
|
+
)
|
592
655
|
when "id_token token"
|
593
656
|
redirect_response_error("invalid_request") unless supports_token_response_type?
|
594
657
|
|
595
|
-
|
658
|
+
oauth_grant = _do_authorize_token(oauth_grants_type_column => "hybrid")
|
659
|
+
generate_id_token(oauth_grant)
|
660
|
+
|
661
|
+
response_params.replace(json_access_token_payload(oauth_grant))
|
596
662
|
when "code id_token token"
|
597
663
|
redirect_response_error("invalid_request") unless supports_token_response_type?
|
598
664
|
|
599
|
-
|
665
|
+
params = create_oauth_grant_with_token
|
666
|
+
oauth_grant = valid_oauth_grant_ds.where(oauth_grants_code_column => params["code"]).first
|
667
|
+
generate_id_token(oauth_grant)
|
668
|
+
|
669
|
+
response_params.replace(params.merge("id_token" => oauth_grant[:id_token]))
|
600
670
|
when "none"
|
601
671
|
response_mode ||= "none"
|
602
672
|
end
|
@@ -605,23 +675,23 @@ module Rodauth
|
|
605
675
|
super(response_params, response_mode)
|
606
676
|
end
|
607
677
|
|
608
|
-
def
|
678
|
+
def oidc_grant_params
|
609
679
|
grant_params = {
|
610
680
|
oauth_grants_account_id_column => account_id,
|
611
681
|
oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column],
|
612
|
-
oauth_grants_scopes_column => scopes.join(
|
682
|
+
oauth_grants_scopes_column => scopes.join(oauth_scope_separator)
|
613
683
|
}
|
614
684
|
if (nonce = param_or_nil("nonce"))
|
615
685
|
grant_params[oauth_grants_nonce_column] = nonce
|
616
686
|
end
|
617
|
-
|
618
|
-
|
687
|
+
grant_params[oauth_grants_acr_column] = @acr if @acr
|
688
|
+
if (claims_locales = param_or_nil("claims_locales"))
|
689
|
+
grant_params[oauth_grants_claims_locales_column] = claims_locales
|
690
|
+
end
|
691
|
+
if (claims = param_or_nil("claims"))
|
692
|
+
grant_params[oauth_grants_claims_column] = claims
|
619
693
|
end
|
620
|
-
|
621
|
-
generate_id_token(oauth_grant)
|
622
|
-
params = json_access_token_payload(oauth_grant)
|
623
|
-
params.delete("access_token")
|
624
|
-
params
|
694
|
+
grant_params
|
625
695
|
end
|
626
696
|
|
627
697
|
def authorize_response(params, mode)
|
@@ -630,18 +700,6 @@ module Rodauth
|
|
630
700
|
super
|
631
701
|
end
|
632
702
|
|
633
|
-
# Logout
|
634
|
-
|
635
|
-
def validate_oidc_logout_params
|
636
|
-
redirect_response_error("invalid_request") unless param_or_nil("id_token_hint")
|
637
|
-
# check if valid token hint type
|
638
|
-
return unless (redirect_uri = param_or_nil("post_logout_redirect_uri"))
|
639
|
-
|
640
|
-
return if check_valid_uri?(redirect_uri)
|
641
|
-
|
642
|
-
redirect_response_error("invalid_request")
|
643
|
-
end
|
644
|
-
|
645
703
|
# Webfinger
|
646
704
|
|
647
705
|
def json_webfinger_payload
|
@@ -673,25 +731,15 @@ module Rodauth
|
|
673
731
|
|
674
732
|
scope_claims.unshift("auth_time")
|
675
733
|
|
676
|
-
response_types_supported = metadata[:response_types_supported]
|
677
|
-
|
678
|
-
response_types_supported |= %w[none]
|
679
|
-
response_types_supported |= ["code id_token"] if metadata[:grant_types_supported].include?("authorization_code")
|
680
|
-
if metadata[:grant_types_supported].include?("implicit")
|
681
|
-
response_types_supported |= ["code token", "id_token token", "code id_token token"]
|
682
|
-
end
|
683
|
-
|
684
|
-
alg_values, enc_values = oauth_jwt_jwe_keys.keys.transpose
|
685
|
-
|
686
734
|
metadata.merge(
|
687
735
|
userinfo_endpoint: userinfo_url,
|
688
|
-
end_session_endpoint: (oidc_logout_url if use_rp_initiated_logout?),
|
689
|
-
response_types_supported: response_types_supported,
|
690
736
|
subject_types_supported: %w[public pairwise],
|
737
|
+
acr_values_supported: oauth_acr_values_supported,
|
738
|
+
claims_parameter_supported: true,
|
691
739
|
|
692
|
-
id_token_signing_alg_values_supported:
|
693
|
-
id_token_encryption_alg_values_supported:
|
694
|
-
id_token_encryption_enc_values_supported:
|
740
|
+
id_token_signing_alg_values_supported: oauth_jwt_jws_algorithms_supported,
|
741
|
+
id_token_encryption_alg_values_supported: oauth_jwt_jwe_algorithms_supported,
|
742
|
+
id_token_encryption_enc_values_supported: oauth_jwt_jwe_encryption_methods_supported,
|
695
743
|
|
696
744
|
userinfo_signing_alg_values_supported: oauth_jwt_jws_algorithms_supported,
|
697
745
|
userinfo_encryption_alg_values_supported: oauth_jwt_jwe_algorithms_supported,
|