From 9a35b8779d624291a9577d48233726757c042b82 Mon Sep 17 00:00:00 2001 From: Pavel Balashou Date: Fri, 11 Oct 2024 19:21:51 +0200 Subject: [PATCH] Save OIDC tokens to OpenProject database. --- .../authentication/omniauth_service.rb | 8 ++--- ...5_add_tokens_to_oidc_user_session_links.rb | 34 +++++++++++++++++++ .../openid_connect/user_session_link.rb | 28 +++++++++++++++ .../lib/open_project/openid_connect/engine.rb | 6 +++- .../open_project/openid_connect/hooks/hook.rb | 16 ++++++++- .../openid_connect/session_mapper.rb | 4 ++- 6 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 db/migrate/20240806174815_add_tokens_to_oidc_user_session_links.rb diff --git a/app/services/authentication/omniauth_service.rb b/app/services/authentication/omniauth_service.rb index 47f18d600691..1f4d41c7f9ba 100644 --- a/app/services/authentication/omniauth_service.rb +++ b/app/services/authentication/omniauth_service.rb @@ -62,7 +62,7 @@ def call(additional_user_params = nil) # Create or update the user from omniauth # and assign non-nil parameters from the registration form - if any - assignable_params = (additional_user_params || {}).reject { |_, v| v.nil? } + assignable_params = (additional_user_params || {}).compact update_user_from_omniauth!(assignable_params) # If we have a new or invited user, we still need to register them @@ -99,7 +99,7 @@ def inspect_response(log_level) # After login flow def tap_service_result(call) if call.success? && user.active? - OpenProject::Hook.call_hook :omniauth_user_authorized, { auth_hash:, controller: } + OpenProject::Hook.call_hook :omniauth_user_authorized, { auth_hash:, controller:, user: } # Call deprecated login hook OpenProject::OmniAuth::Authorization.after_login! user, auth_hash, self end @@ -165,7 +165,7 @@ def find_existing_user def remap_existing_user return unless Setting.oauth_allow_remapping_of_existing_users? - User.not_builtin.find_by_login(user_attributes[:login]) # rubocop:disable Rails/DynamicFindBy + User.not_builtin.find_by_login(user_attributes[:login]) end ## @@ -285,7 +285,7 @@ def identity_url_from_omniauth # Try to provide some context of the auth_hash in case of errors def auth_uid hash = auth_hash || {} - hash.dig(:info, :uid) || hash.dig(:uid) || "unknown" + hash.dig(:info, :uid) || hash[:uid] || "unknown" end end end diff --git a/db/migrate/20240806174815_add_tokens_to_oidc_user_session_links.rb b/db/migrate/20240806174815_add_tokens_to_oidc_user_session_links.rb new file mode 100644 index 000000000000..7976c0ab7dd8 --- /dev/null +++ b/db/migrate/20240806174815_add_tokens_to_oidc_user_session_links.rb @@ -0,0 +1,34 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +class AddTokensToOidcUserSessionLinks < ActiveRecord::Migration[7.1] + def change + add_column :oidc_user_session_links, :access_token, :string + add_column :oidc_user_session_links, :refresh_token, :string + end +end diff --git a/modules/openid_connect/app/models/openid_connect/user_session_link.rb b/modules/openid_connect/app/models/openid_connect/user_session_link.rb index baa3caab442a..3315ca939638 100644 --- a/modules/openid_connect/app/models/openid_connect/user_session_link.rb +++ b/modules/openid_connect/app/models/openid_connect/user_session_link.rb @@ -1,3 +1,31 @@ +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2012-2024 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + module OpenIDConnect class UserSessionLink < ::ApplicationRecord self.table_name = "oidc_user_session_links" diff --git a/modules/openid_connect/lib/open_project/openid_connect/engine.rb b/modules/openid_connect/lib/open_project/openid_connect/engine.rb index 345651d2967e..0d7dc360101b 100644 --- a/modules/openid_connect/lib/open_project/openid_connect/engine.rb +++ b/modules/openid_connect/lib/open_project/openid_connect/engine.rb @@ -49,7 +49,11 @@ class Engine < ::Rails::Engine end # Remember oidc session values when logging in user - h[:retain_from_session] = %w[omniauth.oidc_sid] + h[:retain_from_session] = %w[ + omniauth.oidc_sid + omniauth.oidc_access_token + omniauth.oidc_refresh_token + ] h[:backchannel_logout_callback] = ->(logout_token) do ::OpenProject::OpenIDConnect::SessionMapper.handle_logout(logout_token) diff --git a/modules/openid_connect/lib/open_project/openid_connect/hooks/hook.rb b/modules/openid_connect/lib/open_project/openid_connect/hooks/hook.rb index 20894a869de6..c638a078a79d 100644 --- a/modules/openid_connect/lib/open_project/openid_connect/hooks/hook.rb +++ b/modules/openid_connect/lib/open_project/openid_connect/hooks/hook.rb @@ -35,6 +35,7 @@ class Hook < OpenProject::Hook::Listener def user_logged_in(context) session = context[:session] oidc_sid = session["omniauth.oidc_sid"] + return if oidc_sid.nil? ::OpenProject::OpenIDConnect::SessionMapper.handle_login(oidc_sid, session) @@ -43,7 +44,20 @@ def user_logged_in(context) ## # Called once omniauth has returned with an auth hash # NOTE: It's a passthrough as we no longer persist the access token into the cookie - def omniauth_user_authorized(_context); end + def omniauth_user_authorized(context) + auth_hash = context[:auth_hash] + controller = context[:controller] + _user = context[:user] + credentials = auth_hash[:credentials] + access_token = credentials[:token] + refresh_token = credentials[:refresh_token] + session = controller.session + session["omniauth.oidc_access_token"] = access_token + session["omniauth.oidc_refresh_token"] = refresh_token + nil + rescue StandardError => e + Rails.logger.info "Something went wrong in omniauth_user_authorized hook: #{e}" + end end end end diff --git a/modules/openid_connect/lib/open_project/openid_connect/session_mapper.rb b/modules/openid_connect/lib/open_project/openid_connect/session_mapper.rb index 13107bcffd60..bd34b80af7ec 100644 --- a/modules/openid_connect/lib/open_project/openid_connect/session_mapper.rb +++ b/modules/openid_connect/lib/open_project/openid_connect/session_mapper.rb @@ -14,7 +14,9 @@ def self.handle_login(oidc_session, session) return end - link = ::OpenIDConnect::UserSessionLink.new(oidc_session:) + link = ::OpenIDConnect::UserSessionLink.new(oidc_session:, + access_token: session["omniauth.oidc_access_token"], + refresh_token: session["omniauth.oidc_refresh_token"]) new(link).link_to_internal!(session) rescue StandardError => e Rails.logger.error { "Failed to map OIDC session to internal: #{e.message}" }