omniauth-shibboleth-redux-2.0.0/ 0000755 0001750 0001750 00000000000 14625431717 020601 5 ustar weepingclown weepingclown omniauth-shibboleth-redux-2.0.0/omniauth-shibboleth.gemspec 0000644 0001750 0001750 00000002003 14625431717 026106 0 ustar weepingclown weepingclown # -*- encoding: utf-8 -*-
require File.expand_path('../lib/omniauth-shibboleth/version', __FILE__)
Gem::Specification.new do |gem|
gem.add_dependency 'omniauth', '>= 2.0.0'
gem.add_development_dependency 'rack-test'
gem.add_development_dependency 'rack-session'
gem.add_development_dependency 'rake'
gem.add_development_dependency 'rspec', '>= 2.8'
gem.license = 'MIT'
gem.authors = ["Bobby McDonald", "Toyokazu Akiyama"]
gem.email = ["bobbymcwho@gmail.com", "toyokazu@gmail.com"]
gem.description = %q{OmniAuth Shibboleth strategies for OmniAuth 2.x}
gem.summary = %q{OmniAuth Shibboleth strategies for OmniAuth 2.x}
gem.homepage = ""
gem.files = `find . -not \\( -regex ".*\\.git.*" -o -regex "\\./pkg.*" -o -regex "\\./spec.*" \\)`.split("\n").map{ |f| f.gsub(/^.\//, '') }
gem.test_files = `find spec/*`.split("\n")
gem.name = "omniauth-shibboleth-redux"
gem.require_paths = ["lib"]
gem.version = OmniAuth::Shibboleth::VERSION
end
omniauth-shibboleth-redux-2.0.0/Rakefile 0000644 0001750 0001750 00000000213 14625431717 022242 0 ustar weepingclown weepingclown require 'bundler'
Bundler::GemHelper.install_tasks
require "rspec/core/rake_task"
RSpec::Core::RakeTask.new("spec")
task :default => :spec
omniauth-shibboleth-redux-2.0.0/spec/ 0000755 0001750 0001750 00000000000 14625431717 021533 5 ustar weepingclown weepingclown omniauth-shibboleth-redux-2.0.0/spec/spec_helper.rb 0000644 0001750 0001750 00000000320 14625431717 024344 0 ustar weepingclown weepingclown require 'rspec'
require 'rack/test'
require 'omniauth'
require 'omniauth/version'
require 'omniauth-shibboleth'
RSpec.configure do |config|
config.include Rack::Test::Methods
config.color = true
end
omniauth-shibboleth-redux-2.0.0/spec/omniauth/ 0000755 0001750 0001750 00000000000 14625431717 023357 5 ustar weepingclown weepingclown omniauth-shibboleth-redux-2.0.0/spec/omniauth/strategies/ 0000755 0001750 0001750 00000000000 14625431717 025531 5 ustar weepingclown weepingclown omniauth-shibboleth-redux-2.0.0/spec/omniauth/strategies/shibboleth_spec.rb 0000644 0001750 0001750 00000036576 14625431717 031234 0 ustar weepingclown weepingclown #require 'pry-byebug'
require 'spec_helper'
require 'rack/session'
def make_env(path = '/auth/shibboleth', props = {})
{
'REQUEST_METHOD' => 'GET',
'PATH_INFO' => path,
'rack.session' => {},
'rack.input' => StringIO.new('test=true')
}.merge(props)
end
def without_session_failure_path
"/auth/failure?message=no_shibboleth_session&strategy=shibboleth"
end
def empty_uid_failure_path
"/auth/failure?message=empty_uid&strategy=shibboleth"
end
describe OmniAuth::Strategies::Shibboleth do
let(:app){ Rack::Builder.new do |b|
b.use Rack::Session::Cookie, {:secret => SecureRandom.hex(64) }
b.use OmniAuth::Strategies::Shibboleth
b.run lambda{|env| [200, {}, ['Not Found']]}
end.to_app }
before do
OmniAuth.config.request_validation_phase = ->(_env) { true }
end
context 'request phase' do
before do
post '/auth/shibboleth'
end
it 'is expected to redirect to callback_url' do
expect(last_response.status).to eq(302)
expect(last_response.location).to eq('/auth/shibboleth/callback')
end
end
context 'callback phase' do
context 'without Shibboleth session' do
before do
get '/auth/shibboleth/callback'
end
it 'is expected to fail to get Shib-Session-ID environment variable' do
expect(last_response.status).to eq(302)
expect(last_response.location).to eq(without_session_failure_path)
end
end
context 'with Shibboleth session' do
let(:strategy){ OmniAuth::Strategies::Shibboleth.new(app, {}) }
it 'is expected to set default omniauth.auth fields' do
@dummy_id = 'abcdefg'
@eppn = 'test@example.com'
@display_name = 'Test User'
env = make_env('/auth/shibboleth/callback', 'Shib-Session-ID' => @dummy_id, 'eppn' => @eppn, 'displayName' => @display_name)
strategy.call!(env)
expect(strategy.env['omniauth.auth']['uid']).to eq(@eppn)
expect(strategy.env['omniauth.auth']['info']['name']).to eq(@display_name)
end
end
context 'with Shibboleth session and attribute options' do
let(:options){ {
:shib_session_id_field => 'Shib-Session-ID',
:shib_application_id_field => 'Shib-Application-ID',
:uid_field => :uid,
:name_field => :sn,
:info_fields => {},
:extra_fields => [:o, :affiliation] } }
let(:app){ lambda{|env| [404, {}, ['Not Found']]}}
let(:strategy){ OmniAuth::Strategies::Shibboleth.new(app, options) }
it 'is expected to set specified omniauth.auth fields' do
@dummy_id = 'abcdefg'
@uid = 'test'
@sn = 'User'
@organization = 'Test Corporation'
@affiliation = 'faculty'
env = make_env('/auth/shibboleth/callback', 'Shib-Session-ID' => @dummy_id, 'uid' => @uid, 'sn' => @sn, 'o' => @organization, 'affiliation' => @affiliation)
strategy.call!(env)
expect(strategy.env['omniauth.auth']['uid']).to eq(@uid)
expect(strategy.env['omniauth.auth']['extra']['raw_info']['o']).to eq(@organization)
expect(strategy.env['omniauth.auth']['extra']['raw_info']['affiliation']).to eq(@affiliation)
end
end
context 'with debug options' do
let(:options) { { :debug => true } }
let(:app){ lambda{|env| [404, {}, ['Not Found']]}}
let(:strategy){ OmniAuth::Strategies::Shibboleth.new(app, options) }
it 'is expected to raise environment variables' do
@dummy_id = 'abcdefg'
@eppn = 'test@example.com'
@display_name = 'Test User'
env = make_env('/auth/shibboleth/callback', 'Shib-Session-ID' => @dummy_id, 'eppn' => @eppn, 'displayName' => @display_name)
response = strategy.call!(env)
expect(response[0]).to eq(200)
end
end
context 'with request_type = :header' do
let(:options){ {
:request_type => :header,
:shib_session_id_field => 'Shib-Session-ID',
:shib_application_id_field => 'Shib-Application-ID',
:uid_field => :uid,
:name_field => :displayName,
:info_fields => {},
:extra_fields => [:o, :affiliation] } }
let(:app){ lambda{|env| [200, {}, ['OK']]}}
let(:strategy){ OmniAuth::Strategies::Shibboleth.new(app, options) }
it 'is expected to handle header variables' do
@dummy_id = 'abcdefg'
@display_name = 'Test User'
@uid = 'test'
@organization = 'Test Corporation'
@affiliation = 'faculty'
env = make_env('/auth/shibboleth/callback', 'HTTP_SHIB_SESSION_ID' => @dummy_id, 'HTTP_DISPLAYNAME' => @display_name, 'HTTP_UID' => @uid, 'HTTP_O' => @organization, 'HTTP_AFFILIATION' => @affiliation)
strategy.call!(env)
expect(strategy.env['omniauth.auth']['uid']).to eq(@uid)
expect(strategy.env['omniauth.auth']['info']['name']).to eq(@display_name)
expect(strategy.env['omniauth.auth']['extra']['raw_info']['o']).to eq(@organization)
expect(strategy.env['omniauth.auth']['extra']['raw_info']['affiliation']).to eq(@affiliation)
end
end
context "with request_type = 'header'" do
let(:options){ {
:request_type => 'header',
:shib_session_id_field => 'Shib-Session-ID',
:shib_application_id_field => 'Shib-Application-ID',
:uid_field => :uid,
:name_field => :displayName,
:info_fields => {},
:extra_fields => [:o, :affiliation] } }
let(:app){ lambda{|env| [200, {}, ['OK']]}}
let(:strategy){ OmniAuth::Strategies::Shibboleth.new(app, options) }
it 'is expected to handle header variables' do
@dummy_id = 'abcdefg'
@display_name = 'Test User'
@uid = 'test'
@organization = 'Test Corporation'
@affiliation = 'faculty'
env = make_env('/auth/shibboleth/callback', 'HTTP_SHIB_SESSION_ID' => @dummy_id, 'HTTP_DISPLAYNAME' => @display_name, 'HTTP_UID' => @uid, 'HTTP_O' => @organization, 'HTTP_AFFILIATION' => @affiliation)
strategy.call!(env)
expect(strategy.env['omniauth.auth']['uid']).to eq(@uid)
expect(strategy.env['omniauth.auth']['info']['name']).to eq(@display_name)
expect(strategy.env['omniauth.auth']['extra']['raw_info']['o']).to eq(@organization)
expect(strategy.env['omniauth.auth']['extra']['raw_info']['affiliation']).to eq(@affiliation)
end
end
context 'with request_type = :params' do
let(:options){ {
:request_type => :params,
:shib_session_id_field => 'Shib-Session-ID',
:shib_application_id_field => 'Shib-Application-ID',
:uid_field => :uid,
:name_field => :displayName,
:info_fields => {},
:extra_fields => [:o, :affiliation] } }
let(:app){ lambda{|env| [200, {}, ['OK']]}}
let(:strategy){ OmniAuth::Strategies::Shibboleth.new(app, options) }
it 'is expected to handle params variables' do
@dummy_id = 'abcdefg'
@display_name = 'Test User'
@uid = 'test'
@organization = 'Test Corporation'
@affiliation = 'faculty'
env = make_env('/auth/shibboleth/callback', 'QUERY_STRING' => "Shib-Session-ID=#{@dummy_id}&uid=#{@uid}&displayName=#{@display_name}&o=#{@organization}&affiliation=#{@affiliation}")
strategy.call!(env)
expect(strategy.env['omniauth.auth']['uid']).to eq(@uid)
expect(strategy.env['omniauth.auth']['info']['name']).to eq(@display_name)
expect(strategy.env['omniauth.auth']['extra']['raw_info']['o']).to eq(@organization)
expect(strategy.env['omniauth.auth']['extra']['raw_info']['affiliation']).to eq(@affiliation)
end
end
context 'with Proc option' do
let(:options){ {
:request_type => :env,
:shib_session_id_field => 'Shib-Session-ID',
:shib_application_id_field => 'Shib-Application-ID',
:uid_field => lambda {|request_param| request_param.call('eppn') || request_param.call('mail')},
:name_field => lambda {|request_param| "#{request_param.call('cn')} #{request_param.call('sn')}"},
:info_fields => {:affiliation => lambda {|request_param| "#{request_param.call('affiliation')}@my.localdomain" }},
:extra_fields => [:o, :affiliation] } }
let(:app){ lambda{|env| [200, {}, ['OK']]}}
let(:strategy){ OmniAuth::Strategies::Shibboleth.new(app, options) }
it 'is expected to have eppn as uid and cn + sn as name field.' do
@dummy_id = 'abcdefg'
@display_name = 'Test User'
@uid = 'test'
@eppn = 'test@my.localdomain'
@cn = 'Test'
@sn = 'User'
@organization = 'Test Corporation'
@affiliation = 'faculty'
env = make_env('/auth/shibboleth/callback', 'Shib-Session-ID' => @dummy_id, 'uid' => @uid, 'eppn' => @eppn, 'cn' => @cn, 'sn' => @sn, 'o' => @organization, 'affiliation' => @affiliation)
strategy.call!(env)
expect(strategy.env['omniauth.auth']['uid']).to eq(@eppn)
expect(strategy.env['omniauth.auth']['info']['name']).to eq("#{@cn} #{@sn}")
expect(strategy.env['omniauth.auth']['info']['affiliation']).to eq("#{@affiliation}@my.localdomain")
expect(strategy.env['omniauth.auth']['extra']['raw_info']['o']).to eq(@organization)
expect(strategy.env['omniauth.auth']['extra']['raw_info']['affiliation']).to eq(@affiliation)
end
let(:strategy){ OmniAuth::Strategies::Shibboleth.new(app, options) }
it 'is expected to have mail as uid and cn + sn as name field.' do
@dummy_id = 'abcdefg'
@display_name = 'Test User'
@uid = 'test'
@mail = 'test@my.localdomain'
@cn = 'Test'
@sn = 'User'
@organization = 'Test Corporation'
@affiliation = 'faculty'
env = make_env('/auth/shibboleth/callback', 'Shib-Session-ID' => @dummy_id, 'uid' => @uid, 'mail' => @mail, 'cn' => @cn, 'sn' => @sn, 'o' => @organization, 'affiliation' => @affiliation)
strategy.call!(env)
expect(strategy.env['omniauth.auth']['uid']).to eq(@mail)
expect(strategy.env['omniauth.auth']['info']['name']).to eq("#{@cn} #{@sn}")
expect(strategy.env['omniauth.auth']['info']['affiliation']).to eq("#{@affiliation}@my.localdomain")
expect(strategy.env['omniauth.auth']['extra']['raw_info']['o']).to eq(@organization)
expect(strategy.env['omniauth.auth']['extra']['raw_info']['affiliation']).to eq(@affiliation)
end
end
context 'empty uid with :fail_with_empty_uid = false' do
let(:options){ {
:request_type => :env,
:fail_with_empty_uid => false,
:uid_field => :uid,
:name_field => :displayName,
:info_fields => {} } }
let(:app){ lambda{|env| [200, {}, ['OK']]}}
let(:strategy){ OmniAuth::Strategies::Shibboleth.new(app, options) }
it 'is expected to output null (empty) uid as it is' do
@dummy_id = 'abcdefg'
@display_name = 'Test User'
@uid = ''
env = make_env('/auth/shibboleth/callback', 'Shib-Session-ID' => @dummy_id, 'uid' => @uid, 'displayName' => @display_name)
strategy.call!(env)
expect(strategy.env['omniauth.auth']['uid']).to eq(@uid)
end
end
context 'empty uid with :fail_with_empty_uid = true' do
let(:options){ {
:request_type => :env,
:fail_with_empty_uid => true,
:shib_session_id_field => 'Shib-Session-ID',
:shib_application_id_field => 'Shib-Application-ID',
:uid_field => :uid,
:name_field => :displayName,
:info_fields => {} } }
let(:app){ lambda{|env| [200, {}, ['OK']]}}
let(:strategy){ OmniAuth::Strategies::Shibboleth.new(app, options) }
it 'is expected to fail because of the empty uid' do
@dummy_id = 'abcdefg'
@display_name = 'Test User'
@uid = ''
env = make_env('/auth/shibboleth/callback', 'Shib-Session-ID' => @dummy_id, 'uid' => @uid, 'displayName' => @display_name)
response = strategy.call!(env)
expect(response[0]).to eq(302)
expect(response[1]["Location"]).to eq(empty_uid_failure_path)
end
end
context 'with :multi_values => :raw' do
let(:options){ {
:request_type => :env,
:shib_session_id_field => 'Shib-Session-ID',
:shib_application_id_field => 'Shib-Application-ID',
:uid_field => :uid,
:name_field => :displayName,
:info_fields => {:email => "mail"} } }
let(:app){ lambda{|env| [200, {}, ['OK']]}}
let(:strategy){ OmniAuth::Strategies::Shibboleth.new(app, options) }
it 'is expected to return the raw value' do
@dummy_id = 'abcdefg'
@display_name = 'Test User'
@uid = 'test'
@mail = 'test2\;hoge@example.com;test1\;hoge@example.com;test3\;hoge@example.com'
env = make_env('/auth/shibboleth/callback', 'Shib-Session-ID' => @dummy_id, 'uid' => @uid, 'displayName' => @display_name, 'mail' => @mail)
strategy.call!(env)
expect(strategy.env['omniauth.auth']['uid']).to eq(@uid)
expect(strategy.env['omniauth.auth']['info']['name']).to eq(@display_name)
expect(strategy.env['omniauth.auth']['info']['email']).to eq(@mail)
end
end
context 'with :multi_values => :first' do
let(:options){ {
:multi_values => :first,
:request_type => :env,
:shib_session_id_field => 'Shib-Session-ID',
:shib_application_id_field => 'Shib-Application-ID',
:uid_field => :uid,
:name_field => :displayName,
:info_fields => {:email => "mail"} } }
let(:app){ lambda{|env| [200, {}, ['OK']]}}
let(:strategy){ OmniAuth::Strategies::Shibboleth.new(app, options) }
it 'is expected return the first value by specifying :first' do
@dummy_id = 'abcdefg'
@display_name = 'Test User'
@uid = 'test'
@mail = 'test2\;hoge@example.com;test1\;hoge@example.com;test3\;hoge@example.com'
env = make_env('/auth/shibboleth/callback', 'Shib-Session-ID' => @dummy_id, 'uid' => @uid, 'displayName' => @display_name, 'mail' => @mail)
strategy.call!(env)
expect(strategy.env['omniauth.auth']['uid']).to eq(@uid)
expect(strategy.env['omniauth.auth']['info']['name']).to eq(@display_name)
expect(strategy.env['omniauth.auth']['info']['email']).to eq('test2;hoge@example.com')
end
end
context 'with :multi_values => lambda function' do
let(:options){ {
:multi_values => "lambda {|param_value| param_value.nil? ? nil : param_value.split(/(? :env,
:shib_session_id_field => 'Shib-Session-ID',
:shib_application_id_field => 'Shib-Application-ID',
:uid_field => :uid,
:name_field => :displayName,
:info_fields => {:email => "mail"} } }
let(:app){ lambda{|env| [200, {}, ['OK']]}}
let(:strategy){ OmniAuth::Strategies::Shibboleth.new(app, options) }
it 'is expected return the processed value by specifying lambda function' do
@dummy_id = 'abcdefg'
@display_name = 'Test User'
@uid = 'test'
@mail = 'test2\;hoge@example.com;test1\;hoge@example.com;test3\;hoge@example.com'
env = make_env('/auth/shibboleth/callback', 'Shib-Session-ID' => @dummy_id, 'uid' => @uid, 'displayName' => @display_name, 'mail' => @mail)
strategy.call!(env)
expect(strategy.env['omniauth.auth']['uid']).to eq(@uid)
expect(strategy.env['omniauth.auth']['info']['name']).to eq(@display_name)
expect(strategy.env['omniauth.auth']['info']['email']).to eq('test1;hoge@example.com')
end
end
end
end
omniauth-shibboleth-redux-2.0.0/lib/ 0000755 0001750 0001750 00000000000 14625431717 021347 5 ustar weepingclown weepingclown omniauth-shibboleth-redux-2.0.0/lib/omniauth-shibboleth.rb 0000644 0001750 0001750 00000000243 14625431717 025640 0 ustar weepingclown weepingclown require "omniauth-shibboleth/version"
require "omniauth"
module OmniAuth
module Strategies
autoload :Shibboleth, 'omniauth/strategies/shibboleth'
end
end
omniauth-shibboleth-redux-2.0.0/lib/omniauth/ 0000755 0001750 0001750 00000000000 14625431717 023173 5 ustar weepingclown weepingclown omniauth-shibboleth-redux-2.0.0/lib/omniauth/strategies/ 0000755 0001750 0001750 00000000000 14625431717 025345 5 ustar weepingclown weepingclown omniauth-shibboleth-redux-2.0.0/lib/omniauth/strategies/shibboleth.rb 0000644 0001750 0001750 00000006064 14625431717 030023 0 ustar weepingclown weepingclown module OmniAuth
module Strategies
class Shibboleth
include OmniAuth::Strategy
option :shib_session_id_field, 'Shib-Session-ID'
option :shib_application_id_field, 'Shib-Application-ID'
option :uid_field, 'eppn'
option :name_field, 'displayName'
option :info_fields, {}
option :extra_fields, []
option :debug, false
option :fail_with_empty_uid, false
option :request_type, :env
option :multi_values, :raw
def request_phase
[
302,
{
'Location' => script_name + callback_path + query_string,
'Content-Type' => 'text/plain'
},
["You are being redirected to Shibboleth SP/IdP for sign-in."]
]
end
def request_params
case options.request_type
when :env, 'env', :header, 'header'
request.env
when :params, 'params'
request.params
end
end
def request_param(key)
multi_value_handler(
case options.request_type
when :env, 'env'
request.env[key]
when :header, 'header'
request.env["HTTP_#{key.upcase.gsub('-', '_')}"]
when :params, 'params'
request.params[key]
end
)
end
def multi_value_handler(param_value)
case options.multi_values
when :raw, 'raw'
param_value
when :first, 'first'
return nil if param_value.nil?
param_value.split(/(? 'text/plain'
},
["!!!!! This message is generated by omniauth-shibboleth. To remove it set :debug to false. !!!!!\n#{request_params.sort.map {|i| "#{i[0]}: #{i[1]}" }.join("\n")}"]
]
end
return fail!(:no_shibboleth_session) unless (request_param(options.shib_session_id_field.to_s) || request_param(options.shib_application_id_field.to_s))
return fail!(:empty_uid) if options.fail_with_empty_uid && option_handler(options.uid_field).empty?
super
end
def option_handler(option_field)
if option_field.class == String ||
option_field.class == Symbol
request_param(option_field.to_s)
elsif option_field.class == Proc
option_field.call(self.method(:request_param))
end
end
uid do
option_handler(options.uid_field)
end
info do
res = {
:name => option_handler(options.name_field)
}
options.info_fields.each_pair do |key, field|
res[key] = option_handler(field)
end
res
end
extra do
options.extra_fields.inject({:raw_info => {}}) do |hash, field|
hash[:raw_info][field] = request_param(field.to_s)
hash
end
end
end
end
end
omniauth-shibboleth-redux-2.0.0/lib/omniauth-shibboleth/ 0000755 0001750 0001750 00000000000 14625431717 025314 5 ustar weepingclown weepingclown omniauth-shibboleth-redux-2.0.0/lib/omniauth-shibboleth/version.rb 0000644 0001750 0001750 00000000104 14625431717 027321 0 ustar weepingclown weepingclown module OmniAuth
module Shibboleth
VERSION = "2.0.0"
end
end
omniauth-shibboleth-redux-2.0.0/Gemfile.lock 0000644 0001750 0001750 00000001676 14625431717 023035 0 ustar weepingclown weepingclown PATH
remote: .
specs:
omniauth-shibboleth-redux (2.0.0)
omniauth (>= 2.0.0)
GEM
remote: http://rubygems.org/
specs:
diff-lcs (1.5.0)
hashie (5.0.0)
omniauth (2.1.0)
hashie (>= 3.4.6)
rack (>= 2.2.3)
rack-protection
rack (3.0.0)
rack-protection (3.0.5)
rack
rack-session (2.0.0)
rack (>= 3.0.0)
rack-test (2.0.2)
rack (>= 1.3)
rake (13.0.6)
rspec (3.12.0)
rspec-core (~> 3.12.0)
rspec-expectations (~> 3.12.0)
rspec-mocks (~> 3.12.0)
rspec-core (3.12.1)
rspec-support (~> 3.12.0)
rspec-expectations (3.12.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-mocks (3.12.3)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.12.0)
rspec-support (3.12.0)
PLATFORMS
arm64-darwin-21
DEPENDENCIES
omniauth-shibboleth-redux!
rack-session
rack-test
rake
rspec (>= 2.8)
BUNDLED WITH
2.4.6
omniauth-shibboleth-redux-2.0.0/README.md 0000644 0001750 0001750 00000026272 14625431717 022071 0 ustar weepingclown weepingclown # OmniAuth Shibboleth strategy
OmniAuth Shibboleth strategy is an OmniAuth strategy for authenticating through Shibboleth (SAML). If you do not know OmniAuth, please visit OmniAuth wiki.
https://github.com/omniauth/omniauth/wiki
The detail of the authentication middleware Shibboleth is introduced in Shibboleth wiki.
https://wiki.shibboleth.net/
OmniAuth basically works as a middleware of Rack applications. It provides environment variable named 'omniauth.auth' (auth hash) after authenticating a user. The 'auth hash' includes the user's attributes. By providing user attributes in the fixed format, applications can easily implement authentication function using multiple authentication methods.
OmniAuth Shibboleth strategy uses the 'auth hash' for providing user attributes passed by Shibboleth SP. It enables developers to use Shibboleth and the other authentication methods, including local auth, together in one application.
## Getting Started
### Install
gem 'omniauth-shibboleth-redux', require: 'omniauth-shibboleth'
### Setup Shibboleth Strategy
To use OmniAuth Shibboleth strategy as a middleware in your rails application, add the following file to your rails application initializer directory.
% vi config/initializer/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
provider :shibboleth
end
% vi config/initializer/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
provider :shibboleth, {
:shib_session_id_field => "Shib-Session-ID",
:shib_application_id_field => "Shib-Application-ID",
:debug => false,
:extra_fields => [
:"unscoped-affiliation",
:entitlement
]
}
end
In the above example, 'unscoped-affiliation' and 'entitlement' attributes are additionally provided in the raw_info field. They can be referred like request.env["omniauth.auth"]["extra"]["raw_info"]["unscoped-affiliation"]. The detail of the omniauth auth hash schema is described in the following page.
https://github.com/omniauth/omniauth/wiki/Auth-Hash-Schema
'eppn' attribute is used as uid field. 'displayName' attribute is provided as request.env["omniauth.auth"]["info"]["name"].
These can be changed by :uid_field, :name_field option. You can also add any "info" fields defined in Auth-Hash-Schema by using :info_fields option.
% vi config/initializer/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
provider :shibboleth, {
:uid_field => "uid",
:name_field => "displayName",
:info_fields => {
:email => "mail",
:location => "contactAddress",
:image => "photo_url",
:phone => "contactPhone"
}
}
end
In the previous example, Shibboleth strategy does not pass any :info fields and use 'uid' attribute as uid fields.
### More flexible attribute configuration
If you need more flexible attribute definition, you can use lambda (Proc) to define your attributes. In the following example, 'uid' attribute is chosen from 'eppn' or 'mail', 'info'/'name' attribute is defined as a concatenation of 'cn' and 'sn' and 'info'/'affiliation' attribute is defined as 'affiliation'@my.localdomain. 'request_param' parameter is a method defined in OmniAuth::Shibboleth::Strategy. You can specify attribute names by downcase strings in either request_type, :env, :header and :params.
% vi config/initializer/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
provider :shibboleth, {
:uid_field => lambda {|request_param| request_param.call('eppn') || request_param.call('mail')},
:name_field => lambda {|request_param| "#{request_param.call('cn')} #{request_param.call('sn')}"},
:info_fields => {
:affiliation => lambda {|request_param| "#{request_param.call('affiliation')}@my.localdomain"},
:email => "mail",
:location => "contactAddress",
:image => "photo_url",
:phone => "contactPhone"
}
}
end
### !!!NOTICE!!! devise integration issue
When you use omniauth with devise, the omniauth configuration is applied before devise configuration and some part of the configuration overwritten by the devise's. It may not work as you assume. So thus, in that case, currently you should write your configuration only in device configuration.
config/initializers/devise.rb:
```ruby
config.omniauth :shibboleth, {:uid_field => 'eppn',
:info_fields => {:email => 'mail', :name => 'cn', :last_name => 'sn'},
:extra_fields => [:schacHomeOrganization]
}
```
The detail is discussed in the following thread.
https://github.com/plataformatec/devise/issues/2128
### How to authenticate users
In your application, simply direct users to '/auth/shibboleth' to have them sign in via your company's Shibboleth SP and IdP. '/auth/shibboleth' url simply redirect users to '/auth/shibboleth/callback', so thus you must protect '/auth/shibboleth/callback' by Shibboleth SP.
Example shibd.conf:
AuthType shibboleth
ShibRequestSetting requireSession 1
require valid-user
Shibboleth strategy just checks the existence of Shib-Session-ID or Shib-Application-ID.
If you want to use omniauth-shibboleth-redux without Apache or IIS, you can try **rack-saml**. It supports a part of Shibboleth SP functions.
https://github.com/toyokazu/rack-saml
Shibboleth strategy assumes the attributes are provided via environment variables because the use of ShibUseHeaders option may cause some problems. The details are discussed in the following page:
https://wiki.shibboleth.net/confluence/display/SHIB2/NativeSPSpoofChecking
To provide Shibboleth attributes via environment variables, we can not use proxy based approach, e.g. mod_proxy_balancer. Currently we can realize it by using Phusion Passenger as an application container. An example construction pattern is shown in presence_checker application (https://github.com/toyokazu/presence_checker/).
### :request_type option
You understand the issues using ShibUseHeaders, but and yet if you want to use the proxy based approach, you can use :request_type option. This option enables us to specify what kind of parameters are used to create 'omniauth.auth' (auth hash). This option can also be used to develop your Rails application without local IdP and SP by using :params option. The option values are:
- **:env** (default) The environment variables are used to create auth hash.
- **:header** The auth hash is created from header vaiables. In the Rack middleware, since header variables are treated as environment variables like HTTP_*, the specified variables are converted as the same as header variables, HTTP_*. This :request_type is basically used for mod_proxy_balancer approach.
- **:params** The query string or POST parameters are used to create auth hash. This :request_type is basically used for development phase. You can emulate SP function by providing parameters as query string. In this case, please do not forget to add Shib-Session-ID or Shib-Application-ID value which is used to check the session is created at SP.
The following is an example configuration.
% vi config/initializer/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
provider :shibboleth, { :request_type => :header }
end
If you use proxy based approach, please be sure to add ShibUseHeaders option in mod_shib configuration.
AuthType shibboleth
ShibRequestSetting requireSession 1
ShibUseHeaders On
require valid-user
### debug mode
When you deploy a new application, you may want to confirm the assumed attributes are correctly provided by Shibboleth SP. OmniAuth Shibboleth strategy provides a confirmation option :debug. If you set :debug true, you can see the environment variables provided at the /auth/shibboleth/callback uri.
% vi config/initializer/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
provider :shibboleth, { :debug => true }
end
### :multi_values option
If your application want to receive multiple values as one attribute, Shibboleth passes them as follows:
user2@example2.com;user1@example1.com;user3@example3.com
If your application only wants the first entry sorted by alphabetical order, you can use flexible attribute configuration as follows (since semicolons in attribute values are escaped with a backslash, escaped semicolons are skiped for splitting):
% vi config/initializer/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
provider :shibboleth, {
:info_fields => {
:email => lambda {|request_param| request_param.call('email').split(/(? :first
}
end
If you need the first attribute in alphabetical order, you can specify lambda function in String form as follows:
% vi config/initializer/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
provider :shibboleth, {
:multi_values => 'lambda {|param_value| param_value.nil? ? nil : param_value.split(/(?