Add support of Okta SSO provider [SCI-6184]

This commit is contained in:
Oleksii Kriuchykhin 2021-11-02 16:19:56 +01:00
parent 6dea5f17ff
commit 47b8347ce6
9 changed files with 94 additions and 4 deletions

View file

@ -25,6 +25,7 @@ gem 'doorkeeper', '>= 4.6'
gem 'omniauth' gem 'omniauth'
gem 'omniauth-azure-activedirectory' gem 'omniauth-azure-activedirectory'
gem 'omniauth-linkedin-oauth2' gem 'omniauth-linkedin-oauth2'
gem 'omniauth-okta'
# TODO: remove this when omniauth gem resolves CVE issues # TODO: remove this when omniauth gem resolves CVE issues
# Prevents CVE-2015-9284 (https://github.com/omniauth/omniauth/wiki/FAQ#cve-2015-9284-warnings) # Prevents CVE-2015-9284 (https://github.com/omniauth/omniauth/wiki/FAQ#cve-2015-9284-warnings)

View file

@ -407,6 +407,9 @@ GEM
omniauth-oauth2 (1.7.1) omniauth-oauth2 (1.7.1)
oauth2 (~> 1.4) oauth2 (~> 1.4)
omniauth (>= 1.9, < 3) 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-rails_csrf_protection (0.1.2)
actionpack (>= 4.2) actionpack (>= 4.2)
omniauth (>= 1.3.1) omniauth (>= 1.3.1)
@ -692,6 +695,7 @@ DEPENDENCIES
omniauth omniauth
omniauth-azure-activedirectory omniauth-azure-activedirectory
omniauth-linkedin-oauth2 omniauth-linkedin-oauth2
omniauth-okta
omniauth-rails_csrf_protection (~> 0.1) omniauth-rails_csrf_protection (~> 0.1)
overcommit overcommit
pg (~> 1.1) pg (~> 1.1)

View file

@ -401,6 +401,16 @@ a[data-toggle="tooltip"] {
} }
} }
.okta-sign-in-actions {
margin-bottom: 10px;
margin-top: 10px;
.btn-okta {
background-color: #00297a;
color: $color-white;
}
}
.navbar-secondary { .navbar-secondary {
transition: .4s $timing-function-sharp; transition: .4s $timing-function-sharp;
} }

View file

@ -127,11 +127,50 @@ module Users
redirect_to after_omniauth_failure_path_for(resource_name) and return redirect_to after_omniauth_failure_path_for(resource_name) and return
end end
# Confirm user # Confirm user
@user.update_column(:confirmed_at, @user.created_at) @user.update!(confirmed_at: @user.created_at)
redirect_to users_sign_up_provider_path(user: @user) redirect_to users_sign_up_provider_path(user: @user)
end end
end end
def okta
auth = request.env['omniauth.auth']
user = User.from_omniauth(auth)
# User found in database so just signing in
return sign_in_and_redirect(user) if user.present?
user = User.find_by(email: auth.info.email.downcase)
if user.blank?
# Create new user and identity
user = User.new(full_name: auth.info.name,
initials: generate_initials(auth.info.name),
email: auth.info.email,
password: generate_user_password)
User.transaction do
user.save!
user.user_identities.create!(provider: auth.provider, uid: auth.uid)
user.update!(confirmed_at: user.created_at)
end
else
# Link to existing local account
user.user_identities.create!(provider: auth.provider, uid: auth.uid)
user.update!(confirmed_at: user.created_at) if user.confirmed_at.blank?
end
sign_in_and_redirect(user)
rescue StandardError => e
Rails.logger.error e.message
Rails.logger.error e.backtrace.join("\n")
error_message = I18n.t('devise.okta.errors.failed_to_save') if e.is_a?(ActiveRecord::RecordInvalid)
error_message ||= I18n.t('devise.okta.errors.generic')
redirect_to after_omniauth_failure_path_for(resource_name)
ensure
if error_message
set_flash_message(:alert, :failure, kind: I18n.t('devise.okta.provider_name'), reason: error_message)
else
set_flash_message(:notice, :success, kind: I18n.t('devise.okta.provider_name'))
end
end
# More info at: # More info at:
# https://github.com/plataformatec/devise#omniauth # https://github.com/plataformatec/devise#omniauth

View file

