rodauth-oauth 1.3.2 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +17 -10
  3. data/doc/release_notes/1_4_0.md +57 -0
  4. data/doc/release_notes/1_5_0.md +20 -0
  5. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize.html.erb +28 -23
  6. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/frontchannel_logout.html.erb +10 -0
  7. data/lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_applications.html.erb +1 -1
  8. data/lib/generators/rodauth/oauth/templates/db/migrate/create_rodauth_oauth.rb +37 -1
  9. data/lib/generators/rodauth/oauth/views_generator.rb +2 -2
  10. data/lib/rodauth/features/oauth_application_management.rb +1 -1
  11. data/lib/rodauth/features/oauth_assertion_base.rb +1 -1
  12. data/lib/rodauth/features/oauth_authorize_base.rb +1 -1
  13. data/lib/rodauth/features/oauth_base.rb +49 -38
  14. data/lib/rodauth/features/oauth_device_code_grant.rb +2 -2
  15. data/lib/rodauth/features/oauth_dpop.rb +410 -0
  16. data/lib/rodauth/features/oauth_dynamic_client_registration.rb +12 -2
  17. data/lib/rodauth/features/oauth_grant_management.rb +1 -1
  18. data/lib/rodauth/features/oauth_jwt.rb +12 -13
  19. data/lib/rodauth/features/oauth_jwt_base.rb +57 -34
  20. data/lib/rodauth/features/oauth_jwt_bearer_grant.rb +1 -1
  21. data/lib/rodauth/features/oauth_jwt_jwks.rb +1 -1
  22. data/lib/rodauth/features/oauth_resource_indicators.rb +1 -1
  23. data/lib/rodauth/features/oauth_resource_server.rb +1 -1
  24. data/lib/rodauth/features/oauth_saml_bearer_grant.rb +79 -47
  25. data/lib/rodauth/features/oauth_tls_client_auth.rb +2 -4
  26. data/lib/rodauth/features/oauth_token_introspection.rb +3 -3
  27. data/lib/rodauth/features/oauth_token_revocation.rb +1 -1
  28. data/lib/rodauth/features/oidc.rb +32 -11
  29. data/lib/rodauth/features/oidc_backchannel_logout.rb +120 -0
  30. data/lib/rodauth/features/oidc_dynamic_client_registration.rb +25 -0
  31. data/lib/rodauth/features/oidc_frontchannel_logout.rb +134 -0
  32. data/lib/rodauth/features/oidc_logout_base.rb +76 -0
  33. data/lib/rodauth/features/oidc_rp_initiated_logout.rb +29 -6
  34. data/lib/rodauth/features/oidc_session_management.rb +91 -0
  35. data/lib/rodauth/oauth/database_extensions.rb +4 -0
  36. data/lib/rodauth/oauth/http_extensions.rb +1 -1
  37. data/lib/rodauth/oauth/ttl_store.rb +1 -1
  38. data/lib/rodauth/oauth/version.rb +1 -1
  39. data/locales/en.yml +19 -0
  40. data/locales/pt.yml +9 -0
  41. data/templates/authorize.str +1 -0
  42. data/templates/check_session.str +67 -0
  43. data/templates/frontchannel_logout.str +17 -0
  44. metadata +14 -2
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rodauth/oauth"
4
+
5
+ module Rodauth
6
+ Feature.define(:oidc_logout_base, :OidcLogoutBase) do
7
+ depends :oidc
8
+
9
+ session_key :visited_sites_key, :visited_sites
10
+
11
+ private
12
+
13
+ # set application/sid in visited sites when required
14
+ def create_oauth_grant(create_params = {})
15
+ sid_in_visited_sites
16
+
17
+ super
18
+ end
19
+
20
+ def active_sessions?(session_id)
21
+ !active_sessions_ds.where(active_sessions_session_id_column => session_id).empty?
22
+ end
23
+
24
+ def session_id_in_claims(oauth_grant, claims)
25
+ oauth_application_in_visited_sites do
26
+ if should_set_sid_in_visited_sites?(oauth_application)
27
+ # id_token or token response types
28
+ session_id = if (sess = session[session_id_session_key])
29
+ compute_hmac(sess)
30
+ else
31
+ # code response type
32
+ ds = db[active_sessions_table]
33
+ ds = ds.where(active_sessions_account_id_column => oauth_grant[oauth_grants_account_id_column])
34
+ ds = ds.order(Sequel.desc(active_sessions_last_use_column))
35
+ ds.get(active_sessions_session_id_column)
36
+ end
37
+
38
+ claims[:sid] = session_id
39
+ end
40
+ end
41
+ end
42
+
43
+ def oauth_application_in_visited_sites
44
+ visited_sites = session[visited_sites_key] || []
45
+
46
+ session_id = yield
47
+
48
+ visited_site = [oauth_application[oauth_applications_client_id_column], session_id]
49
+
50
+ return if visited_sites.include?(visited_site)
51
+
52
+ visited_sites << visited_site
53
+ set_session_value(visited_sites_key, visited_sites)
54
+ end
55
+
56
+ def sid_in_visited_sites
57
+ return unless should_set_oauth_application_in_visited_sites?
58
+
59
+ oauth_application_in_visited_sites do
60
+ if should_set_sid_in_visited_sites?(oauth_application)
61
+ ds = active_sessions_ds.order(Sequel.desc(active_sessions_last_use_column))
62
+
63
+ ds.get(active_sessions_session_id_column)
64
+ end
65
+ end
66
+ end
67
+
68
+ def should_set_oauth_application_in_visited_sites?
69
+ false
70
+ end
71
+
72
+ def should_set_sid_in_visited_sites?(*)
73
+ false
74
+ end
75
+ end
76
+ end
@@ -4,11 +4,15 @@ require "rodauth/oauth"
4
4
 
