Merge pull request #4582 from okriuchykhin/ok_SCI_7284

Update Omniauth, Azure and Okta strategies [SCI-7284]
This commit is contained in:
Alex Kriuchykhin 2022-12-20 10:49:28 +01:00 committed by GitHub
commit ed86368146
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 102 additions and 87 deletions

15
Gemfile
View file

@ -7,7 +7,7 @@ ruby '2.7.6'
gem 'bootsnap', require: false
gem 'bootstrap-sass', '~> 3.4.1'
gem 'bootstrap_form', '~> 2.7.0'
gem 'devise', '~> 4.7.1'
gem 'devise', '~> 4.8.1'
gem 'devise_invitable'
gem 'figaro'
gem 'pg', '~> 1.1'
@ -19,18 +19,15 @@ gem 'recaptcha', require: 'recaptcha/rails'
gem 'sanitize', '~> 6.0'
gem 'sassc-rails'
gem 'webpacker', '~> 4.0.0'
gem 'yomu', git: 'https://github.com/biosistemika/yomu', branch: 'master'
gem 'yomu', git: 'https://github.com/biosistemika/yomu', branch: 'tika_1_28_1'
# Gems for OAuth2 subsystem
gem 'doorkeeper', '>= 4.6'
gem 'omniauth'
gem 'omniauth-azure-activedirectory'
gem 'omniauth', '~> 2.1'
gem 'omniauth-azure-activedirectory-v2'
gem 'omniauth-linkedin-oauth2'
gem 'omniauth-okta'
# TODO: remove this when omniauth gem resolves CVE issues
# Prevents CVE-2015-9284 (https://github.com/omniauth/omniauth/wiki/FAQ#cve-2015-9284-warnings)
gem 'omniauth-rails_csrf_protection', '~> 0.1'
gem 'omniauth-okta', git: 'https://github.com/scinote-eln/omniauth-okta', branch: 'org_auth_server_support'
gem 'omniauth-rails_csrf_protection', '~> 1.0'
# Gems for API implementation
gem 'active_model_serializers', '~> 0.10.7'

View file

