rodauth-oauth 0.8.0 → 0.9.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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -2
  3. data/doc/release_notes/0_9_0.md +56 -0
  4. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize.html.erb +22 -1
  5. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/new_oauth_application.html.erb +8 -3
  6. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application.html.erb +8 -2
  7. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application_oauth_tokens.html.erb +1 -0
  8. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_applications.html.erb +1 -0
  9. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_tokens.html.erb +1 -0
  10. data/lib/generators/rodauth/oauth/templates/db/migrate/create_rodauth_oauth.rb +13 -1
  11. data/lib/rodauth/features/oauth.rb +2 -2
  12. data/lib/rodauth/features/oauth_application_management.rb +22 -6
  13. data/lib/rodauth/features/oauth_assertion_base.rb +1 -1
  14. data/lib/rodauth/features/oauth_authorization_code_grant.rb +4 -1
  15. data/lib/rodauth/features/oauth_base.rb +46 -10
  16. data/lib/rodauth/features/oauth_client_credentials_grant.rb +33 -0
  17. data/lib/rodauth/features/oauth_device_grant.rb +4 -5
  18. data/lib/rodauth/features/oauth_dynamic_client_registration.rb +252 -0
  19. data/lib/rodauth/features/oauth_jwt.rb +248 -49
  20. data/lib/rodauth/features/oauth_management_base.rb +68 -0
  21. data/lib/rodauth/features/oauth_pkce.rb +1 -1
  22. data/lib/rodauth/features/oauth_token_management.rb +8 -6
  23. data/lib/rodauth/features/oidc.rb +32 -3
  24. data/lib/rodauth/features/oidc_dynamic_client_registration.rb +147 -0
  25. data/lib/rodauth/oauth/jwe_extensions.rb +64 -0
  26. data/lib/rodauth/oauth/ttl_store.rb +9 -3
  27. data/lib/rodauth/oauth/version.rb +1 -1
  28. data/locales/en.yml +5 -0
  29. data/templates/authorize.str +50 -1
  30. data/templates/jwks_field.str +4 -0
  31. data/templates/oauth_application.str +1 -1
  32. data/templates/oauth_application_oauth_tokens.str +1 -0
  33. data/templates/oauth_applications.str +1 -0
  34. data/templates/oauth_tokens.str +1 -0
  35. metadata +10 -3
  36. data/templates/jws_jwk_field.str +0 -4
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rodauth
4
+ Feature.define(:oauth_management_base, :OauthManagementBase) do
5
+ depends :oauth_base
6
+
7
+ button "Previous", "oauth_management_pagination_previous"
8
+ button "Next", "oauth_management_pagination_next"
9
+
10
+ def oauth_management_pagination_links(paginated_ds)
11
+ html = +'<nav aria-label="Pagination"><ul class="pagination">'
12
+ html << oauth_management_pagination_link(paginated_ds.prev_page, label: oauth_management_pagination_previous_button)
13
+ html << oauth_management_pagination_link(paginated_ds.current_page - 1) unless paginated_ds.first_page?
14
+ html << oauth_management_pagination_link(paginated_ds.current_page, label: paginated_ds.current_page, current: true)
15
+ html << oauth_management_pagination_link(paginated_ds.current_page + 1) unless paginated_ds.last_page?
16
+ html << oauth_management_pagination_link(paginated_ds.next_page, label: oauth_management_pagination_next_button)
17
+ html << "</ul></nav>"
18
+ end
19
+
20
+ def oauth_management_pagination_link(page, label: page, current: false, classes: "")
21
+ classes += " disabled" if current || !page
22
+ classes += " active" if current
23
+ if page
24
+ params = request.GET.merge("page" => page).map do |k, v|
25
+ v ? "#{CGI.escape(String(k))}=#{CGI.escape(String(v))}" : CGI.escape(String(k))
26
+ end.join("&")
27
+
28
+ href = "#{request.path}?#{params}"
29
+
30
+ <<-HTML
31
+ <li class="page-item #{classes}" #{'aria-current="page"' if current}>
32
+ <a class="page-link" href="#{href}" tabindex="-1" aria-disabled="#{current || !page}">
33
+ #{label}
34
+ </a>
35
+ </li>
36
+ HTML
37
+ else
38
+ <<-HTML
39
+ <li class="page-item #{classes}">
40
+ <span class="page-link">
41
+ #{label}
42
+ #{'<span class="sr-only">(current)</span>' if current}
43
+ </span>
44
+ </li>
45
+ HTML
46
+ end
47
+ end
48
+
49
+ def post_configure
50
+ super
51
+ db.extension :pagination
52
+ end
53
+
54
+ private
55
+
56
+ def per_page_param(default_per_page)
57
+ per_page = param_or_nil("per_page")
58
+
59
+ return default_per_page unless per_page
60
+
61
+ per_page = per_page.to_i
62
+
63
+ return default_per_page if per_page <= 0
64
+
65
+ [per_page, default_per_page].min
66
+ end
67
+ end
68
+ end
@@ -23,7 +23,7 @@ module Rodauth
23
23
 
