rodauth-oauth 0.10.4 → 1.0.0.pre.beta1
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/MIGRATION-GUIDE-v1.md +286 -0
- data/README.md +22 -30
- data/doc/release_notes/1_0_0_beta1.md +38 -0
- data/lib/generators/rodauth/oauth/install_generator.rb +0 -1
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize.html.erb +4 -6
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/device_search.html.erb +1 -1
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/device_verification.html.erb +2 -2
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/new_oauth_application.html.erb +1 -6
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application.html.erb +0 -2
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application_oauth_grants.html.erb +41 -0
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_applications.html.erb +2 -2
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_grants.html.erb +37 -0
- data/lib/generators/rodauth/oauth/templates/db/migrate/create_rodauth_oauth.rb +18 -29
- data/lib/rodauth/features/oauth_application_management.rb +59 -72
- data/lib/rodauth/features/oauth_assertion_base.rb +19 -23
- data/lib/rodauth/features/oauth_authorization_code_grant.rb +35 -88
- data/lib/rodauth/features/oauth_authorize_base.rb +103 -20
- data/lib/rodauth/features/oauth_base.rb +365 -302
- data/lib/rodauth/features/oauth_client_credentials_grant.rb +20 -18
- data/lib/rodauth/features/{oauth_device_grant.rb → oauth_device_code_grant.rb} +62 -73
- data/lib/rodauth/features/oauth_dynamic_client_registration.rb +46 -28
- data/lib/rodauth/features/oauth_grant_management.rb +70 -0
- data/lib/rodauth/features/oauth_implicit_grant.rb +25 -24
- data/lib/rodauth/features/oauth_jwt.rb +52 -688
- data/lib/rodauth/features/oauth_jwt_base.rb +435 -0
- data/lib/rodauth/features/oauth_jwt_bearer_grant.rb +45 -17
- data/lib/rodauth/features/oauth_jwt_jwks.rb +47 -0
- data/lib/rodauth/features/oauth_jwt_secured_authorization_request.rb +62 -0
- data/lib/rodauth/features/oauth_management_base.rb +2 -0
- data/lib/rodauth/features/oauth_pkce.rb +22 -26
- data/lib/rodauth/features/oauth_resource_indicators.rb +33 -21
- data/lib/rodauth/features/oauth_resource_server.rb +59 -0
- data/lib/rodauth/features/oauth_saml_bearer_grant.rb +5 -1
- data/lib/rodauth/features/oauth_token_introspection.rb +76 -46
- data/lib/rodauth/features/oauth_token_revocation.rb +46 -33
- data/lib/rodauth/features/oidc.rb +188 -95
- data/lib/rodauth/features/oidc_dynamic_client_registration.rb +89 -53
- data/lib/rodauth/oauth/database_extensions.rb +8 -6
- data/lib/rodauth/oauth/http_extensions.rb +61 -0
- data/lib/rodauth/oauth/railtie.rb +20 -0
- data/lib/rodauth/oauth/version.rb +1 -1
- data/lib/rodauth/oauth.rb +29 -1
- data/locales/en.yml +32 -22
- data/locales/pt.yml +32 -22
- data/templates/authorize.str +19 -24
- data/templates/device_search.str +1 -1
- data/templates/device_verification.str +2 -2
- data/templates/jwks_field.str +1 -0
- data/templates/new_oauth_application.str +1 -2
- data/templates/oauth_application.str +2 -2
- data/templates/oauth_application_oauth_grants.str +54 -0
- data/templates/oauth_applications.str +2 -2
- data/templates/oauth_grants.str +52 -0
- metadata +20 -16
- data/lib/generators/rodauth/oauth/templates/app/models/oauth_token.rb +0 -4
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application_oauth_tokens.html.erb +0 -39
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_tokens.html.erb +0 -35
- data/lib/rodauth/features/oauth.rb +0 -9
- data/lib/rodauth/features/oauth_http_mac.rb +0 -86
- data/lib/rodauth/features/oauth_token_management.rb +0 -81
- data/lib/rodauth/oauth/refinements.rb +0 -48
- data/templates/jwt_public_key_field.str +0 -4
- data/templates/oauth_application_oauth_tokens.str +0 -52
- data/templates/oauth_tokens.str +0 -50
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "rodauth/oauth"
|
4
|
+
|
3
5
|
module Rodauth
|
4
6
|
Feature.define(:oidc, :Oidc) do
|
5
7
|
# https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
|
@@ -60,26 +62,27 @@ module Rodauth
|
|
60
62
|
id_token_signing_alg_values_supported
|
61
63
|
].freeze
|
62
64
|
|
63
|
-
depends :account_expiration, :oauth_jwt
|
65
|
+
depends :account_expiration, :oauth_jwt, :oauth_jwt_jwks, :oauth_authorization_code_grant
|
64
66
|
|
65
|
-
auth_value_method :oauth_application_default_scope, "openid"
|
66
67
|
auth_value_method :oauth_application_scopes, %w[openid]
|
67
68
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
69
|
+
%i[
|
70
|
+
subject_type application_type sector_identifier_uri
|
71
|
+
id_token_signed_response_alg id_token_encrypted_response_alg id_token_encrypted_response_enc
|
72
|
+
userinfo_signed_response_alg userinfo_encrypted_response_alg userinfo_encrypted_response_enc
|
73
|
+
].each do |column|
|
74
|
+
auth_value_method :"oauth_applications_#{column}_column", column
|
75
|
+
end
|
74
76
|
|
75
77
|
auth_value_method :oauth_grants_nonce_column, :nonce
|
76
78
|
auth_value_method :oauth_grants_acr_column, :acr
|
77
|
-
auth_value_method :
|
78
|
-
auth_value_method :
|
79
|
+
auth_value_method :oauth_grants_nonce_column, :nonce
|
80
|
+
auth_value_method :oauth_grants_acr_column, :acr
|
79
81
|
|
80
|
-
|
82
|
+
auth_value_method :oauth_jwt_subject_type, "public" # fallback subject type: public, pairwise
|
83
|
+
auth_value_method :oauth_jwt_subject_secret, nil # salt for pairwise generation
|
81
84
|
|
82
|
-
|
85
|
+
translatable_method :oauth_invalid_scope_message, "The Access Token expired"
|
83
86
|
|
84
87
|
auth_value_method :oauth_prompt_login_cookie_key, "_rodauth_oauth_prompt_login"
|
85
88
|
auth_value_method :oauth_prompt_login_cookie_options, {}.freeze
|
@@ -90,42 +93,42 @@ module Rodauth
|
|
90
93
|
auth_value_method :use_rp_initiated_logout?, false
|
91
94
|
|
92
95
|
auth_value_methods(
|
96
|
+
:get_oidc_account_last_login_at,
|
93
97
|
:get_oidc_param,
|
94
98
|
:get_additional_param,
|
95
99
|
:require_acr_value_phr,
|
96
100
|
:require_acr_value_phrh,
|
97
|
-
:require_acr_value
|
101
|
+
:require_acr_value,
|
102
|
+
:json_webfinger_payload
|
98
103
|
)
|
99
104
|
|
100
105
|
# /userinfo
|
101
|
-
|
102
|
-
next unless is_authorization_server?
|
103
|
-
|
106
|
+
auth_server_route(:userinfo) do |r|
|
104
107
|
r.on method: %i[get post] do
|
105
108
|
catch_error do
|
106
|
-
|
109
|
+
claims = authorization_token
|
107
110
|
|
108
|
-
throw_json_response_error(
|
111
|
+
throw_json_response_error(oauth_authorization_required_error_status, "invalid_token") unless claims
|
109
112
|
|
110
|
-
oauth_scopes =
|
113
|
+
oauth_scopes = claims["scope"].split(" ")
|
111
114
|
|
112
|
-
throw_json_response_error(
|
115
|
+
throw_json_response_error(oauth_authorization_required_error_status, "invalid_token") unless oauth_scopes.include?("openid")
|
113
116
|
|
114
|
-
account = db[accounts_table].where(account_id_column =>
|
117
|
+
account = db[accounts_table].where(account_id_column => claims["sub"]).first
|
115
118
|
|
116
|
-
throw_json_response_error(
|
119
|
+
throw_json_response_error(oauth_authorization_required_error_status, "invalid_token") unless account
|
117
120
|
|
118
121
|
oauth_scopes.delete("openid")
|
119
122
|
|
120
|
-
oidc_claims = { "sub" =>
|
123
|
+
oidc_claims = { "sub" => claims["sub"] }
|
121
124
|
|
122
125
|
fill_with_account_claims(oidc_claims, account, oauth_scopes)
|
123
126
|
|
124
|
-
@oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column =>
|
127
|
+
@oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column => claims["client_id"]).first
|
125
128
|
|
126
129
|
if (algo = @oauth_application && @oauth_application[oauth_applications_userinfo_signed_response_alg_column])
|
127
130
|
params = {
|
128
|
-
jwks: oauth_application_jwks,
|
131
|
+
jwks: oauth_application_jwks(@oauth_application),
|
129
132
|
encryption_algorithm: @oauth_application[oauth_applications_userinfo_encrypted_response_alg_column],
|
130
133
|
encryption_method: @oauth_application[oauth_applications_userinfo_encrypted_response_enc_column]
|
131
134
|
}.compact
|
@@ -141,16 +144,16 @@ module Rodauth
|
|
141
144
|
end
|
142
145
|
end
|
143
146
|
|
144
|
-
throw_json_response_error(
|
147
|
+
throw_json_response_error(oauth_authorization_required_error_status, "invalid_token")
|
145
148
|
end
|
146
149
|
end
|
147
150
|
|
148
151
|
# /oidc-logout
|
149
|
-
|
152
|
+
auth_server_route(:oidc_logout) do |r|
|
150
153
|
next unless use_rp_initiated_logout?
|
151
154
|
|
152
|
-
before_oidc_logout_route
|
153
155
|
require_authorizable_account
|
156
|
+
before_oidc_logout_route
|
154
157
|
|
155
158
|
# OpenID Providers MUST support the use of the HTTP GET and POST methods
|
156
159
|
r.on method: %i[get post] do
|
@@ -165,17 +168,21 @@ module Rodauth
|
|
165
168
|
# beforehand. Hence, we have to do it twice: decode-and-do-not-verify, initialize
|
166
169
|
# the @oauth_application, and then decode-and-verify.
|
167
170
|
#
|
168
|
-
|
169
|
-
|
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
|
170
178
|
|
171
179
|
# check whether ID token belongs to currently logged-in user
|
172
|
-
redirect_response_error("invalid_request") unless
|
173
|
-
|
174
|
-
oauth_tokens_oauth_application_id_column => oauth_application_id
|
180
|
+
redirect_response_error("invalid_request") unless oauth_grant && claims["sub"] == jwt_subject(
|
181
|
+
oauth_grant, oauth_application
|
175
182
|
)
|
176
183
|
|
177
184
|
# When an id_token_hint parameter is present, the OP MUST validate that it was the issuer of the ID Token.
|
178
|
-
redirect_response_error("invalid_request") unless
|
185
|
+
redirect_response_error("invalid_request") unless claims && claims["iss"] == oauth_jwt_issuer
|
179
186
|
|
180
187
|
# now let's logout from IdP
|
181
188
|
transaction do
|
@@ -186,7 +193,7 @@ module Rodauth
|
|
186
193
|
|
187
194
|
if (post_logout_redirect_uri = param_or_nil("post_logout_redirect_uri"))
|
188
195
|
catch(:default_logout_redirect) do
|
189
|
-
oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column =>
|
196
|
+
oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column => claims["client_id"]).first
|
190
197
|
|
191
198
|
throw(:default_logout_redirect) unless oauth_application
|
192
199
|
|
@@ -216,17 +223,19 @@ module Rodauth
|
|
216
223
|
end
|
217
224
|
end
|
218
225
|
|
219
|
-
def
|
226
|
+
def load_openid_configuration_route(alt_issuer = nil)
|
220
227
|
request.on(".well-known/openid-configuration") do
|
221
228
|
allow_cors(request)
|
222
229
|
|
223
|
-
request.
|
224
|
-
|
230
|
+
request.is do
|
231
|
+
request.get do
|
232
|
+
json_response_success(openid_configuration_body(alt_issuer), cache: true)
|
233
|
+
end
|
225
234
|
end
|
226
235
|
end
|
227
236
|
end
|
228
237
|
|
229
|
-
def
|
238
|
+
def load_webfinger_route
|
230
239
|
request.on(".well-known/webfinger") do
|
231
240
|
request.get do
|
232
241
|
resource = param_or_nil("resource")
|
@@ -236,14 +245,7 @@ module Rodauth
|
|
236
245
|
response.status = 200
|
237
246
|
response["Content-Type"] ||= "application/jrd+json"
|
238
247
|
|
239
|
-
|
240
|
-
subject: resource,
|
241
|
-
links: [{
|
242
|
-
rel: webfinger_relation,
|
243
|
-
href: authorization_server_url
|
244
|
-
}]
|
245
|
-
})
|
246
|
-
return_response(json_payload)
|
248
|
+
return_response(json_webfinger_payload)
|
247
249
|
end
|
248
250
|
end
|
249
251
|
end
|
@@ -257,6 +259,16 @@ module Rodauth
|
|
257
259
|
end
|
258
260
|
end
|
259
261
|
|
262
|
+
def oauth_response_types_supported
|
263
|
+
super | %w[id_token none]
|
264
|
+
end
|
265
|
+
|
266
|
+
def current_oauth_account
|
267
|
+
subject_type = current_oauth_application[oauth_applications_subject_type_column] || oauth_jwt_subject_type
|
268
|
+
|
269
|
+
return super unless subject_type == "pairwise"
|
270
|
+
end
|
271
|
+
|
260
272
|
private
|
261
273
|
|
262
274
|
if defined?(::I18n)
|
@@ -279,7 +291,7 @@ module Rodauth
|
|
279
291
|
|
280
292
|
redirect_response_error("invalid_request") unless max_age.positive?
|
281
293
|
|
282
|
-
if Time.now -
|
294
|
+
if Time.now - get_oidc_account_last_login_at(session_value) > max_age
|
283
295
|
# force user to re-login
|
284
296
|
clear_session
|
285
297
|
set_session_value(login_redirect_session_key, request.fullpath)
|
@@ -295,6 +307,41 @@ module Rodauth
|
|
295
307
|
try_acr_values
|
296
308
|
end
|
297
309
|
|
310
|
+
def get_oidc_account_last_login_at(account_id)
|
311
|
+
get_activity_timestamp(account_id, account_activity_last_activity_column)
|
312
|
+
end
|
313
|
+
|
314
|
+
def jwt_subject(oauth_grant, client_application = oauth_application)
|
315
|
+
subject_type = client_application[oauth_applications_subject_type_column] || oauth_jwt_subject_type
|
316
|
+
|
317
|
+
case subject_type
|
318
|
+
when "public"
|
319
|
+
super
|
320
|
+
when "pairwise"
|
321
|
+
identifier_uri = client_application[oauth_applications_sector_identifier_uri_column]
|
322
|
+
|
323
|
+
unless identifier_uri
|
324
|
+
identifier_uri = client_application[oauth_applications_redirect_uri_column]
|
325
|
+
identifier_uri = identifier_uri.split(" ")
|
326
|
+
# If the Client has not provided a value for sector_identifier_uri in Dynamic Client Registration
|
327
|
+
# [OpenID.Registration], the Sector Identifier used for pairwise identifier calculation is the host
|
328
|
+
# component of the registered redirect_uri. If there are multiple hostnames in the registered redirect_uris,
|
329
|
+
# the Client MUST register a sector_identifier_uri.
|
330
|
+
if identifier_uri.size > 1
|
331
|
+
# return error message
|
332
|
+
end
|
333
|
+
identifier_uri = identifier_uri.first
|
334
|
+
end
|
335
|
+
|
336
|
+
identifier_uri = URI(identifier_uri).host
|
337
|
+
|
338
|
+
account_id = oauth_grant[oauth_grants_account_id_column]
|
339
|
+
Digest::SHA256.hexdigest("#{identifier_uri}#{account_id}#{oauth_jwt_subject_secret}")
|
340
|
+
else
|
341
|
+
raise StandardError, "unexpected subject (#{subject_type})"
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
298
345
|
# this executes before checking for a logged in account
|
299
346
|
def try_prompt
|
300
347
|
return unless (prompt = param_or_nil("prompt"))
|
@@ -387,34 +434,27 @@ module Rodauth
|
|
387
434
|
super
|
388
435
|
end
|
389
436
|
|
390
|
-
def
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
super
|
395
|
-
end
|
396
|
-
|
397
|
-
def create_oauth_token(*)
|
398
|
-
oauth_token = super
|
399
|
-
generate_id_token(oauth_token)
|
400
|
-
oauth_token
|
437
|
+
def create_token(*)
|
438
|
+
oauth_grant = super
|
439
|
+
generate_id_token(oauth_grant)
|
440
|
+
oauth_grant
|
401
441
|
end
|
402
442
|
|
403
|
-
def generate_id_token(
|
404
|
-
oauth_scopes =
|
443
|
+
def generate_id_token(oauth_grant)
|
444
|
+
oauth_scopes = oauth_grant[oauth_grants_scopes_column].split(oauth_scope_separator)
|
405
445
|
|
406
446
|
return unless oauth_scopes.include?("openid")
|
407
447
|
|
408
|
-
id_token_claims = jwt_claims(
|
448
|
+
id_token_claims = jwt_claims(oauth_grant)
|
409
449
|
|
410
|
-
id_token_claims[:nonce] =
|
450
|
+
id_token_claims[:nonce] = oauth_grant[oauth_grants_nonce_column] if oauth_grant[oauth_grants_nonce_column]
|
411
451
|
|
412
|
-
id_token_claims[:acr] =
|
452
|
+
id_token_claims[:acr] = oauth_grant[oauth_grants_acr_column] if oauth_grant[oauth_grants_acr_column]
|
413
453
|
|
414
454
|
# Time when the End-User authentication occurred.
|
415
|
-
id_token_claims[:auth_time] =
|
455
|
+
id_token_claims[:auth_time] = get_oidc_account_last_login_at(oauth_grant[oauth_grants_account_id_column]).to_i
|
416
456
|
|
417
|
-
account = db[accounts_table].where(account_id_column =>
|
457
|
+
account = db[accounts_table].where(account_id_column => oauth_grant[oauth_grants_account_id_column]).first
|
418
458
|
|
419
459
|
# this should never happen!
|
420
460
|
# a newly minted oauth token from a grant should have been assigned to an account
|
@@ -424,13 +464,16 @@ module Rodauth
|
|
424
464
|
fill_with_account_claims(id_token_claims, account, oauth_scopes)
|
425
465
|
|
426
466
|
params = {
|
427
|
-
jwks: oauth_application_jwks,
|
428
|
-
signing_algorithm:
|
467
|
+
jwks: oauth_application_jwks(oauth_application),
|
468
|
+
signing_algorithm: (
|
469
|
+
oauth_application[oauth_applications_id_token_signed_response_alg_column] ||
|
470
|
+
oauth_jwt_keys.keys.first
|
471
|
+
),
|
429
472
|
encryption_algorithm: oauth_application[oauth_applications_id_token_encrypted_response_alg_column],
|
430
473
|
encryption_method: oauth_application[oauth_applications_id_token_encrypted_response_enc_column]
|
431
474
|
}.compact
|
432
475
|
|
433
|
-
|
476
|
+
oauth_grant[:id_token] = jwt_encode(id_token_claims, **params)
|
434
477
|
end
|
435
478
|
|
436
479
|
# aka fill_with_standard_claims
|
@@ -507,9 +550,9 @@ module Rodauth
|
|
507
550
|
end
|
508
551
|
end
|
509
552
|
|
510
|
-
def json_access_token_payload(
|
553
|
+
def json_access_token_payload(oauth_grant)
|
511
554
|
payload = super
|
512
|
-
payload["id_token"] =
|
555
|
+
payload["id_token"] = oauth_grant[:id_token] if oauth_grant[:id_token]
|
513
556
|
payload
|
514
557
|
end
|
515
558
|
|
@@ -517,31 +560,45 @@ module Rodauth
|
|
517
560
|
|
518
561
|
def check_valid_response_type?
|
519
562
|
case param_or_nil("response_type")
|
520
|
-
when "none", "id_token",
|
521
|
-
"code token", "code id_token", "id_token token", "code id_token token" # multiple
|
563
|
+
when "none", "id_token", "code id_token" # multiple
|
522
564
|
true
|
565
|
+
when "code token", "id_token token", "code id_token token"
|
566
|
+
supports_token_response_type?
|
523
567
|
else
|
524
568
|
super
|
525
569
|
end
|
526
570
|
end
|
527
571
|
|
528
|
-
def
|
529
|
-
return super unless
|
572
|
+
def supported_response_mode?(response_mode, *)
|
573
|
+
return super unless response_mode == "none"
|
574
|
+
|
575
|
+
param("response_type") == "none"
|
576
|
+
end
|
530
577
|
|
578
|
+
def supports_token_response_type?
|
579
|
+
features.include?(:oauth_implicit_grant)
|
580
|
+
end
|
581
|
+
|
582
|
+
def do_authorize(response_params = {}, response_mode = param_or_nil("response_mode"))
|
531
583
|
case param("response_type")
|
532
584
|
when "id_token"
|
533
585
|
response_params.replace(_do_authorize_id_token)
|
534
586
|
when "code token"
|
535
|
-
redirect_response_error("invalid_request") unless
|
587
|
+
redirect_response_error("invalid_request") unless supports_token_response_type?
|
536
588
|
|
537
589
|
response_params.replace(_do_authorize_code.merge(_do_authorize_token))
|
538
590
|
when "code id_token"
|
539
591
|
response_params.replace(_do_authorize_code.merge(_do_authorize_id_token))
|
540
592
|
when "id_token token"
|
593
|
+
redirect_response_error("invalid_request") unless supports_token_response_type?
|
594
|
+
|
541
595
|
response_params.replace(_do_authorize_id_token.merge(_do_authorize_token))
|
542
596
|
when "code id_token token"
|
597
|
+
redirect_response_error("invalid_request") unless supports_token_response_type?
|
543
598
|
|
544
599
|
response_params.replace(_do_authorize_code.merge(_do_authorize_id_token).merge(_do_authorize_token))
|
600
|
+
when "none"
|
601
|
+
response_mode ||= "none"
|
545
602
|
end
|
546
603
|
response_mode ||= "fragment" unless response_params.empty?
|
547
604
|
|
@@ -549,24 +606,30 @@ module Rodauth
|
|
549
606
|
end
|
550
607
|
|
551
608
|
def _do_authorize_id_token
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
609
|
+
grant_params = {
|
610
|
+
oauth_grants_account_id_column => account_id,
|
611
|
+
oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column],
|
612
|
+
oauth_grants_scopes_column => scopes.join(" ")
|
556
613
|
}
|
557
614
|
if (nonce = param_or_nil("nonce"))
|
558
|
-
|
615
|
+
grant_params[oauth_grants_nonce_column] = nonce
|
559
616
|
end
|
560
617
|
if (acr = param_or_nil("acr"))
|
561
|
-
|
618
|
+
grant_params[oauth_grants_acr_column] = acr
|
562
619
|
end
|
563
|
-
|
564
|
-
generate_id_token(
|
565
|
-
params = json_access_token_payload(
|
620
|
+
oauth_grant = generate_token(grant_params, false)
|
621
|
+
generate_id_token(oauth_grant)
|
622
|
+
params = json_access_token_payload(oauth_grant)
|
566
623
|
params.delete("access_token")
|
567
624
|
params
|
568
625
|
end
|
569
626
|
|
627
|
+
def authorize_response(params, mode)
|
628
|
+
redirect_url = URI.parse(redirect_uri)
|
629
|
+
redirect(redirect_url.to_s) if mode == "none"
|
630
|
+
super
|
631
|
+
end
|
632
|
+
|
570
633
|
# Logout
|
571
634
|
|
572
635
|
def validate_oidc_logout_params
|
@@ -579,6 +642,18 @@ module Rodauth
|
|
579
642
|
redirect_response_error("invalid_request")
|
580
643
|
end
|
581
644
|
|
645
|
+
# Webfinger
|
646
|
+
|
647
|
+
def json_webfinger_payload
|
648
|
+
JSON.dump({
|
649
|
+
subject: param("resource"),
|
650
|
+
links: [{
|
651
|
+
rel: "http://openid.net/specs/connect/1.0/issuer",
|
652
|
+
href: authorization_server_url
|
653
|
+
}]
|
654
|
+
})
|
655
|
+
end
|
656
|
+
|
582
657
|
# Metadata
|
583
658
|
|
584
659
|
def openid_configuration_body(path = nil)
|
@@ -600,27 +675,31 @@ module Rodauth
|
|
600
675
|
|
601
676
|
response_types_supported = metadata[:response_types_supported]
|
602
677
|
|
678
|
+
response_types_supported |= %w[none]
|
679
|
+
response_types_supported |= ["code id_token"] if metadata[:grant_types_supported].include?("authorization_code")
|
603
680
|
if metadata[:grant_types_supported].include?("implicit")
|
604
|
-
response_types_supported
|
681
|
+
response_types_supported |= ["code token", "id_token token", "code id_token token"]
|
605
682
|
end
|
606
683
|
|
684
|
+
alg_values, enc_values = oauth_jwt_jwe_keys.keys.transpose
|
685
|
+
|
607
686
|
metadata.merge(
|
608
687
|
userinfo_endpoint: userinfo_url,
|
609
688
|
end_session_endpoint: (oidc_logout_url if use_rp_initiated_logout?),
|
610
689
|
response_types_supported: response_types_supported,
|
611
|
-
subject_types_supported: [
|
690
|
+
subject_types_supported: %w[public pairwise],
|
612
691
|
|
613
692
|
id_token_signing_alg_values_supported: metadata[:token_endpoint_auth_signing_alg_values_supported],
|
614
|
-
id_token_encryption_alg_values_supported:
|
615
|
-
id_token_encryption_enc_values_supported:
|
693
|
+
id_token_encryption_alg_values_supported: Array(alg_values),
|
694
|
+
id_token_encryption_enc_values_supported: Array(enc_values),
|
616
695
|
|
617
|
-
userinfo_signing_alg_values_supported:
|
618
|
-
userinfo_encryption_alg_values_supported:
|
619
|
-
userinfo_encryption_enc_values_supported:
|
696
|
+
userinfo_signing_alg_values_supported: oauth_jwt_jws_algorithms_supported,
|
697
|
+
userinfo_encryption_alg_values_supported: oauth_jwt_jwe_algorithms_supported,
|
698
|
+
userinfo_encryption_enc_values_supported: oauth_jwt_jwe_encryption_methods_supported,
|
620
699
|
|
621
|
-
request_object_signing_alg_values_supported:
|
622
|
-
request_object_encryption_alg_values_supported:
|
623
|
-
request_object_encryption_enc_values_supported:
|
700
|
+
request_object_signing_alg_values_supported: oauth_jwt_jws_algorithms_supported,
|
701
|
+
request_object_encryption_alg_values_supported: oauth_jwt_jwe_algorithms_supported,
|
702
|
+
request_object_encryption_enc_values_supported: oauth_jwt_jwe_encryption_methods_supported,
|
624
703
|
|
625
704
|
# These Claim Types are described in Section 5.6 of OpenID Connect Core 1.0 [OpenID.Core].
|
626
705
|
# Values defined by this specification are normal, aggregated, and distributed.
|
@@ -644,5 +723,19 @@ module Rodauth
|
|
644
723
|
response.status = 200
|
645
724
|
return_response
|
646
725
|
end
|
726
|
+
|
727
|
+
def jwt_response_success(jwt, cache = false)
|
728
|
+
response.status = 200
|
729
|
+
response["Content-Type"] ||= "application/jwt"
|
730
|
+
if cache
|
731
|
+
# defaulting to 1-day for everyone, for now at least
|
732
|
+
max_age = 60 * 60 * 24
|
733
|
+
response["Cache-Control"] = "private, max-age=#{max_age}"
|
734
|
+
else
|
735
|
+
response["Cache-Control"] = "no-store"
|
736
|
+
response["Pragma"] = "no-cache"
|
737
|
+
end
|
738
|
+
return_response(jwt)
|
739
|
+
end
|
647
740
|
end
|
648
741
|
end
|