rodauth-oauth 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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>