24
24
  private
25
25
 
26
- def authorized_oauth_application?(oauth_application, client_secret)
26
+ def authorized_oauth_application?(oauth_application, client_secret, _)
27
27
  return true if use_oauth_pkce? && param_or_nil("code_verifier")
28
28
 
29
29
  super
@@ -4,7 +4,7 @@ module Rodauth
4
4
  Feature.define(:oauth_token_management, :OauthTokenManagement) do
5
5
  using RegexpExtensions
6
6
 
7
- depends :oauth_base
7
+ depends :oauth_management_base
8
8
 
9
9
  view "oauth_tokens", "My Oauth Tokens", "oauth_tokens"
10
10
 
@@ -18,6 +18,7 @@ module Rodauth
18
18
 
19
19
  auth_value_method :oauth_tokens_route, "oauth-tokens"
20
20
  auth_value_method :oauth_tokens_id_pattern, Integer
21
+ auth_value_method :oauth_tokens_per_page, 20
21
22
 
22
23
  auth_value_methods(
23
24
  :oauth_token_path
@@ -40,12 +41,17 @@ module Rodauth
40
41
  require_account
41
42
 
42
43
  request.get do
44
+ page = Integer(param_or_nil("page") || 1)
45
+ per_page = per_page_param(oauth_tokens_per_page)
46
+
43
47
  scope.instance_variable_set(:@oauth_tokens, db[oauth_tokens_table]
44
48
  .select(Sequel[oauth_tokens_table].*, Sequel[oauth_applications_table][oauth_applications_name_column])
45
49
  .join(oauth_applications_table, Sequel[oauth_tokens_table][oauth_tokens_oauth_application_id_column] =>
46
50
  Sequel[oauth_applications_table][oauth_applications_id_column])
47
51
  .where(Sequel[oauth_tokens_table][oauth_tokens_account_id_column] => account_id)
48
- .where(oauth_tokens_revoked_at_column => nil))
52
+ .where(oauth_tokens_revoked_at_column => nil)
53
+ .order(Sequel.desc(oauth_tokens_id_column))
54
+ .paginate(page, per_page))
49
55
  oauth_tokens_view
50
56
  end
51
57
 
@@ -69,9 +75,5 @@ module Rodauth
69
75
  super
70
76
  end
71
77
  end
72
-
73
- def check_valid_uri?(uri)
74
- URI::DEFAULT_PARSER.make_regexp(oauth_valid_uri_schemes).match?(uri)
75
- end
76
78
  end
77
79
  end
@@ -65,6 +65,13 @@ module Rodauth
65
65
  auth_value_method :oauth_application_default_scope, "openid"
66
66
  auth_value_method :oauth_application_scopes, %w[openid]
67
67
 