@ -25,6 +25,14 @@
<div data-hook="omniauth-sign-in-links"></div> <div data-hook="omniauth-sign-in-links"></div>
<% end -%> <% end -%>
<%- if devise_mapping.omniauthable? && Devise.omniauth_configs[:okta].present? %>
<div class="okta-sign-in-actions">
<%= form_tag omniauth_authorize_path(resource_name, :okta), method: :post do %>
<%= submit_tag t('devise.okta.sign_in_label'), class: 'btn btn-okta' %>
<% end %>
</div>
<% end %>
<%- unless defined?(linkedin_skip) %> <%- unless defined?(linkedin_skip) %>
<%- if Rails.configuration.x.enable_user_registration && Rails.configuration.x.linkedin_signin_enabled && @oauth_authorize != true %> <%- if Rails.configuration.x.enable_user_registration && Rails.configuration.x.linkedin_signin_enabled && @oauth_authorize != true %>
<%= render partial: "users/shared/linkedin_sign_in_links", locals: { resource_name: resource_name } %> <%= render partial: "users/shared/linkedin_sign_in_links", locals: { resource_name: resource_name } %>

View file

@ -295,6 +295,23 @@ Devise.setup do |config|
config.omniauth :linkedin, ENV['LINKEDIN_KEY'], ENV['LINKEDIN_SECRET'], scope: 'r_liteprofile r_emailaddress' config.omniauth :linkedin, ENV['LINKEDIN_KEY'], ENV['LINKEDIN_SECRET'], scope: 'r_liteprofile r_emailaddress'
end end
if [ENV['OKTA_CLIENT_ID'], ENV['OKTA_CLIENT_SECRET'], ENV['OKTA_DOMAIN'], ENV['OKTA_AUTH_SERVER_ID']].all?
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"
},
strategy_class: OmniAuth::Strategies::Okta
)
end
# ==> Warden configuration # ==> Warden configuration
# If you want to use other strategies, that are not supported by Devise, or # 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. # change the failure app, you can configure them inside the config.warden block.

View file

@ -1,6 +1,8 @@
# Extends class holds the arrays for the models enum fields # Extends class holds the arrays for the models enum fields
# so that can be extended in sub modules. # so that can be extended in sub modules.
# rubocop:disable Style/MutableConstant
class Extends class Extends
# To extend the enum fields in the engine you have to put in # To extend the enum fields in the engine you have to put in
# lib/engine_name/engine.rb file as in the example: # lib/engine_name/engine.rb file as in the example:
@ -109,7 +111,7 @@ class Extends
'RepositoryAssetValue' => 'file', 'RepositoryAssetValue' => 'file',
'RepositoryStatusValue' => 'status' } 'RepositoryStatusValue' => 'status' }
OMNIAUTH_PROVIDERS = [:linkedin, :customazureactivedirectory] OMNIAUTH_PROVIDERS = %i(linkedin customazureactivedirectory okta)
INITIAL_USER_OPTIONS = {} INITIAL_USER_OPTIONS = {}
@ -395,3 +397,5 @@ class Extends
change_user_role_on_my_module change_user_role_on_my_module
) )
end end
# rubocop:enable Style/MutableConstant

View file

@ -2,7 +2,7 @@
require 'omniauth/strategies/custom_azure_active_directory' require 'omniauth/strategies/custom_azure_active_directory'
SETUP_PROC = lambda do |env| AZURE_SETUP_PROC = lambda do |env|
providers = Rails.configuration.x.azure_ad_apps.select { |_, v| v[:enable_sign_in] == true } providers = Rails.configuration.x.azure_ad_apps.select { |_, v| v[:enable_sign_in] == true }
raise StandardError, 'No Azure AD config available for sign in' if providers.blank? raise StandardError, 'No Azure AD config available for sign in' if providers.blank?
@ -31,7 +31,7 @@ SETUP_PROC = lambda do |env|
end end
Rails.application.config.middleware.use OmniAuth::Builder do Rails.application.config.middleware.use OmniAuth::Builder do
provider OmniAuth::Strategies::CustomAzureActiveDirectory, setup: SETUP_PROC provider OmniAuth::Strategies::CustomAzureActiveDirectory, setup: AZURE_SETUP_PROC
end end
OmniAuth.config.logger = Rails.logger OmniAuth.config.logger = Rails.logger

View file

@ -74,6 +74,13 @@ en:
no_local_user_map: "No local user record found" no_local_user_map: "No local user record found"
no_email: "Email is missing in auth token" no_email: "Email is missing in auth token"
failed_to_save: "Failed to create new user" failed_to_save: "Failed to create new user"
okta:
provider_name: "Okta"
sign_in_label: "Sign in with Okta"
errors:
generic: "Failed to sign in user"
no_local_user_map: "No local user record found"
failed_to_save: "Failed to create new user"
doorkeeper: doorkeeper:
errors: errors: