rodauth-oauth 1.3.2 → 1.5.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 +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
|