5
5
  module Rodauth
6
6
  Feature.define(:oidc_rp_initiated_logout, :OidcRpInitiatedLogout) do
7
- depends :oidc
7
+ depends :oidc_logout_base
8
+ response "oidc_logout"
8
9
 
9
10
  auth_value_method :oauth_applications_post_logout_redirect_uris_column, :post_logout_redirect_uris
11
+ translatable_method :oauth_invalid_id_token_hint_message, "Invalid ID token hint"
10
12
  translatable_method :oauth_invalid_post_logout_redirect_uri_message, "Invalid post logout redirect URI"
11
13
 
14
+ attr_reader :oidc_logout_redirect
15
+
12
16
  # /oidc-logout
13
17
  auth_server_route(:oidc_logout) do |r|
14
18
  require_authorizable_account
@@ -19,7 +23,7 @@ module Rodauth
19
23
  catch_error do
20
24
  validate_oidc_logout_params
21
25
 
22
- oauth_application = nil
26
+ claims = oauth_application = nil
23
27
 
24
28
  if (id_token_hint = param_or_nil("id_token_hint"))
25
29
  #
@@ -32,7 +36,12 @@ module Rodauth
32
36
  #
33
37
  claims = jwt_decode(id_token_hint, verify_claims: false)
34
38
 
35
- redirect_logout_with_error(oauth_invalid_client_message) unless claims
39
+ redirect_logout_with_error(oauth_invalid_id_token_hint_message) unless claims
40
+
41
+ # If the ID Token's sid claim does not correspond to the RP's current session or a
42
+ # recent session at the OP, the OP SHOULD treat the logout request as suspect, and
43
+ # MAY decline to act upon it.
44
+ redirect_logout_with_error(oauth_invalid_client_message) if claims["sid"] && !active_sessions?(claims["sid"])
36
45
 
37
46
  oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column => claims["aud"]).first
38
47
  oauth_grant = db[oauth_grants_table]
@@ -40,9 +49,15 @@ module Rodauth
40
49
  .where(oauth_grants_oauth_application_id_column => oauth_application[oauth_applications_id_column])
41
50
  .first
42
51
 
52
+ unique_account_id = if oauth_grant
53
+ oauth_grant[oauth_grants_account_id_column]
54
+ else
55
+ account_id
56
+ end
57
+
43
58
  # check whether ID token belongs to currently logged-in user
