rodauth-oauth 1.3.2 → 1.5.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 +17 -10
- data/doc/release_notes/1_4_0.md +57 -0
- data/doc/release_notes/1_5_0.md +20 -0
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize.html.erb +28 -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 +37 -1
- 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 +49 -38
- data/lib/rodauth/features/oauth_device_code_grant.rb +2 -2
- data/lib/rodauth/features/oauth_dpop.rb +410 -0
- data/lib/rodauth/features/oauth_dynamic_client_registration.rb +12 -2
- data/lib/rodauth/features/oauth_grant_management.rb +1 -1
- data/lib/rodauth/features/oauth_jwt.rb +12 -13
- data/lib/rodauth/features/oauth_jwt_base.rb +57 -34
- 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_indicators.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 +2 -4
- data/lib/rodauth/features/oauth_token_introspection.rb +3 -3
- data/lib/rodauth/features/oauth_token_revocation.rb +1 -1
- data/lib/rodauth/features/oidc.rb +32 -11
- 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 +91 -0
- data/lib/rodauth/oauth/database_extensions.rb +4 -0
- data/lib/rodauth/oauth/http_extensions.rb +1 -1
- data/lib/rodauth/oauth/ttl_store.rb +1 -1
- data/lib/rodauth/oauth/version.rb +1 -1
- data/locales/en.yml +19 -0
- data/locales/pt.yml +9 -0
- data/templates/authorize.str +1 -0
- data/templates/check_session.str +67 -0
- data/templates/frontchannel_logout.str +17 -0
- metadata +14 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40ff3b3b3de0595eae98f218aec2f8e876f18329061c537e91e5841ab35e67dc
|
4
|
+
data.tar.gz: 19692e86d66400a9e7227f655bdc6375e38554cb52b97ee3a5e22cceab582168
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cd6efdeda012c25d83949e7f0ea2aa043238851f95bf1217b8156786f7376d39792caed37a2af68fc7777ed5a892fa2f9e5185efe1f8a3e7168df07de84b954d
|
7
|
+
data.tar.gz: 0e435eea239f81ff16db08b187ce7bb06ba3d25359603906e48610e38912162b35c79c3d8b7fbfa74df2c56f52bb653d61b999da76c1cc8d445f2077b876c578
|
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,57 @@
|
|
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
|
+
Some `auth_value_methods` were changed to `auth_methods` everywhere where it made sense. If you were overriding them, you'll have to wrap them in a block:
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
# in 1.3.2
|
36
|
+
oauth_jwt_issuer "http://myissuer.com"
|
37
|
+
# in 1.4.0
|
38
|
+
oauth_jwt_issuer { "http://myissuer.com" }
|
39
|
+
```
|
40
|
+
|
41
|
+
## Improvements
|
42
|
+
|
43
|
+
### OAuth SAML Bearer Grant per oauth application settings
|
44
|
+
|
45
|
+
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.
|
46
|
+
|
47
|
+
## Bugfixes
|
48
|
+
|
49
|
+
* remove `html_safe` usage in rails views to prevent XSS in the authorize form.
|
50
|
+
* fixed for OIDC RFC 5.4 when requesting claims using scope values
|
51
|
+
* `oauth_rp_initiated_logout` does not crash anymore on logout requests with `id_token_hint`
|
52
|
+
* `oauth_rp_initiated_logout` now works with response types other than `code`
|
53
|
+
* `oauth_rp_initiated_logout` emits an ID token hint invalid message when not able to decode the `id_token_hint`
|
54
|
+
|
55
|
+
## Chore
|
56
|
+
|
57
|
+
* `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.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# 1.5.0
|
2
|
+
|
3
|
+
## Highlights
|
4
|
+
|
5
|
+
### OAuth DPoP Support
|
6
|
+
|
7
|
+
`rodauth-oauth` supports Demonstrating Proof-of-Possession at the Application Layer (also known as DPoP), via the `oauth_dpop` feature. This provides a mechanism to bind access tokens to a particular client based on public key cryptography.
|
8
|
+
|
9
|
+
More info about the feature [in the docs](https://gitlab.com/os85/rodauth-oauth/-/wikis/DPoP).
|
10
|
+
|
11
|
+
## Improvements
|
12
|
+
|
13
|
+
All features managing cookies are now able to set configure them as "session cookies" (i.e. removed on browser shutdown) by setting the expiration interval auth method to `nil`. This ncludes:
|
14
|
+
|
15
|
+
* `oauth_prompt_login_interval` (from the `oidc` feature)
|
16
|
+
* `oauth_oidc_user_agent_state_cookie_expires_in` (from the `oidc_session_management` feature)
|
17
|
+
|
18
|
+
## Bugfixes
|
19
|
+
|
20
|
+
* when using the `oauth_token_instrospection` feature, the `token_type` has been fixed to show "Bearer" (instead of "access_token").
|
@@ -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,44 @@
|
|
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
|
+
<% end %>
|
81
|
+
<% end %>
|
82
|
+
<% if rodauth.features.include?(:oidc) %>
|
83
|
+
<% if rodauth.param_or_nil("dpop_jkt") %>
|
84
|
+
<%= hidden_field_tag :dpop_jkt, rodauth.param_or_nil("dpop_jkt") %>
|
80
85
|
<% end %>
|
81
86
|
<% end %>
|
82
87
|
</div>
|
83
88
|
<p class="text-center">
|
84
89
|
<%= 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.
|
90
|
+
<%= 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
91
|
</p>
|
87
92
|
<% 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 %>" />
|
@@ -49,6 +49,9 @@ class CreateRodauthOauth < ActiveRecord::Migration<%= migration_version %>
|
|
49
49
|
t.boolean :require_signed_request_object, null: true
|
50
50
|
t.boolean :require_pushed_authorization_requests, null: false, default: false
|
51
51
|
|
52
|
+
# :oauth_dpop
|
53
|
+
t.string :dpop_bound_access_tokens, null: true
|
54
|
+
|
52
55
|
# :oauth_tls_client_auth
|
53
56
|
t.string :tls_client_auth_subject_dn, null: true
|
54
57
|
t.string :tls_client_auth_san_dns, null: true
|
@@ -59,6 +62,14 @@ class CreateRodauthOauth < ActiveRecord::Migration<%= migration_version %>
|
|
59
62
|
|
60
63
|
# :oidc_rp_initiated_logout enabled
|
61
64
|
t.string :post_logout_redirect_uris, null: false
|
65
|
+
|
66
|
+
# frontchannel logout
|
67
|
+
t.string :frontchannel_logout_uri
|
68
|
+
t.boolean :frontchannel_logout_session_required, default: false
|
69
|
+
|
70
|
+
# backchannel logout
|
71
|
+
t.string :backchannel_logout_uri
|
72
|
+
t.boolean :backchannel_logout_session_required, default: false
|
62
73
|
end
|
63
74
|
|
64
75
|
create_table :oauth_grants do |t|
|
@@ -78,6 +89,9 @@ class CreateRodauthOauth < ActiveRecord::Migration<%= migration_version %>
|
|
78
89
|
t.datetime :created_at, null: false, default: -> { "CURRENT_TIMESTAMP(6)" }
|
79
90
|
t.string :access_type, null: false, default: "offline"
|
80
91
|
|
92
|
+
# :oauth_dpop enabled
|
93
|
+
t.string :dpop_jwk, null: true
|
94
|
+
|
81
95
|
# :oauth_pkce enabled
|
82
96
|
t.string :code_challenge
|
83
97
|
t.string :code_challenge_method
|
@@ -97,15 +111,37 @@ class CreateRodauthOauth < ActiveRecord::Migration<%= migration_version %>
|
|
97
111
|
t.string :acr
|
98
112
|
t.string :claims_locales
|
99
113
|
t.string :claims
|
114
|
+
|
115
|
+
# :oauth_dpop enabled
|
116
|
+
t.string :dpop_jkt
|
100
117
|
end
|
101
118
|
|
102
119
|
create_table :oauth_pushed_requests do |t|
|
103
120
|
t.bigint :oauth_application_id
|
104
121
|
t.foreign_key :oauth_applications, column: :oauth_application_id
|
105
122
|
t.string :code, null: false, index: { unique: true }
|
123
|
+
t.index %i[oauth_application_id code], unique: true
|
106
124
|
t.string :params, null: false
|
107
125
|
t.datetime :expires_in, null: false
|
108
|
-
|
126
|
+
# :oauth_dpop
|
127
|
+
t.string :dpop_jkt
|
128
|
+
end
|
129
|
+
|
130
|
+
create_table :oauth_saml_settings do |t|
|
131
|
+
t.bigint :oauth_application_id
|
132
|
+
t.foreign_key :oauth_applications, column: :oauth_application_id
|
133
|
+
t.text :idp_cert, null: true
|
134
|
+
t.text :idp_cert_fingerprint, null: true
|
135
|
+
t.string :idp_cert_fingerprint_algorithm, null: true
|
136
|
+
t.boolean :check_idp_cert_expiration, null: true
|
137
|
+
t.text :name_identifier_format, null: true
|
138
|
+
t.string :audience, null: true
|
139
|
+
t.string :issuer, null: false, unique: true
|
140
|
+
end
|
141
|
+
|
142
|
+
create_table :oauth_dpop_proofs, primary_key: :jti do |t|
|
143
|
+
t.string :jti, null: false
|
144
|
+
t.datetime :first_use, null: false, default: -> { "CURRENT_TIMESTAMP(6)" }
|
109
145
|
end
|
110
146
|
end
|
111
147
|
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
|
@@ -234,16 +237,22 @@ module Rodauth
|
|
234
237
|
return
|
235
238
|
end
|
236
239
|
else
|
237
|
-
|
240
|
+
token = fetch_access_token_from_authorization_header
|
241
|
+
end
|
238
242
|
|
239
|
-
|
243
|
+
return if token.nil? || token.empty?
|
240
244
|
|
241
|
-
|
245
|
+
token
|
246
|
+
end
|
242
247
|
|
243
|
-
|
244
|
-
|
248
|
+
def fetch_access_token_from_authorization_header(token_type = oauth_token_type)
|
249
|
+
value = request.env["HTTP_AUTHORIZATION"]
|
245
250
|
|
246
|
-
return
|
251
|
+
return unless value && !value.empty?
|
252
|
+
|
253
|
+
scheme, token = value.split(" ", 2)
|
254
|
+
|
255
|
+
return unless scheme.downcase == token_type
|
247
256
|
|
248
257
|
token
|
249
258
|
end
|
@@ -350,7 +359,7 @@ module Rodauth
|
|
350
359
|
# parse client id and secret
|
351
360
|
#
|
352
361
|
def require_oauth_application
|
353
|
-
@oauth_application = if (token = (
|
362
|
+
@oauth_application = if (token = (v = request.env["HTTP_AUTHORIZATION"]) && v[/\A *Basic (.*)\Z/, 1])
|
354
363
|
# client_secret_basic
|
355
364
|
require_oauth_application_from_client_secret_basic(token)
|
356
365
|
elsif (client_id = param_or_nil("client_id"))
|
@@ -753,7 +762,7 @@ module Rodauth
|
|
753
762
|
}
|
754
763
|
end
|
755
764
|
|
756
|
-
def redirect_response_error(error_code,
|
765
|
+
def redirect_response_error(error_code, message = nil)
|
757
766
|
if accepts_json?
|
758
767
|
status_code = if respond_to?(:"oauth_#{error_code}_response_status")
|
759
768
|
send(:"oauth_#{error_code}_response_status")
|
@@ -761,37 +770,41 @@ module Rodauth
|
|
761
770
|
oauth_invalid_response_status
|
762
771
|
end
|
763
772
|
|
764
|
-
throw_json_response_error(status_code, error_code)
|
773
|
+
throw_json_response_error(status_code, error_code, message)
|
765
774
|
else
|
775
|
+
redirect_url = redirect_uri || request.referer || default_redirect
|
766
776
|
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
|
-
|
777
|
+
params = response_error_params(error_code, message)
|
780
778
|
state = param_or_nil("state")
|
781
|
-
|
782
|
-
params << ["state", state] if state
|
783
|
-
|
779
|
+
params["state"] = state if state
|
784
780
|
_redirect_response_error(redirect_url, params)
|
785
781
|
end
|
786
782
|
end
|
787
783
|
|
788
784
|
def _redirect_response_error(redirect_url, params)
|
789
|
-
params = params
|
790
|
-
|
791
|
-
|
785
|
+
params = URI.encode_www_form(params)
|
786
|
+
if redirect_url.query
|
787
|
+
params << "&" unless params.empty?
|
788
|
+
params << redirect_url.query
|
789
|
+
end
|
790
|
+
redirect_url.query = params
|
792
791
|
redirect(redirect_url.to_s)
|
793
792
|
end
|
794
793
|
|
794
|
+
def response_error_params(error_code, message = nil)
|
795
|
+
code = if respond_to?(:"oauth_#{error_code}_error_code")
|
796
|
+
send(:"oauth_#{error_code}_error_code")
|
797
|
+
else
|
798
|
+
error_code
|
799
|
+
end
|
800
|
+
payload = { "error" => code }
|
801
|
+
error_description = message
|
802
|
+
error_description ||= send(:"oauth_#{error_code}_message") if respond_to?(:"oauth_#{error_code}_message")
|
803
|
+
payload["error_description"] = error_description if error_description
|
804
|
+
|
805
|
+
payload
|
806
|
+
end
|
807
|
+
|
795
808
|
def json_response_success(body, cache = false)
|
796
809
|
response.status = 200
|
797
810
|
response["Content-Type"] ||= json_response_content_type
|
@@ -809,19 +822,17 @@ module Rodauth
|
|
809
822
|
|
810
823
|
def throw_json_response_error(status, error_code, message = nil)
|
811
824
|
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"))
|
825
|
+
payload = response_error_params(error_code, message)
|
819
826
|
json_payload = _json_response_body(payload)
|
820
827
|
response["Content-Type"] ||= json_response_content_type
|
821
|
-
response["WWW-Authenticate"] =
|
828
|
+
response["WWW-Authenticate"] = www_authenticate_header(payload) if status == 401
|
822
829
|
return_response(json_payload)
|
823
830
|
end
|
824
831
|
|
832
|
+
def www_authenticate_header(*)
|
833
|
+
oauth_token_type.capitalize
|
834
|
+
end
|
835
|
+
|
825
836
|
def _json_response_body(hash)
|
826
837
|
return super if features.include?(:json)
|
827
838
|
|
@@ -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
|