68
+ auth_value_method :oauth_applications_id_token_signed_response_alg_column, :id_token_signed_response_alg
69
+ auth_value_method :oauth_applications_id_token_encrypted_response_alg_column, :id_token_encrypted_response_alg
70
+ auth_value_method :oauth_applications_id_token_encrypted_response_enc_column, :id_token_encrypted_response_enc
71
+ auth_value_method :oauth_applications_userinfo_signed_response_alg_column, :userinfo_signed_response_alg
72
+ auth_value_method :oauth_applications_userinfo_encrypted_response_alg_column, :userinfo_encrypted_response_alg
73
+ auth_value_method :oauth_applications_userinfo_encrypted_response_enc_column, :userinfo_encrypted_response_enc
74
+
68
75
  auth_value_method :oauth_grants_nonce_column, :nonce
69
76
  auth_value_method :oauth_tokens_nonce_column, :nonce
70
77
 
@@ -106,7 +113,23 @@ module Rodauth
106
113
 
107
114
  fill_with_account_claims(oidc_claims, account, oauth_scopes)
108
115
 
109
- json_response_success(oidc_claims)
116
+ @oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column => oauth_token["client_id"]).first
117
+
118
+ if (algo = @oauth_application && @oauth_application[oauth_applications_userinfo_signed_response_alg_column])
119
+ params = {
120
+ jwks: oauth_application_jwks,
121
+ encryption_algorithm: @oauth_application[oauth_applications_userinfo_encrypted_response_alg_column],
122
+ encryption_method: @oauth_application[oauth_applications_userinfo_encrypted_response_enc_column]
123
+ }
124
+ jwt = jwt_encode(
125
+ oidc_claims,
126
+ signing_algorithm: algo,
127
+ **params
128
+ )
129
+ jwt_response_success(jwt)
130
+ else
131
+ json_response_success(oidc_claims)
132
+ end
110
133
  end
111
134
 
112
135
  throw_json_response_error(authorization_required_error_status, "invalid_token")
@@ -330,7 +353,13 @@ module Rodauth
330
353
 
331
354
  fill_with_account_claims(id_token_claims, account, oauth_scopes)
332
355
 
333
- oauth_token[:id_token] = jwt_encode(id_token_claims)
356
+ params = {
357
+ jwks: oauth_application_jwks,
358
+ signing_algorithm: oauth_application[oauth_applications_id_token_signed_response_alg_column] || oauth_jwt_algorithm,
359
+ encryption_algorithm: oauth_application[oauth_applications_id_token_encrypted_response_alg_column],
360
+ encryption_method: oauth_application[oauth_applications_id_token_encrypted_response_enc_column]
361
+ }
362
+ oauth_token[:id_token] = jwt_encode(id_token_claims, **params)
334
363
  end
335
364
 
336
365
  # aka fill_with_standard_claims
@@ -443,7 +472,7 @@ module Rodauth
443
472
 
444
473
  # Metadata
445
474
 
446
- def openid_configuration_body(path)
475
+ def openid_configuration_body(path = nil)
447
476
  metadata = oauth_server_metadata_body(path).select do |k, _|
448
477
  VALID_METADATA_KEYS.include?(k)
449
478
  end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rodauth