44
- redirect_logout_with_error(oauth_invalid_client_message) unless oauth_grant && claims["sub"] == jwt_subject(oauth_grant,
45
- oauth_application)
59
+ redirect_logout_with_error(oauth_invalid_client_message) unless claims["sub"] == jwt_subject(unique_account_id,
60
+ oauth_application)
46
61
 
47
62
  # When an id_token_hint parameter is present, the OP MUST validate that it was the issuer of the ID Token.
48
63
  redirect_logout_with_error(oauth_invalid_client_message) unless claims && claims["iss"] == oauth_jwt_issuer
@@ -59,6 +74,8 @@ module Rodauth
59
74
 
60
75
  if (post_logout_redirect_uri = param_or_nil("post_logout_redirect_uri"))
61
76
  error_message = catch(:default_logout_redirect) do
77
+ throw(:default_logout_redirect, oauth_invalid_id_token_hint_message) unless claims
78
+
62
79
  oauth_application = db[oauth_applications_table].where(oauth_applications_client_id_column => claims["client_id"]).first
63
80
 
64
81
  throw(:default_logout_redirect, oauth_invalid_client_message) unless oauth_application
@@ -78,7 +95,9 @@ module Rodauth
78
95
  post_logout_redirect_uri = post_logout_redirect_uri.to_s
79
96
  end
80
97
 
81
- redirect(post_logout_redirect_uri)
98
+ @oidc_logout_redirect = post_logout_redirect_uri
99
+
100
+ require_response(:_oidc_logout_response)
82
101
  end
83
102
 
84
103
  end
@@ -90,6 +109,10 @@ module Rodauth
90
109
  end
91
110
  end
92
111
 
112
+ def _oidc_logout_response
113
+ redirect(oidc_logout_redirect)
114
+ end
115
+
93
116
  private
94
117
 
95
118
  # Logout
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rodauth/oauth"
4
+
5
+ module Rodauth
6
+ Feature.define(:oidc_session_management, :OidcSessionManagement) do
7
+ depends :oidc
8
+
9
+ view "check_session", "Check Session", "check_session"
10
+
11
+ auth_value_method :oauth_oidc_user_agent_state_cookie_key, "_rodauth_oauth_user_agent_state"
12
+ auth_value_method :oauth_oidc_user_agent_state_cookie_options, {}.freeze
13
+ auth_value_method :oauth_oidc_user_agent_state_cookie_expires_in, 365 * 24 * 60 * 60 # 1 year
14
+
15
+ auth_value_method :oauth_oidc_user_agent_state_js, nil
16
+
17
+ auth_value_methods(
18
+ :oauth_oidc_session_management_salt
19
+ )
20
+ # /authorize
21
+ auth_server_route(:check_session) do |r|
22
+ allow_cors(r)
23
+
24
+ r.get do
25
+ set_title(:check_session_page_title)
26
+ scope.view(_view_opts("check_session").merge(layout: false))
27
+ end
28
+ end
29
+
30
+ def clear_session
31
+ super
32
+
33
+ # update user agent state in the process
34
+ # TODO: dangerous if this gets overidden by the user
35
+
36
+ user_agent_state_cookie_opts = Hash[oauth_oidc_user_agent_state_cookie_options]
37
+ user_agent_state_cookie_opts[:value] = oauth_unique_id_generator
38
+ user_agent_state_cookie_opts[:secure] = true
39
+ if oauth_oidc_user_agent_state_cookie_expires_in
40
+ user_agent_state_cookie_opts[:expires] = convert_timestamp(Time.now + oauth_oidc_user_agent_state_cookie_expires_in)
41
+ end
42
+ ::Rack::Utils.set_cookie_header!(response.headers, oauth_oidc_user_agent_state_cookie_key, user_agent_state_cookie_opts)
43
+ end
44
+
45
+ private
46
+
47
+ def do_authorize(*)
48
+ params, mode = super
49
+
50
+ params["session_state"] = generate_session_state
51
+
52
+ [params, mode]
53
+ end
54
+
55
+ def response_error_params(*)
56
+ payload = super
57
+
58
+ return payload unless request.path == authorize_path
59
+
60
+ payload["session_state"] = generate_session_state
61
+ payload
62
+ end
63
+
64
+ def generate_session_state
65
+ salt = oauth_oidc_session_management_salt
66
+
67
+ uri = URI(redirect_uri)
68
+ origin = if uri.respond_to?(:origin)
69
+ uri.origin
70
+ else
71
+ # TODO: remove when not supporting uri < 0.11
72
+ "#{uri.scheme}://#{uri.host}#{":#{uri.port}" if uri.port != uri.default_port}"
73
+ end
74
+ session_id = "#{oauth_application[oauth_applications_client_id_column]} " \
75
+ "#{origin} " \
76
+ "#{request.cookies[oauth_oidc_user_agent_state_cookie_key]} #{salt}"
77
+
78
+ "#{Digest::SHA256.hexdigest(session_id)}.#{salt}"
79
+ end
80
+
81
+ def oauth_server_metadata_body(*)
82
+ super.tap do |data|
83
+ data[:check_session_iframe] = check_session_url
84
+ end
85
+ end
86
+
87
+ def oauth_oidc_session_management_salt
88
+ oauth_unique_id_generator
89
+ end
90
+ end
91
+ end
@@ -14,6 +14,10 @@ module Rodauth
14
14
  else
