rodauth-oauth 0.9.3 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -3
- data/doc/release_notes/0_10_0.md +100 -0
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize.html.erb +27 -3
- data/lib/generators/rodauth/oauth/templates/db/migrate/create_rodauth_oauth.rb +4 -0
- data/lib/rodauth/features/oauth_base.rb +5 -0
- data/lib/rodauth/features/oauth_jwt.rb +59 -14
- data/lib/rodauth/features/oauth_resource_indicators.rb +153 -0
- data/lib/rodauth/features/oidc.rb +120 -14
- data/lib/rodauth/oauth/version.rb +1 -1
- data/locales/en.yml +2 -1
- data/locales/pt.yml +57 -0
- data/templates/authorize.str +13 -3
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f9b68ff6e15b91128db72a07fa91b86afb70352f9582fa8c27e7abfe3c0dc17c
|
4
|
+
data.tar.gz: 1c35b67bc10619c8de31cbcef514636e7975307a0cfc02585ae10ec97de74be1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2cf0e357529093b45834697c54bae5eaf17419885e04ccba279d18e65464aa8d8fb2e49da09dd5c96c83331e0f60915e993af5dfc7decfff4c8752b5401dfe8a
|
7
|
+
data.tar.gz: 784d5184526ff8dcbc3c112eb58311705baf82e1bf17b40b87cd55dc43f0b06cc6bf7c2cb71f67caf315008b14d3fe4b9fe8eea991a0475586bf4effb0d77ed3
|
data/README.md
CHANGED
@@ -21,14 +21,16 @@ This gem implements the following RFCs and features of OAuth:
|
|
21
21
|
* `oauth_token_introspection` - [Token introspection](https://tools.ietf.org/html/rfc7662);
|
22
22
|
* [Authorization Server Metadata](https://tools.ietf.org/html/rfc8414);
|
23
23
|
* `oauth_pkce` - [PKCE](https://tools.ietf.org/html/rfc7636);
|
24
|
-
* Access Type (Token refresh online and offline);
|
25
24
|
* `oauth_jwt` - [JWT Access Tokens](https://tools.ietf.org/html/draft-ietf-oauth-access-token-jwt-07);
|
25
|
+
* Supports [JWT Secured Authorization Request](https://tools.ietf.org/html/draft-ietf-oauth-jwsreq-20);
|
26
|
+
* `oauth_resource_indicators` - [Resource Indicators](https://datatracker.ietf.org/doc/html/rfc8707);
|
27
|
+
* Access Type (Token refresh online and offline);
|
26
28
|
* `oauth_http_mac` - [MAC Authentication Scheme](https://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token-02);
|
27
29
|
* `oauth_assertion_base` - [Assertion Framework](https://datatracker.ietf.org/doc/html/rfc7521);
|
28
30
|
* `oauth_saml_bearer_grant` - [SAML 2.0 Bearer Assertion](https://datatracker.ietf.org/doc/html/rfc7522);
|
29
31
|
* `oauth_jwt_bearer_grant` - [JWT Bearer Assertion](https://datatracker.ietf.org/doc/html/rfc7523);
|
30
|
-
|
31
|
-
* [Dynamic Client Registration Protocol](https://datatracker.ietf.org/doc/html/rfc7591);
|
32
|
+
|
33
|
+
* `oauth_dynamic_client_registration` - [Dynamic Client Registration Protocol](https://datatracker.ietf.org/doc/html/rfc7591);
|
32
34
|
* OAuth application and token management dashboards;
|
33
35
|
|
34
36
|
It also implements the [OpenID Connect layer](https://openid.net/connect/) (via the `openid` feature) on top of the OAuth features it provides, including:
|
@@ -0,0 +1,100 @@
|
|
1
|
+
## 0.10.0 (10/06/2022)
|
2
|
+
|
3
|
+
### Features
|
4
|
+
|
5
|
+
#### Resource Indicators
|
6
|
+
|
7
|
+
RFC: https://datatracker.ietf.org/doc/html/rfc8707
|
8
|
+
|
9
|
+
`rodauth-oauth` now supports Resource Indicators, via the optional `:oauth_resource_indicators` feature.
|
10
|
+
|
11
|
+
#### JWT: extra options
|
12
|
+
|
13
|
+
The following extra option values were added:
|
14
|
+
|
15
|
+
* `oauth_jwt_jwe_keys`
|
16
|
+
* `oauth_jwt_public_keys`
|
17
|
+
* `oauth_jwt_jwe_public_keys`
|
18
|
+
|
19
|
+
`:oauth_jwt_jwe_keys` should be used to store all provider combos of encryption keys, indexed by an algo/method tuple:
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
oauth_jwt_jwe_keys { { %w[RSA-OAEP A128CBC-HS256] => key } }
|
23
|
+
```
|
24
|
+
|
25
|
+
The first element of the hash should indicate the preferred encryption mode, when no combination is specifically requested.
|
26
|
+
|
27
|
+
It should be considered the most future-proof way of declaring JWE keys, and support for `oauth_jwt_jwe_key` and friends should be soon deprecated.
|
28
|
+
|
29
|
+
Both `oauth_jwt_public_keys` and `oauth_jwt_jwe_public_keys` provide a way to declare multiple keys to be exposed as the provider JWKs in the `/jwks` endpoint.
|
30
|
+
|
31
|
+
### Improvements
|
32
|
+
|
33
|
+
* Added translations for portuguese.
|
34
|
+
|
35
|
+
#### OpenID Connect improvements
|
36
|
+
|
37
|
+
* The `:oidc` feature now depends on `rodauth`'s [account_expiration](http://rodauth.jeremyevans.net/rdoc/files/doc/account_expiration_rdoc.html) feature.
|
38
|
+
|
39
|
+
Although a more-involved-somewhat-breaking change, it was required in order to keep track of account login event timestamps, necessary for correct `"auth_time"` calculation (see the first bugfix mention for more details, and Breaking Changes for migration path).
|
40
|
+
|
41
|
+
|
42
|
+
* Support for the `ui_locales` parameter was added. This feature depends on the `:i18n` feature provided by [rodauth-i18n](https://github.com/janko/rodauth-i18n).
|
43
|
+
* Support for the `claims_locales` parameter was added, in that the `get_oidc_param` and `get_additional_param`, when accepting a 3rd parameter, will be passed a locale code:
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
# given "claims_locales=en pt"
|
47
|
+
|
48
|
+
get_oidc_param { |account, param, locale| }
|
49
|
+
# will be called twice for the same param, one with locale as "en", another as "pt"
|
50
|
+
|
51
|
+
get_oidc_param { |account, param| }
|
52
|
+
# will be called once without locale
|
53
|
+
```
|
54
|
+
|
55
|
+
* Support for `max_age` parameter was added.
|
56
|
+
|
57
|
+
* Support for `acr_values` parameter was added.
|
58
|
+
|
59
|
+
When "phr", and a `rodauth` 2-factor feature (like [otp](http://rodauth.jeremyevans.net/rdoc/files/doc/otp_rdoc.html)) is enabled, the user will be requested for 2-factor authentication before performing the OpenID Authorization Request.
|
60
|
+
|
61
|
+
When "phrh", and `rodauth`'s [webauthn_login](http://rodauth.jeremyevans.net/rdoc/files/doc/webauthn_login_rdoc.html) feature is enabled, the user will be requested for WebAuthn authentication before performing the OpenID Authorization Request.
|
62
|
+
|
63
|
+
Any other acr values are considered provider-specific, and the `require_acr_value(acr_value)` option should be provided to deal with it (it'll be called after authentication is ensured and before the authorization request is processed).
|
64
|
+
|
65
|
+
### Bugfixes
|
66
|
+
|
67
|
+
* reverted the `"auth_time"` calculation "fix" introduced in 0.9.3, which broke compliance with the RFC (the implementation prior to that was also broken, hence why `"account_expiration"` plugin was introduced as a dependency).
|
68
|
+
|
69
|
+
### Breaking Changes
|
70
|
+
|
71
|
+
As you read already, the `"account_expiration"` feature is now required by default by `"oidc"`. In order to migrate to it, here's a suggested strategy:
|
72
|
+
|
73
|
+
1. Add the relevant database tables
|
74
|
+
|
75
|
+
Add a migration looking roughly like this:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
create_table(:account_activity_times) do
|
79
|
+
foreign_key :id, :accounts, primary_key: true, type: Integer
|
80
|
+
DateTime :last_activity_at, null: false
|
81
|
+
DateTime :last_login_at, null: false
|
82
|
+
DateTime :expired_at
|
83
|
+
end
|
84
|
+
```
|
85
|
+
|
86
|
+
2. Update and deploy `rodauth-oauth` 0.10.0
|
87
|
+
|
88
|
+
(Nothing required beyond `enable :oidc`.)
|
89
|
+
|
90
|
+
3. Set `:last_login_at` to a value.
|
91
|
+
|
92
|
+
Like now. You can , for example, run this SQL:
|
93
|
+
|
94
|
+
```sql
|
95
|
+
UPDATE account_activity_times SET last_login_at = CURRENT_TIMESTAMP;
|
96
|
+
```
|
97
|
+
|
98
|
+
---
|
99
|
+
|
100
|
+
That's it, nothing fancy or accurate. Yes, the `last_login_at` is wrong, but as sessions expire, it should go back to normal.
|
@@ -34,13 +34,37 @@
|
|
34
34
|
</div>
|
35
35
|
<% end %>
|
36
36
|
<%= hidden_field_tag :client_id, params[:client_id] %>
|
37
|
-
<% %i[access_type response_type state
|
37
|
+
<% %i[access_type response_type response_mode state redirect_uri].each do |oauth_param| %>
|
38
38
|
<% if params[oauth_param] %>
|
39
39
|
<%= hidden_field_tag oauth_param, params[oauth_param] %>
|
40
40
|
<% end %>
|
41
41
|
<% end %>
|
42
|
-
<% if
|
43
|
-
|
42
|
+
<% if rodauth.features.include?(:oauth_resource_indicators) && rodauth.resource_indicators %>
|
43
|
+
<% rodauth.resource_indicators.each do |resource| %>
|
44
|
+
<%= hidden_field_tag "resource", resource %>
|
45
|
+
<% end %>
|
46
|
+
<% end %>
|
47
|
+
<% if rodauth.features.include?(:oauth_pkce) %>
|
48
|
+
<% if params[:code_challenge] %>
|
49
|
+
<%= hidden_field_tag :code_challenge, params[:code_challenge] %>
|
50
|
+
<% end %>
|
51
|
+
<% if params[:code_challenge_method] %>
|
52
|
+
<%= hidden_field_tag :code_challenge_method, params[:code_challenge_method] %>
|
53
|
+
<% end %>
|
54
|
+
<% end %>
|
55
|
+
<% if rodauth.features.include?(:oidc) %>
|
56
|
+
<% if params[:nonce] %>
|
57
|
+
<%= hidden_field_tag :nonce, params[:nonce] %>
|
58
|
+
<% end %>
|
59
|
+
<% if params[:ui_locales] %>
|
60
|
+
<%= hidden_field_tag :ui_locales, params[:ui_locales] %>
|
61
|
+
<% end %>
|
62
|
+
<% if params[:claims_locales] %>
|
63
|
+
<%= hidden_field_tag :claims_locales, params[:claims_locales] %>
|
64
|
+
<% end %>
|
65
|
+
<% if params[:acr_values] %>
|
66
|
+
<%= hidden_field_tag :acr, params[:acr_values] %>
|
67
|
+
<% end %>
|
44
68
|
<% end %>
|
45
69
|
</div>
|
46
70
|
<p class="text-center">
|
@@ -52,6 +52,8 @@ class CreateRodauthOauth < ActiveRecord::Migration<%= migration_version %>
|
|
52
52
|
# device code grant
|
53
53
|
# t.string :user_code, null: true, unique: true
|
54
54
|
# t.datetime :last_polled_at, null: true
|
55
|
+
# when using :oauth_resource_indicators feature
|
56
|
+
# t.string :resource
|
55
57
|
end
|
56
58
|
|
57
59
|
create_table :oauth_tokens do |t|
|
@@ -78,6 +80,8 @@ class CreateRodauthOauth < ActiveRecord::Migration<%= migration_version %>
|
|
78
80
|
# uncomment to use OIDC nonce
|
79
81
|
# t.string :nonce
|
80
82
|
# t.datetime :auth_time
|
83
|
+
# when using :oauth_resource_indicators feature
|
84
|
+
# t.string :resource
|
81
85
|
end
|
82
86
|
end
|
83
87
|
end
|
@@ -425,6 +425,11 @@ module Rodauth
|
|
425
425
|
oauth_tokens_expires_in_column => Sequel.date_add(Sequel::CURRENT_TIMESTAMP, seconds: oauth_token_expires_in)
|
426
426
|
}.merge(params)
|
427
427
|
|
428
|
+
if create_params[oauth_tokens_scopes_column].is_a?(Array)
|
429
|
+
create_params[oauth_tokens_scopes_column] =
|
430
|
+
create_params[oauth_tokens_scopes_column].join(" ")
|
431
|
+
end
|
432
|
+
|
428
433
|
rescue_from_uniqueness_error do
|
429
434
|
access_token = _generate_access_token(create_params)
|
430
435
|
refresh_token = _generate_refresh_token(create_params) if should_generate_refresh_token
|
@@ -44,10 +44,13 @@ module Rodauth
|
|
44
44
|
|
45
45
|
auth_value_method :oauth_jwt_keys, {}
|
46
46
|
auth_value_method :oauth_jwt_key, nil
|
47
|
+
auth_value_method :oauth_jwt_public_keys, {}
|
47
48
|
auth_value_method :oauth_jwt_public_key, nil
|
48
49
|
auth_value_method :oauth_jwt_algorithm, "RS256"
|
49
50
|
|
51
|
+
auth_value_method :oauth_jwt_jwe_keys, {}
|
50
52
|
auth_value_method :oauth_jwt_jwe_key, nil
|
53
|
+
auth_value_method :oauth_jwt_jwe_public_keys, {}
|
51
54
|
auth_value_method :oauth_jwt_jwe_public_key, nil
|
52
55
|
auth_value_method :oauth_jwt_jwe_algorithm, nil
|
53
56
|
auth_value_method :oauth_jwt_jwe_encryption_method, nil
|
@@ -407,10 +410,11 @@ module Rodauth
|
|
407
410
|
|
408
411
|
def jwt_encode(payload,
|
409
412
|
jwks: nil,
|
410
|
-
jwe_key: oauth_jwt_jwe_public_key || oauth_jwt_jwe_key,
|
411
|
-
signing_algorithm: oauth_jwt_algorithm,
|
412
413
|
encryption_algorithm: oauth_jwt_jwe_algorithm,
|
413
|
-
encryption_method: oauth_jwt_jwe_encryption_method
|
414
|
+
encryption_method: oauth_jwt_jwe_encryption_method,
|
415
|
+
jwe_key: oauth_jwt_jwe_keys[[encryption_algorithm,
|
416
|
+
encryption_method]] || oauth_jwt_jwe_public_key || oauth_jwt_jwe_key,
|
417
|
+
signing_algorithm: oauth_jwt_algorithm || oauth_jwt_keys.keys.first)
|
414
418
|
payload[:jti] = generate_jti(payload)
|
415
419
|
jwt = JSON::JWT.new(payload)
|
416
420
|
|
@@ -427,6 +431,7 @@ module Rodauth
|
|
427
431
|
jwe = jwt.encrypt(jwk, encryption_algorithm.to_sym, encryption_method.to_sym)
|
428
432
|
jwe.to_s
|
429
433
|
elsif jwe_key
|
434
|
+
jwe_key = jwe_key.first if jwe_key.is_a?(Array)
|
430
435
|
algorithm = encryption_algorithm.to_sym if encryption_algorithm
|
431
436
|
meth = encryption_method.to_sym if encryption_method
|
432
437
|
jwt.encrypt(jwe_key, algorithm, meth)
|
@@ -438,18 +443,23 @@ module Rodauth
|
|
438
443
|
def jwt_decode(
|
439
444
|
token,
|
440
445
|
jwks: nil,
|
441
|
-
|
442
|
-
|
443
|
-
jwe_key: oauth_jwt_jwe_key,
|
446
|
+
jws_algorithm: oauth_jwt_algorithm || oauth_jwt_public_key.keys.first || oauth_jwt_keys.keys.first,
|
447
|
+
jws_key: oauth_jwt_public_key || oauth_jwt_keys[jws_algorithm] || _jwt_key,
|
444
448
|
jws_encryption_algorithm: oauth_jwt_jwe_algorithm,
|
445
449
|
jws_encryption_method: oauth_jwt_jwe_encryption_method,
|
450
|
+
jwe_key: oauth_jwt_jwe_keys[[jws_encryption_algorithm, jws_encryption_method]] || oauth_jwt_jwe_key,
|
446
451
|
verify_claims: true,
|
447
452
|
verify_jti: true,
|
448
453
|
verify_iss: true,
|
449
454
|
verify_aud: false,
|
450
455
|
**
|
451
456
|
)
|
452
|
-
|
457
|
+
jws_key = jws_key.first if jws_key.is_a?(Array)
|
458
|
+
|
459
|
+
if jwe_key
|
460
|
+
jwe_key = jwe_key.first if jwe_key.is_a?(Array)
|
461
|
+
token = JSON::JWT.decode(token, jwe_key).plain_text
|
462
|
+
end
|
453
463
|
|
454
464
|
claims = if is_authorization_server?
|
455
465
|
if oauth_jwt_legacy_public_key
|
@@ -487,6 +497,21 @@ module Rodauth
|
|
487
497
|
|
488
498
|
def jwks_set
|
489
499
|
@jwks_set ||= [
|
500
|
+
*(
|
501
|
+
unless oauth_jwt_public_keys.empty?
|
502
|
+
oauth_jwt_public_keys.flat_map { |algo, pkeys| pkeys.map { |pkey| JSON::JWK.new(pkey).merge(use: "sig", alg: algo) } }
|
503
|
+
end
|
504
|
+
),
|
505
|
+
*(
|
506
|
+
unless oauth_jwt_jwe_public_keys.empty?
|
507
|
+
oauth_jwt_jwe_public_keys.flat_map do |(algo, _enc), pkeys|
|
508
|
+
pkeys.map do |pkey|
|
509
|
+
JSON::JWK.new(pkey).merge(use: "enc", alg: algo)
|
510
|
+
end
|
511
|
+
end
|
512
|
+
end
|
513
|
+
),
|
514
|
+
# legacy
|
490
515
|
(JSON::JWK.new(oauth_jwt_public_key).merge(use: "sig", alg: oauth_jwt_algorithm) if oauth_jwt_public_key),
|
491
516
|
(JSON::JWK.new(oauth_jwt_legacy_public_key).merge(use: "sig", alg: oauth_jwt_legacy_algorithm) if oauth_jwt_legacy_public_key),
|
492
517
|
(JSON::JWK.new(oauth_jwt_jwe_public_key).merge(use: "enc", alg: oauth_jwt_jwe_algorithm) if oauth_jwt_jwe_public_key)
|
@@ -522,7 +547,8 @@ module Rodauth
|
|
522
547
|
JWT::JWK.import(data).keypair
|
523
548
|
end
|
524
549
|
|
525
|
-
def jwt_encode(payload,
|
550
|
+
def jwt_encode(payload,
|
551
|
+
signing_algorithm: oauth_jwt_algorithm || oauth_jwt_keys.keys.first)
|
526
552
|
headers = {}
|
527
553
|
|
528
554
|
key = oauth_jwt_keys[signing_algorithm] || _jwt_key
|
@@ -545,11 +571,11 @@ module Rodauth
|
|
545
571
|
def jwt_encode_with_jwe(
|
546
572
|
payload,
|
547
573
|
jwks: nil,
|
548
|
-
jwe_key: oauth_jwt_jwe_public_key || oauth_jwt_jwe_key,
|
549
574
|
encryption_algorithm: oauth_jwt_jwe_algorithm,
|
550
|
-
encryption_method: oauth_jwt_jwe_encryption_method,
|
575
|
+
encryption_method: oauth_jwt_jwe_encryption_method,
|
576
|
+
jwe_key: oauth_jwt_jwe_public_key || oauth_jwt_jwe_keys[[encryption_algorithm, encryption_method]] || oauth_jwt_jwe_key,
|
577
|
+
**args
|
551
578
|
)
|
552
|
-
|
553
579
|
token = jwt_encode_without_jwe(payload, **args)
|
554
580
|
|
555
581
|
return token unless encryption_algorithm && encryption_method
|
@@ -557,6 +583,7 @@ module Rodauth
|
|
557
583
|
if jwks && jwks.any? { |k| k[:use] == "enc" }
|
558
584
|
JWE.__rodauth_oauth_encrypt_from_jwks(token, jwks, alg: encryption_algorithm, enc: encryption_method)
|
559
585
|
elsif jwe_key
|
586
|
+
jwe_key = jwe_key.first if jwe_key.is_a?(Array)
|
560
587
|
params = {
|
561
588
|
zip: "DEF",
|
562
589
|
copyright: oauth_jwt_jwe_copyright
|
@@ -576,13 +603,15 @@ module Rodauth
|
|
576
603
|
def jwt_decode(
|
577
604
|
token,
|
578
605
|
jwks: nil,
|
579
|
-
|
580
|
-
|
606
|
+
jws_algorithm: oauth_jwt_algorithm || oauth_jwt_public_key.keys.first || oauth_jwt_keys.keys.first,
|
607
|
+
jws_key: oauth_jwt_public_key || oauth_jwt_keys[jws_algorithm] || _jwt_key,
|
581
608
|
verify_claims: true,
|
582
609
|
verify_jti: true,
|
583
610
|
verify_iss: true,
|
584
611
|
verify_aud: false
|
585
612
|
)
|
613
|
+
jws_key = jws_key.first if jws_key.is_a?(Array)
|
614
|
+
|
586
615
|
# verifying the JWT implies verifying:
|
587
616
|
#
|
588
617
|
# issuer: check that server generated the token
|
@@ -631,15 +660,16 @@ module Rodauth
|
|
631
660
|
def jwt_decode_with_jwe(
|
632
661
|
token,
|
633
662
|
jwks: nil,
|
634
|
-
jwe_key: oauth_jwt_jwe_key,
|
635
663
|
jws_encryption_algorithm: oauth_jwt_jwe_algorithm,
|
636
664
|
jws_encryption_method: oauth_jwt_jwe_encryption_method,
|
665
|
+
jwe_key: oauth_jwt_jwe_keys[[jws_encryption_algorithm, jws_encryption_method]] || oauth_jwt_jwe_key,
|
637
666
|
**args
|
638
667
|
)
|
639
668
|
|
640
669
|
token = if jwks && jwks.any? { |k| k[:use] == "enc" }
|
641
670
|
JWE.__rodauth_oauth_decrypt_from_jwks(token, jwks, alg: jws_encryption_algorithm, enc: jws_encryption_method)
|
642
671
|
elsif jwe_key
|
672
|
+
jwe_key = jwe_key.first if jwe_key.is_a?(Array)
|
643
673
|
JWE.decrypt(token, jwe_key)
|
644
674
|
else
|
645
675
|
token
|
@@ -656,6 +686,21 @@ module Rodauth
|
|
656
686
|
|
657
687
|
def jwks_set
|
658
688
|
@jwks_set ||= [
|
689
|
+
*(
|
690
|
+
unless oauth_jwt_public_keys.empty?
|
691
|
+
oauth_jwt_public_keys.flat_map { |algo, pkeys| pkeys.map { |pkey| JWT::JWK.new(pkey).export.merge(use: "sig", alg: algo) } }
|
692
|
+
end
|
693
|
+
),
|
694
|
+
*(
|
695
|
+
unless oauth_jwt_jwe_public_keys.empty?
|
696
|
+
oauth_jwt_jwe_public_keys.flat_map do |(algo, _enc), pkeys|
|
697
|
+
pkeys.map do |pkey|
|
698
|
+
JWT::JWK.new(pkey).export.merge(use: "enc", alg: algo)
|
699
|
+
end
|
700
|
+
end
|
701
|
+
end
|
702
|
+
),
|
703
|
+
# legacy
|
659
704
|
(JWT::JWK.new(oauth_jwt_public_key).export.merge(use: "sig", alg: oauth_jwt_algorithm) if oauth_jwt_public_key),
|
660
705
|
(
|
661
706
|
if oauth_jwt_legacy_public_key
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
require "rodauth/oauth/version"
|
4
|
+
require "rodauth/oauth/ttl_store"
|
5
|
+
|
6
|
+
module Rodauth
|
7
|
+
Feature.define(:oauth_resource_indicators, :OauthResourceIndicators) do
|
8
|
+
depends :oauth_base
|
9
|
+
|
10
|
+
auth_value_method :oauth_grants_resource_column, :resource
|
11
|
+
auth_value_method :oauth_tokens_resource_column, :resource
|
12
|
+
|
13
|
+
def resource_indicators
|
14
|
+
return @resource_indicators if defined?(@resource_indicators)
|
15
|
+
|
16
|
+
resources = param_or_nil("resource")
|
17
|
+
|
18
|
+
return unless resources
|
19
|
+
|
20
|
+
if json_request? || param_or_nil("request") # signed request
|
21
|
+
resources = Array(resources)
|
22
|
+
else
|
23
|
+
query = request.form_data? ? request.body.read : request.query_string
|
24
|
+
# resource query param does not conform to rack parsing rules
|
25
|
+
resources = URI.decode_www_form(query).each_with_object([]) do |(k, v), memo|
|
26
|
+
memo << v if k == "resource"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
@resource_indicators = resources
|
31
|
+
end
|
32
|
+
|
33
|
+
def require_oauth_authorization(*)
|
34
|
+
super
|
35
|
+
|
36
|
+
return unless authorization_token[oauth_tokens_resource_column]
|
37
|
+
|
38
|
+
token_indicators = authorization_token[oauth_tokens_resource_column]
|
39
|
+
|
40
|
+
token_indicators = token_indicators.split(" ") if token_indicators.is_a?(String)
|
41
|
+
|
42
|
+
authorization_required unless token_indicators.any? { |resource| base_url.start_with?(resource) }
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def validate_oauth_token_params
|
48
|
+
super
|
49
|
+
|
50
|
+
return unless resource_indicators
|
51
|
+
|
52
|
+
resource_indicators.each do |resource|
|
53
|
+
redirect_response_error("invalid_target") unless check_valid_no_fragment_uri?(resource)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def create_oauth_token_from_token(oauth_token, update_params)
|
58
|
+
return super unless resource_indicators
|
59
|
+
|
60
|
+
return super unless oauth_token[oauth_tokens_oauth_grant_id_column]
|
61
|
+
|
62
|
+
oauth_grant = db[oauth_grants_table].where(
|
63
|
+
oauth_grants_id_column => oauth_token[oauth_tokens_oauth_grant_id_column],
|
64
|
+
oauth_grants_revoked_at_column => nil
|
65
|
+
).first
|
66
|
+
|
67
|
+
grant_indicators = oauth_grant[oauth_grants_resource_column]
|
68
|
+
|
69
|
+
grant_indicators = grant_indicators.split(" ") if grant_indicators.is_a?(String)
|
70
|
+
|
71
|
+
redirect_response_error("invalid_target") unless (grant_indicators - resource_indicators) != grant_indicators
|
72
|
+
|
73
|
+
super(oauth_token, update_params.merge(oauth_tokens_resource_column => resource_indicators))
|
74
|
+
end
|
75
|
+
|
76
|
+
def check_valid_no_fragment_uri?(uri)
|
77
|
+
check_valid_uri?(uri) && URI.parse(uri).fragment.nil?
|
78
|
+
end
|
79
|
+
|
80
|
+
module IndicatorAuthorizationCodeGrant
|
81
|
+
private
|
82
|
+
|
83
|
+
def validate_oauth_grant_params
|
84
|
+
super
|
85
|
+
|
86
|
+
return unless resource_indicators
|
87
|
+
|
88
|
+
resource_indicators.each do |resource|
|
89
|
+
redirect_response_error("invalid_target") unless check_valid_no_fragment_uri?(resource)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def create_oauth_token_from_authorization_code(oauth_grant, create_params)
|
94
|
+
return super unless resource_indicators
|
95
|
+
|
96
|
+
redirect_response_error("invalid_target") unless oauth_grant[oauth_grants_resource_column]
|
97
|
+
|
98
|
+
grant_indicators = oauth_grant[oauth_grants_resource_column]
|
99
|
+
|
100
|
+
grant_indicators = grant_indicators.split(" ") if grant_indicators.is_a?(String)
|
101
|
+
|
102
|
+
redirect_response_error("invalid_target") unless (grant_indicators - resource_indicators) != grant_indicators
|
103
|
+
|
104
|
+
super(oauth_grant, create_params.merge(oauth_tokens_resource_column => resource_indicators))
|
105
|
+
end
|
106
|
+
|
107
|
+
def create_oauth_grant(create_params = {})
|
108
|
+
create_params[oauth_grants_resource_column] = resource_indicators.join(" ") if resource_indicators
|
109
|
+
|
110
|
+
super
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
module IndicatorIntrospection
|
115
|
+
def json_token_introspect_payload(token)
|
116
|
+
return super unless token[oauth_tokens_oauth_grant_id_column]
|
117
|
+
|
118
|
+
payload = super
|
119
|
+
|
120
|
+
token_indicators = token[oauth_tokens_resource_column]
|
121
|
+
|
122
|
+
token_indicators = token_indicators.split(" ") if token_indicators.is_a?(String)
|
123
|
+
|
124
|
+
payload[:aud] = token_indicators
|
125
|
+
|
126
|
+
payload
|
127
|
+
end
|
128
|
+
|
129
|
+
def introspection_request(*)
|
130
|
+
payload = super
|
131
|
+
|
132
|
+
payload[oauth_tokens_resource_column] = payload["aud"] if payload["aud"]
|
133
|
+
|
134
|
+
payload
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
module IndicatorJwt
|
139
|
+
def jwt_claims(*)
|
140
|
+
return super unless resource_indicators
|
141
|
+
|
142
|
+
super.merge(aud: resource_indicators)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.included(rodauth)
|
147
|
+
super
|
148
|
+
rodauth.send(:include, IndicatorAuthorizationCodeGrant) if rodauth.features.include?(:oauth_authorization_code_grant)
|
149
|
+
rodauth.send(:include, IndicatorIntrospection) if rodauth.features.include?(:oauth_token_introspection)
|
150
|
+
rodauth.send(:include, IndicatorJwt) if rodauth.features.include?(:oauth_jwt)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -60,7 +60,7 @@ module Rodauth
|
|
60
60
|
id_token_signing_alg_values_supported
|
61
61
|
].freeze
|
62
62
|
|
63
|
-
depends :oauth_jwt
|
63
|
+
depends :account_expiration, :oauth_jwt
|
64
64
|
|
65
65
|
auth_value_method :oauth_application_default_scope, "openid"
|
66
66
|
auth_value_method :oauth_application_scopes, %w[openid]
|
@@ -73,8 +73,9 @@ module Rodauth
|
|
73
73
|
auth_value_method :oauth_applications_userinfo_encrypted_response_enc_column, :userinfo_encrypted_response_enc
|
74
74
|
|
75
75
|
auth_value_method :oauth_grants_nonce_column, :nonce
|
76
|
+
auth_value_method :oauth_grants_acr_column, :acr
|
76
77
|
auth_value_method :oauth_tokens_nonce_column, :nonce
|
77
|
-
auth_value_method :
|
78
|
+
auth_value_method :oauth_tokens_acr_column, :acr
|
78
79
|
|
79
80
|
translatable_method :invalid_scope_message, "The Access Token expired"
|
80
81
|
|
@@ -88,7 +89,13 @@ module Rodauth
|
|
88
89
|
auth_value_method :oauth_applications_post_logout_redirect_uri_column, :post_logout_redirect_uri
|
89
90
|
auth_value_method :use_rp_initiated_logout?, false
|
90
91
|
|
91
|
-
auth_value_methods(
|
92
|
+
auth_value_methods(
|
93
|
+
:get_oidc_param,
|
94
|
+
:get_additional_param,
|
95
|
+
:require_acr_value_phr,
|
96
|
+
:require_acr_value_phrh,
|
97
|
+
:require_acr_value
|
98
|
+
)
|
92
99
|
|
93
100
|
# /userinfo
|
94
101
|
route(:userinfo) do |r|
|
@@ -252,14 +259,43 @@ module Rodauth
|
|
252
259
|
|
253
260
|
private
|
254
261
|
|
262
|
+
if defined?(::I18n)
|
263
|
+
def before_authorize_route
|
264
|
+
if (ui_locales = param_or_nil("ui_locales"))
|
265
|
+
ui_locales = ui_locales.split(" ").map(&:to_sym)
|
266
|
+
ui_locales &= ::I18n.available_locales
|
267
|
+
|
268
|
+
::I18n.locale = ui_locales.first unless ui_locales.empty?
|
269
|
+
end
|
270
|
+
|
271
|
+
super
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
def validate_oauth_grant_params
|
276
|
+
return super unless (max_age = param_or_nil("max_age"))
|
277
|
+
|
278
|
+
max_age = Integer(max_age)
|
279
|
+
|
280
|
+
redirect_response_error("invalid_request") unless max_age.positive?
|
281
|
+
|
282
|
+
return unless Time.now - last_account_login_at > max_age
|
283
|
+
|
284
|
+
# force user to re-login
|
285
|
+
clear_session
|
286
|
+
set_session_value(login_redirect_session_key, request.fullpath)
|
287
|
+
redirect require_login_redirect
|
288
|
+
end
|
289
|
+
|
255
290
|
def require_authorizable_account
|
256
|
-
try_prompt
|
291
|
+
try_prompt
|
257
292
|
super
|
293
|
+
try_acr_values
|
258
294
|
end
|
259
295
|
|
260
296
|
# this executes before checking for a logged in account
|
261
297
|
def try_prompt
|
262
|
-
prompt = param_or_nil("prompt")
|
298
|
+
return unless (prompt = param_or_nil("prompt"))
|
263
299
|
|
264
300
|
case prompt
|
265
301
|
when "none"
|
@@ -314,16 +350,46 @@ module Rodauth
|
|
314
350
|
end
|
315
351
|
end
|
316
352
|
|
317
|
-
def
|
318
|
-
return
|
353
|
+
def try_acr_values
|
354
|
+
return unless (acr_values = param_or_nil("acr_values"))
|
355
|
+
|
356
|
+
acr_values.split(" ").each do |acr_value|
|
357
|
+
case acr_value
|
358
|
+
when "phr" then require_acr_value_phr
|
359
|
+
when "phrh" then require_acr_value_phrh
|
360
|
+
else
|
361
|
+
require_acr_value(acr_value)
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
319
365
|
|
320
|
-
|
366
|
+
def require_acr_value_phr
|
367
|
+
return unless respond_to?(:require_two_factor_authenticated)
|
368
|
+
|
369
|
+
require_two_factor_authenticated
|
370
|
+
end
|
371
|
+
|
372
|
+
def require_acr_value_phrh
|
373
|
+
require_acr_value_phr && two_factor_login_type_match?("webauthn")
|
374
|
+
end
|
375
|
+
|
376
|
+
def require_acr_value(_acr); end
|
377
|
+
|
378
|
+
def create_oauth_grant(create_params = {})
|
379
|
+
if (nonce = param_or_nil("nonce"))
|
380
|
+
create_params[oauth_grants_nonce_column] = nonce
|
381
|
+
end
|
382
|
+
if (acr = param_or_nil("acr"))
|
383
|
+
create_params[oauth_grants_acr_column] = acr
|
384
|
+
end
|
385
|
+
super
|
321
386
|
end
|
322
387
|
|
323
388
|
def create_oauth_token_from_authorization_code(oauth_grant, create_params)
|
324
|
-
|
389
|
+
create_params[oauth_tokens_nonce_column] = oauth_grant[oauth_grants_nonce_column] if oauth_grant[oauth_grants_nonce_column]
|
390
|
+
create_params[oauth_tokens_acr_column] = oauth_grant[oauth_grants_acr_column] if oauth_grant[oauth_grants_acr_column]
|
325
391
|
|
326
|
-
super
|
392
|
+
super
|
327
393
|
end
|
328
394
|
|
329
395
|
def create_oauth_token(*)
|
@@ -338,11 +404,13 @@ module Rodauth
|
|
338
404
|
return unless oauth_scopes.include?("openid")
|
339
405
|
|
340
406
|
id_token_claims = jwt_claims(oauth_token)
|
407
|
+
|
341
408
|
id_token_claims[:nonce] = oauth_token[oauth_tokens_nonce_column] if oauth_token[oauth_tokens_nonce_column]
|
342
409
|
|
410
|
+
id_token_claims[:acr] = oauth_token[oauth_tokens_acr_column] if oauth_token[oauth_tokens_acr_column]
|
411
|
+
|
343
412
|
# Time when the End-User authentication occurred.
|
344
|
-
|
345
|
-
id_token_claims[:auth_time] = oauth_token[oauth_tokens_auth_time_column].to_i
|
413
|
+
id_token_claims[:auth_time] = last_account_login_at.to_i
|
346
414
|
|
347
415
|
account = db[accounts_table].where(account_id_column => oauth_token[oauth_tokens_account_id_column]).first
|
348
416
|
|
@@ -377,16 +445,23 @@ module Rodauth
|
|
377
445
|
|
378
446
|
oidc_scopes, additional_scopes = scopes_by_claim.keys.partition { |key| OIDC_SCOPES_MAP.key?(key) }
|
379
447
|
|
448
|
+
if (claims_locales = param_or_nil("claims_locales"))
|
449
|
+
claims_locales = claims_locales.split(" ").map(&:to_sym)
|
450
|
+
end
|
451
|
+
|
380
452
|
unless oidc_scopes.empty?
|
381
453
|
if respond_to?(:get_oidc_param)
|
454
|
+
get_oidc_param = proxy_get_param(:get_oidc_param, claims, claims_locales)
|
455
|
+
|
382
456
|
oidc_scopes.each do |scope|
|
383
457
|
scope_claims = claims
|
384
458
|
params = scopes_by_claim[scope]
|
385
459
|
params = params.empty? ? OIDC_SCOPES_MAP[scope] : (OIDC_SCOPES_MAP[scope] & params)
|
386
460
|
|
387
461
|
scope_claims = (claims["address"] = {}) if scope == "address"
|
462
|
+
|
388
463
|
params.each do |param|
|
389
|
-
|
464
|
+
get_oidc_param[account, param, scope_claims]
|
390
465
|
end
|
391
466
|
end
|
392
467
|
else
|
@@ -397,14 +472,39 @@ module Rodauth
|
|
397
472
|
return if additional_scopes.empty?
|
398
473
|
|
399
474
|
if respond_to?(:get_additional_param)
|
475
|
+
get_additional_param = proxy_get_param(:get_additional_param, claims, claims_locales)
|
476
|
+
|
400
477
|
additional_scopes.each do |scope|
|
401
|
-
|
478
|
+
get_additional_param[account, scope.to_sym]
|
402
479
|
end
|
403
480
|
else
|
404
481
|
warn "`get_additional_param(account, claim)` must be implemented to use oidc scopes."
|
405
482
|
end
|
406
483
|
end
|
407
484
|
|
485
|
+
def proxy_get_param(get_param_func, claims, claims_locales)
|
486
|
+
meth = method(get_param_func)
|
487
|
+
if meth.arity == 2
|
488
|
+
->(account, param, cl = claims) { cl[param] = meth[account, param] }
|
489
|
+
elsif claims_locales.nil?
|
490
|
+
->(account, param, cl = claims) { cl[param] = meth[account, param, nil] }
|
491
|
+
else
|
492
|
+
lambda do |account, param, cl = claims|
|
493
|
+
claims_values = claims_locales.map do |locale|
|
494
|
+
meth[account, param, locale]
|
495
|
+
end
|
496
|
+
|
497
|
+
if claims_values.uniq.size == 1
|
498
|
+
cl[param] = claims_values.first
|
499
|
+
else
|
500
|
+
claims_locales.zip(claims_values).each do |locale, value|
|
501
|
+
cl["#{param}##{locale}"] = value
|
502
|
+
end
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
408
508
|
def json_access_token_payload(oauth_token)
|
409
509
|
payload = super
|
410
510
|
payload["id_token"] = oauth_token[:id_token] if oauth_token[:id_token]
|
@@ -452,6 +552,12 @@ module Rodauth
|
|
452
552
|
oauth_tokens_oauth_application_id_column => oauth_application[oauth_applications_id_column],
|
453
553
|
oauth_tokens_scopes_column => scopes
|
454
554
|
}
|
555
|
+
if (nonce = param_or_nil("nonce"))
|
556
|
+
create_params[oauth_grants_nonce_column] = nonce
|
557
|
+
end
|
558
|
+
if (acr = param_or_nil("acr"))
|
559
|
+
create_params[oauth_grants_acr_column] = acr
|
560
|
+
end
|
455
561
|
oauth_token = generate_oauth_token(create_params, false)
|
456
562
|
generate_id_token(oauth_token)
|
457
563
|
params = json_access_token_payload(oauth_token)
|
data/locales/en.yml
CHANGED
@@ -7,7 +7,7 @@ en:
|
|
7
7
|
revoke_oauth_token_notice_flash: "The oauth token has been revoked"
|
8
8
|
device_verification_notice_flash: "The device is verified"
|
9
9
|
user_code_not_found_error_flash: "No device to authorize with the given user code"
|
10
|
-
|
10
|
+
authorize_page_title: "Authorize"
|
11
11
|
oauth_applications_page_title: "Oauth Applications"
|
12
12
|
oauth_application_page_title: "Oauth Application"
|
13
13
|
new_oauth_application_page_title: "New Oauth Application"
|
@@ -17,6 +17,7 @@ en:
|
|
17
17
|
device_search_page_title: "Device Search"
|
18
18
|
oauth_management_pagination_previous_button: "Previous"
|
19
19
|
oauth_management_pagination_next_button: "Next"
|
20
|
+
oauth_tokens_scopes_label: "Scopes"
|
20
21
|
oauth_applications_name_label: "Name"
|
21
22
|
oauth_applications_description_label: "Description"
|
22
23
|
oauth_applications_scopes_label: "Default scopes"
|
data/locales/pt.yml
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
pt:
|
2
|
+
rodauth:
|
3
|
+
require_authorization_error_flash: "Autorize para continuar"
|
4
|
+
create_oauth_application_error_flash: "Aconteceu um erro ao registar o aplicativo oauth"
|
5
|
+
create_oauth_application_notice_flash: "O seu aplicativo oauth foi registado com sucesso"
|
6
|
+
revoke_unauthorized_account_error_flash: "Não está autorizado a revogar este token"
|
7
|
+
revoke_oauth_token_notice_flash: "O token oauth foi revogado com sucesso"
|
8
|
+
device_verification_notice_flash: "O dispositivo foi verificado com sucesso"
|
9
|
+
user_code_not_found_error_flash: "Não existe nenhum dispositivo a ser autorizado com o código de usuário inserido"
|
10
|
+
authorize_page_title: "Autorizar"
|
11
|
+
oauth_applications_page_title: "Aplicativos OAuth"
|
12
|
+
oauth_application_page_title: "Aplicativo Oauth"
|
13
|
+
new_oauth_application_page_title: "Novo Aplicativo Oauth"
|
14
|
+
oauth_application_oauth_tokens_page_title: "Tokens Oauth do Aplicativo"
|
15
|
+
oauth_tokens_page_title: "Os meus Tokens Oauth"
|
16
|
+
device_verification_page_title: "Verificação de dispositivo"
|
17
|
+
device_search_page_title: "Pesquisa de dispositivo"
|
18
|
+
oauth_management_pagination_previous_button: "Anterior"
|
19
|
+
oauth_management_pagination_next_button: "Próxima"
|
20
|
+
oauth_tokens_scopes_label: "Escopos"
|
21
|
+
oauth_applications_name_label: "Nome"
|
22
|
+
oauth_applications_description_label: "Descrição"
|
23
|
+
oauth_applications_scopes_label: "Escopos prédefinidos"
|
24
|
+
oauth_applications_contacts_label: "Contactos"
|
25
|
+
oauth_applications_homepage_url_label: "URL da página principal"
|
26
|
+
oauth_applications_tos_uri_label: "URL dos termos de serviço"
|
27
|
+
oauth_applications_policy_uri_label: "URL das diretrizes"
|
28
|
+
oauth_applications_redirect_uri_label: "URL para redireccionamento"
|
29
|
+
oauth_applications_client_secret_label: "Segredo de cliente"
|
30
|
+
oauth_applications_client_id_label: "ID do cliente"
|
31
|
+
oauth_grant_user_code_label: "Código do usuário"
|
32
|
+
oauth_grant_user_jws_jwk_label: "Chaves JSON Web"
|
33
|
+
oauth_grant_user_jwt_public_key_label: "Chave pública"
|
34
|
+
oauth_application_button: "Registar"
|
35
|
+
oauth_authorize_button: "Autorizar"
|
36
|
+
oauth_token_revoke_button: "Revogar"
|
37
|
+
oauth_authorize_post_button: "Voltar para o aplicativo cliente"
|
38
|
+
oauth_device_verification_button: "Verificar"
|
39
|
+
oauth_device_search_button: "Pesquisar"
|
40
|
+
invalid_client_message: "A autenticação do cliente falhou"
|
41
|
+
invalid_grant_type_message: "Tipo de atribuição inválida"
|
42
|
+
invalid_grant_message: "Atribuição inválida"
|
43
|
+
invalid_scope_message: "Escopo inválido"
|
44
|
+
invalid_url_message: "URL inválido"
|
45
|
+
unsupported_token_type_message: "Sugestão de tipo de token inválida"
|
46
|
+
unique_error_message: "já está sendo utilizado"
|
47
|
+
null_error_message: "não está preenchido"
|
48
|
+
already_in_use_message: "erro ao gerar token único"
|
49
|
+
expired_token_message: "o código de dispositivo expirou"
|
50
|
+
access_denied_message: "o pedido de autorização foi negado"
|
51
|
+
authorization_pending_message: "o pedido de autorização ainda está pendente"
|
52
|
+
slow_down_message: "o pedido de autorização ainda está pendente mas o intervalo de actualização deve ser aumentado"
|
53
|
+
code_challenge_required_message: "código de negociação necessário"
|
54
|
+
unsupported_transform_algorithm_message: "algoritmo de transformação não suportado"
|
55
|
+
request_uri_not_supported_message: "request_uri não é suportado"
|
56
|
+
invalid_request_object_message: "request_object é inválido"
|
57
|
+
invalid_scope_message: "O Token de acesso expirou"
|
data/templates/authorize.str
CHANGED
@@ -81,10 +81,20 @@
|
|
81
81
|
#{"<input type=\"hidden\" name=\"response_type\" value=\"#{rodauth.param("response_type")}\"/>" if rodauth.param_or_nil("response_type")}
|
82
82
|
#{"<input type=\"hidden\" name=\"response_mode\" value=\"#{rodauth.param("response_mode")}\"/>" if rodauth.param_or_nil("response_mode")}
|
83
83
|
#{"<input type=\"hidden\" name=\"state\" value=\"#{rodauth.param("state")}\"/>" if rodauth.param_or_nil("state")}
|
84
|
-
#{"<input type=\"hidden\" name=\"nonce\" value=\"#{rodauth.param("nonce")}\"/>" if rodauth.param_or_nil("nonce")}
|
85
84
|
#{"<input type=\"hidden\" name=\"redirect_uri\" value=\"#{rodauth.redirect_uri}\"/>" if rodauth.param_or_nil("redirect_uri")}
|
86
|
-
#{"<input type=\"hidden\" name=\"code_challenge\" value=\"#{rodauth.param("code_challenge")}\"/>" if rodauth.param_or_nil("code_challenge")}
|
87
|
-
#{"<input type=\"hidden\" name=\"code_challenge_method\" value=\"#{rodauth.param("code_challenge_method")}\"/>" if rodauth.param_or_nil("code_challenge_method")}
|
85
|
+
#{"<input type=\"hidden\" name=\"code_challenge\" value=\"#{rodauth.param("code_challenge")}\"/>" if rodauth.features.include?(:oauth_pkce) && rodauth.param_or_nil("code_challenge")}
|
86
|
+
#{"<input type=\"hidden\" name=\"code_challenge_method\" value=\"#{rodauth.param("code_challenge_method")}\"/>" if rodauth.features.include?(:oauth_pkce) && rodauth.param_or_nil("code_challenge_method")}
|
87
|
+
#{"<input type=\"hidden\" name=\"nonce\" value=\"#{rodauth.param("nonce")}\"/>" if rodauth.features.include?(:oidc) && rodauth.param_or_nil("nonce")}
|
88
|
+
#{"<input type=\"hidden\" name=\"ui_locales\" value=\"#{rodauth.param("ui_locales")}\"/>" if rodauth.features.include?(:oidc) && rodauth.param_or_nil("ui_locales")}
|
89
|
+
#{"<input type=\"hidden\" name=\"claims_locales\" value=\"#{rodauth.param("claims_locales")}\"/>" if rodauth.features.include?(:oidc) && rodauth.param_or_nil("claims_locales")}
|
90
|
+
#{"<input type=\"hidden\" name=\"acr\" value=\"#{rodauth.param("acr_values")}\"/>" if rodauth.features.include?(:oidc) && rodauth.param_or_nil("acr_values")}
|
91
|
+
#{
|
92
|
+
if rodauth.features.include?(:oauth_resource_indicators) && rodauth.resource_indicators
|
93
|
+
rodauth.resource_indicators.map do |resource|
|
94
|
+
"<input type=\"hidden\" name=\"resource\" value=\"#{resource}\"/>"
|
95
|
+
end.join
|
96
|
+
end
|
97
|
+
}
|
88
98
|
</div>
|
89
99
|
<p class="text-center">
|
90
100
|
<input type="submit" class="btn btn-outline-primary" value="#{h(rodauth.oauth_authorize_button)}"/>
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rodauth-oauth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tiago Cardoso
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-06-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rodauth
|
@@ -39,6 +39,7 @@ extra_rdoc_files:
|
|
39
39
|
- doc/release_notes/0_0_4.md
|
40
40
|
- doc/release_notes/0_0_5.md
|
41
41
|
- doc/release_notes/0_0_6.md
|
42
|
+
- doc/release_notes/0_10_0.md
|
42
43
|
- doc/release_notes/0_1_0.md
|
43
44
|
- doc/release_notes/0_2_0.md
|
44
45
|
- doc/release_notes/0_3_0.md
|
@@ -70,6 +71,7 @@ files:
|
|
70
71
|
- doc/release_notes/0_0_4.md
|
71
72
|
- doc/release_notes/0_0_5.md
|
72
73
|
- doc/release_notes/0_0_6.md
|
74
|
+
- doc/release_notes/0_10_0.md
|
73
75
|
- doc/release_notes/0_1_0.md
|
74
76
|
- doc/release_notes/0_2_0.md
|
75
77
|
- doc/release_notes/0_3_0.md
|
@@ -120,6 +122,7 @@ files:
|
|
120
122
|
- lib/rodauth/features/oauth_jwt_bearer_grant.rb
|
121
123
|
- lib/rodauth/features/oauth_management_base.rb
|
122
124
|
- lib/rodauth/features/oauth_pkce.rb
|
125
|
+
- lib/rodauth/features/oauth_resource_indicators.rb
|
123
126
|
- lib/rodauth/features/oauth_resource_server.rb
|
124
127
|
- lib/rodauth/features/oauth_saml_bearer_grant.rb
|
125
128
|
- lib/rodauth/features/oauth_token_introspection.rb
|
@@ -135,6 +138,7 @@ files:
|
|
135
138
|
- lib/rodauth/oauth/ttl_store.rb
|
136
139
|
- lib/rodauth/oauth/version.rb
|
137
140
|
- locales/en.yml
|
141
|
+
- locales/pt.yml
|
138
142
|
- templates/authorize.str
|
139
143
|
- templates/client_secret_field.str
|
140
144
|
- templates/description_field.str
|