class OmniAuth::Strategies::Signicat

OmniAuth Strategy for authenticating with Signicat based identity solutions

Constants

XML_DS_NS

Public Class Methods

inherited(subclass) click to toggle source
# File lib/omniauth/strategies/signicat.rb, line 14
def self.inherited(subclass)
  OmniAuth::Strategy.included(subclass)
end

Public Instance Methods

callback_phase() click to toggle source
Calls superclass method
# File lib/omniauth/strategies/signicat.rb, line 30
def callback_phase
  unless request.params['SAMLResponse']
    raise OmniAuth::Strategies::Signicat::ValidationError, 'SAML response missing'
  end

  saml_response = Base64.decode64(request.params['SAMLResponse'])
  xml = Nokogiri.parse(saml_response)
  verify_signature!(xml)
  assign_attributes(xml)

  super
rescue OmniAuth::Strategies::Signicat::ValidationError
  fail!(:invalid_ticket, $ERROR_INFO)
end
request_phase() click to toggle source
# File lib/omniauth/strategies/signicat.rb, line 26
def request_phase
  redirect(target_url)
end
target_url() click to toggle source
# File lib/omniauth/strategies/signicat.rb, line 45
def target_url
  [
    "https://#{options[:env]}.signicat.com",
    "/std/method/#{options[:service]}",
    "?id=#{options[:method]}:#{options[:profile]}:#{options[:language]}",
    "&#{prefilled_query_params}",
    "&target=#{CGI.escape(callback_url)}"
  ].join('')
end

Private Instance Methods

assign_attributes(xml) click to toggle source
# File lib/omniauth/strategies/signicat.rb, line 112
def assign_attributes(xml)
  @attributes = {}
  xml.xpath('//saml:Attribute').each do |attr_node|
    key   = attr_node['AttributeName']
    value = attr_node.text
    @attributes[key] = value
  end
  @name_id = @attributes['unique-id']
end
expected_cert_subject() click to toggle source
# File lib/omniauth/strategies/signicat.rb, line 122
def expected_cert_subject
  if options[:env] == 'id'
    '/C=NO/ST=Norway/L=Trondheim/O=Signicat/OU=Signicat/CN=id.signicat.com/std'
  else
    '/C=NO/ST=Norway/L=Trondheim/O=Signicat/OU=Signicat/CN=test.signicat.com/std'
  end
end
extract_public_key(xml) click to toggle source
# File lib/omniauth/strategies/signicat.rb, line 90
def extract_public_key(xml)
  raw_cert = xml.xpath('//ds:X509Certificate/text()', 'ds' => XML_DS_NS).text
  cert = OpenSSL::X509::Certificate.new(Base64.decode64(raw_cert))
  if cert.subject.to_s != expected_cert_subject
    raise OmniAuth::Strategies::Signicat::ValidationError, 'Invalid certificate'
  end
  cert.public_key
end
extract_signature(xml) click to toggle source
# File lib/omniauth/strategies/signicat.rb, line 107
def extract_signature(xml)
  raw_signature = xml.xpath('//ds:SignatureValue', 'ds' => XML_DS_NS).text
  Base64.decode64(raw_signature)
end
extract_signed_info(xml) click to toggle source
# File lib/omniauth/strategies/signicat.rb, line 99
def extract_signed_info(xml)
  noko_sig_element = xml.at_xpath('//ds:Signature', 'ds' => XML_DS_NS)
  noko_signed_info_element = noko_sig_element.at_xpath('./ds:SignedInfo', 'ds' => XML_DS_NS)

  canon_algorithm = Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
  noko_signed_info_element.canonicalize(canon_algorithm)
end
prefilled_query_params() click to toggle source
# File lib/omniauth/strategies/signicat.rb, line 69
def prefilled_query_params
  options.fetch(:prefilled, {}).map do |key, value|
    "prefilled.#{key}=#{value}"
  end.join('&')
end
verify_signature!(xml) click to toggle source
# File lib/omniauth/strategies/signicat.rb, line 75
def verify_signature!(xml)
  key = extract_public_key(xml)
  begin
    signed_info = extract_signed_info(xml)
    signature = extract_signature(xml)
    return if key.verify(OpenSSL::Digest::SHA1.new, signature, signed_info)

    raise OmniAuth::Strategies::Signicat::ValidationError, 'Invalid signature (SHA1)'
  rescue OmniAuth::Strategies::Signicat::ValidationError
    return if key.verify(OpenSSL::Digest::SHA256.new, signature, signed_info)

    raise OmniAuth::Strategies::Signicat::ValidationError, 'Invalid signature (SHA256)'
  end
end