15
15
  def __insert_and_return__(dataset, pkey, params)
16
16
  id = dataset.insert(params)
17
+ if params.key?(pkey)
18
+ # mysql returns 0 when the primary key is a varchar.
19
+ id = params[pkey]
20
+ end
17
21
  dataset.where(pkey => id).first
18
22
  end
19
23
  end
@@ -32,7 +32,7 @@ module Rodauth
32
32
  yield request if block_given?
33
33
 
34
34
  response = http.request(request)
35
- authorization_required unless response.code.to_i == 200
35
+ authorization_required unless (200..299).include?(response.code.to_i)
36
36
  response
37
37
  end
38
38
 
@@ -35,7 +35,7 @@ class Rodauth::OAuth::TtlStore
35
35
  # at the same time, this ensures the first one wins.
36
36
  return @store[key][:payload] if @store[key] && @store[key][:ttl] < now
37
37
 
38
- @store[key] = { payload: payload, ttl: (ttl || (now + DEFAULT_TTL)) }
38
+ @store[key] = { payload: payload, ttl: ttl || (now + DEFAULT_TTL) }
39
39
  end
40
40
  @store[key][:payload]
41
41
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Rodauth
4
4
  module OAuth
5
- VERSION = "1.3.2"
5
+ VERSION = "1.5.0"
6
6
  end
7
7
  end
data/locales/en.yml CHANGED
@@ -8,7 +8,9 @@ en:
8
8
  device_verification_notice_flash: "The device is verified"
9
9
  user_code_not_found_error_flash: "No device to authorize with the given user code"
10
10
  authorize_page_title: "Authorize"
11
+ frontchannel_logout_page_title: "Logout"
11
12
  authorize_page_lead: "The application %{name} would like to access your data."
13
+ oauth_frontchannel_logout_redirecting_lead: "You are being redirected..."
12
14
  authorize_error_page_title: "Authorize Error"
13
15
  oauth_cancel_button: "Cancel"
14
16
  oauth_applications_page_title: "Oauth Applications"
@@ -18,6 +20,7 @@ en:
18
20
  oauth_grants_page_title: "My Oauth Grants"
19
21
  device_verification_page_title: "Device Verification"
20
22
  device_search_page_title: "Device Search"
23
+ check_session_page_title: "Check Session"
21
24
  oauth_management_pagination_previous_button: "Previous"
22
25
  oauth_management_pagination_next_button: "Next"
23
26
  oauth_grants_type_label: "Grant Type"
@@ -41,6 +44,8 @@ en:
41
44
  oauth_grant_user_code_label: "User code"
42
45
  oauth_grant_user_jws_jwk_label: "JSON Web Keys"
43
46
  oauth_grant_user_jwt_public_key_label: "Public key"
47
+ oauth_frontchannel_logout_redirecting_label: "please click %{link} if your browser does not redirect you in a few seconds."
48
+ oauth_frontchannel_logout_redirecting_link_label: "here"
44
49
  oauth_application_button: "Register"
