rodauth-oauth 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +11 -8
- data/doc/release_notes/1_1_0.md +1 -1
- data/doc/release_notes/1_2_0.md +36 -0
- data/doc/release_notes/1_3_0.md +38 -0
- data/lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize.html.erb +3 -0
- data/lib/generators/rodauth/oauth/templates/db/migrate/create_rodauth_oauth.rb +32 -9
- data/lib/rodauth/features/oauth_authorization_code_grant.rb +55 -33
- data/lib/rodauth/features/oauth_authorize_base.rb +25 -3
- data/lib/rodauth/features/oauth_base.rb +16 -16
- data/lib/rodauth/features/oauth_device_code_grant.rb +1 -2
- data/lib/rodauth/features/oauth_dynamic_client_registration.rb +182 -29
- data/lib/rodauth/features/oauth_implicit_grant.rb +23 -5
- data/lib/rodauth/features/oauth_jwt.rb +2 -0
- data/lib/rodauth/features/oauth_jwt_base.rb +52 -11
- data/lib/rodauth/features/oauth_jwt_secured_authorization_request.rb +30 -22
- data/lib/rodauth/features/oauth_jwt_secured_authorization_response_mode.rb +126 -0
- data/lib/rodauth/features/oauth_management_base.rb +1 -3
- data/lib/rodauth/features/oauth_pushed_authorization_request.rb +135 -0
- data/lib/rodauth/features/oauth_tls_client_auth.rb +170 -0
- data/lib/rodauth/features/oidc.rb +97 -59
- data/lib/rodauth/features/oidc_dynamic_client_registration.rb +52 -2
- data/lib/rodauth/features/oidc_rp_initiated_logout.rb +3 -4
- data/lib/rodauth/features/oidc_self_issued.rb +73 -0
- data/lib/rodauth/oauth/version.rb +1 -1
- data/templates/authorize.str +1 -0
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4d7d5f8b68686703954bf4e335cef0ea33f9e31c94c439df84f08e8ff3270829
|
4
|
+
data.tar.gz: 1da57ba2082818a74dbca4d1c6bcab0c15f97da891e12c03a8bf91440a4edcfd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8230b54e51d2081e25d1386d6294745d54eebbe11a6677bdb9cade14e0a418658bc2b8a67ae2e6355458f4b43d8a2df1700cd3e0496fa8a10e690318f3d03ba0
|
7
|
+
data.tar.gz: 31ab5721a6464b751860b6896f47999e189592582842ba419ab0a057ff38af98612d54a8b00177092e2fe5993af1e5554cecafbbfaab18a495656117f19ce4fd
|
data/README.md
CHANGED
@@ -17,6 +17,7 @@ This is an extension to the `rodauth` gem which implements the [OAuth 2.0 framew
|
|
17
17
|
* Config OP
|
18
18
|
* Dynamic OP
|
19
19
|
* Form Post OP
|
20
|
+
* 3rd Party-Init OP
|
20
21
|
|
21
22
|
(it also passes the conformance tests for the RP-Initiated Logout OP).
|
22
23
|
|
@@ -24,7 +25,7 @@ This is an extension to the `rodauth` gem which implements the [OAuth 2.0 framew
|
|
24
25
|
|
25
26
|
This gem implements the following RFCs and features of OAuth:
|
26
27
|
|
27
|
-
* `oauth` - [The OAuth 2.0 protocol framework](
|
28
|
+
* `oauth` - [The OAuth 2.0 protocol framework](-/wikis/home#oauth-20-protocol-framework):
|
28
29
|
* [Access Token generation](https://tools.ietf.org/html/rfc6749#section-1.4);
|
29
30
|
* [Access Token refresh token grant](https://tools.ietf.org/html/rfc6749#section-1.5);
|
30
31
|
* `oauth_authorization_code_grant` - [Authorization code grant](https://tools.ietf.org/html/rfc6749#section-1.3);
|
@@ -33,10 +34,13 @@ This gem implements the following RFCs and features of OAuth:
|
|
33
34
|
* `oauth_device_code_grant` - [Device code grant (off by default)](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-device-flow-15);
|
34
35
|
* `oauth_token_revocation` - [Token revocation](https://tools.ietf.org/html/rfc7009);
|
35
36
|
* `oauth_token_introspection` - [Token introspection](https://tools.ietf.org/html/rfc7662);
|
37
|
+
* `oauth_pushed_authorization_request` - [Pushed Authorization Request](https://datatracker.ietf.org/doc/html/rfc9126);
|
36
38
|
* [Authorization Server Metadata](https://tools.ietf.org/html/rfc8414);
|
37
39
|
* `oauth_pkce` - [PKCE](https://tools.ietf.org/html/rfc7636);
|
40
|
+
* `oauth_tls_client_auth` - [Mutual-TLS Client Authentication](https://datatracker.ietf.org/doc/html/rfc8705);
|
38
41
|
* `oauth_jwt` - [JWT Access Tokens](https://tools.ietf.org/html/draft-ietf-oauth-access-token-jwt-07);
|
39
42
|
* `oauth_jwt_secured_authorization_request` - [JWT Secured Authorization Request](https://tools.ietf.org/html/draft-ietf-oauth-jwsreq-20);
|
43
|
+
* `oauth_jwt_secured_authorization_response_mode` - [JWT Secured Authorization Response_mode](https://openid.net/specs/openid-financial-api-jarm.html);
|
40
44
|
* `oauth_resource_indicators` - [Resource Indicators](https://datatracker.ietf.org/doc/html/rfc8707);
|
41
45
|
* Access Type (Token refresh online and offline);
|
42
46
|
* `oauth_http_mac` - [MAC Authentication Scheme](https://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token-02);
|
@@ -44,18 +48,17 @@ This gem implements the following RFCs and features of OAuth:
|
|
44
48
|
* `oauth_saml_bearer_grant` - [SAML 2.0 Bearer Assertion](https://datatracker.ietf.org/doc/html/rfc7522);
|
45
49
|
* `oauth_jwt_bearer_grant` - [JWT Bearer Assertion](https://datatracker.ietf.org/doc/html/rfc7523);
|
46
50
|
|
47
|
-
* `oauth_dynamic_client_registration` - [Dynamic Client Registration Protocol](https://datatracker.ietf.org/doc/html/rfc7591);
|
51
|
+
* `oauth_dynamic_client_registration` - [Dynamic Client Registration Protocol](https://datatracker.ietf.org/doc/html/rfc7591) and [Dynamic Client Registration Management](https://www.rfc-editor.org/rfc/rfc7592);
|
48
52
|
* OAuth application and token management dashboards;
|
49
53
|
* The recommendations for [Native Apps](https://www.rfc-editor.org/rfc/rfc8252);
|
50
54
|
|
51
55
|
It also implements the [OpenID Connect layer](https://openid.net/connect/) (via the `openid` feature) on top of the OAuth features it provides, including:
|
52
56
|
|
53
|
-
*
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
*
|
58
|
-
* `oidc_rp_initiated_logout` - [RP Initiated Logout](https://openid.net/specs/openid-connect-rpinitiated-1_0.html);
|
57
|
+
* [OpenID Connect Core](https://gitlab.com/os85/rodauth-oauth/-/wikis/Id-Token-Authentication);
|
58
|
+
* [OpenID Connect Discovery](https://gitlab.com/os85/rodauth-oauth/-/wikis/OIDC-Dynamic-Client-Registration);
|
59
|
+
* [OpenID Multiple Response Types](https://gitlab.com/os85/rodauth-oauth/-/wikis/Hybrid-flow);
|
60
|
+
* [OpenID Connect Dynamic Client Registration](https://gitlab.com/os85/rodauth-oauth/-/wikis/OIDC-Dynamic-Client-Registration);
|
61
|
+
* [RP Initiated Logout](https://gitlab.com/os85/rodauth-oauth/-/wikis/RP-Initiated-Logout);
|
59
62
|
|
60
63
|
This gem supports also rails (through [rodauth-rails]((https://github.com/janko/rodauth-rails))).
|
61
64
|
|
data/doc/release_notes/1_1_0.md
CHANGED
@@ -0,0 +1,36 @@
|
|
1
|
+
## 1.2.0 (13/02/2023)
|
2
|
+
|
3
|
+
### Features
|
4
|
+
|
5
|
+
#### Pushed Authorization Requests (PAR)
|
6
|
+
|
7
|
+
RFC: https://datatracker.ietf.org/doc/html/rfc9126
|
8
|
+
|
9
|
+
`rodauth-oauth` supports Pushed Authorization Requests, via the `:oauth_pushed_authorization_request` feature.
|
10
|
+
|
11
|
+
More info about the feature [in the wiki](https://gitlab.com/os85/rodauth-oauth/-/wikis/Pushed-Authorization-Requests).
|
12
|
+
|
13
|
+
#### mTLS Client Auth (+ certificate-bound access tokens)
|
14
|
+
|
15
|
+
RFC: https://www.rfc-editor.org/rfc/rfc8705
|
16
|
+
|
17
|
+
The `:oauth_tls_client_auth` feature adds support for the variants of mTLS Client Authentication "PKI Mutual-TLS Method" and 2Self-Signed Certificate Mutual-TLS Method". It also supports client certificate bound access tokens.
|
18
|
+
|
19
|
+
More about it [in the wiki](https://gitlab.com/os85/rodauth-oauth/-/wikis/mTLS-Client-Authentication).
|
20
|
+
|
21
|
+
#### Dynamic Client Registration management
|
22
|
+
|
23
|
+
RFC: https://www.rfc-editor.org/rfc/rfc7592
|
24
|
+
|
25
|
+
Support for dynamci client registration management was added to the `:oauth_dynamic_client_registration` feature.
|
26
|
+
|
27
|
+
More info about it [in the wiki](https://gitlab.com/os85/rodauth-oauth/-/wikis/Dynamic-Client-Registration#getputdelete-registerclient_id).
|
28
|
+
|
29
|
+
### Improvements
|
30
|
+
|
31
|
+
* Support for 3rd-party initiated login was added, by including support for the `initiate_login_uri` attribute in the register route from the `:oauth_dynamic_client_registration` feature.
|
32
|
+
* Support for multitenant resource ownership was added, here's a [description from the wiki](https://gitlab.com/os85/rodauth-oauth/-/wikis/How-to#scoping-grants-from-the-same-resource-owner).
|
33
|
+
|
34
|
+
### Bugfixes
|
35
|
+
|
36
|
+
* oidc: userinfo claims were not including claims with value `false`, such as `"email_verified"`. This behaviour has been fixed, and only claims of value `null` are omitted.
|
@@ -0,0 +1,38 @@
|
|
1
|
+
## 1.3.0 (02/04/2023)
|
2
|
+
|
3
|
+
## Features
|
4
|
+
|
5
|
+
### Self-Signed Issued Tokens
|
6
|
+
|
7
|
+
`rodauth-oauth` supports self-signed issued tokens, via the `oidc_self_issued` feature.
|
8
|
+
|
9
|
+
More info about the feature [in the docs](https://gitlab.com/os85/rodauth-oauth/-/wikis/Self-Issued-OpenID).
|
10
|
+
|
11
|
+
#### JARM
|
12
|
+
|
13
|
+
`rodauth-oauth` supports JWT-secured Authorization Response Mode, also known as JARM, via the `oauth_jwt_secured_authorization_response_mode`.
|
14
|
+
|
15
|
+
More info about the feature [in the docs](https://gitlab.com/os85/rodauth-oauth/-/wikis/JWT-Secured-Authorization-Response-Mode).
|
16
|
+
|
17
|
+
## Improvements
|
18
|
+
|
19
|
+
### `fill_with_account_claims` auth method
|
20
|
+
|
21
|
+
`fill_with_account_claims` is now exposed as an auth method. This allows one to override to be able to cover certain requirements, such as aggregated and distributed claims. Here's a [link to the docs](https://gitlab.com/os85/rodauth-oauth/-/wikis/Id-Token-Authentication#claim-types) explaining how to do it.
|
22
|
+
|
23
|
+
### oidc: only generate refresh token when `offline_access` scope is used.
|
24
|
+
|
25
|
+
When the `oidc` feature is used, refresh tokens won't be generated anymore by default; in order to do so, the `offline_access` needs to be requested for in the respective authorization request, [as the spec mandates](https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess).
|
26
|
+
|
27
|
+
### oidc: implicit grant loaded by default
|
28
|
+
|
29
|
+
The `oidc` feature now loads the `oauth_implicit_grant` feature by default. This hadn't been done before due to the wish to ship a secure integration by default, but since then, spec compliance became more prioritary, and this is a requirement.
|
30
|
+
|
31
|
+
## Bugfixes
|
32
|
+
|
33
|
+
* rails integration: activerecord migrations fixes:
|
34
|
+
* use `bigint` for foreign keys;
|
35
|
+
* index creation instruction with the wrong syntax;
|
36
|
+
* set precision 6 for default timestamps, to comply with AR defaults;
|
37
|
+
* add missing `code` column to the `oauth_pushed_requests` table;
|
38
|
+
* oidc: when using the `id_token` , or any composite response type including `id_token`, using any response mode other than `fragment` will result in an invalid request.
|
@@ -75,6 +75,9 @@
|
|
75
75
|
<% if params[:acr_values] %>
|
76
76
|
<%= hidden_field_tag :acr_values, params[:acr_values] %>
|
77
77
|
<% end %>
|
78
|
+
<% if params[:registration] %>
|
79
|
+
<%= hidden_field_tag :registration, params[:registration] %>
|
80
|
+
<% end %>
|
78
81
|
<% end %>
|
79
82
|
</div>
|
80
83
|
<p class="text-center">
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class CreateRodauthOauth < ActiveRecord::Migration<%= migration_version %>
|
2
2
|
def change
|
3
3
|
create_table :oauth_applications do |t|
|
4
|
-
t.
|
4
|
+
t.bigint :account_id
|
5
5
|
t.foreign_key :accounts, column: :account_id
|
6
6
|
t.string :name, null: false
|
7
7
|
t.string :description, null: true
|
@@ -9,8 +9,9 @@ class CreateRodauthOauth < ActiveRecord::Migration<%= migration_version %>
|
|
9
9
|
t.string :redirect_uri, null: false
|
10
10
|
t.string :client_id, null: false, index: { unique: true }
|
11
11
|
t.string :client_secret, null: false, index: { unique: true }
|
12
|
+
t.string :registration_access_token, null: true
|
12
13
|
t.string :scopes, null: false
|
13
|
-
t.datetime :created_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
14
|
+
t.datetime :created_at, null: false, default: -> { "CURRENT_TIMESTAMP(6)" }
|
14
15
|
|
15
16
|
# :oauth_dynamic_client_configuration enabled, extra optional params
|
16
17
|
t.string :token_endpoint_auth_method, null: true
|
@@ -29,6 +30,7 @@ class CreateRodauthOauth < ActiveRecord::Migration<%= migration_version %>
|
|
29
30
|
# :oidc_dynamic_client_configuration enabled, extra optional params
|
30
31
|
t.string :sector_identifier_uri, null: true
|
31
32
|
t.string :application_type, null: true
|
33
|
+
t.string :initiate_login_uri, null: true
|
32
34
|
|
33
35
|
# :oidc enabled
|
34
36
|
t.string :subject_type, null: true
|
@@ -44,26 +46,35 @@ class CreateRodauthOauth < ActiveRecord::Migration<%= migration_version %>
|
|
44
46
|
t.string :request_object_encryption_alg, null: true
|
45
47
|
t.string :request_object_encryption_enc, null: true
|
46
48
|
t.string :request_uris, null: true
|
49
|
+
t.boolean :require_pushed_authorization_requests, null: false, default: false
|
50
|
+
|
51
|
+
# :oauth_tls_client_auth
|
52
|
+
t.string :tls_client_auth_subject_dn, null: true
|
53
|
+
t.string :tls_client_auth_san_dns, null: true
|
54
|
+
t.string :tls_client_auth_san_uri, null: true
|
55
|
+
t.string :tls_client_auth_san_ip, null: true
|
56
|
+
t.string :tls_client_auth_san_email, null: true
|
57
|
+
t.boolean :tls_client_certificate_bound_access_tokens, default: false
|
47
58
|
|
48
59
|
# :oidc_rp_initiated_logout enabled
|
49
60
|
t.string :post_logout_redirect_uris, null: false
|
50
61
|
end
|
51
62
|
|
52
63
|
create_table :oauth_grants do |t|
|
53
|
-
t.
|
64
|
+
t.bigint :account_id
|
54
65
|
t.foreign_key :accounts, column: :account_id
|
55
|
-
t.
|
66
|
+
t.bigint :oauth_application_id
|
56
67
|
t.foreign_key :oauth_applications, column: :oauth_application_id
|
57
68
|
t.string :type, null: true
|
58
69
|
t.string :code, null: true
|
59
70
|
t.index(%i[oauth_application_id code], unique: true)
|
60
|
-
t.string :token, unique: true
|
61
|
-
t.string :refresh_token, unique: true
|
71
|
+
t.string :token, index: { unique: true }
|
72
|
+
t.string :refresh_token, index: { unique: true }
|
62
73
|
t.datetime :expires_in, null: false
|
63
74
|
t.string :redirect_uri
|
64
75
|
t.datetime :revoked_at
|
65
76
|
t.string :scopes, null: false
|
66
|
-
t.datetime :created_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
77
|
+
t.datetime :created_at, null: false, default: -> { "CURRENT_TIMESTAMP(6)" }
|
67
78
|
t.string :access_type, null: false, default: "offline"
|
68
79
|
|
69
80
|
# :oauth_pkce enabled
|
@@ -71,9 +82,12 @@ class CreateRodauthOauth < ActiveRecord::Migration<%= migration_version %>
|
|
71
82
|
t.string :code_challenge_method
|
72
83
|
|
73
84
|
# :oauth_device_code_grant enabled
|
74
|
-
t.string :user_code, null: true, unique: true
|
85
|
+
t.string :user_code, null: true, index: { unique: true }
|
75
86
|
t.datetime :last_polled_at, null: true
|
76
87
|
|
88
|
+
# :oauth_tls_client_auth
|
89
|
+
t.string :certificate_thumbprint, null: true
|
90
|
+
|
77
91
|
# :resource_indicators enabled
|
78
92
|
t.string :resource
|
79
93
|
|
@@ -83,5 +97,14 @@ class CreateRodauthOauth < ActiveRecord::Migration<%= migration_version %>
|
|
83
97
|
t.string :claims_locales
|
84
98
|
t.string :claims
|
85
99
|
end
|
100
|
+
|
101
|
+
create_table :oauth_pushed_requests do |t|
|
102
|
+
t.bigint :oauth_application_id
|
103
|
+
t.foreign_key :oauth_applications, column: :oauth_application_id
|
104
|
+
t.string :code, null: false, index: { unique: true }
|
105
|
+
t.string :params, null: false
|
106
|
+
t.datetime :expires_in, null: false
|
107
|
+
t.index %i[oauth_application_id code], unique: true
|
108
|
+
end
|
86
109
|
end
|
87
|
-
end
|
110
|
+
end
|
@@ -27,7 +27,19 @@ module Rodauth
|
|
27
27
|
|
28
28
|
response_mode = param_or_nil("response_mode")
|
29
29
|
|
30
|
-
|
30
|
+
return unless response_mode
|
31
|
+
|
32
|
+
redirect_response_error("invalid_request") unless oauth_response_modes_supported.include?(response_mode)
|
33
|
+
|
34
|
+
response_type = param_or_nil("response_type")
|
35
|
+
|
36
|
+
return unless response_type.nil? || response_type == "code"
|
37
|
+
|
38
|
+
redirect_response_error("invalid_request") unless oauth_response_modes_for_code_supported.include?(response_mode)
|
39
|
+
end
|
40
|
+
|
41
|
+
def oauth_response_modes_for_code_supported
|
42
|
+
%w[query form_post]
|
31
43
|
end
|
32
44
|
|
33
45
|
def validate_token_params
|
@@ -57,7 +69,7 @@ module Rodauth
|
|
57
69
|
def _do_authorize_code
|
58
70
|
create_params = {
|
59
71
|
oauth_grants_type_column => "authorization_code",
|
60
|
-
|
72
|
+
**resource_owner_params
|
61
73
|
}
|
62
74
|
|
63
75
|
{ "code" => create_oauth_grant(create_params) }
|
@@ -67,55 +79,65 @@ module Rodauth
|
|
67
79
|
redirect_url = URI.parse(redirect_uri)
|
68
80
|
case mode
|
69
81
|
when "query"
|
70
|
-
params =
|
82
|
+
params = [URI.encode_www_form(params)]
|
71
83
|
params << redirect_url.query if redirect_url.query
|
72
84
|
redirect_url.query = params.join("&")
|
73
85
|
redirect(redirect_url.to_s)
|
74
86
|
when "form_post"
|
75
|
-
|
76
|
-
|
77
|
-
<
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
params.map do |name, value|
|
82
|
-
"<input type=\"hidden\" name=\"#{scope.h(name)}\" value=\"#{scope.h(value)}\" />"
|
83
|
-
end.join
|
84
|
-
}
|
85
|
-
<input type="submit" class="btn btn-outline-primary" value="#{scope.h(oauth_authorize_post_button)}"/>
|
86
|
-
</form>
|
87
|
-
</body>
|
88
|
-
</html>
|
89
|
-
FORM
|
87
|
+
inline_html = form_post_response_html(redirect_uri) do
|
88
|
+
params.map do |name, value|
|
89
|
+
"<input type=\"hidden\" name=\"#{scope.h(name)}\" value=\"#{scope.h(value)}\" />"
|
90
|
+
end.join
|
91
|
+
end
|
92
|
+
scope.view layout: false, inline: inline_html
|
90
93
|
end
|
91
94
|
end
|
92
95
|
|
93
|
-
def _redirect_response_error(redirect_url,
|
96
|
+
def _redirect_response_error(redirect_url, params)
|
94
97
|
response_mode = param_or_nil("response_mode") || oauth_response_mode
|
95
98
|
|
96
99
|
case response_mode
|
97
100
|
when "form_post"
|
98
101
|
response["Content-Type"] = "text/html"
|
99
|
-
|
100
|
-
|
101
|
-
<
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
query_params.map do |name, value|
|
106
|
-
"<input type=\"hidden\" name=\"#{name}\" value=\"#{scope.h(value)}\" />"
|
107
|
-
end.join
|
108
|
-
}
|
109
|
-
</form>
|
110
|
-
</body>
|
111
|
-
</html>
|
112
|
-
FORM
|
102
|
+
error_body = form_post_error_response_html(redirect_url) do
|
103
|
+
params.map do |name, value|
|
104
|
+
"<input type=\"hidden\" name=\"#{name}\" value=\"#{scope.h(value)}\" />"
|
105
|
+
end.join
|
106
|
+
end
|
107
|
+
response.write(error_body)
|
113
108
|
request.halt
|
114
109
|
else
|
115
110
|
super
|
116
111
|
end
|
117
112
|
end
|
118
113
|
|
114
|
+
def form_post_response_html(url)
|
115
|
+
<<-FORM
|
116
|
+
<html>
|
117
|
+
<head><title>Authorized</title></head>
|
118
|
+
<body onload="javascript:document.forms[0].submit()">
|
119
|
+
<form method="post" action="#{url}">
|
120
|
+
#{yield}
|
121
|
+
<input type="submit" class="btn btn-outline-primary" value="#{scope.h(oauth_authorize_post_button)}"/>
|
122
|
+
</form>
|
123
|
+
</body>
|
124
|
+
</html>
|
125
|
+
FORM
|
126
|
+
end
|
127
|
+
|
128
|
+
def form_post_error_response_html(url)
|
129
|
+
<<-FORM
|
130
|
+
<html>
|
131
|
+
<head><title></title></head>
|
132
|
+
<body onload="javascript:document.forms[0].submit()">
|
133
|
+
<form method="post" action="#{url}">
|
134
|
+
#{yield}
|
135
|
+
</form>
|
136
|
+
</body>
|
137
|
+
</html>
|
138
|
+
FORM
|
139
|
+
end
|
140
|
+
|
119
141
|
def create_token(grant_type)
|
120
142
|
return super unless supported_grant_type?(grant_type, "authorization_code")
|
121
143
|
|
@@ -28,6 +28,11 @@ 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
|
+
auth_value_methods(
|
32
|
+
:resource_owner_params,
|
33
|
+
:oauth_grants_resource_owner_columns
|
34
|
+
)
|
35
|
+
|
31
36
|
# /authorize
|
32
37
|
auth_server_route(:authorize) do |r|
|
33
38
|
require_authorizable_account
|
@@ -73,7 +78,9 @@ module Rodauth
|
|
73
78
|
|
74
79
|
if (redirect_uri = param_or_nil("redirect_uri"))
|
75
80
|
normalized_redirect_uri = normalize_redirect_uri_for_comparison(redirect_uri)
|
76
|
-
|
81
|
+
unless redirect_uris.include?(normalized_redirect_uri) || redirect_uris.include?(redirect_uri)
|
82
|
+
redirect_authorize_error("redirect_uri")
|
83
|
+
end
|
77
84
|
elsif redirect_uris.size > 1
|
78
85
|
redirect_authorize_error("redirect_uri")
|
79
86
|
end
|
@@ -85,6 +92,14 @@ module Rodauth
|
|
85
92
|
try_approval_prompt if use_oauth_access_type? && request.get?
|
86
93
|
|
87
94
|
redirect_response_error("invalid_scope") if (request.post? || param_or_nil("scope")) && !check_valid_scopes?
|
95
|
+
|
96
|
+
response_mode = param_or_nil("response_mode")
|
97
|
+
|
98
|
+
redirect_response_error("invalid_request") unless response_mode.nil? || oauth_response_modes_supported.include?(response_mode)
|
99
|
+
end
|
100
|
+
|
101
|
+
def check_valid_scopes?(scp = scopes)
|
102
|
+
super(scp - %w[offline_access])
|
88
103
|
end
|
89
104
|
|
90
105
|
def check_valid_response_type?
|
@@ -109,13 +124,20 @@ module Rodauth
|
|
109
124
|
!approval_prompt || APPROVAL_PROMPTS.include?(approval_prompt)
|
110
125
|
end
|
111
126
|
|
127
|
+
def resource_owner_params
|
128
|
+
{ oauth_grants_account_id_column => account_id }
|
129
|
+
end
|
130
|
+
|
131
|
+
def oauth_grants_resource_owner_columns
|
132
|
+
[oauth_grants_account_id_column]
|
133
|
+
end
|
134
|
+
|
112
135
|
def try_approval_prompt
|
113
136
|
approval_prompt = param_or_nil("approval_prompt")
|
114
137
|
|
115
138
|
return unless approval_prompt && approval_prompt == "auto"
|
116
139
|
|
117
|
-
return if db[oauth_grants_table].where(
|
118
|
-
oauth_grants_account_id_column => account_id,
|
140
|
+
return if db[oauth_grants_table].where(resource_owner_params).where(
|
119
141
|
oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column],
|
120
142
|
oauth_grants_redirect_uri_column => redirect_uri,
|
121
143
|
oauth_grants_scopes_column => scopes.join(oauth_scope_separator),
|
@@ -762,31 +762,31 @@ module Rodauth
|
|
762
762
|
throw_json_response_error(status_code, error_code)
|
763
763
|
else
|
764
764
|
redirect_url = URI.parse(redirect_url)
|
765
|
-
|
765
|
+
params = []
|
766
766
|
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
767
|
+
params << if respond_to?(:"oauth_#{error_code}_error_code")
|
768
|
+
["error", send(:"oauth_#{error_code}_error_code")]
|
769
|
+
else
|
770
|
+
["error", error_code]
|
771
|
+
end
|
772
772
|
|
773
773
|
if respond_to?(:"oauth_#{error_code}_message")
|
774
774
|
message = send(:"oauth_#{error_code}_message")
|
775
|
-
|
775
|
+
params << ["error_description", CGI.escape(message)]
|
776
776
|
end
|
777
777
|
|
778
778
|
state = param_or_nil("state")
|
779
779
|
|
780
|
-
|
780
|
+
params << ["state", state] if state
|
781
781
|
|
782
|
-
_redirect_response_error(redirect_url,
|
782
|
+
_redirect_response_error(redirect_url, params)
|
783
783
|
end
|
784
784
|
end
|
785
785
|
|
786
|
-
def _redirect_response_error(redirect_url,
|
787
|
-
|
788
|
-
|
789
|
-
redirect_url.query =
|
786
|
+
def _redirect_response_error(redirect_url, params)
|
787
|
+
params = params.map { |k, v| "#{k}=#{v}" }
|
788
|
+
params << redirect_url.query if redirect_url.query
|
789
|
+
redirect_url.query = params.join("&")
|
790
790
|
redirect(redirect_url.to_s)
|
791
791
|
end
|
792
792
|
|
@@ -841,10 +841,10 @@ module Rodauth
|
|
841
841
|
throw_json_response_error(oauth_authorization_required_error_status, "invalid_client")
|
842
842
|
end
|
843
843
|
|
844
|
-
def check_valid_scopes?
|
845
|
-
return false unless
|
844
|
+
def check_valid_scopes?(scp = scopes)
|
845
|
+
return false unless scp
|
846
846
|
|
847
|
-
(
|
847
|
+
(scp - oauth_application[oauth_applications_scopes_column].split(oauth_scope_separator)).empty?
|
848
848
|
end
|
849
849
|
|
850
850
|
def check_valid_uri?(uri)
|
@@ -193,9 +193,8 @@ module Rodauth
|
|
193
193
|
|
194
194
|
# do not clean up device code just yet
|
195
195
|
update_params.delete(oauth_grants_code_column)
|
196
|
-
|
197
196
|
update_params[oauth_grants_user_code_column] = nil
|
198
|
-
update_params
|
197
|
+
update_params.merge!(resource_params)
|
199
198
|
|
200
199
|
super(grant_params, update_params)
|
201
200
|
end
|