rodauth-oauth 0.0.4 → 0.3.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 +197 -4
- data/README.md +45 -21
- data/lib/generators/roda/oauth/templates/db/migrate/create_rodauth_oauth.rb +8 -5
- data/lib/rodauth/features/oauth.rb +624 -449
- data/lib/rodauth/features/oauth_jwt.rb +244 -113
- data/lib/rodauth/features/oauth_saml.rb +104 -0
- data/lib/rodauth/features/oidc.rb +388 -0
- data/lib/rodauth/oauth/database_extensions.rb +73 -0
- data/lib/rodauth/oauth/ttl_store.rb +59 -0
- data/lib/rodauth/oauth/version.rb +1 -1
- data/templates/authorize.str +33 -0
- data/templates/client_secret_field.str +4 -0
- data/templates/description_field.str +4 -0
- data/templates/homepage_url_field.str +4 -0
- data/templates/name_field.str +4 -0
- data/templates/new_oauth_application.str +10 -0
- data/templates/oauth_application.str +11 -0
- data/templates/oauth_applications.str +14 -0
- data/templates/oauth_tokens.str +49 -0
- data/templates/redirect_uri_field.str +4 -0
- data/templates/scope_field.str +10 -0
- metadata +24 -9
@@ -1,36 +1,54 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
|
3
|
+
require "rodauth/oauth/ttl_store"
|
4
|
+
|
3
5
|
module Rodauth
|
4
6
|
Feature.define(:oauth_jwt) do
|
5
7
|
depends :oauth
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
+
JWKS = OAuth::TtlStore.new
|
10
|
+
|
11
|
+
auth_value_method :oauth_jwt_subject_type, "public" # public, pairwise
|
12
|
+
auth_value_method :oauth_jwt_subject_secret, nil # salt for pairwise generation
|
9
13
|
|
10
|
-
auth_value_method :oauth_jwt_token_issuer,
|
14
|
+
auth_value_method :oauth_jwt_token_issuer, nil
|
15
|
+
|
16
|
+
auth_value_method :oauth_application_jws_jwk_column, nil
|
11
17
|
|
12
18
|
auth_value_method :oauth_jwt_key, nil
|
13
19
|
auth_value_method :oauth_jwt_public_key, nil
|
14
20
|
auth_value_method :oauth_jwt_algorithm, "HS256"
|
15
21
|
|
16
|
-
auth_value_method :oauth_jwt_jwk_key, nil
|
17
|
-
auth_value_method :oauth_jwt_jwk_public_key, nil
|
18
|
-
auth_value_method :oauth_jwt_jwk_algorithm, "RS256"
|
19
|
-
|
20
22
|
auth_value_method :oauth_jwt_jwe_key, nil
|
21
23
|
auth_value_method :oauth_jwt_jwe_public_key, nil
|
22
24
|
auth_value_method :oauth_jwt_jwe_algorithm, nil
|
23
25
|
auth_value_method :oauth_jwt_jwe_encryption_method, nil
|
24
26
|
|
27
|
+
# values used for rotating keys
|
28
|
+
auth_value_method :oauth_jwt_legacy_public_key, nil
|
29
|
+
auth_value_method :oauth_jwt_legacy_algorithm, nil
|
30
|
+
|
25
31
|
auth_value_method :oauth_jwt_jwe_copyright, nil
|
26
32
|
auth_value_method :oauth_jwt_audience, nil
|
27
33
|
|
34
|
+
auth_value_method :request_uri_not_supported_message, "request uri is unsupported"
|
35
|
+
auth_value_method :invalid_request_object_message, "request object is invalid"
|
36
|
+
|
28
37
|
auth_value_methods(
|
29
38
|
:jwt_encode,
|
30
39
|
:jwt_decode,
|
31
|
-
:jwks_set
|
40
|
+
:jwks_set,
|
41
|
+
:last_account_login_at
|
32
42
|
)
|
33
43
|
|
44
|
+
route(:jwks) do |r|
|
45
|
+
next unless is_authorization_server?
|
46
|
+
|
47
|
+
r.get do
|
48
|
+
json_response_success({ keys: jwks_set }, true)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
34
52
|
def require_oauth_authorization(*scopes)
|
35
53
|
authorization_required unless authorization_token
|
36
54
|
|
@@ -43,31 +61,97 @@ module Rodauth
|
|
43
61
|
|
44
62
|
private
|
45
63
|
|
64
|
+
unless method_defined?(:last_account_login_at)
|
65
|
+
def last_account_login_at
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
46
70
|
def authorization_token
|
47
71
|
return @authorization_token if defined?(@authorization_token)
|
48
72
|
|
49
|
-
@authorization_token =
|
73
|
+
@authorization_token = begin
|
74
|
+
bearer_token = fetch_access_token
|
75
|
+
|
76
|
+
return unless bearer_token
|
77
|
+
|
78
|
+
jwt_token = jwt_decode(bearer_token)
|
79
|
+
|
80
|
+
return unless jwt_token
|
81
|
+
|
82
|
+
return if jwt_token["iss"] != (oauth_jwt_token_issuer || authorization_server_url) ||
|
83
|
+
(oauth_jwt_audience && jwt_token["aud"] != oauth_jwt_audience) ||
|
84
|
+
!jwt_token["sub"]
|
85
|
+
|
86
|
+
jwt_token
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# /authorize
|
91
|
+
|
92
|
+
def validate_oauth_grant_params
|
93
|
+
# TODO: add support for requst_uri
|
94
|
+
redirect_response_error("request_uri_not_supported") if param_or_nil("request_uri")
|
95
|
+
|
96
|
+
request_object = param_or_nil("request")
|
97
|
+
|
98
|
+
return super unless request_object && oauth_application
|
99
|
+
|
100
|
+
jws_jwk = if oauth_application[oauth_application_jws_jwk_column]
|
101
|
+
jwk = oauth_application[oauth_application_jws_jwk_column]
|
102
|
+
|
103
|
+
jwk = JSON.parse(jwk, symbolize_names: true) if jwk && jwk.is_a?(String)
|
104
|
+
else
|
105
|
+
redirect_response_error("invalid_request_object")
|
106
|
+
end
|
107
|
+
|
108
|
+
claims = jwt_decode(request_object, jws_key: jwk_import(jws_jwk), jws_algorithm: jwk[:alg])
|
109
|
+
|
110
|
+
redirect_response_error("invalid_request_object") unless claims
|
111
|
+
|
112
|
+
# If signed, the Authorization Request
|
113
|
+
# Object SHOULD contain the Claims "iss" (issuer) and "aud" (audience)
|
114
|
+
# as members, with their semantics being the same as defined in the JWT
|
115
|
+
# [RFC7519] specification. The value of "aud" should be the value of
|
116
|
+
# the Authorization Server (AS) "issuer" as defined in RFC8414
|
117
|
+
# [RFC8414].
|
118
|
+
claims.delete("iss")
|
119
|
+
audience = claims.delete("aud")
|
120
|
+
|
121
|
+
redirect_response_error("invalid_request_object") if audience && audience != authorization_server_url
|
122
|
+
|
123
|
+
claims.each do |k, v|
|
124
|
+
request.params[k.to_s] = v
|
125
|
+
end
|
126
|
+
|
127
|
+
super
|
50
128
|
end
|
51
129
|
|
52
130
|
# /token
|
53
131
|
|
54
|
-
def
|
132
|
+
def require_oauth_application
|
55
133
|
# requset authentication optional for assertions
|
56
|
-
return
|
134
|
+
return super unless param("grant_type") == "urn:ietf:params:oauth:grant-type:jwt-bearer"
|
57
135
|
|
58
|
-
|
136
|
+
claims = jwt_decode(param("assertion"))
|
137
|
+
|
138
|
+
redirect_response_error("invalid_grant") unless claims
|
139
|
+
|
140
|
+
@oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column => claims["client_id"]).first
|
141
|
+
|
142
|
+
authorization_required unless @oauth_application
|
59
143
|
end
|
60
144
|
|
61
145
|
def validate_oauth_token_params
|
62
|
-
if param(
|
63
|
-
redirect_response_error("invalid_client") unless param_or_nil(
|
146
|
+
if param("grant_type") == "urn:ietf:params:oauth:grant-type:jwt-bearer"
|
147
|
+
redirect_response_error("invalid_client") unless param_or_nil("assertion")
|
64
148
|
else
|
65
149
|
super
|
66
150
|
end
|
67
151
|
end
|
68
152
|
|
69
153
|
def create_oauth_token
|
70
|
-
if param(
|
154
|
+
if param("grant_type") == "urn:ietf:params:oauth:grant-type:jwt-bearer"
|
71
155
|
create_oauth_token_from_assertion
|
72
156
|
else
|
73
157
|
super
|
@@ -75,11 +159,7 @@ module Rodauth
|
|
75
159
|
end
|
76
160
|
|
77
161
|
def create_oauth_token_from_assertion
|
78
|
-
claims = jwt_decode(param(
|
79
|
-
|
80
|
-
redirect_response_error("invalid_grant") unless claims
|
81
|
-
|
82
|
-
@oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column => claims["client_id"]).first
|
162
|
+
claims = jwt_decode(param("assertion"))
|
83
163
|
|
84
164
|
account = account_ds(claims["sub"]).first
|
85
165
|
|
@@ -96,26 +176,40 @@ module Rodauth
|
|
96
176
|
|
97
177
|
def generate_oauth_token(params = {}, should_generate_refresh_token = true)
|
98
178
|
create_params = {
|
99
|
-
oauth_grants_expires_in_column =>
|
179
|
+
oauth_grants_expires_in_column => Sequel.date_add(Sequel::CURRENT_TIMESTAMP, seconds: oauth_token_expires_in)
|
100
180
|
}.merge(params)
|
101
181
|
|
102
|
-
|
103
|
-
|
182
|
+
oauth_token = rescue_from_uniqueness_error do
|
183
|
+
if should_generate_refresh_token
|
184
|
+
refresh_token = oauth_unique_id_generator
|
104
185
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
186
|
+
if oauth_tokens_refresh_token_hash_column
|
187
|
+
create_params[oauth_tokens_refresh_token_hash_column] = generate_token_hash(refresh_token)
|
188
|
+
else
|
189
|
+
create_params[oauth_tokens_refresh_token_column] = refresh_token
|
190
|
+
end
|
109
191
|
end
|
192
|
+
|
193
|
+
_generate_oauth_token(create_params)
|
110
194
|
end
|
111
195
|
|
112
|
-
|
196
|
+
claims = jwt_claims(oauth_token)
|
197
|
+
|
198
|
+
# one of the points of using jwt is avoiding database lookups, so we put here all relevant
|
199
|
+
# token data.
|
200
|
+
claims[:scope] = oauth_token[oauth_tokens_scopes_column]
|
201
|
+
|
202
|
+
token = jwt_encode(claims)
|
203
|
+
|
204
|
+
oauth_token[oauth_tokens_token_column] = token
|
205
|
+
oauth_token
|
206
|
+
end
|
113
207
|
|
114
|
-
|
208
|
+
def jwt_claims(oauth_token)
|
209
|
+
issued_at = Time.now.to_i
|
115
210
|
|
116
|
-
|
117
|
-
|
118
|
-
iss: oauth_jwt_token_issuer, # issuer
|
211
|
+
claims = {
|
212
|
+
iss: (oauth_jwt_token_issuer || authorization_server_url), # issuer
|
119
213
|
iat: issued_at, # issued at
|
120
214
|
#
|
121
215
|
# sub REQUIRED - as defined in section 4.1.2 of [RFC7519]. In case of
|
@@ -126,23 +220,32 @@ module Rodauth
|
|
126
220
|
# owner is involved, such as the client credentials grant, the value
|
127
221
|
# of "sub" SHOULD correspond to an identifier the authorization
|
128
222
|
# server uses to indicate the client application.
|
223
|
+
sub: jwt_subject(oauth_token),
|
129
224
|
client_id: oauth_application[oauth_applications_client_id_column],
|
130
225
|
|
131
226
|
exp: issued_at + oauth_token_expires_in,
|
132
|
-
aud: oauth_jwt_audience
|
133
|
-
|
134
|
-
# one of the points of using jwt is avoiding database lookups, so we put here all relevant
|
135
|
-
# token data.
|
136
|
-
scope: oauth_token[oauth_tokens_scopes_column].gsub(",", " ")
|
227
|
+
aud: (oauth_jwt_audience || oauth_application[oauth_applications_client_id_column])
|
137
228
|
}
|
138
229
|
|
139
|
-
|
230
|
+
claims[:auth_time] = last_account_login_at.to_i if last_account_login_at
|
140
231
|
|
141
|
-
|
142
|
-
oauth_token
|
232
|
+
claims
|
143
233
|
end
|
144
234
|
|
145
|
-
def
|
235
|
+
def jwt_subject(oauth_token)
|
236
|
+
case oauth_jwt_subject_type
|
237
|
+
when "public"
|
238
|
+
oauth_token[oauth_tokens_account_id_column]
|
239
|
+
when "pairwise"
|
240
|
+
id = oauth_token[oauth_tokens_account_id_column]
|
241
|
+
application_id = oauth_token[oauth_tokens_oauth_application_id_column]
|
242
|
+
Digest::SHA256.hexdigest("#{id}#{application_id}#{oauth_jwt_subject_secret}")
|
243
|
+
else
|
244
|
+
raise StandardError, "unexpected subject (#{oauth_jwt_subject_type})"
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def oauth_token_by_token(token)
|
146
249
|
jwt_decode(token)
|
147
250
|
end
|
148
251
|
|
@@ -170,35 +273,64 @@ module Rodauth
|
|
170
273
|
def oauth_server_metadata_body(path)
|
171
274
|
metadata = super
|
172
275
|
metadata.merge! \
|
173
|
-
jwks_uri:
|
276
|
+
jwks_uri: jwks_url,
|
174
277
|
token_endpoint_auth_signing_alg_values_supported: [oauth_jwt_algorithm]
|
175
278
|
metadata
|
176
279
|
end
|
177
280
|
|
178
|
-
def
|
179
|
-
|
180
|
-
|
181
|
-
oauth_token["client_id"] == oauth_application[oauth_applications_client_id_column]
|
281
|
+
def _jwt_key
|
282
|
+
@_jwt_key ||= oauth_jwt_key || (oauth_application[oauth_applications_client_secret_column] if oauth_application)
|
182
283
|
end
|
183
284
|
|
184
|
-
|
185
|
-
|
285
|
+
# Resource Server only!
|
286
|
+
#
|
287
|
+
# returns the jwks set from the authorization server.
|
288
|
+
def auth_server_jwks_set
|
289
|
+
metadata = authorization_server_metadata
|
290
|
+
|
291
|
+
return unless metadata && (jwks_uri = metadata[:jwks_uri])
|
292
|
+
|
293
|
+
jwks_uri = URI(jwks_uri)
|
294
|
+
|
295
|
+
jwks = JWKS[jwks_uri]
|
296
|
+
|
297
|
+
return jwks if jwks
|
298
|
+
|
299
|
+
JWKS.set(jwks_uri) do
|
300
|
+
http = Net::HTTP.new(jwks_uri.host, jwks_uri.port)
|
301
|
+
http.use_ssl = jwks_uri.scheme == "https"
|
302
|
+
|
303
|
+
request = Net::HTTP::Get.new(jwks_uri.request_uri)
|
304
|
+
request["accept"] = json_response_content_type
|
305
|
+
response = http.request(request)
|
306
|
+
authorization_required unless response.code.to_i == 200
|
307
|
+
|
308
|
+
# time-to-live
|
309
|
+
ttl = if response.key?("cache-control")
|
310
|
+
cache_control = response["cache-control"]
|
311
|
+
cache_control[/max-age=(\d+)/, 1].to_i
|
312
|
+
elsif response.key?("expires")
|
313
|
+
Time.parse(response["expires"]).to_i - Time.now.to_i
|
314
|
+
end
|
315
|
+
|
316
|
+
[JSON.parse(response.body, symbolize_names: true), ttl]
|
317
|
+
end
|
186
318
|
end
|
187
319
|
|
188
320
|
if defined?(JSON::JWT)
|
189
|
-
|
321
|
+
|
322
|
+
def jwk_import(data)
|
323
|
+
JSON::JWK.new(data)
|
324
|
+
end
|
190
325
|
|
191
326
|
# json-jwt
|
192
327
|
def jwt_encode(payload)
|
193
328
|
jwt = JSON::JWT.new(payload)
|
329
|
+
jwk = JSON::JWK.new(_jwt_key)
|
330
|
+
|
331
|
+
jwt = jwt.sign(jwk, oauth_jwt_algorithm)
|
332
|
+
jwt.kid = jwk.thumbprint
|
194
333
|
|
195
|
-
jwt = if oauth_jwt_jwk_key
|
196
|
-
jwk = JSON::JWK.new(oauth_jwt_jwk_key)
|
197
|
-
jwt.kid = jwk.thumbprint
|
198
|
-
jwt.sign(oauth_jwt_jwk_key, oauth_jwt_jwk_algorithm)
|
199
|
-
else
|
200
|
-
jwt.sign(_jwt_key, oauth_jwt_algorithm)
|
201
|
-
end
|
202
334
|
if oauth_jwt_jwe_key
|
203
335
|
algorithm = oauth_jwt_jwe_algorithm.to_sym if oauth_jwt_jwe_algorithm
|
204
336
|
jwt = jwt.encrypt(oauth_jwt_jwe_public_key || oauth_jwt_jwe_key,
|
@@ -208,47 +340,49 @@ module Rodauth
|
|
208
340
|
jwt.to_s
|
209
341
|
end
|
210
342
|
|
211
|
-
def jwt_decode(token)
|
212
|
-
return @jwt_token if defined?(@jwt_token)
|
213
|
-
|
343
|
+
def jwt_decode(token, jws_key: oauth_jwt_public_key || _jwt_key, **)
|
214
344
|
token = JSON::JWT.decode(token, oauth_jwt_jwe_key).plain_text if oauth_jwt_jwe_key
|
215
345
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
346
|
+
if is_authorization_server?
|
347
|
+
if oauth_jwt_legacy_public_key
|
348
|
+
JSON::JWT.decode(token, JSON::JWK::Set.new({ keys: jwks_set }))
|
349
|
+
elsif jws_key
|
350
|
+
JSON::JWT.decode(token, jws_key)
|
351
|
+
end
|
352
|
+
elsif (jwks = auth_server_jwks_set)
|
353
|
+
JSON::JWT.decode(token, JSON::JWK::Set.new(jwks))
|
354
|
+
end
|
222
355
|
rescue JSON::JWT::Exception
|
223
356
|
nil
|
224
357
|
end
|
225
358
|
|
226
359
|
def jwks_set
|
227
|
-
[
|
228
|
-
(JSON::JWK.new(
|
360
|
+
@jwks_set ||= [
|
361
|
+
(JSON::JWK.new(oauth_jwt_public_key).merge(use: "sig", alg: oauth_jwt_algorithm) if oauth_jwt_public_key),
|
362
|
+
(JSON::JWK.new(oauth_jwt_legacy_public_key).merge(use: "sig", alg: oauth_jwt_legacy_algorithm) if oauth_jwt_legacy_public_key),
|
229
363
|
(JSON::JWK.new(oauth_jwt_jwe_public_key).merge(use: "enc", alg: oauth_jwt_jwe_algorithm) if oauth_jwt_jwe_public_key)
|
230
364
|
].compact
|
231
365
|
end
|
232
|
-
|
366
|
+
|
233
367
|
elsif defined?(JWT)
|
234
368
|
|
235
369
|
# ruby-jwt
|
236
370
|
|
371
|
+
def jwk_import(data)
|
372
|
+
JWT::JWK.import(data).keypair
|
373
|
+
end
|
374
|
+
|
237
375
|
def jwt_encode(payload)
|
238
376
|
headers = {}
|
239
377
|
|
240
|
-
key
|
241
|
-
jwk_key = JWT::JWK.new(oauth_jwt_jwk_key)
|
242
|
-
# JWK
|
243
|
-
# Currently only supports RSA public keys.
|
244
|
-
headers[:kid] = jwk_key.kid
|
378
|
+
key = _jwt_key
|
245
379
|
|
246
|
-
|
247
|
-
|
248
|
-
|
380
|
+
if key.is_a?(OpenSSL::PKey::RSA)
|
381
|
+
jwk = JWT::JWK.new(_jwt_key)
|
382
|
+
headers[:kid] = jwk.kid
|
249
383
|
|
250
|
-
|
251
|
-
|
384
|
+
key = jwk.keypair
|
385
|
+
end
|
252
386
|
|
253
387
|
# Use the key and iat to create a unique key per request to prevent replay attacks
|
254
388
|
jti_raw = [key, payload[:iat]].join(":").to_s
|
@@ -256,7 +390,7 @@ module Rodauth
|
|
256
390
|
|
257
391
|
# @see JWT reserved claims - https://tools.ietf.org/html/draft-jones-json-web-token-07#page-7
|
258
392
|
payload[:jti] = jti
|
259
|
-
token = JWT.encode(payload, key,
|
393
|
+
token = JWT.encode(payload, key, oauth_jwt_algorithm, headers)
|
260
394
|
|
261
395
|
if oauth_jwt_jwe_key
|
262
396
|
params = {
|
@@ -271,52 +405,47 @@ module Rodauth
|
|
271
405
|
token
|
272
406
|
end
|
273
407
|
|
274
|
-
def jwt_decode(token)
|
275
|
-
return @jwt_token if defined?(@jwt_token)
|
276
|
-
|
408
|
+
def jwt_decode(token, jws_key: oauth_jwt_public_key || _jwt_key, jws_algorithm: oauth_jwt_algorithm)
|
277
409
|
# decrypt jwe
|
278
410
|
token = JWE.decrypt(token, oauth_jwt_jwe_key) if oauth_jwt_jwe_key
|
279
|
-
|
280
411
|
# decode jwt
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
headers[:jwks] = jwk_loader
|
294
|
-
|
295
|
-
nil
|
296
|
-
else
|
297
|
-
# JWS
|
298
|
-
# worst case scenario, the key is the application key
|
299
|
-
oauth_jwt_public_key || _jwt_key
|
300
|
-
end
|
301
|
-
@jwt_token, = JWT.decode(token, key, true, headers)
|
302
|
-
@jwt_token
|
303
|
-
rescue JWT::DecodeError
|
412
|
+
if is_authorization_server?
|
413
|
+
if oauth_jwt_legacy_public_key
|
414
|
+
algorithms = jwks_set.select { |k| k[:use] == "sig" }.map { |k| k[:alg] }
|
415
|
+
JWT.decode(token, nil, true, jwks: { keys: jwks_set }, algorithms: algorithms).first
|
416
|
+
elsif jws_key
|
417
|
+
JWT.decode(token, jws_key, true, algorithms: [jws_algorithm]).first
|
418
|
+
end
|
419
|
+
elsif (jwks = auth_server_jwks_set)
|
420
|
+
algorithms = jwks[:keys].select { |k| k[:use] == "sig" }.map { |k| k[:alg] }
|
421
|
+
JWT.decode(token, nil, true, jwks: jwks, algorithms: algorithms).first
|
422
|
+
end
|
423
|
+
rescue JWT::DecodeError, JWT::JWKError
|
304
424
|
nil
|
305
425
|
end
|
306
426
|
|
307
427
|
def jwks_set
|
308
|
-
[
|
309
|
-
(JWT::JWK.new(
|
428
|
+
@jwks_set ||= [
|
429
|
+
(JWT::JWK.new(oauth_jwt_public_key).export.merge(use: "sig", alg: oauth_jwt_algorithm) if oauth_jwt_public_key),
|
430
|
+
(
|
431
|
+
if oauth_jwt_legacy_public_key
|
432
|
+
JWT::JWK.new(oauth_jwt_legacy_public_key).export.merge(use: "sig", alg: oauth_jwt_legacy_algorithm)
|
433
|
+
end
|
434
|
+
),
|
310
435
|
(JWT::JWK.new(oauth_jwt_jwe_public_key).export.merge(use: "enc", alg: oauth_jwt_jwe_algorithm) if oauth_jwt_jwe_public_key)
|
311
436
|
].compact
|
312
437
|
end
|
313
438
|
else
|
314
439
|
# :nocov:
|
440
|
+
def jwk_import(_data)
|
441
|
+
raise "#{__method__} is undefined, redefine it or require either \"jwt\" or \"json-jwt\""
|
442
|
+
end
|
443
|
+
|
315
444
|
def jwt_encode(_token)
|
316
445
|
raise "#{__method__} is undefined, redefine it or require either \"jwt\" or \"json-jwt\""
|
317
446
|
end
|
318
447
|
|
319
|
-
def jwt_decode(_token)
|
448
|
+
def jwt_decode(_token, **)
|
320
449
|
raise "#{__method__} is undefined, redefine it or require either \"jwt\" or \"json-jwt\""
|
321
450
|
end
|
322
451
|
|
@@ -326,10 +455,12 @@ module Rodauth
|
|
326
455
|
# :nocov:
|
327
456
|
end
|
328
457
|
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
458
|
+
def validate_oauth_revoke_params
|
459
|
+
token_hint = param_or_nil("token_type_hint")
|
460
|
+
|
461
|
+
throw(:rodauth_error) if !token_hint || token_hint == "access_token"
|
462
|
+
|
463
|
+
super
|
333
464
|
end
|
334
465
|
end
|
335
466
|
end
|