45
50
  oauth_authorize_button: "Authorize"
46
51
  oauth_grant_revoke_button: "Revoke"
@@ -68,3 +73,17 @@ en:
68
73
  oauth_invalid_scope_message: "The Access Token expired"
69
74
  oauth_authorize_parameter_required: "Invalid or missing '%{parameter}'"
70
75
  oauth_invalid_post_logout_redirect_uri_message: "Invalid post logout redirect URI"
76
+ oauth_saml_assertion_not_base64_message: "SAML assertion must be in base64 format"
77
+ oauth_saml_assertion_single_issuer_message: "SAML assertion must have a single issuer"
78
+ oauth_saml_settings_not_found_message: "No SAML settings found for issuer"
79
+ oauth_invalid_id_token_hint_message: "Invalid ID token hint"
80
+ translatable_method :oauth_saml_settings_not_found_message: "No SAML settings found for issuer"
81
+ oauth_invalid_dpop_proof_message: "Invalid DPoP proof"
82
+ oauth_multiple_dpop_proofs_message: "Multiple DPoP proofs used"
83
+ oauth_invalid_dpop_jkt_message: "Invalid DPoP JKT"
84
+ oauth_invalid_dpop_jti_message: "Invalid DPoP jti"
85
+ oauth_invalid_dpop_htm_message: "Invalid DPoP htm"
86
+ oauth_invalid_dpop_htu_message: "Invalid DPoP htu"
87
+ oauth_use_dpop_nonce_message: "DPoP nonce is required"
88
+ oauth_access_token_dpop_bound_message: "DPoP bound access token requires DPoP proof"
89
+ oauth_multiple_auth_methods_message: "Multiple methods used to include access token"
data/locales/pt.yml CHANGED
@@ -8,7 +8,9 @@ pt:
8
8
  device_verification_notice_flash: "O dispositivo foi verificado com sucesso"
9
9
  user_code_not_found_error_flash: "Não existe nenhum dispositivo a ser autorizado com o código de usuário inserido"
10
10
  authorize_page_title: "Autorizar"
11
+ frontchannel_logout_page_title: "Saindo"
11
12
  authorize_page_lead: "O aplicativo %{name} gostaria de aceder aos seus dados."
13
+ oauth_frontchannel_logout_redirecting_lead: "Redireccionando.."
12
14
  authorize_error_page_title: Erro de autorização
13
15
  oauth_cancel_button: "Cancelar"
14
16
  oauth_applications_page_title: "Aplicativos OAuth"
@@ -18,6 +20,7 @@ pt:
18
20
  oauth_grants_page_title: "As minhas concessões Oauth"
19
21
  device_verification_page_title: "Verificação de dispositivo"
20
22
  device_search_page_title: "Pesquisa de dispositivo"
23
+ check_session_page_title: "Verificação de Sessão"
21
24
  oauth_management_pagination_previous_button: "Anterior"
22
25
  oauth_management_pagination_next_button: "Próxima"
23
26
  oauth_grants_type_label: "Tipo de concessão"
@@ -41,6 +44,8 @@ pt:
41
44
  oauth_grant_user_code_label: "Código do usuário"
42
45
  oauth_grant_user_jws_jwk_label: "Chaves JSON Web"
43
46
  oauth_grant_user_jwt_public_key_label: "Chave pública"
47
+ oauth_frontchannel_logout_redirecting_label: "por favor clique %{link} se o seu navegador não lhe redireccionar em alguns segundos."
48
+ oauth_frontchannel_logout_redirecting_link_label: "aqui"
44
49
  oauth_application_button: "Registar"
45
50
  oauth_authorize_button: "Autorizar"
46
51
  oauth_grant_revoke_button: "Revogar"
@@ -68,3 +73,7 @@ pt:
68
73
  oauth_invalid_scope_message: "O Token de acesso expirou"
69
74
  oauth_authorize_parameter_required: "'%{parameter}' inválido ou em falta"
70
75
  oauth_invalid_post_logout_redirect_uri_message: "URI de redireccionamento pós-logout inválido"
