rodauth-oauth 1.1.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 12c86242a8a2001fba629cb6bd8e25886b8805fce5d0965ebc70377824e25e91
4
- data.tar.gz: 2fdc78f81b737c9c0d0086f258a86807f8855d3f0bc9be89bffb6d6a90946ed3
3
+ metadata.gz: 4d7d5f8b68686703954bf4e335cef0ea33f9e31c94c439df84f08e8ff3270829
4
+ data.tar.gz: 1da57ba2082818a74dbca4d1c6bcab0c15f97da891e12c03a8bf91440a4edcfd
5
5
  SHA512:
6
- metadata.gz: be15b77c46a135d213cd6e6e6bc7000b961e036febdb8f75bb922f09554d68ab757d5094fe96816c83c5966a3094daa32ecbbee798b2a8bb72417df9506ac3b3
7
- data.tar.gz: a5fec610e9193d449ef49fbfde36688398c9e4e576da5ea579165c306ecc51bc910d0d758c923a9bece59ef4e7651347f9ebb7951504f3354b101fe5f845173a
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](https://tools.ietf.org/html/rfc6749):
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
- * `oidc`
54
- * [OpenID Connect Core](https://openid.net/specs/openid-connect-core-1_0.html);
55
- * [OpenID Connect Discovery](https://openid.net/specs/openid-connect-discovery-1_0-29.html);
56
- * [OpenID Multiple Response Types](https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html);
57
- * `oidc_dynamic_client_registration` - [OpenID Connect Dynamic Client Registration](https://openid.net/specs/openid-connect-registration-1_0.html);
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
 
@@ -1,4 +1,4 @@
1
- ## 1.0.0 (10/01/2023)
1
+ ## 1.1.0 (10/01/2023)
2
2
 
3
3
  ## Features
4
4
 
@@ -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.integer :account_id
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.integer :account_id
64
+ t.bigint :account_id
54
65
  t.foreign_key :accounts, column: :account_id
55
- t.integer :oauth_application_id
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
- redirect_response_error("invalid_request") if response_mode && !oauth_response_modes_supported.include?(response_mode)
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
- oauth_grants_account_id_column => account_id
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 = params.map { |k, v| "#{CGI.escape(k)}=#{CGI.escape(v)}" }
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
- scope.view layout: false, inline: <<-FORM
76
- <html>
77
- <head><title>Authorized</title></head>
78
- <body onload="javascript:document.forms[0].submit()">
79
- <form method="post" action="#{redirect_uri}">
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, query_params)
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
- response.write <<-FORM
100
- <html>
101
- <head><title></title></head>
102
- <body onload="javascript:document.forms[0].submit()">
103
- <form method="post" action="#{redirect_uri}">
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
- redirect_authorize_error("redirect_uri") unless redirect_uris.include?(normalized_redirect_uri)
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
- query_params = []
765
+ params = []
766
766
 
767
- query_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
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
- query_params << ["error_description", CGI.escape(message)]
775
+ params << ["error_description", CGI.escape(message)]
776
776
  end
777
777
 
778
778
  state = param_or_nil("state")
779
779
 
780
- query_params << ["state", state] if state
780
+ params << ["state", state] if state
781
781
 
782
- _redirect_response_error(redirect_url, query_params)
782
+ _redirect_response_error(redirect_url, params)
783
783
  end
784
784
  end
785
785
 
786
- def _redirect_response_error(redirect_url, query_params)
787
- query_params = query_params.map { |k, v| "#{k}=#{v}" }
788
- query_params << redirect_url.query if redirect_url.query
789
- redirect_url.query = query_params.join("&")
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 scopes
844
+ def check_valid_scopes?(scp = scopes)
845
+ return false unless scp
846
846
 
847
- (scopes - oauth_application[oauth_applications_scopes_column].split(oauth_scope_separator)).empty?
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[oauth_grants_account_id_column] = account_id
197
+ update_params.merge!(resource_params)
199
198
 
200
199
  super(grant_params, update_params)
201
200
  end