rodauth-oauth 1.3.2 → 1.4.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/README.md +17 -10
- data/doc/release_notes/1_4_0.md +49 -0
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize.html.erb +23 -23
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/frontchannel_logout.html.erb +10 -0
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_applications.html.erb +1 -1
- data/lib/generators/rodauth/oauth/templates/db/migrate/create_rodauth_oauth.rb +20 -0
- data/lib/generators/rodauth/oauth/views_generator.rb +2 -2
- data/lib/rodauth/features/oauth_application_management.rb +1 -1
- data/lib/rodauth/features/oauth_assertion_base.rb +1 -1
- data/lib/rodauth/features/oauth_authorize_base.rb +1 -1
- data/lib/rodauth/features/oauth_base.rb +31 -30
- data/lib/rodauth/features/oauth_device_code_grant.rb +2 -2
- data/lib/rodauth/features/oauth_dynamic_client_registration.rb +1 -0
- data/lib/rodauth/features/oauth_grant_management.rb +1 -1
- data/lib/rodauth/features/oauth_jwt.rb +3 -3
- data/lib/rodauth/features/oauth_jwt_base.rb +15 -10
- data/lib/rodauth/features/oauth_jwt_bearer_grant.rb +1 -1
- data/lib/rodauth/features/oauth_jwt_jwks.rb +1 -1
- data/lib/rodauth/features/oauth_resource_server.rb +1 -1
- data/lib/rodauth/features/oauth_saml_bearer_grant.rb +79 -47
- data/lib/rodauth/features/oauth_tls_client_auth.rb +1 -1
- data/lib/rodauth/features/oauth_token_introspection.rb +1 -1
- data/lib/rodauth/features/oauth_token_revocation.rb +1 -1
- data/lib/rodauth/features/oidc.rb +27 -8
- data/lib/rodauth/features/oidc_backchannel_logout.rb +120 -0
- data/lib/rodauth/features/oidc_dynamic_client_registration.rb +25 -0
- data/lib/rodauth/features/oidc_frontchannel_logout.rb +134 -0
- data/lib/rodauth/features/oidc_logout_base.rb +76 -0
- data/lib/rodauth/features/oidc_rp_initiated_logout.rb +29 -6
- data/lib/rodauth/features/oidc_session_management.rb +89 -0
- data/lib/rodauth/oauth/http_extensions.rb +1 -1
- data/lib/rodauth/oauth/version.rb +1 -1
- data/locales/en.yml +9 -0
- data/locales/pt.yml +9 -0
- data/templates/check_session.str +67 -0
- data/templates/frontchannel_logout.str +17 -0
- metadata +11 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87a675b4d44ba1003c451dc25b22c0b9fec5f346e2d03ef114d19f77ffd768da
|
4
|
+
data.tar.gz: 6921bd4b1bef1c88124bc323e293d9218959f94c985b9ed4c7e4b4452d474d55
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4969a6eba906a0b180c325528c780c122b841ce7cc59c844d510a0dee8791de41cebb294f0f11d6bd23e09644732bf4b32b72e9b8ac918f0c53249fdcf4e6fd0
|
7
|
+
data.tar.gz: 4fae6adcc4b8ac567160182c52dc1c8d8f6fe34d0875f617b5284777fb64f0f5e7c4430ec9e769d78a8774e04d1c755bbabc4be6757d916b07fa5d14dc2b7eb4
|
data/README.md
CHANGED
@@ -18,8 +18,12 @@ This is an extension to the `rodauth` gem which implements the [OAuth 2.0 framew
|
|
18
18
|
* Dynamic OP
|
19
19
|
* Form Post OP
|
20
20
|
* 3rd Party-Init OP
|
21
|
+
* Session Management OP
|
22
|
+
* RP-Initiated Logout OP
|
23
|
+
* Front-Channel Logout OP
|
24
|
+
* Back-Channel Logout OP
|
21
25
|
|
22
|
-
|
26
|
+
The certifications were obtained using the [example OIDC server](/examples/oidc/authentication_server.rb) deployed [here](https://rodauth-oauth-oidc.onrender.com/).
|
23
27
|
|
24
28
|
## Features
|
25
29
|
|
@@ -43,7 +47,6 @@ This gem implements the following RFCs and features of OAuth:
|
|
43
47
|
* `oauth_jwt_secured_authorization_response_mode` - [JWT Secured Authorization Response_mode](https://openid.net/specs/openid-financial-api-jarm.html);
|
44
48
|
* `oauth_resource_indicators` - [Resource Indicators](https://datatracker.ietf.org/doc/html/rfc8707);
|
45
49
|
* Access Type (Token refresh online and offline);
|
46
|
-
* `oauth_http_mac` - [MAC Authentication Scheme](https://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token-02);
|
47
50
|
* `oauth_assertion_base` - [Assertion Framework](https://datatracker.ietf.org/doc/html/rfc7521);
|
48
51
|
* `oauth_saml_bearer_grant` - [SAML 2.0 Bearer Assertion](https://datatracker.ietf.org/doc/html/rfc7522);
|
49
52
|
* `oauth_jwt_bearer_grant` - [JWT Bearer Assertion](https://datatracker.ietf.org/doc/html/rfc7523);
|
@@ -52,13 +55,17 @@ This gem implements the following RFCs and features of OAuth:
|
|
52
55
|
* OAuth application and token management dashboards;
|
53
56
|
* The recommendations for [Native Apps](https://www.rfc-editor.org/rfc/rfc8252);
|
54
57
|
|
55
|
-
It also implements
|
58
|
+
It also implements several components of [OpenID Connect](https://openid.net/connect/) on top of the OAuth features it provides, including:
|
56
59
|
|
57
|
-
* [OpenID Connect Core](https://gitlab.com/os85/rodauth-oauth/-/wikis/Id-Token-Authentication);
|
58
|
-
* [OpenID
|
59
|
-
* [OpenID Multiple Response Types](https://gitlab.com/os85/rodauth-oauth/-/wikis/Hybrid-flow);
|
60
|
-
* [OpenID Connect
|
61
|
-
* [
|
60
|
+
* `oidc` - [OpenID Connect Core](https://gitlab.com/os85/rodauth-oauth/-/wikis/Id-Token-Authentication);
|
61
|
+
* `oidc_self_issued` - [Self-Issued OpenID Provider](https://openid.net/specs/openid-connect-core-1_0.html#SelfIssued)
|
62
|
+
* [OpenID Multiple Response Types](https://gitlab.com/os85/rodauth-oauth/-/wikis/Hybrid-flow);
|
63
|
+
* [OpenID Connect Discovery](https://gitlab.com/os85/rodauth-oauth/-/wikis/OIDC-Dynamic-Client-Registration);
|
64
|
+
* `oidc_dynamic_client_registration` - [OpenID Connect Dynamic Client Registration](https://gitlab.com/os85/rodauth-oauth/-/wikis/OIDC-Dynamic-Client-Registration);
|
65
|
+
* `oidc_session_management` - [Session Management](https://gitlab.com/os85/rodauth-oauth/-/wikis/Session-Management);
|
66
|
+
* `oidc_rp_initiated_logout` - [RP Initiated Logout](https://gitlab.com/os85/rodauth-oauth/-/wikis/RP-Initiated-Logout);
|
67
|
+
* `oidc_frontchannel_logout` - [Frontchannel Logout](https://gitlab.com/os85/rodauth-oauth/-/wikis/Frontchannel-Logout);
|
68
|
+
* `oidc_backchannel_logout` - [Backchannel Logout](https://gitlab.com/os85/rodauth-oauth/-/wikis/Backchannel-Logout);
|
62
69
|
|
63
70
|
This gem supports also rails (through [rodauth-rails]((https://github.com/janko/rodauth-rails))).
|
64
71
|
|
@@ -83,8 +90,8 @@ Or install it yourself as:
|
|
83
90
|
## Resources
|
84
91
|
| | |
|
85
92
|
| ------------- | ----------------------------------------------------------- |
|
86
|
-
| Website | https://
|
87
|
-
| Documentation | https://
|
93
|
+
| Website | https://honeyryderchuck.gitlab.io/rodauth-oauth/ |
|
94
|
+
| Documentation | https://honeyryderchuck.gitlab.io/rodauth-oauth/rdoc/ |
|
88
95
|
| Wiki | https://gitlab.com/os85/rodauth-oauth/wikis/home |
|
89
96
|
| CI | https://gitlab.com/os85/rodauth-oauth/pipelines |
|
90
97
|
|
@@ -0,0 +1,49 @@
|
|
1
|
+
## 1.4.0 (08/11/2023)
|
2
|
+
|
3
|
+
## Highlights
|
4
|
+
|
5
|
+
rodauth-oauth is now [OpenID certified](https://openid.net/certification/) for the following logout profiles:
|
6
|
+
|
7
|
+
* Session Management OP
|
8
|
+
* RP-Initiated Logout OP
|
9
|
+
* Front-Channel Logout OP
|
10
|
+
* Back-Channel Logout OP
|
11
|
+
|
12
|
+
The OIDC server used to run the test can be found [here](https://gitlab.com/os85/rodauth-oauth/-/blob/master/examples/oidc/authentication_server.rb) and deployed [here](https://rodauth-oauth-oidc.onrender.com).
|
13
|
+
|
14
|
+
## Features
|
15
|
+
|
16
|
+
### OIDC logout features
|
17
|
+
|
18
|
+
`rodauth-oauth` ships with the following new features:
|
19
|
+
|
20
|
+
* `oidc_sesssion_management` - enables [OIDC session management](https://openid.net/specs/openid-connect-session-1_0.html)
|
21
|
+
* `oidc_frontchannel_logout` - enables [OIDC frontchannel logout](https://openid.net/specs/openid-connect-frontchannel-1_0.html)
|
22
|
+
* `oidc_backchannel_logout` - enables [OIDC backchannel logout](https://openid.net/specs/openid-connect-backchannel-1_0.html)
|
23
|
+
|
24
|
+
which, along with the existing `oidc_rp_initiated_logout`, implemment all OIDC logout profiles.
|
25
|
+
|
26
|
+
## Breaking changes
|
27
|
+
|
28
|
+
If you're using `oidc`, the dependency on `account_expiration` has been replaced by the `active_sessions` rodauth feature. This change is required because it fixes bugs associated with accounts expiring in order for id token invalidation to work.
|
29
|
+
|
30
|
+
If you're migrating, it's recommended that you keep depending on `account_expiration` during the transition, add `active_sessions` tables as per [rodauth specs](https://github.com/jeremyevans/rodauth/blob/master/spec/migrate/001_tables.rb#L150), and run them alongside one another for the max period ID tokens should be valid, after which you can remove `account_expiration` and its tables.
|
31
|
+
|
32
|
+
## Improvements
|
33
|
+
|
34
|
+
### OAuth SAML Bearer Grant per oauth application settings
|
35
|
+
|
36
|
+
The `oauth_saml_bearer_grant` feature requires a new table/resource, SAML settings, which enable "per client applicatioon" SAML settings, and therefore, make this feature usable in enterprise/multi-tenancy scenarios.
|
37
|
+
|
38
|
+
## Bugfixes
|
39
|
+
|
40
|
+
* remove `html_safe` usage in rails views to prevent XSS in the authorize form.
|
41
|
+
* fixed for OIDC RFC 5.4 when requesting claims using scope values
|
42
|
+
* `oauth_rp_initiated_logout` does not crash anymore on logout requests with `id_token_hint`
|
43
|
+
* `oauth_rp_initiated_logout` now works with response types other than `code`
|
44
|
+
* `oauth_rp_initiated_logout` emits an ID token hint invalid message when not able to decode the `id_token_hint`
|
45
|
+
|
46
|
+
## Chore
|
47
|
+
|
48
|
+
* Using `auth_methods` everywhere where `auth_value_methods` was used and didn't make sense.
|
49
|
+
* `oauth_tls_client_auth` is not dependent on the `oauth_jwt` feature, and can therefore be used with non-JWT access tokens, at least with the features which do not require it.
|
@@ -4,7 +4,7 @@
|
|
4
4
|
<% end %>
|
5
5
|
<% application_uri = rodauth.oauth_application[rodauth.oauth_applications_homepage_url_column] %>
|
6
6
|
<% application_name = application_uri ? link_to(rodauth.oauth_application[rodauth.oauth_applications_name_column], application_uri) : rodauth.oauth_application[rodauth.oauth_applications_name_column] %>
|
7
|
-
<p class="lead"><%= rodauth.authorize_page_lead(name: application_name)
|
7
|
+
<p class="lead"><%= rodauth.authorize_page_lead(name: application_name) %></p>
|
8
8
|
|
9
9
|
<div class="list-group">
|
10
10
|
<% if rodauth.oauth_application[rodauth.oauth_applications_tos_uri_column] %>
|
@@ -37,10 +37,10 @@
|
|
37
37
|
</div>
|
38
38
|
<% end %>
|
39
39
|
<% end %>
|
40
|
-
<%= hidden_field_tag :client_id, rodauth.
|
40
|
+
<%= hidden_field_tag :client_id, rodauth.param_or_nil("client_id") %>
|
41
41
|
<% %w[access_type response_type response_mode state redirect_uri].each do |oauth_param| %>
|
42
|
-
<% if rodauth.
|
43
|
-
<%= hidden_field_tag oauth_param, rodauth.
|
42
|
+
<% if rodauth.param_or_nil(oauth_param) %>
|
43
|
+
<%= hidden_field_tag oauth_param, rodauth.param_or_nil(oauth_param) %>
|
44
44
|
<% end %>
|
45
45
|
<% end %>
|
46
46
|
<% if rodauth.features.include?(:oauth_resource_indicators) && rodauth.resource_indicators %>
|
@@ -49,39 +49,39 @@
|
|
49
49
|
<% end %>
|
50
50
|
<% end %>
|
51
51
|
<% if rodauth.features.include?(:oauth_pkce) %>
|
52
|
-
<% if rodauth.
|
53
|
-
<%= hidden_field_tag :code_challenge, rodauth.
|
52
|
+
<% if rodauth.param_or_nil("code_challenge") %>
|
53
|
+
<%= hidden_field_tag :code_challenge, rodauth.param_or_nil("code_challenge") %>
|
54
54
|
<% end %>
|
55
|
-
<% if rodauth.
|
56
|
-
<%= hidden_field_tag :code_challenge_method, rodauth.
|
55
|
+
<% if rodauth.param_or_nil("code_challenge_method") %>
|
56
|
+
<%= hidden_field_tag :code_challenge_method, rodauth.param_or_nil("code_challenge_method") %>
|
57
57
|
<% end %>
|
58
58
|
<% end %>
|
59
59
|
<% if rodauth.features.include?(:oidc) %>
|
60
|
-
<% if rodauth.
|
61
|
-
<%= hidden_field_tag :prompt, rodauth.
|
60
|
+
<% if rodauth.param_or_nil("prompt") %>
|
61
|
+
<%= hidden_field_tag :prompt, rodauth.param_or_nil("prompt") %>
|
62
62
|
<% end %>
|
63
|
-
<% if rodauth.
|
64
|
-
<%= hidden_field_tag :nonce, rodauth.
|
63
|
+
<% if rodauth.param_or_nil("nonce") %>
|
64
|
+
<%= hidden_field_tag :nonce, rodauth.param_or_nil("nonce") %>
|
65
65
|
<% end %>
|
66
|
-
<% if rodauth.
|
67
|
-
<%= hidden_field_tag :ui_locales, rodauth.
|
66
|
+
<% if rodauth.param_or_nil("ui_locales") %>
|
67
|
+
<%= hidden_field_tag :ui_locales, rodauth.param_or_nil("ui_locales") %>
|
68
68
|
<% end %>
|
69
|
-
<% if rodauth.
|
70
|
-
<%= hidden_field_tag :claims_locales, rodauth.
|
69
|
+
<% if rodauth.param_or_nil("claims_locales") %>
|
70
|
+
<%= hidden_field_tag :claims_locales, rodauth.param_or_nil("claims_locales") %>
|
71
71
|
<% end %>
|
72
|
-
<% if rodauth.
|
73
|
-
<%= hidden_field_tag :claims, sanitize(rodauth.
|
72
|
+
<% if rodauth.param_or_nil("claims") %>
|
73
|
+
<%= hidden_field_tag :claims, sanitize(rodauth.param_or_nil("claims")) %>
|
74
74
|
<% end %>
|
75
|
-
<% if rodauth.
|
76
|
-
<%= hidden_field_tag :acr_values, rodauth.
|
75
|
+
<% if rodauth.param_or_nil("acr_values") %>
|
76
|
+
<%= hidden_field_tag :acr_values, rodauth.param_or_nil("acr_values") %>
|
77
77
|
<% end %>
|
78
|
-
<% if rodauth.
|
79
|
-
<%= hidden_field_tag :registration, rodauth.
|
78
|
+
<% if rodauth.param_or_nil("registration") %>
|
79
|
+
<%= hidden_field_tag :registration, rodauth.param_or_nil("registration") %>
|
80
80
|
<% end %>
|
81
81
|
<% end %>
|
82
82
|
</div>
|
83
83
|
<p class="text-center">
|
84
84
|
<%= submit_tag rodauth.oauth_authorize_button, class: "btn btn-outline-primary" %>
|
85
|
-
<%= link_to rodauth.oauth_cancel_button, "#{rodauth.redirect_uri}?error=access_denied&error_description=The+resource+owner+or+authorization+server+denied+the+request#{"&state=\#{CGI.escape(rodauth.state)}" if rodauth.
|
85
|
+
<%= link_to rodauth.oauth_cancel_button, "#{rodauth.redirect_uri}?error=access_denied&error_description=The+resource+owner+or+authorization+server+denied+the+request#{"&state=\#{CGI.escape(rodauth.state)}" if rodauth.param_or_nil("state") }", class: "btn btn-outline-danger" %>
|
86
86
|
</p>
|
87
87
|
<% end %>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<div class="mb-3">
|
2
|
+
<h1><%= rodauth.oauth_frontchannel_logout_redirecting_lead %></h1>
|
3
|
+
<p>
|
4
|
+
<%= rodauth.oauth_frontchannel_logout_redirecting_label(link: link_to(rodauth.oauth_frontchannel_logout_redirecting_link_label, rodauth.logout_redirect)) %>
|
5
|
+
</p>
|
6
|
+
<% rodauth.frontchannel_logout_urls.each do |logout_url| %>
|
7
|
+
<iframe src="<%= logout_url %>"></iframe>
|
8
|
+
<% end %>
|
9
|
+
</div>
|
10
|
+
<meta http-equiv="refresh" content="5; URL=<%= rodauth.logout_redirect %>" />
|
@@ -59,6 +59,14 @@ class CreateRodauthOauth < ActiveRecord::Migration<%= migration_version %>
|
|
59
59
|
|
60
60
|
# :oidc_rp_initiated_logout enabled
|
61
61
|
t.string :post_logout_redirect_uris, null: false
|
62
|
+
|
63
|
+
# frontchannel logout
|
64
|
+
t.string :frontchannel_logout_uri
|
65
|
+
t.boolean :frontchannel_logout_session_required, default: false
|
66
|
+
|
67
|
+
# backchannel logout
|
68
|
+
t.string :backchannel_logout_uri
|
69
|
+
t.boolean :backchannel_logout_session_required, default: false
|
62
70
|
end
|
63
71
|
|
64
72
|
create_table :oauth_grants do |t|
|
@@ -107,5 +115,17 @@ class CreateRodauthOauth < ActiveRecord::Migration<%= migration_version %>
|
|
107
115
|
t.datetime :expires_in, null: false
|
108
116
|
t.index %i[oauth_application_id code], unique: true
|
109
117
|
end
|
118
|
+
|
119
|
+
create_table :oauth_saml_settings do |t|
|
120
|
+
t.bigint :oauth_application_id
|
121
|
+
t.foreign_key :oauth_applications, column: :oauth_application_id
|
122
|
+
t.text :idp_cert, null: true
|
123
|
+
t.text :idp_cert_fingerprint, null: true
|
124
|
+
t.string :idp_cert_fingerprint_algorithm, null: true
|
125
|
+
t.boolean :check_idp_cert_expiration, null: true
|
126
|
+
t.text :name_identifier_format, null: true
|
127
|
+
t.string :audience, null: true
|
128
|
+
t.string :issuer, null: false, unique: true
|
129
|
+
end
|
110
130
|
end
|
111
131
|
end
|
@@ -20,11 +20,11 @@ module Rodauth::OAuth
|
|
20
20
|
}.freeze
|
21
21
|
|
22
22
|
class_option :features, type: :array,
|
23
|
-
desc: "
|
23
|
+
desc: "Rodauth OAuth features to generate views for (oauth_applications etc.)",
|
24
24
|
default: DEFAULT
|
25
25
|
|
26
26
|
class_option :all, aliases: "-a", type: :boolean,
|
27
|
-
desc: "Generates views for all
|
27
|
+
desc: "Generates views for all Rodauth OAuth features",
|
28
28
|
default: false
|
29
29
|
|
30
30
|
class_option :directory, aliases: "-d", type: :string,
|
@@ -28,7 +28,7 @@ module Rodauth
|
|
28
28
|
translatable_method :oauth_unsupported_response_type_message, "Unsupported response type"
|
29
29
|
translatable_method :oauth_authorize_parameter_required, "Invalid or missing '%<parameter>s'"
|
30
30
|
|
31
|
-
|
31
|
+
auth_methods(
|
32
32
|
:resource_owner_params,
|
33
33
|
:oauth_grants_resource_owner_columns
|
34
34
|
)
|
@@ -109,13 +109,16 @@ module Rodauth
|
|
109
109
|
auth_value_method :oauth_metadata_op_tos_uri, nil
|
110
110
|
|
111
111
|
auth_value_methods(
|
112
|
+
:authorization_server_url,
|
113
|
+
:oauth_grants_unique_columns
|
114
|
+
)
|
115
|
+
|
116
|
+
auth_methods(
|
112
117
|
:fetch_access_token,
|
113
118
|
:secret_hash,
|
114
119
|
:generate_token_hash,
|
115
120
|
:secret_matches?,
|
116
|
-
:authorization_server_url,
|
117
121
|
:oauth_unique_id_generator,
|
118
|
-
:oauth_grants_unique_columns,
|
119
122
|
:require_authorizable_account,
|
120
123
|
:oauth_account_ds,
|
121
124
|
:oauth_application_ds
|
@@ -753,7 +756,7 @@ module Rodauth
|
|
753
756
|
}
|
754
757
|
end
|
755
758
|
|
756
|
-
def redirect_response_error(error_code,
|
759
|
+
def redirect_response_error(error_code, message = nil)
|
757
760
|
if accepts_json?
|
758
761
|
status_code = if respond_to?(:"oauth_#{error_code}_response_status")
|
759
762
|
send(:"oauth_#{error_code}_response_status")
|
@@ -761,37 +764,41 @@ module Rodauth
|
|
761
764
|
oauth_invalid_response_status
|
762
765
|
end
|
763
766
|
|
764
|
-
throw_json_response_error(status_code, error_code)
|
767
|
+
throw_json_response_error(status_code, error_code, message)
|
765
768
|
else
|
769
|
+
redirect_url = redirect_uri || request.referer || default_redirect
|
766
770
|
redirect_url = URI.parse(redirect_url)
|
767
|
-
params =
|
768
|
-
|
769
|
-
params << if respond_to?(:"oauth_#{error_code}_error_code")
|
770
|
-
["error", send(:"oauth_#{error_code}_error_code")]
|
771
|
-
else
|
772
|
-
["error", error_code]
|
773
|
-
end
|
774
|
-
|
775
|
-
if respond_to?(:"oauth_#{error_code}_message")
|
776
|
-
message = send(:"oauth_#{error_code}_message")
|
777
|
-
params << ["error_description", CGI.escape(message)]
|
778
|
-
end
|
779
|
-
|
771
|
+
params = response_error_params(error_code, message)
|
780
772
|
state = param_or_nil("state")
|
781
|
-
|
782
|
-
params << ["state", state] if state
|
783
|
-
|
773
|
+
params["state"] = state if state
|
784
774
|
_redirect_response_error(redirect_url, params)
|
785
775
|
end
|
786
776
|
end
|
787
777
|
|
788
778
|
def _redirect_response_error(redirect_url, params)
|
789
|
-
params = params
|
790
|
-
|
791
|
-
|
779
|
+
params = URI.encode_www_form(params)
|
780
|
+
if redirect_url.query
|
781
|
+
params << "&" unless params.empty?
|
782
|
+
params << redirect_url.query
|
783
|
+
end
|
784
|
+
redirect_url.query = params
|
792
785
|
redirect(redirect_url.to_s)
|
793
786
|
end
|
794
787
|
|
788
|
+
def response_error_params(error_code, message = nil)
|
789
|
+
code = if respond_to?(:"oauth_#{error_code}_error_code")
|
790
|
+
send(:"oauth_#{error_code}_error_code")
|
791
|
+
else
|
792
|
+
error_code
|
793
|
+
end
|
794
|
+
payload = { "error" => code }
|
795
|
+
error_description = message
|
796
|
+
error_description ||= send(:"oauth_#{error_code}_message") if respond_to?(:"oauth_#{error_code}_message")
|
797
|
+
payload["error_description"] = error_description if error_description
|
798
|
+
|
799
|
+
payload
|
800
|
+
end
|
801
|
+
|
795
802
|
def json_response_success(body, cache = false)
|
796
803
|
response.status = 200
|
797
804
|
response["Content-Type"] ||= json_response_content_type
|
@@ -809,13 +816,7 @@ module Rodauth
|
|
809
816
|
|
810
817
|
def throw_json_response_error(status, error_code, message = nil)
|
811
818
|
set_response_error_status(status)
|
812
|
-
|
813
|
-
send(:"oauth_#{error_code}_error_code")
|
814
|
-
else
|
815
|
-
error_code
|
816
|
-
end
|
817
|
-
payload = { "error" => code }
|
818
|
-
payload["error_description"] = message || (send(:"oauth_#{error_code}_message") if respond_to?(:"oauth_#{error_code}_message"))
|
819
|
+
payload = response_error_params(error_code, message)
|
819
820
|
json_payload = _json_response_body(payload)
|
820
821
|
response["Content-Type"] ||= json_response_content_type
|
821
822
|
response["WWW-Authenticate"] = oauth_token_type.upcase if status == 401
|
@@ -35,7 +35,7 @@ module Rodauth
|
|
35
35
|
end
|
36
36
|
translatable_method :oauth_grant_user_code_label, "User code"
|
37
37
|
|
38
|
-
|
38
|
+
auth_methods(
|
39
39
|
:generate_user_code
|
40
40
|
)
|
41
41
|
|
@@ -173,7 +173,7 @@ module Rodauth
|
|
173
173
|
oauth_grants_user_code_column => param("user_code")
|
174
174
|
).update(oauth_grants_user_code_column => nil, oauth_grants_type_column => "device_code")
|
175
175
|
|
176
|
-
|
176
|
+
rs if rs.positive?
|
177
177
|
else
|
178
178
|
super
|
179
179
|
end
|
@@ -9,7 +9,7 @@ module Rodauth
|
|
9
9
|
|
10
10
|
auth_value_method :oauth_jwt_access_tokens, true
|
11
11
|
|
12
|
-
|
12
|
+
auth_methods(:jwt_claims)
|
13
13
|
|
14
14
|
def require_oauth_authorization(*scopes)
|
15
15
|
return super unless oauth_jwt_access_tokens
|
@@ -99,7 +99,7 @@ module Rodauth
|
|
99
99
|
end
|
100
100
|
|
101
101
|
def _generate_access_token(*)
|
102
|
-
|
102
|
+
super unless oauth_jwt_access_tokens
|
103
103
|
end
|
104
104
|
|
105
105
|
def jwt_claims(oauth_grant)
|
@@ -117,7 +117,7 @@ module Rodauth
|
|
117
117
|
# owner is involved, such as the client credentials grant, the value
|
118
118
|
# of "sub" SHOULD correspond to an identifier the authorization
|
119
119
|
# server uses to indicate the client application.
|
120
|
-
sub: jwt_subject(oauth_grant),
|
120
|
+
sub: jwt_subject(oauth_grant[oauth_grants_account_id_column]),
|
121
121
|
client_id: oauth_application[oauth_applications_client_id_column],
|
122
122
|
|
123
123
|
exp: issued_at + oauth_access_token_expires_in,
|
@@ -18,7 +18,7 @@ module Rodauth
|
|
18
18
|
|
19
19
|
auth_value_method :oauth_jwt_jwe_copyright, nil
|
20
20
|
|
21
|
-
|
21
|
+
auth_methods(
|
22
22
|
:jwt_encode,
|
23
23
|
:jwt_decode,
|
24
24
|
:jwt_decode_no_key,
|
@@ -63,12 +63,8 @@ module Rodauth
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
-
def jwt_subject(
|
67
|
-
|
68
|
-
|
69
|
-
return account_id.to_s if account_id
|
70
|
-
|
71
|
-
client_application[oauth_applications_client_id_column]
|
66
|
+
def jwt_subject(account_unique_id, client_application = oauth_application)
|
67
|
+
(account_unique_id || client_application[oauth_applications_client_id_column]).to_s
|
72
68
|
end
|
73
69
|
|
74
70
|
def resource_owner_params_from_jwt_claims(claims)
|
@@ -173,6 +169,7 @@ module Rodauth
|
|
173
169
|
|
174
170
|
def jwt_encode(payload,
|
175
171
|
jwks: nil,
|
172
|
+
headers: {},
|
176
173
|
encryption_algorithm: oauth_jwt_jwe_keys.keys.dig(0, 0),
|
177
174
|
encryption_method: oauth_jwt_jwe_keys.keys.dig(0, 1),
|
178
175
|
jwe_key: oauth_jwt_jwe_keys[[encryption_algorithm,
|
@@ -186,8 +183,16 @@ module Rodauth
|
|
186
183
|
|
187
184
|
jwk = JSON::JWK.new(key || "")
|
188
185
|
|
186
|
+
# update headers
|
187
|
+
headers.each_key do |k|
|
188
|
+
if jwt.respond_to?(:"#{k}=")
|
189
|
+
jwt.send(:"#{k}=", headers[k])
|
190
|
+
headers.delete(k)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
jwt.header.merge(headers) unless headers.empty?
|
194
|
+
|
189
195
|
jwt = jwt.sign(jwk, signing_algorithm)
|
190
|
-
jwt.kid = jwk.thumbprint
|
191
196
|
|
192
197
|
return jwt.to_s unless encryption_algorithm && encryption_method
|
193
198
|
|
@@ -329,8 +334,8 @@ module Rodauth
|
|
329
334
|
end
|
330
335
|
|
331
336
|
def jwt_encode(payload,
|
332
|
-
signing_algorithm: oauth_jwt_keys.keys.first,
|
333
|
-
|
337
|
+
signing_algorithm: oauth_jwt_keys.keys.first,
|
338
|
+
headers: {}, **)
|
334
339
|
|
335
340
|
key = oauth_jwt_keys[signing_algorithm] || _jwt_key
|
336
341
|
key = key.first if key.is_a?(Array)
|
@@ -8,7 +8,7 @@ module Rodauth
|
|
8
8
|
|
9
9
|
auth_value_method :max_param_bytesize, nil if Rodauth::VERSION >= "2.26.0"
|
10
10
|
|
11
|
-
|
11
|
+
auth_methods(
|
12
12
|
:require_oauth_application_from_jwt_bearer_assertion_issuer,
|
13
13
|
:require_oauth_application_from_jwt_bearer_assertion_subject,
|
14
14
|
:account_from_jwt_bearer_assertion
|