4
+ Feature.define(:oidc_dynamic_client_registration, :OidcDynamicClientRegistration) do
5
+ depends :oauth_dynamic_client_registration, :oidc
6
+
7
+ auth_value_method :oauth_applications_application_type_column, :application_type
8
+
9
+ private
10
+
11
+ def registration_metadata
12
+ openid_configuration_body
13
+ end
14
+
15
+ def validate_client_registration_params
16
+ super
17
+
18
+ if (value = @oauth_application_params[oauth_applications_application_type_column])
19
+ case value
20
+ when "native"
21
+ request.params["redirect_uris"].each do |uri|
22
+ uri = URI(uri)
23
+ # Native Clients MUST only register redirect_uris using custom URI schemes or
24
+ # URLs using the http: scheme with localhost as the hostname.
25
+ case uri.scheme
26
+ when "http"
27
+ register_throw_json_response_error("invalid_redirect_uri", register_invalid_uri_message(uri)) unless uri.host == "localhost"
28
+ when "https"
29
+ register_throw_json_response_error("invalid_redirect_uri", register_invalid_uri_message(uri))
30
+ end
31
+ end
32
+ when "web"
33
+ # Web Clients using the OAuth Implicit Grant Type MUST only register URLs using the https scheme as redirect_uris;
34
+ # they MUST NOT use localhost as the hostname.
35
+ if request.params["grant_types"].include?("implicit")
36
+ request.params["redirect_uris"].each do |uri|
37
+ uri = URI(uri)
38
+ unless uri.scheme == "https" && uri.host != "localhost"
39
+ register_throw_json_response_error("invalid_redirect_uri", register_invalid_uri_message(uri))
40
+ end
41
+ end
42
+ end
43
+ else
44
+ register_throw_json_response_error("invalid_client_metadata", register_invalid_application_type_message(type))
45
+ end
46
+ elsif (value = @oauth_application_params[oauth_applications_subject_type_column])
47
+ unless %w[pairwise public].include?(value)
48
+ register_throw_json_response_error("invalid_client_metadata", register_invalid_param_message("subject_type"))
49
+ end
50
+ elsif (value = @oauth_application_params[oauth_applications_id_token_signed_response_alg_column])
51
+ if value == "none"
52
+ # The value none MUST NOT be used as the ID Token alg value unless the Client uses only Response Types
53
+ # that return no ID Token from the Authorization Endpoint
54
+ response_types = @oauth_application_params[oauth_applications_response_types_column]
55
+ if response_types && response_types.include?("id_token")
56
+ register_throw_json_response_error("invalid_client_metadata", register_invalid_param_message("id_token_signed_response_alg"))
57
+ end
58
+ elsif !oauth_jwt_algorithms_supported.include?(value)
59
+ register_throw_json_response_error("invalid_client_metadata", register_invalid_param_message("id_token_signed_response_alg"))
60
+ end
61
+ elsif (value = @oauth_application_params[oauth_applications_id_token_encrypted_response_alg_column])
62
+ unless oauth_jwt_jwe_algorithms_supported.include?(value)
63
+ register_throw_json_response_error("invalid_client_metadata", register_invalid_param_message("id_token_encrypted_response_alg"))
64
+ end
65
+ elsif (value = @oauth_application_params[oauth_applications_id_token_encrypted_response_enc_column])
66
+ unless oauth_jwt_jwe_encryption_methods_supported.include?(value)
67
+ register_throw_json_response_error("invalid_client_metadata", register_invalid_param_message("id_token_encrypted_response_enc"))
68
+ end
69
+ elsif (value = @oauth_application_params[oauth_applications_userinfo_signed_response_alg_column])
70
+ unless oauth_jwt_algorithms_supported.include?(value)
71
+ register_throw_json_response_error("invalid_client_metadata", register_invalid_param_message("userinfo_signed_response_alg"))
72
+ end
73
+ elsif (value = @oauth_application_params[oauth_applications_userinfo_encrypted_response_alg_column])
74
+ unless oauth_jwt_jwe_algorithms_supported.include?(value)
75
+ register_throw_json_response_error("invalid_client_metadata", register_invalid_param_message("userinfo_encrypted_response_alg"))
76
+ end
77
+ elsif (value = @oauth_application_params[oauth_applications_userinfo_encrypted_response_enc_column])
78
+ unless oauth_jwt_jwe_encryption_methods_supported.include?(value)
79
+ register_throw_json_response_error("invalid_client_metadata", register_invalid_param_message("userinfo_encrypted_response_enc"))
80
+ end
81
+ elsif (value = @oauth_application_params[oauth_applications_request_object_signing_alg_column])
82
+ unless oauth_jwt_algorithms_supported.include?(value)
83
+ register_throw_json_response_error("invalid_client_metadata", register_invalid_param_message("request_object_signing_alg"))
84
+ end
85
+ elsif (value = @oauth_application_params[oauth_applications_request_object_encryption_alg_column])
86
+ unless oauth_jwt_jwe_algorithms_supported.include?(value)
87
+ register_throw_json_response_error("invalid_client_metadata", register_invalid_param_message("request_object_encryption_alg"))
88
+ end
89
+ elsif (value = @oauth_application_params[oauth_applications_request_object_encryption_enc_column])
90
+ unless oauth_jwt_jwe_encryption_methods_supported.include?(value)
91
+ register_throw_json_response_error("invalid_client_metadata", register_invalid_param_message("request_object_encryption_enc"))
92
+ end
93
+ end
94
+ end
95
+
96
+ def validate_client_registration_response_type(response_type, grant_types)
97
+ case response_type
98
+ when "id_token"
99
+ unless grant_types.include?("implicit")
100
+ register_throw_json_response_error("invalid_client_metadata",
101
+ register_invalid_response_type_for_grant_type_message(response_type, "implicit"))
102
+ end
103
+ else
104
+ super
105
+ end
106
+ end
107
+
108
+ def do_register(return_params = request.params.dup)
109
+ # set defaults
110
+
111
+ create_params = @oauth_application_params
112
+
113
+ create_params[oauth_applications_application_type_column] ||= begin
114
+ return_params["application_type"] = "web"
115
+ "web"
116
+ end
117
+ create_params[oauth_applications_id_token_signed_response_alg_column] ||= begin
118
+ return_params["id_token_signed_response_alg"] = oauth_jwt_algorithm
119
+ oauth_jwt_algorithm
120
+ end
121
+ if create_params.key?(oauth_applications_id_token_encrypted_response_alg_column)
122
+ create_params[oauth_applications_id_token_encrypted_response_enc_column] ||= begin
123
+ return_params["id_token_encrypted_response_enc"] = "A128CBC-HS256"
124
+ "A128CBC-HS256"
125
+ end
126
+ end
127
+ if create_params.key?(oauth_applications_userinfo_encrypted_response_alg_column)
128
+ create_params[oauth_applications_userinfo_encrypted_response_enc_column] ||= begin
129
+ return_params["userinfo_encrypted_response_enc"] = "A128CBC-HS256"
130
+ "A128CBC-HS256"
131
+ end
132
+ end
133
+ if create_params.key?(oauth_applications_request_object_encryption_alg_column)
134
+ create_params[oauth_applications_request_object_encryption_enc_column] ||= begin
135
+ return_params["request_object_encryption_enc"] = "A128CBC-HS256"
136
+ "A128CBC-HS256"
137
+ end
138
+ end
139
+
140
+ super(return_params)
141
+ end
142
+
143
+ def register_invalid_application_type_message(application_type)
144
+ "The application type '#{application_type}' is not allowed."
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWE
4
+ #
5
+ # this is a monkey-patch!
6
+ # it's necessary, as the original jwe does not support jwks.
7
+ # if this works long term, it may be merged upstreamm.
8
+ #
9
+ def self.__rodauth_oauth_decrypt_from_jwks(payload, jwks, alg: "RSA-OAEP", enc: "A128GCM")
10
+ header, enc_key, iv, ciphertext, tag = Serialization::Compact.decode(payload)
11
+ header = JSON.parse(header)
12
+
13
+ key = find_key_by_kid(jwks, header["kid"], alg, enc)
14
+
15
+ check_params(header, key)
16
+
17
+ cek = Alg.decrypt_cek(header["alg"], key, enc_key)
18
+ cipher = Enc.for(header["enc"], cek, iv, tag)
19
+
20
+ plaintext = cipher.decrypt(ciphertext, payload.split(".").first)
21
+
22
+ apply_zip(header, plaintext, :decompress)
23
+ end
24
+
25
+ def self.__rodauth_oauth_encrypt_from_jwks(payload, jwks, alg: "RSA-OAEP", enc: "A128GCM", **more_headers)
26
+ header = generate_header(alg, enc, more_headers)
27
+
28
+ key = find_key_by_alg_enc(jwks, alg, enc)
29
+
30
+ check_params(header, key)
31
+ payload = apply_zip(header, payload, :compress)
32
+
33
+ cipher = Enc.for(enc)
34
+ cipher.cek = key if alg == "dir"
35
+
36
+ json_hdr = header.to_json
37
+ ciphertext = cipher.encrypt(payload, Base64.jwe_encode(json_hdr))
38
+
39
+ generate_serialization(json_hdr, Alg.encrypt_cek(alg, key, cipher.cek), ciphertext, cipher)
40
+ end
41
+
42
+ def self.find_key_by_kid(jwks, kid, alg, enc)
43
+ raise DecodeError, "No key id (kid) found from token headers" unless kid
44
+
45
+ jwk = jwks.find { |key, _| (key[:kid] || key["kid"]) == kid }
46
+
47
+ raise DecodeError, "Could not find public key for kid #{kid}" unless jwk
48
+ raise DecodeError, "Expected a different encryption algorithm" unless alg == (jwk[:alg] || jwk["alg"])
49
+ raise DecodeError, "Expected a different encryption method" unless enc == (jwk[:enc] || jwk["enc"])
50
+
51
+ ::JWT::JWK.import(jwk).keypair
52
+ end
53
+
54
+ def self.find_key_by_alg_enc(jwks, alg, enc)
55
+ jwk = jwks.find do |key, _|
56
+ (key[:alg] || key["alg"]) == alg &&
57
+ (key[:enc] || key["enc"]) == enc
58
+ end
59
+
60
+ raise DecodeError, "No key found" unless jwk
61
+
62
+ ::JWT::JWK.import(jwk).keypair
63
+ end
64
+ end
@@ -24,12 +24,18 @@ class Rodauth::OAuth::TtlStore
24
24
  @store_mutex.synchronize do