@ -39,6 +39,15 @@ GIT
devise-async (0.10.2)
devise (>= 4.0)
GIT
remote: https://github.com/scinote-eln/omniauth-okta
revision: fc4d833532dec050a3c6296691d631b64de1ee19
branch: org_auth_server_support
specs:
omniauth-okta (2.0.0)
omniauth (~> 2.0)
omniauth-oauth2 (~> 1.7, >= 1.7.1)
GEM
remote: http://rubygems.org/
specs:
@ -164,7 +173,7 @@ GEM
aws-eventstream (~> 1, >= 1.0.2)
backports (3.20.2)
base62 (1.0.0)
bcrypt (3.1.16)
bcrypt (3.1.18)
better_errors (2.9.1)
coderay (>= 1.0.0)
erubi (>= 1.0.0)
@ -256,7 +265,7 @@ GEM
delayed_job_active_record (4.1.5)
activerecord (>= 3.0, < 6.2)
delayed_job (>= 3.0, < 5)
devise (4.7.3)
devise (4.8.1)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0)
@ -273,7 +282,7 @@ GEM
railties (>= 5)
down (5.2.0)
addressable (~> 2.5)
erubi (1.10.0)
erubi (1.11.0)
et-orbi (1.2.4)
tzinfo
execjs (2.7.0)
@ -284,11 +293,10 @@ GEM
railties (>= 5.0.0)
faker (2.15.1)
i18n (>= 1.6, < 2)
faraday (1.3.0)
faraday-net_http (~> 1.0)
multipart-post (>= 1.2, < 3)
ruby2_keywords
faraday-net_http (1.0.1)
faraday (2.5.2)
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday-net_http (3.0.0)
fastimage (2.2.1)
ffi (1.15.5)
figaro (1.2.0)
@ -308,7 +316,7 @@ GEM
httparty (0.17.3)
mime-types (~> 3.0)
multi_xml (>= 0.5.2)
i18n (1.11.0)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
i18n-js (3.8.0)
i18n (>= 0.6.6)
@ -370,14 +378,13 @@ GEM
mini_magick (4.11.0)
mini_mime (1.1.2)
mini_portile2 (2.8.0)
minitest (5.16.2)
minitest (5.16.3)
momentjs-rails (2.17.1)
railties (>= 3.1)
msgpack (1.4.2)
multi_json (1.15.0)
multi_test (0.1.2)
multi_xml (0.6.0)
multipart-post (2.1.1)
nested_form_fields (0.8.4)
coffee-rails (>= 3.2.1)
jquery-rails
@ -387,29 +394,27 @@ GEM
nokogiri (1.13.10)
mini_portile2 (~> 2.8.0)
racc (~> 1.4)
oauth2 (1.4.4)
faraday (>= 0.8, < 2.0)
oauth2 (2.0.9)
faraday (>= 0.17.3, < 3.0)
jwt (>= 1.0, < 3.0)
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
omniauth (1.9.2)
rack (>= 1.2, < 4)
snaky_hash (~> 2.0)
version_gem (~> 1.1)
omniauth (2.1.0)
hashie (>= 3.4.6)
rack (>= 1.6.2, < 3)
omniauth-azure-activedirectory (1.0.0)
jwt (~> 1.5)
omniauth (~> 1.1)
rack (>= 2.2.3)
rack-protection
omniauth-azure-activedirectory-v2 (2.0.0)
omniauth-oauth2 (~> 1.8)
omniauth-linkedin-oauth2 (1.0.0)
omniauth-oauth2
omniauth-oauth2 (1.7.1)
oauth2 (~> 1.4)
omniauth (>= 1.9, < 3)
omniauth-okta (0.1.3)
omniauth (~> 1.5)
omniauth-oauth2 (>= 1.6.0, < 2.0)
omniauth-rails_csrf_protection (0.1.2)
omniauth-oauth2 (1.8.0)
oauth2 (>= 1.4, < 3)
omniauth (~> 2.0)
omniauth-rails_csrf_protection (1.0.1)
actionpack (>= 4.2)
omniauth (>= 1.3.1)
omniauth (~> 2.0)
orm_adapter (0.5.0)
overcommit (0.57.0)
childprocess (>= 0.6.3, < 5)
@ -447,6 +452,8 @@ GEM
rack (>= 1.0, < 3)
rack-cors (1.1.1)
rack (>= 2.0.0)
rack-protection (3.0.1)
rack
rack-proxy (0.6.5)
rack
rack-test (2.0.2)
@ -545,7 +552,7 @@ GEM
ruby-progressbar (1.11.0)
ruby-vips (2.1.4)
ffi (~> 1.12)
ruby2_keywords (0.0.4)
ruby2_keywords (0.0.5)
rubyzip (1.3.0)
rufus-scheduler (3.7.0)
fugit (~> 1.1, >= 1.1.6)
@ -577,6 +584,9 @@ GEM
simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3)
simplecov_json_formatter (0.1.2)
snaky_hash (2.0.1)
hashie
version_gem (~> 1.1, >= 1.1.1)
spinjs-rails (1.4)
rails (>= 3.1)
sprockets (4.1.1)
@ -596,13 +606,14 @@ GEM
turbolinks (5.1.1)
turbolinks-source (~> 5.1)
turbolinks-source (5.2.0)
tzinfo (2.0.4)
tzinfo (2.0.5)
concurrent-ruby (~> 1.0)
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
underscore-rails (1.8.3)
unicode-display_width (1.7.0)
uniform_notifier (1.13.2)
version_gem (1.1.1)
view_component (2.28.0)
activesupport (>= 5.0.0, < 7.0)
warden (1.2.9)
@ -659,7 +670,7 @@ DEPENDENCIES
database_cleaner
deface (~> 1.0)
delayed_job_active_record
devise (~> 4.7.1)
devise (~> 4.8.1)
devise-async!
devise_invitable
discard (~> 1.0)
@ -690,11 +701,11 @@ DEPENDENCIES
nested_form_fields
newrelic_rpm
nokogiri (~> 1.13.10)
omniauth
omniauth-azure-activedirectory
omniauth (~> 2.1)
omniauth-azure-activedirectory-v2
omniauth-linkedin-oauth2
omniauth-okta
omniauth-rails_csrf_protection (~> 0.1)
omniauth-okta!
omniauth-rails_csrf_protection (~> 1.0)
overcommit
pg (~> 1.1)
pg_search

