-
Notifications
You must be signed in to change notification settings - Fork 162
Setting Up with Multiple Providers
To setup multiple providers start by following the getting started guide. The following is a greatly reduced adaption of our implementation. Okta and Onelogin samples are included at the end of the wiki.
gem 'devise_saml_authenticatable'
bundle install
devise :saml_authenticatable, :trackable
This wiki is assuming your devise model is called User
require 'id_p_settings_adapter'
Devise.setup do |config|
config.saml_create_user = true
config.saml_update_user = true
config.saml_default_user_key = :email
config.saml_session_index_key = :session_index
config.saml_use_subject = true
config.idp_settings_adapter = IdPSettingsAdapter
config.saml_configure do |settings|
settings.assertion_consumer_service_url = ""
settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
settings.name_identifier_format = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
settings.issuer = ""
settings.authn_context = ""
settings.idp_slo_target_url = ""
settings.idp_sso_target_url = ""
settings.idp_cert_fingerprint = ''
settings.idp_cert_fingerprint_algorithm = 'http://www.w3.org/2000/09/xmldsig#sha256'
endNote the idp_settings_adapter statement. We will come back to this soon.
We are providing a few defaults here for your users. It is helpful to document these and show them to your users. It can help them setup their provider.
"email": "email"
"lastName": "last_name"
"firstName": "first_name"Again, you will need to document and tell your users to add email as an attribute or parameter depending on their setup. Other attributes are optional. Our implementation requires first and last name validation so we require it from our customer's provider.
For allowing users to self service their own IDP we use a tenancy strategy, but you can do this however you want. We have a IdPSetup model with the attributes:
"id", "assertion_consumer_service_url", "assertion_consumer_service_binding", "name_identifier_format", "issuer", "idp_entitiy_id", "authn_context", "idp_slo_target_url", "idp_sso_target_url", "idp_cert_fingerprint", "organization_id", "created_at", "updated_at", "idp_cert_fingerprint_algorithm", "idp_entity_id"
This model belongs to an organization. We reference the organization later and use these attributes to override the settings in the devise.rb initializer. For a bare bones setup you must require: idp_slo_target_url idp_cert_fingerprint idp_entity_id # This is used to find the idp_setup and organization when the provider sends the user back. Entity ID, or SAML Issuer ID would go here to be referenced later.
Create a file called id_p_settings_adapter.rb in your lib directory.
class IdPSettingsAdapter
def self.settings(idp_entity_id)
# Find your settings. Here we identify our tenant (Organization class)
tenant = IdpSetup.find_by_idp_entity_id(idp_entity_id).organization
# Urls below are for a development enviroment
if tenant.present?
{
assertion_consumer_service_url: "http://#{tenant.short_name.parameterize}.localhost:3000/auth",
assertion_consumer_service_binding: tenant.idp_setup.assertion_consumer_service_binding.present? ? tenant.idp_setup.assertion_consumer_service_binding : "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
name_identifier_format: tenant.idp_setup.name_identifier_format.present? ? tenant.idp_setup.name_identifier_format : "urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
issuer: "http://#{tenant.short_name.parameterize}.localhost:3000/metadata",
authn_context: "",
idp_slo_target_url: "",
idp_sso_target_url: tenant.idp_setup.idp_sso_target_url,
idp_cert_fingerprint: tenant.idp_setup.idp_cert_fingerprint,
idp_cert_fingerprint_algorithm: tenant.idp_setup.idp_cert_fingerprint_algorithm.present? ? tenant.idp_setup.idp_cert_fingerprint_algorithm : 'http://www.w3.org/2000/09/xmldsig#sha256'
}
else
{}
end
end
endIn the above example you must update your assertion_consumer_service_url & issuer for your production environment.
In the routes we will override much of the default behavior and enable the use of saml_sessions controller. That controller code is below.
...
devise_for :users, skip: [:registrations, :saml_authenticatable], controllers: { sessions: 'user/sessions' }
as :user do
get 'users/edit' => 'devise_registrations#edit', as: 'edit_registration'
patch 'users' => 'devise_registrations#update', as: 'registration'
# To avoid conflict saml_authenticatable routes are manually defined here.
# Also we override the controller.
resource :session, as: 'saml_session', only: [], controller: 'saml_sessions', path: '/' do
get :new, path: 'sign_in', as: 'new'
match :destroy, path: 'sign_out', as: 'destroy', via: :get
post :create, path: 'auth'
get :metadata, path: 'metadata'
match :idp_sign_out, path: 'idp_sign_out', via: [:get, :post]
end
end
...In the saml_sessions controller we will define the idp_setup that will be sent to the idp_settings_adapter for use. We also use the :store_winning_strategy provided in one of the issue resolutions. In the create session you can add attributes specific to your application.
class SamlSessionsController < Devise::SamlSessionsController
after_filter :store_winning_strategy, only: :create
def new
request = OneLogin::RubySaml::Authrequest.new
action = request.create(saml_config(current_tenant.idp_setup.idp_entity_id))
redirect_to action
end
def create
if current_user.type.blank?
current_user.type = "SampleUser"
current_user.save
end
super
end
private
def store_winning_strategy
warden.session(:user)[:strategy] = warden.winning_strategies[:user].class.name.demodulize.underscore.to_sym
end
endThe idp_setup
The okta setup
The idp_setup
The One Login Setup