25
25
  # short circuit
26
26
  return @store[key][:payload] if @store[key] && @store[key][:ttl] < now
27
+ end
27
28
 
28
- payload, ttl = block.call
29
- @store[key] = { payload: payload, ttl: (ttl || (now + DEFAULT_TTL)) }
29
+ payload, ttl = block.call
30
+
31
+ @store_mutex.synchronize do
32
+ # given that the block call triggers network, and two requests for the same key be processed
33
+ # at the same time, this ensures the first one wins.
34
+ return @store[key][:payload] if @store[key] && @store[key][:ttl] < now
30
35
 
31
- @store[key][:payload]
36
+ @store[key] = { payload: payload, ttl: (ttl || (now + DEFAULT_TTL)) }
32
37
  end
38
+ @store[key][:payload]
33
39
  end
34
40
 
35
41
  def uncache(key)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Rodauth
4
4
  module OAuth
5
- VERSION = "0.8.0"
5
+ VERSION = "0.9.0"
6
6
  end
7
7
  end
data/locales/en.yml CHANGED
@@ -15,10 +15,15 @@ en:
15
15
  oauth_tokens_page_title: "My Oauth Tokens"
16
16
  device_verification_page_title: "Device Verification"
17
17
  device_search_page_title: "Device Search"
18
+ oauth_management_pagination_previous_button: "Previous"
19
+ oauth_management_pagination_next_button: "Next"
18
20
  oauth_applications_name_label: "Name"