76
+ oauth_saml_assertion_not_base64_message: "A asserção SAML tem que estar no formato base64"
77
+ oauth_saml_assertion_single_issuer_message: "A asserção SAML tem que ter um único emissor"
78
+ oauth_saml_settings_not_found_message: "Nenhuma configuração SAML encontrada para o emissor"
79
+ oauth_invalid_id_token_hint_message: "ID token hint inválido"
@@ -96,6 +96,7 @@
96
96
  end.join
97
97
  end
98
98
  }
99
+ #{"<input type=\"hidden\" name=\"dpop_jkt\" value=\"#{h(rodauth.param("dpop_jkt"))}\"/>" if rodauth.features.include?(:oauth_dpop) && rodauth.param_or_nil("dpop_jkt")}
99
100
  </div>
100
101
  <p class="text-center">
101
102
  <input type="submit" class="btn btn-outline-primary" value="#{h(rodauth.oauth_authorize_button)}"/>
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>#{@page_title}</title>
7
+ </head>
8
+ <body>
9
+ <script type="text/javascript">
10
+ window.addEventListener("message", receiveMessage, false);
11
+
12
+ function receiveMessage(e) { // e.data has client_id and session_state
13
+ var client_id = e.data.substr(0, e.data.lastIndexOf(' '));
14
+ var session_state = e.data.substr(e.data.lastIndexOf(' ') + 1);
15
+ var salt = session_state.split('.')[1];
16
+
17
+ if (!client_id || !session_state || !salt) {
18
+ postMessage('error', e.origin);
19
+ return;
20
+ }
21
+
22
+ #{rodauth.oauth_oidc_user_agent_state_js}
23
+
24
+ // get_op_user_agent_state() is an OP defined function
25
+ // that returns the User Agent's login status at the OP.
26
+ // How it is done is entirely up to the OP.
27
+ var opuas = getOpUserAgentState();
28
+
29
+ // Here, the session_state is calculated in this particular way,
30
+ // but it is entirely up to the OP how to do it under the
31
+ // requirements defined in this specification.
32
+ var msgBuffer = new TextEncoder('utf-8').encode(client_id + ' ' + e.origin + ' ' + opuas + ' ' + salt);
33
+ crypto.subtle.digest('SHA-256', msgBuffer).then(function(hash) {
34
+ var hashArray = Array.from(new Uint8Array(hash)); // convert buffer to byte array
35
+ var hashHex = hashArray
36
+ .map(function(b) { return b.toString(16).padStart(2, "0"); })
37
+ .join("");
38
+ var ss = hashHex + "." + salt;
39
+
40
+ var stat = '';
41
+ if (session_state === ss) {
42
+ stat = 'unchanged';
43
+ } else {
44
+ stat = 'changed';
45
+ }
46
+
47
+ e.source.postMessage(stat, e.origin);
48
+ });
49
+ };
50
+
51
+ function getOpUserAgentState() {
52
+ var name = "#{rodauth.oauth_oidc_user_agent_state_cookie_key}=";
53
+ var ca = document.cookie.split(';');
54
+ var value = null;
55
+ for (var i = 0; i < ca.length; i++) {
56
+ var c = ca[i].trim();
57
+ if ((c.indexOf(name)) == 0) {
58
+ value = c.substr(name.length);
59
+ break;
60
+ }
61
+ }
62
+
63
+ return value;
64
+ }
65
+ </script>
66
+ </body>
67
+ </html>
@@ -0,0 +1,17 @@
1
+ <div class="mb-3">
2
+ <h1>#{rodauth.oauth_frontchannel_logout_redirecting_lead}</h1>
3
+ <p>
4
+ #{
5
+ rodauth.oauth_frontchannel_logout_redirecting_label(
6
+ link: "<a href=\"#{rodauth.frontchannel_logout_redirect}\">" \
7
+ "#{rodauth.oauth_frontchannel_logout_redirecting_link_label}</a>"
8
+ )
9
+ }
10
+ </p>
11
+ #{
12
+ rodauth.frontchannel_logout_urls.map do |logout_url|
13
+ "<iframe src=\"#{logout_url}\"></iframe>"
14
+ end.join
15
+ }
16
+ </div>
17
+ <meta http-equiv="refresh" content="#{rodauth.frontchannel_logout_redirect_timeout}; URL=#{rodauth.frontchannel_logout_redirect}" />
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: 1.3.2
4
+ version: 1.5.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: 2023-07-26 00:00:00.000000000 Z
11
+ date: 2024-02-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rodauth
@@ -72,6 +72,8 @@ extra_rdoc_files:
72
72
  - doc/release_notes/1_3_0.md
