diff --git a/app/controllers/users/omniauth_callbacks_controller.rb b/app/controllers/users/omniauth_callbacks_controller.rb
index 4a6057d69..62016a421 100644
--- a/app/controllers/users/omniauth_callbacks_controller.rb
+++ b/app/controllers/users/omniauth_callbacks_controller.rb
@@ -18,10 +18,11 @@ module Users
def customazureactivedirectory
auth = request.env['omniauth.auth']
provider_id = auth.dig(:extra, :raw_info, :aud)
- provider_conf = Rails.configuration.x.azure_ad_apps[provider_id]
+ settings = ApplicationSettings.instance
+ provider_conf = settings.values['azure_ad_apps'].find { |v| v['enable_sign_in'] && v['app_id'] == provider_id }
raise StandardError, 'No matching Azure AD provider config found' if provider_conf.blank?
- auth.provider = provider_conf[:provider]
+ auth.provider = provider_conf['provider_name']
return redirect_to connected_accounts_path if current_user
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 4bada48a7..a9f5f1fdb 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -219,6 +219,14 @@ module ApplicationHelper
'icon_small/missing.png'
end
+ def okta_configured?
+ ApplicationSettings.instance.values['okta'].present?
+ end
+
+ def azure_ad_configured?
+ ApplicationSettings.instance.values['azure_ad_apps'].present?
+ end
+
def wopi_enabled?
ENV['WOPI_ENABLED'] == 'true'
end
diff --git a/app/views/users/shared/_azure_sign_in_links.html.erb b/app/views/users/shared/_azure_sign_in_links.html.erb
index fb5c5fab8..284530242 100644
--- a/app/views/users/shared/_azure_sign_in_links.html.erb
+++ b/app/views/users/shared/_azure_sign_in_links.html.erb
@@ -1,7 +1,7 @@
-<% Rails.configuration.x.azure_ad_apps.select { |uid, config| config[:enable_sign_in] }.each do |uid, config| %>
+<% ApplicationSettings.instance.values['azure_ad_apps'].select { |v| v['enable_sign_in'] }.each do |config| %>
- <%= form_tag user_customazureactivedirectory_omniauth_authorize_path(provider: config[:provider]), method: :post do %>
- <%= submit_tag config[:sign_in_label], class: 'btn btn-azure-ad' %>
+ <%= form_tag user_customazureactivedirectory_omniauth_authorize_path(provider: config['provider']), method: :post do %>
+ <%= submit_tag config['sign_in_label'] || t('devise.sessions.new.azure_ad_submit'), class: 'btn btn-azure-ad' %>
<% end %>
<% end %>
diff --git a/app/views/users/shared/_links.html.erb b/app/views/users/shared/_links.html.erb
index f8782f162..db7ce9d64 100644
--- a/app/views/users/shared/_links.html.erb
+++ b/app/views/users/shared/_links.html.erb
@@ -27,7 +27,7 @@
<% end -%>
- <%- if devise_mapping.omniauthable? && Devise.omniauth_configs[:okta].present? %>
+ <%- if devise_mapping.omniauthable? && okta_configured? %>
<%= form_tag omniauth_authorize_path(resource_name, :okta), method: :post do %>
<%= submit_tag t('devise.okta.sign_in_label'), class: 'btn btn-okta' %>
@@ -41,7 +41,9 @@
<% end -%>
<% end -%>
-
- <%= render partial: "users/shared/azure_sign_in_links", locals: { resource_name: resource_name } %>
-
+ <% if devise_mapping.omniauthable? && azure_ad_configured? %>
+
+ <%= render partial: "users/shared/azure_sign_in_links", locals: { resource_name: resource_name } %>
+
+ <% end %>
diff --git a/config/initializers/azure_ad.rb b/config/initializers/azure_ad.rb
index caecfadba..f785a79e0 100644
--- a/config/initializers/azure_ad.rb
+++ b/config/initializers/azure_ad.rb
@@ -1,88 +1,58 @@
# frozen_string_literal: true
-Rails.application.configure do
- vars = ENV.select { |name, _| name =~ /^[[:alnum:]]*_AZURE_AD_APP_ID/ }
- config.x.azure_ad_apps = HashWithIndifferentAccess.new if vars.present?
+begin
+ azure_app_ids = ENV.select { |name, _| name =~ /^[[:alnum:]]*_AZURE_AD_APP_ID/ }
+ settings = ApplicationSettings.instance
- vars.each do |name, value|
+ azure_app_ids.each do |name, value|
app_name = name.sub('_AZURE_AD_APP_ID', '')
- config.x.azure_ad_apps[value] = {}
+ app_config = {}
+ app_config['app_id'] = value
- tenant_id = ENV["#{app_name}_AZURE_AD_TENANT_ID"]
+ tenant_id = ENV.fetch("#{app_name}_AZURE_AD_TENANT_ID")
raise StandardError, "No Tenant ID for #{app_name} Azure app" unless tenant_id
- config.x.azure_ad_apps[value][:tenant_id] = tenant_id
+ app_config['tenant_id'] = tenant_id
- client_secret = ENV["#{app_name}_AZURE_AD_CLIENT_SECRET"]
+ client_secret = ENV.fetch("#{app_name}_AZURE_AD_CLIENT_SECRET")
raise StandardError, "No Client Secret for #{app_name} Azure app" unless client_secret
- config.x.azure_ad_apps[value][:client_secret] = client_secret
+ app_config['client_secret'] = client_secret
- iss = ENV["#{app_name}_AZURE_AD_ISS"]
+ iss = ENV.fetch("#{app_name}_AZURE_AD_ISS")
raise StandardError, "No ISS for #{app_name} Azure app" unless iss
- config.x.azure_ad_apps[value][:iss] = iss
+ app_config['iss'] = iss
- conf_url = ENV["#{app_name}_AZURE_AD_CONF_URL"]
+ conf_url = ENV.fetch("#{app_name}_AZURE_AD_CONF_URL")
raise StandardError, "No CONF_URL for #{app_name} Azure app" unless conf_url
- config.x.azure_ad_apps[value][:conf_url] = conf_url
+ app_config['conf_url'] = conf_url
- provider = ENV["#{app_name}_AZURE_AD_PROVIDER_NAME"]
+ provider = ENV.fetch("#{app_name}_AZURE_AD_PROVIDER_NAME")
raise StandardError, "No PROVIDER_NAME for #{app_name} Azure app" unless provider
- config.x.azure_ad_apps[value][:provider] = provider
+ app_config['provider_name'] = provider
- config.x.azure_ad_apps[value][:enable_sign_in] = ENV["#{app_name}_AZURE_AD_ENABLE_SIGN_IN"] == 'true'
+ app_config['enable_sign_in'] = ENV["#{app_name}_AZURE_AD_ENABLE_SIGN_IN"] == 'true'
- next unless config.x.azure_ad_apps[value][:enable_sign_in]
+ next unless app_config['enable_sign_in']
- config.x.azure_ad_apps[value][:sign_in_label] = ENV["#{app_name}_AZURE_AD_SIGN_IN_LABEL"] || 'Sign in with Azure AD'
- config.x.azure_ad_apps[value][:auto_link_on_sign_in] = ENV["#{app_name}_AZURE_AD_AUTO_LINK_ON_SIGN_IN"] == 'true'
+ app_config['sign_in_label'] = ENV.fetch("#{app_name}_AZURE_AD_SIGN_IN_LABEL")
+ app_config['auto_link_on_sign_in'] = ENV["#{app_name}_AZURE_AD_AUTO_LINK_ON_SIGN_IN"] == 'true'
if ENV["#{app_name}_AZURE_AD_SIGN_IN_POLICY"]
- config.x.azure_ad_apps[value][:sign_in_policy] = ENV["#{app_name}_AZURE_AD_SIGN_IN_POLICY"]
+ app_config['sign_in_policy'] = ENV["#{app_name}_AZURE_AD_SIGN_IN_POLICY"]
+ end
+
+ existing_index = settings.values['azure_ad_apps'].find_index { |v| v['app_id'] == value }
+ if existing_index
+ settings.values['azure_ad_apps'][existing_index] = app_config
+ else
+ settings.values['azure_ad_apps'] << app_config
end
end
-
- # Checking additional configurations in ApplicationSettings JSON. Key and values should be strings there.
- begin
- if ApplicationSettings.instance.values['azure_ad_apps']&.is_a?(Array)
- config.x.azure_ad_apps ||= HashWithIndifferentAccess.new
- settings = ApplicationSettings.instance
-
- settings.values['azure_ad_apps'].each do |azure_ad_app|
- app_config = {}
- app_id = azure_ad_app['app_id']
- Rails.logger.error('No app_id present for the entry in Azure app settings') && next unless app_id
-
- app_config[:tenant_id] = azure_ad_app['tenant_id']
- Rails.logger.error("No tenant id for #{app_id} Azure app") && next unless app_config[:tenant_id]
-
- app_config[:client_secret] = azure_ad_app['client_secret']
- Rails.logger.error("No client secret for #{app_id} Azure app") && next unless app_config[:client_secret]
-
- app_config[:iss] = azure_ad_app['iss']
- Rails.logger.error("No iss for #{app_id} Azure app") && next unless app_config[:iss]
-
- app_config[:conf_url] = azure_ad_app['conf_url']
- Rails.logger.error("No conf_url for #{app_id} Azure app") && next unless app_config[:conf_url]
-
- app_config[:provider] = azure_ad_app['provider_name']
- Rails.logger.error("No provider_name for #{app_id} Azure app") && next unless app_config[:provider]
-
- app_config[:enable_sign_in] = azure_ad_app['enable_sign_in'] == 'true'
-
- if app_config[:enable_sign_in]
- app_config[:sign_in_label] = azure_ad_app['sign_in_label'] || 'Sign in with Azure AD'
- app_config[:auto_link_on_sign_in] = azure_ad_app['auto_link_on_sign_in'] == 'true'
- app_config[:sign_in_policy] = azure_ad_app['sign_in_policy'] if azure_ad_app['sign_in_policy']
- end
-
- config.x.azure_ad_apps[app_id] = app_config
- end
- end
- rescue ActiveRecord::ActiveRecordError, PG::ConnectionBad
- Rails.logger.info('Not connected to database, skipping additional Azure AD configuration')
- end
+ settings.save! if azure_app_ids.present?
+rescue ActiveRecord::ActiveRecordError, PG::ConnectionBad
+ Rails.logger.info('Not connected to database, skipping additional Azure AD configuration')
end
diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb
index ef4f8a7c5..f1ecbfbe1 100644
--- a/config/initializers/devise.rb
+++ b/config/initializers/devise.rb
@@ -301,36 +301,6 @@ Devise.setup do |config|
Rails.application.config.x.disable_local_passwords = ENV['DISABLE_LOCAL_PASSWORDS'] == 'true'
- if [ENV['OKTA_CLIENT_ID'], ENV['OKTA_CLIENT_SECRET'], ENV['OKTA_DOMAIN']].all?(&:present?)
- OKTA_OAUTH2_BASE_URL =
- if ENV['OKTA_AUTH_SERVER_ID'].blank?
- "https://#{ENV['OKTA_DOMAIN']}/oauth2"
- else
- "https://#{ENV['OKTA_DOMAIN']}/oauth2/#{ENV['OKTA_AUTH_SERVER_ID']}"
- end
- client_options = {
- site: "https://#{ENV['OKTA_DOMAIN']}",
- authorize_url: "#{OKTA_OAUTH2_BASE_URL}/v1/authorize",
- token_url: "#{OKTA_OAUTH2_BASE_URL}/v1/token",
- user_info_url: "#{OKTA_OAUTH2_BASE_URL}/v1/userinfo"
- }
- client_options[:audience] = ENV['OKTA_CLIENT_ID'] if ENV['OKTA_AUDIENCE'].present?
- if ENV['OKTA_AUTH_SERVER_ID'].present?
- client_options[:authorization_server] = ENV['OKTA_AUTH_SERVER_ID']
- else
- client_options[:use_org_auth_server] = true
- end
- config.omniauth(
- :okta,
- ENV['OKTA_CLIENT_ID'],
- ENV['OKTA_CLIENT_SECRET'],
- scope: 'openid profile email',
- fields: %w(profile email),
- client_options: client_options,
- strategy_class: OmniAuth::Strategies::Okta
- )
- end
-
# ==> Warden configuration
# If you want to use other strategies, that are not supported by Devise, or
# change the failure app, you can configure them inside the config.warden block.
diff --git a/config/initializers/okta.rb b/config/initializers/okta.rb
new file mode 100644
index 000000000..735633957
--- /dev/null
+++ b/config/initializers/okta.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+begin
+ settings = ApplicationSettings.instance
+ provider_conf = settings.values['okta']
+ if provider_conf.blank? && %w(OKTA_CLIENT_ID OKTA_CLIENT_SECRET OKTA_DOMAIN).all? { |v| ENV.fetch(v, nil).present? }
+ provider_conf = {}
+ provider_conf['client_id'] = ENV.fetch('OKTA_CLIENT_ID')
+ provider_conf['client_secret'] = ENV.fetch('OKTA_CLIENT_SECRET')
+ provider_conf['domain'] = ENV.fetch('OKTA_DOMAIN')
+ provider_conf['auth_server_id'] = ENV['OKTA_AUTH_SERVER_ID'] if ENV['OKTA_AUTH_SERVER_ID'].present?
+ provider_conf['audience'] = ENV['OKTA_AUDIENCE'] if ENV['OKTA_AUDIENCE'].present?
+ settings.values['okta'] = provider_conf
+ settings.save!
+ end
+rescue ActiveRecord::ActiveRecordError, PG::ConnectionBad
+ Rails.logger.info('Not connected to database, skipping additional Okta configuration')
+end
diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb
index bd240a6cc..c1f7c45ff 100644
--- a/config/initializers/omniauth.rb
+++ b/config/initializers/omniauth.rb
@@ -3,7 +3,8 @@
require 'omniauth/strategies/custom_azure_active_directory'
AZURE_SETUP_PROC = lambda do |env|
- providers = Rails.configuration.x.azure_ad_apps.select { |_, v| v[:enable_sign_in] == true }
+ settings = ApplicationSettings.instance
+ providers = settings.values['azure_ad_apps'].select { |v| v['enable_sign_in'] }
raise StandardError, 'No Azure AD config available for sign in' if providers.blank?
req = Rack::Request.new(env)
@@ -11,28 +12,60 @@ AZURE_SETUP_PROC = lambda do |env|
if providers.size > 1
if req.params['id_token'].present? # Callback phase
unverified_jwt_payload, = JWT.decode(req.params['id_token'], nil, false)
- raise StandardError, 'No Azure AD config available for sign in' if providers[unverified_jwt_payload['aud']].blank?
-
- provider_id = unverified_jwt_payload['aud']
+ provider_conf = providers.select { |v| v['app_id'] == unverified_jwt_payload['aud'] }
else # Authorization phase
raise ActionController::ParameterMissing, 'Provider name is missing' if req.params['provider'].blank?
- provider_id = providers.select { |_, v| v[:provider] == req.params['provider'] }.keys.first
- raise StandardError, 'No Azure AD config available for sign in' if provider_id.blank?
+ provider_conf = providers.find { |v| v['provider_name'] == req.params['provider'] }
end
+ raise StandardError, 'No Azure AD config available for sign in' if provider_conf.blank?
end
- provider_id ||= providers.keys.first
- provider_conf = providers[provider_id]
- env['omniauth.strategy'].options[:client_id] = provider_id.to_s
- env['omniauth.strategy'].options[:client_secret] = provider_conf[:client_secret]
- env['omniauth.strategy'].options[:tenant_id] = provider_conf[:tenant_id]
- env['omniauth.strategy'].options[:sign_in_policy] = provider_conf[:sign_in_policy]
+ provider_conf ||= providers.first
+ env['omniauth.strategy'].options[:client_id] = provider_conf['app_id']
+ env['omniauth.strategy'].options[:client_secret] = provider_conf['client_secret']
+ env['omniauth.strategy'].options[:tenant_id] = provider_conf['tenant_id']
+ env['omniauth.strategy'].options[:sign_in_policy] = provider_conf['sign_in_policy']
env['omniauth.strategy'].options[:name] = 'customazureactivedirectory'
end
+OKTA_SETUP_PROC = lambda do |env|
+ settings = ApplicationSettings.instance
+ provider_conf = settings.values['okta']
+ raise StandardError, 'No Okta config available for sign in' if provider_conf.blank?
+
+ oauth2_base_url =
+ if provider_conf['auth_server_id'].blank?
+ "https://#{provider_conf['domain']}/oauth2"
+ else
+ "https://#{provider_conf['domain']}/oauth2/#{provider_conf['auth_server_id']}"
+ end
+
+ client_options = {
+ site: "https://#{provider_conf['domain']}",
+ authorize_url: "#{oauth2_base_url}/v1/authorize",
+ token_url: "#{oauth2_base_url}/v1/token",
+ user_info_url: "#{oauth2_base_url}/v1/userinfo"
+ }
+ client_options[:audience] = provider_conf['audience'] if provider_conf['audience'].present?
+ if provider_conf['auth_server_id'].present?
+ client_options[:authorization_server] = provider_conf['auth_server_id']
+ client_options[:use_org_auth_server] = false
+ else
+ client_options[:use_org_auth_server] = true
+ end
+
+ env['omniauth.strategy'].options[:client_id] = provider_conf['client_id']
+ env['omniauth.strategy'].options[:client_secret] = provider_conf['client_secret']
+ env['omniauth.strategy'].options[:client_options] = client_options
+end
+
Rails.application.config.middleware.use OmniAuth::Builder do
provider OmniAuth::Strategies::CustomAzureActiveDirectory, setup: AZURE_SETUP_PROC
end
+Rails.application.config.middleware.use OmniAuth::Builder do
+ provider OmniAuth::Strategies::Okta, setup: OKTA_SETUP_PROC
+end
+
OmniAuth.config.logger = Rails.logger
diff --git a/config/locales/en.yml b/config/locales/en.yml
index ea90f2033..46ccf3a37 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -33,6 +33,7 @@ en:
password_placeholder: "Enter password"
remember_me: "Remember me"
submit: "Log in"
+ azure_ad_submit: "Sign in with Azure AD"
2fa:
title: "Two-factor authentication"
description: "Enter the one-time code found in your authenticator app to log in to SciNote."