19
21
  oauth_applications_description_label: "Description"
20
22
  oauth_applications_scopes_label: "Scopes"
23
+ oauth_applications_contacts_label: "Contacts"
21
24
  oauth_applications_homepage_url_label: "Homepage URL"
25
+ oauth_applications_tos_uri_label: "Terms of Service URL"
26
+ oauth_applications_policy_uri_label: "Policy URL"
22
27
  oauth_applications_redirect_uri_label: "Redirect URL"
23
28
  oauth_applications_client_secret_label: "Client Secret"
24
29
  oauth_applications_client_id_label: "Client ID"
@@ -1,6 +1,55 @@
1
1
  <form method="post" action="#{rodauth.authorize_path}" class="form-horizontal" role="form" id="authorize-form">
2
2
  #{csrf_tag(rodauth.authorize_path) if respond_to?(:csrf_tag)}
3
- <p class="lead">The application #{rodauth.oauth_application[rodauth.oauth_applications_name_column]} would like to access your data.</p>
3
+ #{
4
+ if rodauth.oauth_application[rodauth.oauth_applications_logo_uri_column]
5
+ <<-HTML
6
+ <img src="#{h(rodauth.oauth_application[rodauth.oauth_applications_logo_uri_column])}" />
7
+ HTML
8
+ end
9
+ }
10
+ <p class="lead">
11
+ The application
12
+ <a target="_blank" href="#{h(rodauth.oauth_application[rodauth.oauth_applications_homepage_url_column])}">
13
+ #{h(rodauth.oauth_application[rodauth.oauth_applications_name_column])}
14
+ </a> would like to access your data.
15
+ </p>
16
+ <div class="list-group">
17
+ #{
18
+ if rodauth.oauth_application[rodauth.oauth_applications_tos_uri_column]
19
+ <<-HTML
20
+ <a class="list-group-item" target="_blank" href="#{h(rodauth.oauth_application[rodauth.oauth_applications_tos_uri_column])}">
21
+ #{rodauth.oauth_applications_tos_uri_label}
22
+ </a>
23
+ HTML
24
+ end
25
+ }
26
+ #{
27
+ if rodauth.oauth_application[rodauth.oauth_applications_policy_uri_column]
28
+ <<-HTML
29
+ <a class="list-group-item" target="_blank" href="#{h(rodauth.oauth_application[rodauth.oauth_applications_policy_uri_column])}">
30
+ #{rodauth.oauth_applications_policy_uri_label}
31
+ </a>
32
+ HTML
33
+ end
34
+ }
35
+ </div>
36
+
37
+ #{
38
+ if rodauth.oauth_application[rodauth.oauth_applications_contacts_column]
39
+ data = <<-HTML
40
+ <div class="list-group">
41
+ <h3 class="display-6">#{rodauth.oauth_applications_contacts_label}</h3>
42
+ HTML
43
+ rodauth.oauth_application[rodauth.oauth_applications_contacts_column].split(/ +/).each do |contact|
44
+ data << <<-HTML
45
+ <div class="list-group-item">
46
+ #{h(contact)}
47
+ </div>
48
+ HTML
49
+ end
50
+ data << "</div>"
51
+ end
52
+ }
4
53
 