73
73
  - doc/release_notes/1_3_1.md
74
74
  - doc/release_notes/1_3_2.md
75
+ - doc/release_notes/1_4_0.md
76
+ - doc/release_notes/1_5_0.md
75
77
  files:
76
78
  - CHANGELOG.md
77
79
  - LICENSE.txt
@@ -115,6 +117,8 @@ files:
115
117
  - doc/release_notes/1_3_0.md
116
118
  - doc/release_notes/1_3_1.md
117
119
  - doc/release_notes/1_3_2.md
120
+ - doc/release_notes/1_4_0.md
121
+ - doc/release_notes/1_5_0.md
118
122
  - lib/generators/rodauth/oauth/install_generator.rb
119
123
  - lib/generators/rodauth/oauth/templates/app/models/oauth_application.rb
120
124
  - lib/generators/rodauth/oauth/templates/app/models/oauth_grant.rb
@@ -122,6 +126,7 @@ files:
122
126
  - lib/generators/rodauth/oauth/templates/app/views/rodauth/authorize_error.erb
123
127
  - lib/generators/rodauth/oauth/templates/app/views/rodauth/device_search.html.erb
124
128
  - lib/generators/rodauth/oauth/templates/app/views/rodauth/device_verification.html.erb
129
+ - lib/generators/rodauth/oauth/templates/app/views/rodauth/frontchannel_logout.html.erb
125
130
  - lib/generators/rodauth/oauth/templates/app/views/rodauth/new_oauth_application.html.erb
126
131
  - lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application.html.erb
127
132
  - lib/generators/rodauth/oauth/templates/app/views/rodauth/oauth_application_oauth_grants.html.erb
@@ -136,6 +141,7 @@ files:
136
141
  - lib/rodauth/features/oauth_base.rb
137
142
  - lib/rodauth/features/oauth_client_credentials_grant.rb
138
143
  - lib/rodauth/features/oauth_device_code_grant.rb
144
+ - lib/rodauth/features/oauth_dpop.rb
139
145
  - lib/rodauth/features/oauth_dynamic_client_registration.rb
140
146
  - lib/rodauth/features/oauth_grant_management.rb
141
147
  - lib/rodauth/features/oauth_implicit_grant.rb
@@ -155,9 +161,13 @@ files:
155
161
  - lib/rodauth/features/oauth_token_introspection.rb
156
162
  - lib/rodauth/features/oauth_token_revocation.rb
157
163
  - lib/rodauth/features/oidc.rb
164
+ - lib/rodauth/features/oidc_backchannel_logout.rb
158
165
  - lib/rodauth/features/oidc_dynamic_client_registration.rb
166
+ - lib/rodauth/features/oidc_frontchannel_logout.rb
167
+ - lib/rodauth/features/oidc_logout_base.rb
159
168
  - lib/rodauth/features/oidc_rp_initiated_logout.rb
160
169
  - lib/rodauth/features/oidc_self_issued.rb
170
+ - lib/rodauth/features/oidc_session_management.rb
161
171
  - lib/rodauth/oauth.rb
162
172
  - lib/rodauth/oauth/database_extensions.rb
163
173
  - lib/rodauth/oauth/http_extensions.rb
@@ -169,10 +179,12 @@ files:
169
179
  - locales/pt.yml
170
180
  - templates/authorize.str
171
181
  - templates/authorize_error.str
182
+ - templates/check_session.str
172
183
  - templates/client_secret_field.str
173
184
  - templates/description_field.str
174
185
  - templates/device_search.str
175
186
  - templates/device_verification.str
187
+ - templates/frontchannel_logout.str
176
188
  - templates/homepage_url_field.str
177
189
  - templates/jwks_field.str
178
190
  - templates/name_field.str