View file

@ -17,7 +17,7 @@ module Users
def customazureactivedirectory
auth = request.env['omniauth.auth']
provider_id = auth.dig(:extra, :raw_info, :id_token_claims, :aud)
provider_id = auth.dig(:extra, :raw_info, :aud)
provider_conf = Rails.configuration.x.azure_ad_apps[provider_id]
raise StandardError, 'No matching Azure AD provider config found' if provider_conf.blank?

View file

@ -8,6 +8,16 @@ Rails.application.configure do
app_name = name.sub('_AZURE_AD_APP_ID', '')
config.x.azure_ad_apps[value] = {}
tenant_id = ENV["#{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
client_secret = ENV["#{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
iss = ENV["#{app_name}_AZURE_AD_ISS"]
raise StandardError, "No ISS for #{app_name} Azure app" unless iss
@ -46,6 +56,12 @@ Rails.application.configure do
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]

View file

@ -301,19 +301,32 @@ 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'], ENV['OKTA_AUTH_SERVER_ID']].all?(&:present?)
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: {
site: "https://#{ENV['OKTA_DOMAIN']}",
authorize_url: "https://#{ENV['OKTA_DOMAIN']}/oauth2/#{ENV['OKTA_AUTH_SERVER_ID']}/v1/authorize",
token_url: "https://#{ENV['OKTA_DOMAIN']}/oauth2/#{ENV['OKTA_AUTH_SERVER_ID']}/v1/token",
user_info_url: "https://#{ENV['OKTA_DOMAIN']}/oauth2/#{ENV['OKTA_AUTH_SERVER_ID']}/v1/userinfo"
},
client_options: client_options,
strategy_class: OmniAuth::Strategies::Okta
)
end

View file

@ -24,10 +24,11 @@ AZURE_SETUP_PROC = lambda do |env|
provider_id ||= providers.keys.first
provider_conf = providers[provider_id]
env['omniauth.strategy'].options[:client_id] = provider_id
env['omniauth.strategy'].options[:openid_config_url] = provider_conf[:conf_url]
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]
env['omniauth.strategy'].options[:name] = 'customazureactivedirectory'
end
Rails.application.config.middleware.use OmniAuth::Builder do

View file

@ -2,42 +2,19 @@
module OmniAuth
module Strategies
class CustomAzureActiveDirectory < AzureActiveDirectory
class CustomAzureActiveDirectory < AzureActivedirectoryV2
include OmniAuth::Strategy
option :openid_config_url
option :sign_in_policy
option :name, 'customazureactivedirectory'
# Azure doesn't allow query params in callback URL
def callback_url
full_host + script_name + callback_path
def raw_info
if @raw_info.nil?
id_token_data = ::JWT.decode(access_token.params['id_token'], nil, false).first rescue {}
auth_token_data = ::JWT.decode(access_token.token, nil, false).first rescue {}
@raw_info = auth_token_data.merge(id_token_data)
end
def openid_config_url
options[:openid_config_url]
end
def authorize_endpoint_url
uri = URI(openid_config['authorization_endpoint'])
params = {
client_id: client_id,
redirect_uri: callback_url,
response_mode: response_mode,
response_type: response_type,
nonce: new_nonce,
scope: 'openid profile email'
}
params[:p] = options[:sign_in_policy] if options[:sign_in_policy].present?
uri.query = URI.encode_www_form(params)
uri.to_s
end
def validate_and_parse_id_token(id_token)
jwt_claims, jwt_header = Api::AzureJwt.decode(id_token)
return jwt_claims, jwt_header if jwt_claims['nonce'] == read_nonce
raise JWT::DecodeError, 'Returned nonce did not match.'
@raw_info
end
end
end