5
54
  <div class="form-group">
6
55
  <h1 class="display-6">#{rodauth.oauth_tokens_scopes_label}</h1>
@@ -0,0 +1,4 @@
1
+ <div class="form-group">
2
+ <label for="name">#{rodauth.oauth_applications_jwks_label}#{rodauth.input_field_label_suffix}</label>
3
+ #{rodauth.input_field_string(rodauth.oauth_application_jwks_param, "jwks", :type=>"text")}
4
+ </div>
@@ -3,7 +3,7 @@
3
3
  #{
4
4
  params = [*rodauth.oauth_application_required_params, "client_id", "client_secret"]
5
5
  if rodauth.features.include?(:oauth_jwt)
6
- params += %w[jws_jwk jwt_public_key]
6
+ params += %w[jwks jwt_public_key]
7
7
  end
8
8
  params.map do |param|
9
9
  "<dt class=\"#{param}\">#{rodauth.send(:"oauth_applications_#{param}_label")}: </dt>" +
@@ -45,6 +45,7 @@
45
45
  }
46
46
  </tbody>
47
47
  </table>
48
+ #{rodauth.oauth_management_pagination_links(@oauth_tokens)}
48
49
  HTML
49
50
  end
50
51
  }
@@ -11,4 +11,5 @@
11
11
  "</ul>"
12
12
  end
13
13
  }
14
+ #{rodauth.oauth_management_pagination_links(@oauth_applications)}
14
15
  </div>
@@ -43,6 +43,7 @@
43
43
  }
44
44
  </tbody>
45
45
  </table>
46
+ #{rodauth.oauth_management_pagination_links(@oauth_tokens)}
46
47
  HTML
47
48
  end
48
49
  }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rodauth-oauth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tiago Cardoso
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-12 00:00:00.000000000 Z
11
+ date: 2022-04-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rodauth
@@ -56,6 +56,7 @@ extra_rdoc_files:
56
56
  - doc/release_notes/0_7_3.md
57
57
  - doc/release_notes/0_7_4.md
58
58
  - doc/release_notes/0_8_0.md
59
+ - doc/release_notes/0_9_0.md
59
60
  files:
60
61
  - CHANGELOG.md
61
62
  - LICENSE.txt
@@ -83,6 +84,7 @@ files:
83
84
  - doc/release_notes/0_7_3.md
84
85
  - doc/release_notes/0_7_4.md
85
86
  - doc/release_notes/0_8_0.md
87
+ - doc/release_notes/0_9_0.md
86
88
  - lib/generators/rodauth/oauth/install_generator.rb
87
89
  - lib/generators/rodauth/oauth/templates/app/models/oauth_application.rb
88
90
  - lib/generators/rodauth/oauth/templates/app/models/oauth_grant.rb
@@ -103,11 +105,14 @@ files:
103
105
  - lib/rodauth/features/oauth_authorization_code_grant.rb
104
106
  - lib/rodauth/features/oauth_authorization_server.rb
105
107
  - lib/rodauth/features/oauth_base.rb
108
+ - lib/rodauth/features/oauth_client_credentials_grant.rb
106
109
  - lib/rodauth/features/oauth_device_grant.rb
110
+ - lib/rodauth/features/oauth_dynamic_client_registration.rb
107
111
  - lib/rodauth/features/oauth_http_mac.rb
108
112
  - lib/rodauth/features/oauth_implicit_grant.rb
109
113
  - lib/rodauth/features/oauth_jwt.rb
110
114
  - lib/rodauth/features/oauth_jwt_bearer_grant.rb
115
+ - lib/rodauth/features/oauth_management_base.rb
111
116
  - lib/rodauth/features/oauth_pkce.rb
112
117
  - lib/rodauth/features/oauth_resource_server.rb
113
118
  - lib/rodauth/features/oauth_saml_bearer_grant.rb
@@ -115,8 +120,10 @@ files:
115
120
  - lib/rodauth/features/oauth_token_management.rb
116
121
  - lib/rodauth/features/oauth_token_revocation.rb
117
122
  - lib/rodauth/features/oidc.rb
123
+ - lib/rodauth/features/oidc_dynamic_client_registration.rb
118
124
  - lib/rodauth/oauth.rb
119
125
  - lib/rodauth/oauth/database_extensions.rb
126
+ - lib/rodauth/oauth/jwe_extensions.rb
120
127
  - lib/rodauth/oauth/railtie.rb
121
128
  - lib/rodauth/oauth/refinements.rb
122
129
  - lib/rodauth/oauth/ttl_store.rb
@@ -128,7 +135,7 @@ files:
128
135
  - templates/device_search.str
129
136
  - templates/device_verification.str
130
137
  - templates/homepage_url_field.str
131
- - templates/jws_jwk_field.str
138
+ - templates/jwks_field.str
132
139
  - templates/jwt_public_key_field.str
133
140
  - templates/name_field.str
134
141
  - templates/new_oauth_application.str
@@ -1,4 +0,0 @@
1
- <div class="form-group">
2
- <label for="name">#{rodauth.oauth_applications_jws_jwk_label}#{rodauth.input_field_label_suffix}</label>
3
- #{rodauth.input_field_string(rodauth.oauth_application_jws_jwk_param, "jws_jwk", :type=>"text")}
4
- </div>